fix skip time fetching with a slice cache

This commit is contained in:
Pas 2026-02-01 12:44:31 -07:00
parent 644b6ca8c9
commit 6bfb07f867
5 changed files with 50 additions and 10 deletions

View file

@ -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<SegmentData[]>([]);
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<void> => {
// 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 : [];
}

View file

@ -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<SkipSegmentsSlice> = (set) => ({
skipSegmentsCacheKey: null,
skipSegments: [],
setSkipSegments(cacheKey, segments) {
set((s) => {
s.skipSegmentsCacheKey = cacheKey;
s.skipSegments = segments;
});
},
clearSkipSegments() {
set((s) => {
s.skipSegmentsCacheKey = null;
s.skipSegments = [];
});
},
});

View file

@ -396,6 +396,7 @@ export const createSourceSlice: MakeSlice<SourceSlice> = (set, get) => ({
});
},
reset() {
get().clearSkipSegments?.();
set((s) => {
s.source = null;
s.sourceId = null;

View file

@ -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<Slice> = StateCreator<
AllSlices,
[["zustand/immer", never]],

View file

@ -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),
})),
);