diff --git a/yummyanime/yummyanime.js b/yummyanime/yummyanime.js index 9b871b5..c69025f 100644 --- a/yummyanime/yummyanime.js +++ b/yummyanime/yummyanime.js @@ -26,7 +26,11 @@ function _wrapImage(url) { } function _safeJsonParse(s, fallback) { - try { return JSON.parse(s); } catch (_) { return fallback; } + try { + return JSON.parse(s); + } catch (_) { + return fallback; + } } function scoreTitle(title, keyword) { @@ -68,6 +72,7 @@ function _dubbingRank(name) { function _pack(obj) { return "yummy:" + encodeURIComponent(JSON.stringify(obj || {})); } + function _unpack(href) { const s = String(href || ""); if (!s.startsWith("yummy:")) return null; @@ -168,11 +173,15 @@ async function extractEpisodes(animeIdOrUrl) { 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 iframe = String(v?.iframe_url || ""); - const player = String(v?.data?.player || ""); - return iframe.includes("kodik.info") || player.toLowerCase().includes("kodik"); + const player = String(v?.data?.player || "").toLowerCase(); + return ( + iframe.includes("kodik.info") || + iframe.includes("kodikplayer.com") || + player.includes("kodik") + ); }); // 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 } : undefined; - // raw timecode format (time + length) const skips = (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)) @@ -230,12 +238,9 @@ async function extractEpisodes(animeIdOrUrl) { const ep = byNum.get(num); - // Save first available skips/duration if (!ep.opening && opening) ep.opening = opening; if (!ep.ending && ending) ep.ending = ending; if (!ep.duration && duration) ep.duration = duration; - - // keep first raw skips object if present if (!ep.skips && skips) ep.skips = skips; ep.options.push({ @@ -250,7 +255,6 @@ async function extractEpisodes(animeIdOrUrl) { const out = Array.from(byNum.values()) .sort((a, b) => a.num - b.num) .map(ep => { - // sort voiceovers ep.options.sort((x, y) => _dubbingRank(x.dubbing) - _dubbingRank(y.dubbing)); const payload = { @@ -265,21 +269,19 @@ async function extractEpisodes(animeIdOrUrl) { title: `Episode ${ep.num}` }; - // Use skips from the first (sorted) voiceover option const primary = ep.options[0]; if (primary?.opening) item.opening = primary.opening; if (primary?.ending) item.ending = primary.ending; - // raw timecodes (time + length) if (ep.skips) item.skips = ep.skips; - if (ep.duration) item.duration = ep.duration; return item; }); return JSON.stringify(out); - } catch (_) { + } catch (err) { + console.log("extractEpisodes error:", err?.message || err); return JSON.stringify([]); } } @@ -301,7 +303,12 @@ async function extractStreamUrl(href) { for (const opt of options) { 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 qualities = _safeJsonParse(qualitiesJson, {}); @@ -327,7 +334,7 @@ async function extractStreamUrl(href) { streamUrl: finalUrl, headers: { "User-Agent": _ua(), - "Referer": "https://kodik.info/" + "Referer": IMAGE_REFERER } }); } @@ -336,7 +343,8 @@ async function extractStreamUrl(href) { streams, subtitle: "https://none.com" }); - } catch (_) { + } catch (err) { + console.log("extractStreamUrl error:", err?.message || err); return JSON.stringify({ streams: [], subtitle: "https://none.com" }); } } @@ -348,6 +356,7 @@ async function kodikParser(url) { "Referer": IMAGE_REFERER, "User-Agent": _ua() }; + const response = await fetchv2(url, headers); const htmlText = await response.text(); @@ -362,23 +371,28 @@ async function kodikParser(url) { const videoInfo_id = videoInfoIdMatch ? videoInfoIdMatch[1] : ""; const finalData = - `d=${urlParams.d}` + - `&d_sign=${urlParams.d_sign}` + - `&pd=${urlParams.pd}` + - `&pd_sign=${urlParams.pd_sign}` + - `&ref=${urlParams.ref}` + - `&ref_sign=${urlParams.ref_sign}` + - `&bad_user=false&cdn_is_working=false` + + `d=${urlParams.d || ""}` + + `&d_sign=${urlParams.d_sign || ""}` + + `&pd=${urlParams.pd || ""}` + + `&pd_sign=${urlParams.pd_sign || ""}` + + `&ref=${urlParams.ref || ""}` + + `&ref_sign=${urlParams.ref_sign || ""}` + + `&bad_user=false&cdn_is_working=true` + `&type=${videoInfo_type}&hash=${videoInfo_hash}&id=${videoInfo_id}&info=%7B%7D`; const headers2 = { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", - "Referer": "https://kodik.info", + "Referer": IMAGE_REFERER, "User-Agent": _ua(), "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 qualities = {}; @@ -396,7 +410,8 @@ async function kodikParser(url) { } return JSON.stringify(qualities, null, 2); - } catch (_) { + } catch (err) { + console.log("kodikParser error:", err?.message || err); return JSON.stringify({ error: "kodik_parse_failed" }); } } @@ -406,7 +421,6 @@ function decode(input) { const map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; let out = "", b = 0, c = 0; - // ROT +18 letters const r = []; for (let i = 0; i < input.length; i++) { const ch = input[i]; @@ -415,7 +429,9 @@ function decode(input) { const max = ch <= "Z" ? 90 : 122; const sh = cc + 18; r.push(String.fromCharCode(sh <= max ? sh : sh - 26)); - } else r.push(ch); + } else { + r.push(ch); + } } const rot = r.join(""); @@ -446,6 +462,15 @@ function _defaultExport() { }; } -try { globalThis.default = _defaultExport; } catch (_) {} -try { this.default = _defaultExport; } catch (_) {} -try { globalThis.module = globalThis.module || {}; globalThis.module.exports = { default: _defaultExport }; } catch (_) {} \ No newline at end of file +try { + globalThis.default = _defaultExport; +} catch (_) {} + +try { + this.default = _defaultExport; +} catch (_) {} + +try { + globalThis.module = globalThis.module || {}; + globalThis.module.exports = { default: _defaultExport }; +} catch (_) {} \ No newline at end of file diff --git a/yummyanime/yummyanime.json b/yummyanime/yummyanime.json index a023186..a4bfb8b 100644 --- a/yummyanime/yummyanime.json +++ b/yummyanime/yummyanime.json @@ -5,7 +5,7 @@ "name": "emp0ry", "icon": "https://avatars.githubusercontent.com/u/64217088" }, - "version": "1.0.2", + "version": "1.0.3", "language": "Russian", "streamType": "HLS", "quality": "1080p", @@ -18,6 +18,7 @@ "type": "anime", "downloadSupport": false, "supportsMojuru": true, + "supportsDartotsu": true, "supportsSora": true, "supportsLuna": true } \ No newline at end of file