Fix Anikoto dub episode detection
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.52.18 - 2026-08-30
|
||||||
|
|
||||||
|
- Fixed Anikoto dub availability detection when episode-list flags report sub-only episodes but the episode server list exposes dub streams.
|
||||||
|
|
||||||
## 0.52.17 - 2026-08-20
|
## 0.52.17 - 2026-08-20
|
||||||
|
|
||||||
- Fixed the Watchlist refresh-all progress fill color so the advancing bar is visible.
|
- Fixed the Watchlist refresh-all progress fill color so the advancing bar is visible.
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ Kaizoku uses additive SQLite migrations for queue and watchlist schema changes.
|
|||||||
|
|
||||||
The Anikoto, AniNeko, and AnimePahe parser modules in `providers/extensions/Anime/` are adapted from [TheYogMehta/extensions](https://github.com/TheYogMehta/extensions) and retain their GPL/license headers. The bundled anime provider versions are currently:
|
The Anikoto, AniNeko, and AnimePahe parser modules in `providers/extensions/Anime/` are adapted from [TheYogMehta/extensions](https://github.com/TheYogMehta/extensions) and retain their GPL/license headers. The bundled anime provider versions are currently:
|
||||||
|
|
||||||
- Anikoto `5.0.2`
|
- Anikoto `5.0.3`
|
||||||
- AniNeko `3.0.3`
|
- AniNeko `3.0.3`
|
||||||
- AnimePahe `4.0.1`
|
- AnimePahe `4.0.1`
|
||||||
|
|
||||||
|
|||||||
@@ -217,6 +217,46 @@ async function AnimeInfo(id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchServerTypes(dataIds) {
|
||||||
|
if (!dataIds) return new Set();
|
||||||
|
try {
|
||||||
|
const serverUrl = `${baseUrl}/ajax/server/list?servers=${dataIds}`;
|
||||||
|
const serverRes = await global.axios.get(serverUrl, {
|
||||||
|
headers: {
|
||||||
|
"X-Requested-With": "XMLHttpRequest",
|
||||||
|
},
|
||||||
|
timeout: 7000,
|
||||||
|
});
|
||||||
|
const $ = cheerio.load(serverRes.data?.result || "");
|
||||||
|
const types = new Set();
|
||||||
|
$(".servers .type[data-type], [data-type]").each((i, el) => {
|
||||||
|
const type = String($(el).attr("data-type") || "")
|
||||||
|
.trim()
|
||||||
|
.toLowerCase();
|
||||||
|
if (type) types.add(type);
|
||||||
|
});
|
||||||
|
return types;
|
||||||
|
} catch (e) {
|
||||||
|
return new Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function mapWithConcurrency(items, limit, mapper) {
|
||||||
|
const results = new Array(items.length);
|
||||||
|
let nextIndex = 0;
|
||||||
|
const workerCount = Math.min(Math.max(Number(limit) || 1, 1), items.length);
|
||||||
|
await Promise.all(
|
||||||
|
Array.from({ length: workerCount }, async () => {
|
||||||
|
while (nextIndex < items.length) {
|
||||||
|
const index = nextIndex;
|
||||||
|
nextIndex += 1;
|
||||||
|
results[index] = await mapper(items[index], index);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchEpisode(dataId, page = 1) {
|
async function fetchEpisode(dataId, page = 1) {
|
||||||
try {
|
try {
|
||||||
let numericId = dataId;
|
let numericId = dataId;
|
||||||
@@ -247,6 +287,9 @@ async function fetchEpisode(dataId, page = 1) {
|
|||||||
const $ = cheerio.load(data.result || "");
|
const $ = cheerio.load(data.result || "");
|
||||||
let episodes = [];
|
let episodes = [];
|
||||||
|
|
||||||
|
const hasDubFilter =
|
||||||
|
$('.filter.type [data-value="dub"], [data-value="dub"]').length > 0;
|
||||||
|
|
||||||
$("a[data-id][data-ids], .ep-item, li a, .ssl-item, a.item").each(
|
$("a[data-id][data-ids], .ep-item, li a, .ssl-item, a.item").each(
|
||||||
(i, el) => {
|
(i, el) => {
|
||||||
const epNum =
|
const epNum =
|
||||||
@@ -274,6 +317,21 @@ async function fetchEpisode(dataId, page = 1) {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const hasDubFlag = episodes.some((episode) =>
|
||||||
|
(episode.langs || []).includes("dub"),
|
||||||
|
);
|
||||||
|
if (hasDubFilter && episodes.length > 0 && !hasDubFlag) {
|
||||||
|
episodes = await mapWithConcurrency(episodes, 4, async (episode) => {
|
||||||
|
const dataIds = String(episode.id || "").split("|")[1] || "";
|
||||||
|
const serverTypes = await fetchServerTypes(dataIds);
|
||||||
|
const langs = new Set(episode.langs || []);
|
||||||
|
if (serverTypes.has("hsub")) langs.add("sub");
|
||||||
|
if (serverTypes.has("sub")) langs.add("sub");
|
||||||
|
if (serverTypes.has("dub")) langs.add("dub");
|
||||||
|
return { ...episode, langs: Array.from(langs) };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
episodes: episodes,
|
episodes: episodes,
|
||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
@@ -526,7 +584,7 @@ async function fetchEpisodeSources(episodeIdStr, category = null) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
name: "anikoto",
|
name: "anikoto",
|
||||||
version: "5.0.2",
|
version: "5.0.3",
|
||||||
SearchAnime,
|
SearchAnime,
|
||||||
AnimeInfo,
|
AnimeInfo,
|
||||||
fetchEpisodeSources,
|
fetchEpisodeSources,
|
||||||
|
|||||||
Reference in New Issue
Block a user