diff --git a/src/components/player/AndroidVideoPlayer.tsx b/src/components/player/AndroidVideoPlayer.tsx index af9eb4c0..f5141880 100644 --- a/src/components/player/AndroidVideoPlayer.tsx +++ b/src/components/player/AndroidVideoPlayer.tsx @@ -1022,6 +1022,7 @@ const AndroidVideoPlayer: React.FC = () => { episode={episode} malId={(metadata as any)?.mal_id || (metadata as any)?.external_ids?.mal_id} kitsuId={id?.startsWith('kitsu:') ? id.split(':')[1] : undefined} + releaseDate={releaseDate} currentTime={playerState.currentTime} onSkip={(endTime) => controlsHook.seekToTime(endTime)} controlsVisible={playerState.showControls} diff --git a/src/components/player/KSPlayerCore.tsx b/src/components/player/KSPlayerCore.tsx index f6e69eaa..9f8538d6 100644 --- a/src/components/player/KSPlayerCore.tsx +++ b/src/components/player/KSPlayerCore.tsx @@ -91,7 +91,7 @@ const KSPlayerCore: React.FC = () => { const { uri, title, episodeTitle, season, episode, id, type, quality, year, episodeId, imdbId, backdrop, availableStreams, - headers, streamProvider, streamName, + headers, streamProvider, streamName, releaseDate, initialPosition: routeInitialPosition } = params; @@ -998,6 +998,7 @@ const KSPlayerCore: React.FC = () => { episode={episode} malId={(metadata as any)?.mal_id || (metadata as any)?.external_ids?.mal_id} kitsuId={id?.startsWith('kitsu:') ? id.split(':')[1] : undefined} + releaseDate={releaseDate} currentTime={currentTime} onSkip={(endTime) => controls.seekToTime(endTime)} controlsVisible={showControls} diff --git a/src/components/player/overlays/SkipIntroButton.tsx b/src/components/player/overlays/SkipIntroButton.tsx index b5329c6a..85b865f8 100644 --- a/src/components/player/overlays/SkipIntroButton.tsx +++ b/src/components/player/overlays/SkipIntroButton.tsx @@ -22,6 +22,7 @@ interface SkipIntroButtonProps { episode?: number; malId?: string; kitsuId?: string; + releaseDate?: string; currentTime: number; onSkip: (endTime: number) => void; controlsVisible?: boolean; @@ -35,6 +36,7 @@ export const SkipIntroButton: React.FC = ({ episode, malId, kitsuId, + releaseDate, currentTime, onSkip, controlsVisible = false, @@ -96,7 +98,7 @@ export const SkipIntroButton: React.FC = ({ const fetchSkipData = async () => { logger.log(`[SkipIntroButton] Fetching skip data for S${season}E${episode} (IMDB: ${imdbId}, MAL: ${malId}, Kitsu: ${kitsuId})...`); try { - const intervals = await introService.getSkipTimes(imdbId, season, episode, malId, kitsuId); + const intervals = await introService.getSkipTimes(imdbId, season, episode, malId, kitsuId, releaseDate); setSkipIntervals(intervals); if (intervals.length > 0) { @@ -111,7 +113,7 @@ export const SkipIntroButton: React.FC = ({ }; fetchSkipData(); - }, [imdbId, type, season, episode, malId, kitsuId, skipIntroEnabled]); + }, [imdbId, type, season, episode, malId, kitsuId, releaseDate, skipIntroEnabled]); // Determine active interval based on current playback position useEffect(() => { diff --git a/src/services/introService.ts b/src/services/introService.ts index 9d225903..3f3c84ec 100644 --- a/src/services/introService.ts +++ b/src/services/introService.ts @@ -1,6 +1,7 @@ import axios from 'axios'; import { logger } from '../utils/logger'; import { tmdbService } from './tmdbService'; +import { ArmSyncService } from './mal/ArmSyncService'; /** * IntroDB API service for fetching TV show intro timestamps @@ -195,7 +196,8 @@ export async function getSkipTimes( season: number, episode: number, malId?: string, - kitsuId?: string + kitsuId?: string, + releaseDate?: string ): Promise { // 1. Try IntroDB (TV Shows) first if (imdbId) { @@ -207,7 +209,22 @@ export async function getSkipTimes( // 2. Try AniSkip (Anime) if we have MAL ID or Kitsu ID let finalMalId = malId; + let finalEpisode = episode; + // If we have IMDb ID and Release Date, try ArmSyncService to resolve exact MAL ID and Episode + if (!finalMalId && imdbId && releaseDate) { + try { + const armResult = await ArmSyncService.resolveByDate(imdbId, releaseDate); + if (armResult) { + finalMalId = armResult.malId.toString(); + finalEpisode = armResult.episode; + logger.log(`[IntroService] ArmSync resolved: MAL ${finalMalId} Ep ${finalEpisode}`); + } + } catch (e) { + logger.warn('[IntroService] ArmSync failed', e); + } + } + // If we have Kitsu ID but no MAL ID, try to resolve it if (!finalMalId && kitsuId) { logger.log(`[IntroService] Resolving MAL ID from Kitsu ID: ${kitsuId}`); @@ -228,8 +245,8 @@ export async function getSkipTimes( } if (finalMalId) { - logger.log(`[IntroService] Fetching AniSkip for MAL ID: ${finalMalId} Ep: ${episode}`); - const aniSkipIntervals = await fetchFromAniSkip(finalMalId, episode); + logger.log(`[IntroService] Fetching AniSkip for MAL ID: ${finalMalId} Ep: ${finalEpisode}`); + const aniSkipIntervals = await fetchFromAniSkip(finalMalId, finalEpisode); if (aniSkipIntervals.length > 0) { logger.log(`[IntroService] Found ${aniSkipIntervals.length} skip intervals from AniSkip`); return aniSkipIntervals; @@ -269,4 +286,4 @@ export const introService = { getSkipTimes }; -export default introService; +export default introService; \ No newline at end of file