fix unmapped anime

This commit is contained in:
janisslsm committed 2026-10-27 13:12:57 +02:00
1 parent 4ae2142e8f
commit bee6625a9e
3 files changed
+123 -12

No files matched your search

+69 -5
View File
@@ -15,6 +15,7 @@
import { HEADERS, apiGet, fetchJson } from './http.js';
const ANIMAP_URL = 'https://animap.id/api/map';
const ANIZIP_URL = 'https://api.ani.zip/mappings';
const ANILIST_URL = 'https://graphql.anilist.co';
const SEARCH_LIMIT = 15;
const MAX_SEARCH_QUERIES = 5;
@@ -131,6 +132,60 @@ function getMapping(records, identifier, mediaType) {
return { ids, titles, synonyms };
}
/**
* AniMap's legacy TMDB/IMDb mapping is missing for newer shows. AnimeAPI
* (api.ani.zip) indexes the same services and fills those gaps.
*/
async function getAniZipMapping(identifier) {
const queryKey = identifier.service === 'tmdb' ? 'themoviedb_id' : 'imdb_id';
try {
const response = await fetchJson(
`${ANIZIP_URL}?${queryKey}=${encodeURIComponent(identifier.value)}`,
{ headers: { 'Accept': 'application/json' } }
);
const entries = Array.isArray(response) ? response : [response];
let mapping = null;
let titleTable = {};
for (const entry of entries) {
const candidate = entry && entry.mappings ? entry.mappings : entry;
if (candidate && asNumber(candidate.anilist_id)) {
mapping = candidate;
titleTable = entry && entry.titles && typeof entry.titles === 'object'
? entry.titles
: {};
break;
}
}
if (!mapping) {
return null;
}
const titles = [];
const synonyms = [];
for (const key of ['x-jat', 'en', 'ja']) {
if (titleTable[key]) {
titles.push(String(titleTable[key]));
}
}
for (const [key, value] of Object.entries(titleTable)) {
if (!['x-jat', 'en', 'ja'].includes(key) && value) {
synonyms.push(String(value));
}
}
return { ids: [asNumber(mapping.anilist_id)], titles, synonyms };
} catch (error) {
console.warn(`[Miruro] AnimeAPI mapping for ${identifier.value} failed: ${error.message}`);
return null;
}
}
async function getAniListMeta(anilistId) {
if (anilistCache.has(anilistId)) {
return anilistCache.get(anilistId);
@@ -611,21 +666,30 @@ async function resolveAnime(mediaId, mediaType, season, episode) {
return null;
}
let mappingRecords;
let mapping;
try {
mappingRecords = await fetchJson(
const mappingRecords = await fetchJson(
`${ANIMAP_URL}/${identifier.service}/${encodeURIComponent(identifier.value)}`,
{ headers: { 'Accept': 'application/json' } }
);
mapping = getMapping(mappingRecords, identifier, mediaType);
} catch (error) {
console.warn(`[Miruro] No anime mapping for ${mediaId}: ${error.message}`);
return null;
console.warn(`[Miruro] AniMap lookup for ${mediaId} failed: ${error.message}`);
mapping = { ids: [], titles: [], synonyms: [] };
}
const mapping = getMapping(mappingRecords, identifier, mediaType);
if (mapping.ids.length === 0) {
const fallback = await getAniZipMapping(identifier);
if (fallback) {
mapping = fallback;
}
}
if (mapping.ids.length === 0) {
console.warn(`[Miruro] No anime mapping for ${mediaId}`);
return null;
}