diff --git a/src/components/player/hooks/useSkipTime.ts b/src/components/player/hooks/useSkipTime.ts index 5f4b261a..b0f83a1f 100644 --- a/src/components/player/hooks/useSkipTime.ts +++ b/src/components/player/hooks/useSkipTime.ts @@ -1,9 +1,11 @@ -import { useEffect, useState } from "react"; +import { useEffect } from "react"; // import { proxiedFetch } from "@/backend/helpers/fetch"; import { mwFetch, proxiedFetch } from "@/backend/helpers/fetch"; import { usePlayerMeta } from "@/components/player/hooks/usePlayerMeta"; import { conf } from "@/setup/config"; +import { usePlayerStore } from "@/stores/player/store"; +import { getMediaKey } from "@/stores/player/slices/source"; import { usePreferencesStore } from "@/stores/preferences"; import { getTurnstileToken } from "@/utils/turnstile"; @@ -30,10 +32,16 @@ export interface SegmentData { export function useSkipTime() { const { playerMeta: meta } = usePlayerMeta(); - const [segments, setSegments] = useState([]); const febboxKey = usePreferencesStore((s) => s.febboxKey); + const cacheKey = getMediaKey(meta ?? null); + const skipSegmentsCacheKey = usePlayerStore((s) => s.skipSegmentsCacheKey); + const skipSegments = usePlayerStore((s) => s.skipSegments); + const setSkipSegments = usePlayerStore((s) => s.setSkipSegments); useEffect(() => { + if (!cacheKey) return; + // Already have segments for this media – don't refetch (e.g. when opening menu) + if (cacheKey === skipSegmentsCacheKey) return; // Validate segment data according to rules // eslint-disable-next-line camelcase const validateSegment = ( @@ -212,9 +220,7 @@ export function useSkipTime() { }; const fetchSkipTime = async (): Promise => { - // Reset source and segments currentSkipTimeSource = null; - setSegments([]); // Try TheIntroDB API first (supports both movies and TV shows with full segment data) const theIntroDBSegments = await fetchTheIntroDBSegments(); @@ -228,7 +234,7 @@ export function useSkipTime() { // If we have a valid intro from TIDB, use all TIDB segments if (hasIntroSegment) { currentSkipTimeSource = "theintrodb"; - setSegments(theIntroDBSegments); + setSkipSegments(cacheKey, theIntroDBSegments); return; } @@ -275,13 +281,15 @@ export function useSkipTime() { // Add any valid recap/credits segments from TIDB finalSegments.push(...nonIntroSegments); - if (finalSegments.length > 0) { - setSegments(finalSegments); - } + // Always update cache (even when empty) so we don't refetch for this media + setSkipSegments(cacheKey, finalSegments); }; fetchSkipTime(); }, [ + cacheKey, + skipSegmentsCacheKey, + setSkipSegments, meta?.tmdbId, meta?.imdbId, meta?.title, @@ -291,5 +299,6 @@ export function useSkipTime() { febboxKey, ]); - return segments; + // Only return segments when they're for the current media (avoid showing stale data) + return cacheKey === skipSegmentsCacheKey ? skipSegments : []; } diff --git a/src/stores/player/slices/skipSegments.ts b/src/stores/player/slices/skipSegments.ts new file mode 100644 index 00000000..875ab1d7 --- /dev/null +++ b/src/stores/player/slices/skipSegments.ts @@ -0,0 +1,26 @@ +import type { SegmentData } from "@/components/player/hooks/useSkipTime"; +import { MakeSlice } from "@/stores/player/slices/types"; + +export interface SkipSegmentsSlice { + skipSegmentsCacheKey: string | null; + skipSegments: SegmentData[]; + setSkipSegments(cacheKey: string, segments: SegmentData[]): void; + clearSkipSegments(): void; +} + +export const createSkipSegmentsSlice: MakeSlice = (set) => ({ + skipSegmentsCacheKey: null, + skipSegments: [], + setSkipSegments(cacheKey, segments) { + set((s) => { + s.skipSegmentsCacheKey = cacheKey; + s.skipSegments = segments; + }); + }, + clearSkipSegments() { + set((s) => { + s.skipSegmentsCacheKey = null; + s.skipSegments = []; + }); + }, +}); diff --git a/src/stores/player/slices/source.ts b/src/stores/player/slices/source.ts index b8147616..c369da35 100644 --- a/src/stores/player/slices/source.ts +++ b/src/stores/player/slices/source.ts @@ -396,6 +396,7 @@ export const createSourceSlice: MakeSlice = (set, get) => ({ }); }, reset() { + get().clearSkipSegments?.(); set((s) => { s.source = null; s.sourceId = null; diff --git a/src/stores/player/slices/types.ts b/src/stores/player/slices/types.ts index 6f358945..24ed4f80 100644 --- a/src/stores/player/slices/types.ts +++ b/src/stores/player/slices/types.ts @@ -5,6 +5,7 @@ import { DisplaySlice } from "@/stores/player/slices/display"; import { InterfaceSlice } from "@/stores/player/slices/interface"; import { PlayingSlice } from "@/stores/player/slices/playing"; import { ProgressSlice } from "@/stores/player/slices/progress"; +import { SkipSegmentsSlice } from "@/stores/player/slices/skipSegments"; import { SourceSlice } from "@/stores/player/slices/source"; import { ThumbnailSlice } from "@/stores/player/slices/thumbnails"; @@ -14,7 +15,8 @@ export type AllSlices = InterfaceSlice & SourceSlice & DisplaySlice & CastingSlice & - ThumbnailSlice; + ThumbnailSlice & + SkipSegmentsSlice; export type MakeSlice = StateCreator< AllSlices, [["zustand/immer", never]], diff --git a/src/stores/player/store.ts b/src/stores/player/store.ts index 2235c30f..44711923 100644 --- a/src/stores/player/store.ts +++ b/src/stores/player/store.ts @@ -6,6 +6,7 @@ import { createDisplaySlice } from "@/stores/player/slices/display"; import { createInterfaceSlice } from "@/stores/player/slices/interface"; import { createPlayingSlice } from "@/stores/player/slices/playing"; import { createProgressSlice } from "@/stores/player/slices/progress"; +import { createSkipSegmentsSlice } from "@/stores/player/slices/skipSegments"; import { createSourceSlice } from "@/stores/player/slices/source"; import { createThumbnailSlice } from "@/stores/player/slices/thumbnails"; import { AllSlices } from "@/stores/player/slices/types"; @@ -19,5 +20,6 @@ export const usePlayerStore = create( ...createDisplaySlice(...a), ...createCastingSlice(...a), ...createThumbnailSlice(...a), + ...createSkipSegmentsSlice(...a), })), );