Update
Sync Versions to index.json / sync-versions (push) Successful in 44s

This commit is contained in:
aka paul
2026-03-28 13:10:11 +01:00
parent 5e26b3b171
commit 98ae0a541b
2 changed files with 58 additions and 32 deletions
+56 -31
View File
@@ -26,7 +26,11 @@ function _wrapImage(url) {
} }
function _safeJsonParse(s, fallback) { function _safeJsonParse(s, fallback) {
try { return JSON.parse(s); } catch (_) { return fallback; } try {
return JSON.parse(s);
} catch (_) {
return fallback;
}
} }
function scoreTitle(title, keyword) { function scoreTitle(title, keyword) {
@@ -68,6 +72,7 @@ function _dubbingRank(name) {
function _pack(obj) { function _pack(obj) {
return "yummy:" + encodeURIComponent(JSON.stringify(obj || {})); return "yummy:" + encodeURIComponent(JSON.stringify(obj || {}));
} }
function _unpack(href) { function _unpack(href) {
const s = String(href || ""); const s = String(href || "");
if (!s.startsWith("yummy:")) return null; if (!s.startsWith("yummy:")) return null;
@@ -168,11 +173,15 @@ async function extractEpisodes(animeIdOrUrl) {
const vids = Array.isArray(json?.response) ? json.response : []; const vids = Array.isArray(json?.response) ? json.response : [];
// Keep only Kodik entries (we parse kodik) // Keep only Kodik entries (we parse Kodik)
const kodikVids = vids.filter(v => { const kodikVids = vids.filter(v => {
const iframe = String(v?.iframe_url || ""); const iframe = String(v?.iframe_url || "");
const player = String(v?.data?.player || ""); const player = String(v?.data?.player || "").toLowerCase();
return iframe.includes("kodik.info") || player.toLowerCase().includes("kodik"); return (
iframe.includes("kodik.info") ||
iframe.includes("kodikplayer.com") ||
player.includes("kodik")
);
}); });
// Group by episode number // Group by episode number
@@ -202,7 +211,6 @@ async function extractEpisodes(animeIdOrUrl) {
? { start: v.skips.ending.time, stop: v.skips.ending.time + v.skips.ending.length } ? { start: v.skips.ending.time, stop: v.skips.ending.time + v.skips.ending.length }
: undefined; : undefined;
// raw timecode format (time + length)
const skips = const skips =
(v?.skips?.opening && Number.isFinite(v.skips.opening.time) && Number.isFinite(v.skips.opening.length)) || (v?.skips?.opening && Number.isFinite(v.skips.opening.time) && Number.isFinite(v.skips.opening.length)) ||
(v?.skips?.ending && Number.isFinite(v.skips.ending.time) && Number.isFinite(v.skips.ending.length)) (v?.skips?.ending && Number.isFinite(v.skips.ending.time) && Number.isFinite(v.skips.ending.length))
@@ -230,12 +238,9 @@ async function extractEpisodes(animeIdOrUrl) {
const ep = byNum.get(num); const ep = byNum.get(num);
// Save first available skips/duration
if (!ep.opening && opening) ep.opening = opening; if (!ep.opening && opening) ep.opening = opening;
if (!ep.ending && ending) ep.ending = ending; if (!ep.ending && ending) ep.ending = ending;
if (!ep.duration && duration) ep.duration = duration; if (!ep.duration && duration) ep.duration = duration;
// keep first raw skips object if present
if (!ep.skips && skips) ep.skips = skips; if (!ep.skips && skips) ep.skips = skips;
ep.options.push({ ep.options.push({
@@ -250,7 +255,6 @@ async function extractEpisodes(animeIdOrUrl) {
const out = Array.from(byNum.values()) const out = Array.from(byNum.values())
.sort((a, b) => a.num - b.num) .sort((a, b) => a.num - b.num)
.map(ep => { .map(ep => {
// sort voiceovers
ep.options.sort((x, y) => _dubbingRank(x.dubbing) - _dubbingRank(y.dubbing)); ep.options.sort((x, y) => _dubbingRank(x.dubbing) - _dubbingRank(y.dubbing));
const payload = { const payload = {
@@ -265,21 +269,19 @@ async function extractEpisodes(animeIdOrUrl) {
title: `Episode ${ep.num}` title: `Episode ${ep.num}`
}; };
// Use skips from the first (sorted) voiceover option
const primary = ep.options[0]; const primary = ep.options[0];
if (primary?.opening) item.opening = primary.opening; if (primary?.opening) item.opening = primary.opening;
if (primary?.ending) item.ending = primary.ending; if (primary?.ending) item.ending = primary.ending;
// raw timecodes (time + length)
if (ep.skips) item.skips = ep.skips; if (ep.skips) item.skips = ep.skips;
if (ep.duration) item.duration = ep.duration; if (ep.duration) item.duration = ep.duration;
return item; return item;
}); });
return JSON.stringify(out); return JSON.stringify(out);
} catch (_) { } catch (err) {
console.log("extractEpisodes error:", err?.message || err);
return JSON.stringify([]); return JSON.stringify([]);
} }
} }
@@ -301,7 +303,12 @@ async function extractStreamUrl(href) {
for (const opt of options) { for (const opt of options) {
const iframeUrl = _absUrl(opt?.iframe_url); const iframeUrl = _absUrl(opt?.iframe_url);
if (!iframeUrl || !iframeUrl.includes("kodik.info")) continue; if (
!iframeUrl ||
(!iframeUrl.includes("kodik.info") && !iframeUrl.includes("kodikplayer.com"))
) {
continue;
}
const qualitiesJson = await kodikParser(iframeUrl); const qualitiesJson = await kodikParser(iframeUrl);
const qualities = _safeJsonParse(qualitiesJson, {}); const qualities = _safeJsonParse(qualitiesJson, {});
@@ -327,7 +334,7 @@ async function extractStreamUrl(href) {
streamUrl: finalUrl, streamUrl: finalUrl,
headers: { headers: {
"User-Agent": _ua(), "User-Agent": _ua(),
"Referer": "https://kodik.info/" "Referer": IMAGE_REFERER
} }
}); });
} }
@@ -336,7 +343,8 @@ async function extractStreamUrl(href) {
streams, streams,
subtitle: "https://none.com" subtitle: "https://none.com"
}); });
} catch (_) { } catch (err) {
console.log("extractStreamUrl error:", err?.message || err);
return JSON.stringify({ streams: [], subtitle: "https://none.com" }); return JSON.stringify({ streams: [], subtitle: "https://none.com" });
} }
} }
@@ -348,6 +356,7 @@ async function kodikParser(url) {
"Referer": IMAGE_REFERER, "Referer": IMAGE_REFERER,
"User-Agent": _ua() "User-Agent": _ua()
}; };
const response = await fetchv2(url, headers); const response = await fetchv2(url, headers);
const htmlText = await response.text(); const htmlText = await response.text();
@@ -362,23 +371,28 @@ async function kodikParser(url) {
const videoInfo_id = videoInfoIdMatch ? videoInfoIdMatch[1] : ""; const videoInfo_id = videoInfoIdMatch ? videoInfoIdMatch[1] : "";
const finalData = const finalData =
`d=${urlParams.d}` + `d=${urlParams.d || ""}` +
`&d_sign=${urlParams.d_sign}` + `&d_sign=${urlParams.d_sign || ""}` +
`&pd=${urlParams.pd}` + `&pd=${urlParams.pd || ""}` +
`&pd_sign=${urlParams.pd_sign}` + `&pd_sign=${urlParams.pd_sign || ""}` +
`&ref=${urlParams.ref}` + `&ref=${urlParams.ref || ""}` +
`&ref_sign=${urlParams.ref_sign}` + `&ref_sign=${urlParams.ref_sign || ""}` +
`&bad_user=false&cdn_is_working=false` + `&bad_user=false&cdn_is_working=true` +
`&type=${videoInfo_type}&hash=${videoInfo_hash}&id=${videoInfo_id}&info=%7B%7D`; `&type=${videoInfo_type}&hash=${videoInfo_hash}&id=${videoInfo_id}&info=%7B%7D`;
const headers2 = { const headers2 = {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Referer": "https://kodik.info", "Referer": IMAGE_REFERER,
"User-Agent": _ua(), "User-Agent": _ua(),
"X-Requested-With": "XMLHttpRequest" "X-Requested-With": "XMLHttpRequest"
}; };
const apiResponse = await fetchv2("https://kodik.info/ftor", headers2, "POST", finalData); const apiResponse = await fetchv2(
"https://kodikplayer.com/ftor",
headers2,
"POST",
finalData
);
const apiJson = await apiResponse.json(); const apiJson = await apiResponse.json();
const qualities = {}; const qualities = {};
@@ -396,7 +410,8 @@ async function kodikParser(url) {
} }
return JSON.stringify(qualities, null, 2); return JSON.stringify(qualities, null, 2);
} catch (_) { } catch (err) {
console.log("kodikParser error:", err?.message || err);
return JSON.stringify({ error: "kodik_parse_failed" }); return JSON.stringify({ error: "kodik_parse_failed" });
} }
} }
@@ -406,7 +421,6 @@ function decode(input) {
const map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let out = "", b = 0, c = 0; let out = "", b = 0, c = 0;
// ROT +18 letters
const r = []; const r = [];
for (let i = 0; i < input.length; i++) { for (let i = 0; i < input.length; i++) {
const ch = input[i]; const ch = input[i];
@@ -415,7 +429,9 @@ function decode(input) {
const max = ch <= "Z" ? 90 : 122; const max = ch <= "Z" ? 90 : 122;
const sh = cc + 18; const sh = cc + 18;
r.push(String.fromCharCode(sh <= max ? sh : sh - 26)); r.push(String.fromCharCode(sh <= max ? sh : sh - 26));
} else r.push(ch); } else {
r.push(ch);
}
} }
const rot = r.join(""); const rot = r.join("");
@@ -446,6 +462,15 @@ function _defaultExport() {
}; };
} }
try { globalThis.default = _defaultExport; } catch (_) {} try {
try { this.default = _defaultExport; } catch (_) {} globalThis.default = _defaultExport;
try { globalThis.module = globalThis.module || {}; globalThis.module.exports = { default: _defaultExport }; } catch (_) {} } catch (_) {}
try {
this.default = _defaultExport;
} catch (_) {}
try {
globalThis.module = globalThis.module || {};
globalThis.module.exports = { default: _defaultExport };
} catch (_) {}
+2 -1
View File
@@ -5,7 +5,7 @@
"name": "emp0ry", "name": "emp0ry",
"icon": "https://avatars.githubusercontent.com/u/64217088" "icon": "https://avatars.githubusercontent.com/u/64217088"
}, },
"version": "1.0.2", "version": "1.0.3",
"language": "Russian", "language": "Russian",
"streamType": "HLS", "streamType": "HLS",
"quality": "1080p", "quality": "1080p",
@@ -18,6 +18,7 @@
"type": "anime", "type": "anime",
"downloadSupport": false, "downloadSupport": false,
"supportsMojuru": true, "supportsMojuru": true,
"supportsDartotsu": true,
"supportsSora": true, "supportsSora": true,
"supportsLuna": true "supportsLuna": true
} }