fix unmapped anime
This commit is contained in:
1 parent
4ae2142e8f
commit
bee6625a9e
3 files changed
+123
-12
No files matched your search
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Nuvio Providers",
|
||||
"version": "1.0.14",
|
||||
"version": "1.0.15",
|
||||
"scrapers": [
|
||||
{
|
||||
"id": "bcine",
|
||||
@@ -54,7 +54,7 @@
|
||||
"id": "miruro",
|
||||
"name": "Miruro",
|
||||
"description": "Anime streams from Miruro with subbed and dubbed sources.",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"author": "Jānis L",
|
||||
"supportedTypes": [
|
||||
"movie",
|
||||
|
||||
+52
-5
@@ -520,6 +520,7 @@ async function apiGet(path, query = {}) {
|
||||
|
||||
// src/miruro/extractor.js
|
||||
var ANIMAP_URL = "https://animap.id/api/map";
|
||||
var ANIZIP_URL = "https://api.ani.zip/mappings";
|
||||
var ANILIST_URL = "https://graphql.anilist.co";
|
||||
var SEARCH_LIMIT = 15;
|
||||
var MAX_SEARCH_QUERIES = 5;
|
||||
@@ -600,6 +601,45 @@ function getMapping(records, identifier, mediaType) {
|
||||
}
|
||||
return { ids, titles, synonyms };
|
||||
}
|
||||
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);
|
||||
@@ -926,18 +966,25 @@ async function resolveAnime(mediaId, mediaType, season, episode) {
|
||||
if (!identifier) {
|
||||
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;
|
||||
}
|
||||
const requestedSeason = asNumber(season) || 1;
|
||||
|
||||
+69
-5
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user