persist caption across sources

This commit is contained in:
Pas 2026-03-04 21:43:53 -07:00
parent 9009bf814c
commit 314f9574f6
4 changed files with 47 additions and 33 deletions

View file

@ -294,7 +294,7 @@ export function CustomCaptionOption() {
const { t } = useTranslation(); const { t } = useTranslation();
const lang = usePlayerStore((s) => s.caption.selected?.language); const lang = usePlayerStore((s) => s.caption.selected?.language);
const setCaption = usePlayerStore((s) => s.setCaption); const setCaption = usePlayerStore((s) => s.setCaption);
const setCustomSubs = useSubtitleStore((s) => s.setCustomSubs); const setSubtitle = useSubtitleStore((s) => s.setSubtitle);
const fileInput = useRef<HTMLInputElement>(null); const fileInput = useRef<HTMLInputElement>(null);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
@ -315,7 +315,7 @@ export function CustomCaptionOption() {
srtData: converted, srtData: converted,
id: "custom-caption", id: "custom-caption",
}); });
setCustomSubs(); setSubtitle(true, "custom", "custom-caption");
} catch (err) { } catch (err) {
setError( setError(
err instanceof Error err instanceof Error
@ -370,7 +370,7 @@ export function CustomCaptionOption() {
export function PasteCaptionOption(props: { selected?: boolean }) { export function PasteCaptionOption(props: { selected?: boolean }) {
const { t } = useTranslation(); const { t } = useTranslation();
const setCaption = usePlayerStore((s) => s.setCaption); 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 setDelay = useSubtitleStore((s) => s.setDelay);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
@ -409,7 +409,7 @@ export function PasteCaptionOption(props: { selected?: boolean }) {
srtData: converted, srtData: converted,
id: "pasted-caption", id: "pasted-caption",
}); });
setCustomSubs(); setSubtitle(true, parsedData.language, "pasted-caption");
// Set delay if included in the pasted data, otherwise reset to 0 // Set delay if included in the pasted data, otherwise reset to 0
if (parsedData.delay !== undefined) { if (parsedData.delay !== undefined) {
@ -476,7 +476,7 @@ export function CaptionsView({
); );
const delay = useSubtitleStore((s) => s.delay); const delay = useSubtitleStore((s) => s.delay);
const appLanguage = useLanguageStore((s) => s.language); const appLanguage = useLanguageStore((s) => s.language);
const setCustomSubs = useSubtitleStore((s) => s.setCustomSubs); const setSubtitle = useSubtitleStore((s) => s.setSubtitle);
const matchScore = useCaptionMatchScore(); const matchScore = useCaptionMatchScore();
// Get combined caption list // Get combined caption list
@ -580,7 +580,6 @@ export function CaptionsView({
srtData: converted, srtData: converted,
id: "custom-caption", id: "custom-caption",
}); });
setCustomSubs();
} catch (err) { } catch (err) {
// Silently fail on drop - user can use the upload button for better error feedback // Silently fail on drop - user can use the upload button for better error feedback
} }

View file

@ -13,7 +13,7 @@ import {
} from "../utils/captions"; } from "../utils/captions";
export function useCaptions() { export function useCaptions() {
const setLanguage = useSubtitleStore((s) => s.setLanguage); const setSubtitle = useSubtitleStore((s) => s.setSubtitle);
const enabled = useSubtitleStore((s) => s.enabled); const enabled = useSubtitleStore((s) => s.enabled);
const resetSubtitleSpecificSettings = useSubtitleStore( const resetSubtitleSpecificSettings = useSubtitleStore(
(s) => s.resetSubtitleSpecificSettings, (s) => s.resetSubtitleSpecificSettings,
@ -21,6 +21,9 @@ export function useCaptions() {
const setCaption = usePlayerStore((s) => s.setCaption); const setCaption = usePlayerStore((s) => s.setCaption);
const currentTranslateTask = usePlayerStore((s) => s.caption.translateTask); const currentTranslateTask = usePlayerStore((s) => s.caption.translateTask);
const lastSelectedLanguage = useSubtitleStore((s) => s.lastSelectedLanguage); const lastSelectedLanguage = useSubtitleStore((s) => s.lastSelectedLanguage);
const lastSelectedSubtitleId = useSubtitleStore(
(s) => s.lastSelectedSubtitleId,
);
const setIsOpenSubtitles = useSubtitleStore((s) => s.setIsOpenSubtitles); const setIsOpenSubtitles = useSubtitleStore((s) => s.setIsOpenSubtitles);
const captionList = usePlayerStore((s) => s.captionList); const captionList = usePlayerStore((s) => s.captionList);
@ -53,7 +56,7 @@ export function useCaptions() {
resetSubtitleSpecificSettings(); resetSubtitleSpecificSettings();
} }
setLanguage(caption.language); setSubtitle(true, caption.language, caption.id);
// Use native tracks for MP4 streams instead of custom rendering // Use native tracks for MP4 streams instead of custom rendering
if (source?.type === "file" && enableNativeSubtitles) { if (source?.type === "file" && enableNativeSubtitles) {
@ -65,7 +68,7 @@ export function useCaptions() {
}, },
[ [
setIsOpenSubtitles, setIsOpenSubtitles,
setLanguage, setSubtitle,
setCaption, setCaption,
resetSubtitleSpecificSettings, resetSubtitleSpecificSettings,
source, source,
@ -135,14 +138,25 @@ export function useCaptions() {
const disable = useCallback(async () => { const disable = useCallback(async () => {
setIsOpenSubtitles(false); setIsOpenSubtitles(false);
setCaption(null); setCaption(null);
setLanguage(null); setSubtitle(false);
}, [setCaption, setLanguage, setIsOpenSubtitles]); }, [setCaption, setSubtitle, setIsOpenSubtitles]);
const selectLastUsedLanguage = useCallback(async () => { const selectLastUsedLanguage = useCallback(async () => {
if (lastSelectedSubtitleId) {
const caption = captions.find((v) => v.id === lastSelectedSubtitleId);
if (caption) return selectCaptionById(caption.id);
}
const language = lastSelectedLanguage ?? "en"; const language = lastSelectedLanguage ?? "en";
await selectLanguage(language); await selectLanguage(language);
return true; return true;
}, [lastSelectedLanguage, selectLanguage]); }, [
lastSelectedLanguage,
selectLanguage,
lastSelectedSubtitleId,
captions,
selectCaptionById,
]);
const toggleLastUsed = useCallback(async () => { const toggleLastUsed = useCallback(async () => {
if (enabled) disable(); if (enabled) disable();

View file

@ -26,12 +26,8 @@ export function useInitializeSource() {
); );
const { selectLastUsedLanguageIfEnabled } = useCaptions(); const { selectLastUsedLanguageIfEnabled } = useCaptions();
// Only select subtitles on initial load, not when source changes
const hasInitializedRef = useRef(false);
useEffect(() => { useEffect(() => {
if (sourceIdentifier && !hasInitializedRef.current) { if (sourceIdentifier) {
hasInitializedRef.current = true;
selectLastUsedLanguageIfEnabled(); selectLastUsedLanguageIfEnabled();
} }
}, [sourceIdentifier, selectLastUsedLanguageIfEnabled]); }, [sourceIdentifier, selectLastUsedLanguageIfEnabled]);

View file

@ -59,6 +59,7 @@ export interface SubtitleStore {
}; };
enabled: boolean; enabled: boolean;
lastSelectedLanguage: string | null; lastSelectedLanguage: string | null;
lastSelectedSubtitleId: string | null;
isOpenSubtitles: boolean; isOpenSubtitles: boolean;
styling: SubtitleStyling; styling: SubtitleStyling;
overrideCasing: boolean; overrideCasing: boolean;
@ -66,9 +67,12 @@ export interface SubtitleStore {
showDelayIndicator: boolean; showDelayIndicator: boolean;
updateStyling(newStyling: Partial<SubtitleStyling>): void; updateStyling(newStyling: Partial<SubtitleStyling>): void;
resetStyling(): void; resetStyling(): void;
setLanguage(language: string | null): void; setSubtitle(
enabled: boolean,
language?: string | null,
subtitleId?: string | null,
): void;
setIsOpenSubtitles(isOpenSubtitles: boolean): void; setIsOpenSubtitles(isOpenSubtitles: boolean): void;
setCustomSubs(): void;
setOverrideCasing(enabled: boolean): void; setOverrideCasing(enabled: boolean): void;
setDelay(delay: number): void; setDelay(delay: number): void;
importSubtitleLanguage(lang: string | null): void; importSubtitleLanguage(lang: string | null): void;
@ -84,6 +88,7 @@ export const useSubtitleStore = create(
lastSelectedLanguage: null, lastSelectedLanguage: null,
}, },
lastSelectedLanguage: null, lastSelectedLanguage: null,
lastSelectedSubtitleId: null,
isOpenSubtitles: false, isOpenSubtitles: false,
overrideCasing: false, overrideCasing: false,
delay: 0, delay: 0,
@ -105,6 +110,11 @@ export const useSubtitleStore = create(
s.overrideCasing = false; s.overrideCasing = false;
}); });
}, },
setIsOpenSubtitles(isOpenSubtitles) {
set((s) => {
s.isOpenSubtitles = isOpenSubtitles;
});
},
updateStyling(newStyling) { updateStyling(newStyling) {
set((s) => { set((s) => {
if (newStyling.backgroundOpacity !== undefined) if (newStyling.backgroundOpacity !== undefined)
@ -153,21 +163,16 @@ export const useSubtitleStore = create(
}; };
}); });
}, },
setLanguage(lang) { setSubtitle(enabled, language, subtitleId) {
set((s) => { set((s) => {
s.enabled = !!lang; s.enabled = enabled;
if (lang) s.lastSelectedLanguage = lang; if (enabled) {
}); s.lastSelectedLanguage = language ?? null;
}, s.lastSelectedSubtitleId = subtitleId ?? null;
setIsOpenSubtitles(isOpenSubtitles) { } else {
set((s) => { s.lastSelectedLanguage = null;
s.isOpenSubtitles = isOpenSubtitles; s.lastSelectedSubtitleId = null;
}); }
},
setCustomSubs() {
set((s) => {
s.enabled = true;
s.lastSelectedLanguage = null;
}); });
}, },
setOverrideCasing(enabled) { setOverrideCasing(enabled) {