diff --git a/src/components/player/atoms/settings/CaptionsView.tsx b/src/components/player/atoms/settings/CaptionsView.tsx index 41acf04d..80f59050 100644 --- a/src/components/player/atoms/settings/CaptionsView.tsx +++ b/src/components/player/atoms/settings/CaptionsView.tsx @@ -294,7 +294,7 @@ export function CustomCaptionOption() { const { t } = useTranslation(); const lang = usePlayerStore((s) => s.caption.selected?.language); const setCaption = usePlayerStore((s) => s.setCaption); - const setCustomSubs = useSubtitleStore((s) => s.setCustomSubs); + const setSubtitle = useSubtitleStore((s) => s.setSubtitle); const fileInput = useRef(null); const [error, setError] = useState(null); @@ -315,7 +315,7 @@ export function CustomCaptionOption() { srtData: converted, id: "custom-caption", }); - setCustomSubs(); + setSubtitle(true, "custom", "custom-caption"); } catch (err) { setError( err instanceof Error @@ -370,7 +370,7 @@ export function CustomCaptionOption() { export function PasteCaptionOption(props: { selected?: boolean }) { const { t } = useTranslation(); const setCaption = usePlayerStore((s) => s.setCaption); - const setCustomSubs = useSubtitleStore((s) => s.setCustomSubs); + const setSubtitle = useSubtitleStore((s) => s.setSubtitle); const setDelay = useSubtitleStore((s) => s.setDelay); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); @@ -409,7 +409,7 @@ export function PasteCaptionOption(props: { selected?: boolean }) { srtData: converted, id: "pasted-caption", }); - setCustomSubs(); + setSubtitle(true, parsedData.language, "pasted-caption"); // Set delay if included in the pasted data, otherwise reset to 0 if (parsedData.delay !== undefined) { @@ -476,7 +476,7 @@ export function CaptionsView({ ); const delay = useSubtitleStore((s) => s.delay); const appLanguage = useLanguageStore((s) => s.language); - const setCustomSubs = useSubtitleStore((s) => s.setCustomSubs); + const setSubtitle = useSubtitleStore((s) => s.setSubtitle); const matchScore = useCaptionMatchScore(); // Get combined caption list @@ -580,7 +580,6 @@ export function CaptionsView({ srtData: converted, id: "custom-caption", }); - setCustomSubs(); } catch (err) { // Silently fail on drop - user can use the upload button for better error feedback } diff --git a/src/components/player/hooks/useCaptions.ts b/src/components/player/hooks/useCaptions.ts index 7d51475c..76b428f0 100644 --- a/src/components/player/hooks/useCaptions.ts +++ b/src/components/player/hooks/useCaptions.ts @@ -13,7 +13,7 @@ import { } from "../utils/captions"; export function useCaptions() { - const setLanguage = useSubtitleStore((s) => s.setLanguage); + const setSubtitle = useSubtitleStore((s) => s.setSubtitle); const enabled = useSubtitleStore((s) => s.enabled); const resetSubtitleSpecificSettings = useSubtitleStore( (s) => s.resetSubtitleSpecificSettings, @@ -21,6 +21,9 @@ export function useCaptions() { const setCaption = usePlayerStore((s) => s.setCaption); const currentTranslateTask = usePlayerStore((s) => s.caption.translateTask); const lastSelectedLanguage = useSubtitleStore((s) => s.lastSelectedLanguage); + const lastSelectedSubtitleId = useSubtitleStore( + (s) => s.lastSelectedSubtitleId, + ); const setIsOpenSubtitles = useSubtitleStore((s) => s.setIsOpenSubtitles); const captionList = usePlayerStore((s) => s.captionList); @@ -53,7 +56,7 @@ export function useCaptions() { resetSubtitleSpecificSettings(); } - setLanguage(caption.language); + setSubtitle(true, caption.language, caption.id); // Use native tracks for MP4 streams instead of custom rendering if (source?.type === "file" && enableNativeSubtitles) { @@ -65,7 +68,7 @@ export function useCaptions() { }, [ setIsOpenSubtitles, - setLanguage, + setSubtitle, setCaption, resetSubtitleSpecificSettings, source, @@ -135,14 +138,25 @@ export function useCaptions() { const disable = useCallback(async () => { setIsOpenSubtitles(false); setCaption(null); - setLanguage(null); - }, [setCaption, setLanguage, setIsOpenSubtitles]); + setSubtitle(false); + }, [setCaption, setSubtitle, setIsOpenSubtitles]); const selectLastUsedLanguage = useCallback(async () => { + if (lastSelectedSubtitleId) { + const caption = captions.find((v) => v.id === lastSelectedSubtitleId); + if (caption) return selectCaptionById(caption.id); + } + const language = lastSelectedLanguage ?? "en"; await selectLanguage(language); return true; - }, [lastSelectedLanguage, selectLanguage]); + }, [ + lastSelectedLanguage, + selectLanguage, + lastSelectedSubtitleId, + captions, + selectCaptionById, + ]); const toggleLastUsed = useCallback(async () => { if (enabled) disable(); diff --git a/src/components/player/hooks/useInitializePlayer.ts b/src/components/player/hooks/useInitializePlayer.ts index f867c803..2d001c6a 100644 --- a/src/components/player/hooks/useInitializePlayer.ts +++ b/src/components/player/hooks/useInitializePlayer.ts @@ -26,12 +26,8 @@ export function useInitializeSource() { ); const { selectLastUsedLanguageIfEnabled } = useCaptions(); - // Only select subtitles on initial load, not when source changes - const hasInitializedRef = useRef(false); - useEffect(() => { - if (sourceIdentifier && !hasInitializedRef.current) { - hasInitializedRef.current = true; + if (sourceIdentifier) { selectLastUsedLanguageIfEnabled(); } }, [sourceIdentifier, selectLastUsedLanguageIfEnabled]); diff --git a/src/stores/subtitles/index.ts b/src/stores/subtitles/index.ts index 81a3884e..3767ccfc 100644 --- a/src/stores/subtitles/index.ts +++ b/src/stores/subtitles/index.ts @@ -59,6 +59,7 @@ export interface SubtitleStore { }; enabled: boolean; lastSelectedLanguage: string | null; + lastSelectedSubtitleId: string | null; isOpenSubtitles: boolean; styling: SubtitleStyling; overrideCasing: boolean; @@ -66,9 +67,12 @@ export interface SubtitleStore { showDelayIndicator: boolean; updateStyling(newStyling: Partial): void; resetStyling(): void; - setLanguage(language: string | null): void; + setSubtitle( + enabled: boolean, + language?: string | null, + subtitleId?: string | null, + ): void; setIsOpenSubtitles(isOpenSubtitles: boolean): void; - setCustomSubs(): void; setOverrideCasing(enabled: boolean): void; setDelay(delay: number): void; importSubtitleLanguage(lang: string | null): void; @@ -84,6 +88,7 @@ export const useSubtitleStore = create( lastSelectedLanguage: null, }, lastSelectedLanguage: null, + lastSelectedSubtitleId: null, isOpenSubtitles: false, overrideCasing: false, delay: 0, @@ -105,6 +110,11 @@ export const useSubtitleStore = create( s.overrideCasing = false; }); }, + setIsOpenSubtitles(isOpenSubtitles) { + set((s) => { + s.isOpenSubtitles = isOpenSubtitles; + }); + }, updateStyling(newStyling) { set((s) => { if (newStyling.backgroundOpacity !== undefined) @@ -153,21 +163,16 @@ export const useSubtitleStore = create( }; }); }, - setLanguage(lang) { + setSubtitle(enabled, language, subtitleId) { set((s) => { - s.enabled = !!lang; - if (lang) s.lastSelectedLanguage = lang; - }); - }, - setIsOpenSubtitles(isOpenSubtitles) { - set((s) => { - s.isOpenSubtitles = isOpenSubtitles; - }); - }, - setCustomSubs() { - set((s) => { - s.enabled = true; - s.lastSelectedLanguage = null; + s.enabled = enabled; + if (enabled) { + s.lastSelectedLanguage = language ?? null; + s.lastSelectedSubtitleId = subtitleId ?? null; + } else { + s.lastSelectedLanguage = null; + s.lastSelectedSubtitleId = null; + } }); }, setOverrideCasing(enabled) {