From 1e60af1ffb817ab3c98e918698fa5b69c4957e29 Mon Sep 17 00:00:00 2001 From: paregi12 Date: Mon, 5 Jan 2026 00:33:33 +0530 Subject: [PATCH] feat: prioritize IntroDB and implement ARM API for faster MAL ID resolution --- src/services/introService.ts | 55 ++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/src/services/introService.ts b/src/services/introService.ts index e8cdac49..9d225903 100644 --- a/src/services/introService.ts +++ b/src/services/introService.ts @@ -10,6 +10,7 @@ import { tmdbService } from './tmdbService'; const INTRODB_API_URL = process.env.EXPO_PUBLIC_INTRODB_API_URL; const ANISKIP_API_URL = 'https://api.aniskip.com/v2'; const KITSU_API_URL = 'https://kitsu.io/api/edge'; +const ARM_IMDB_URL = 'https://arm.haglund.dev/api/v2/imdb'; export type SkipType = 'op' | 'ed' | 'recap' | 'intro' | 'outro' | 'mixed-op' | 'mixed-ed'; @@ -32,6 +33,31 @@ export interface IntroTimestamps { confidence: number; } +async function getMalIdFromArm(imdbId: string): Promise { + try { + const response = await axios.get(ARM_IMDB_URL, { + params: { + id: imdbId, + include: 'myanimelist' + } + }); + + // ARM returns an array of matches (e.g. for different seasons) + // We typically take the first one or try to match logic if possible + if (Array.isArray(response.data) && response.data.length > 0) { + const result = response.data[0]; + if (result && result.myanimelist) { + logger.log(`[IntroService] Found MAL ID via ARM: ${result.myanimelist}`); + return result.myanimelist.toString(); + } + } + } catch (error) { + // Silent fail as this is just one of the resolution methods + // logger.warn('[IntroService] Failed to fetch MAL ID from ARM', error); + } + return null; +} + async function getMalIdFromKitsu(kitsuId: string): Promise { try { const response = await axios.get(`${KITSU_API_URL}/anime/${kitsuId}/mappings`); @@ -171,7 +197,15 @@ export async function getSkipTimes( malId?: string, kitsuId?: string ): Promise { - // 1. Try AniSkip (Anime) if we have MAL ID or Kitsu ID + // 1. Try IntroDB (TV Shows) first + if (imdbId) { + const introDbIntervals = await fetchFromIntroDb(imdbId, season, episode); + if (introDbIntervals.length > 0) { + return introDbIntervals; + } + } + + // 2. Try AniSkip (Anime) if we have MAL ID or Kitsu ID let finalMalId = malId; // If we have Kitsu ID but no MAL ID, try to resolve it @@ -182,8 +216,15 @@ export async function getSkipTimes( // If we still don't have MAL ID but have IMDb ID (e.g. Cinemeta), try to resolve it if (!finalMalId && imdbId) { - logger.log(`[IntroService] Attempting to resolve MAL ID from IMDb ID: ${imdbId}`); - finalMalId = await getMalIdFromImdb(imdbId) || undefined; + // Priority 1: ARM API (Fastest) + logger.log(`[IntroService] Attempting to resolve MAL ID via ARM for: ${imdbId}`); + finalMalId = await getMalIdFromArm(imdbId) || undefined; + + // Priority 2: Kitsu/TMDB Chain (Fallback) + if (!finalMalId) { + logger.log(`[IntroService] ARM failed, falling back to Kitsu/TMDB chain for: ${imdbId}`); + finalMalId = await getMalIdFromImdb(imdbId) || undefined; + } } if (finalMalId) { @@ -195,14 +236,6 @@ export async function getSkipTimes( } } - // 2. Try IntroDB (TV Shows) as fallback or for non-anime - if (imdbId) { - const introDbIntervals = await fetchFromIntroDb(imdbId, season, episode); - if (introDbIntervals.length > 0) { - return introDbIntervals; - } - } - return []; }