From 1aa47cfe7c57f71ab104b3b8d53a180b025a4e58 Mon Sep 17 00:00:00 2001 From: Pas <74743263+Pasithea0@users.noreply.github.com> Date: Sat, 15 Mar 2025 21:42:30 -0700 Subject: [PATCH] Add toggle for skipping at 99% --- src/assets/locales/en.json | 3 +++ .../player/atoms/NextEpisodeButton.tsx | 18 ++++++++++--- src/hooks/useSettingsState.ts | 14 +++++++++++ src/pages/Settings.tsx | 12 ++++++++- src/pages/parts/settings/PreferencesPart.tsx | 25 +++++++++++++++++++ src/stores/preferences/index.tsx | 8 ++++++ 6 files changed, 76 insertions(+), 4 deletions(-) diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json index 92641a0d..1b756ffa 100644 --- a/src/assets/locales/en.json +++ b/src/assets/locales/en.json @@ -673,6 +673,9 @@ "autoplay": "Autoplay", "autoplayDescription": "Automatically play the next episode in a series after reaching the end. Can be enabled by users with the browser extension, a custom proxy, or with the default setup if allowed by the host.", "autoplayLabel": "Autoplay", + "skipCredits": "Skip End Credits", + "skipCreditsDescription": "When enabled, automatically play the next episode at 99% completion to skip end credits. When disabled, wait until the episode is fully completed.", + "skipCreditsLabel": "Skip end credits", "sourceOrder": "Reordering sources", "sourceOrderDescription": "Drag and drop to reorder sources. This will determine the order in which sources are checked for the media you are trying to watch. If a source is greyed out, it means the extension is required for that source.

(The default order is best for most users)", "title": "Preferences", diff --git a/src/components/player/atoms/NextEpisodeButton.tsx b/src/components/player/atoms/NextEpisodeButton.tsx index e1c071e2..cefa8cf7 100644 --- a/src/components/player/atoms/NextEpisodeButton.tsx +++ b/src/components/player/atoms/NextEpisodeButton.tsx @@ -101,13 +101,14 @@ export function NextEpisodeButton(props: { const { setDirectMeta } = usePlayerMeta(); const metaType = usePlayerStore((s) => s.meta?.type); const time = usePlayerStore((s) => s.progress.time); + const enableAutoplay = usePreferencesStore((s) => s.enableAutoplay); + const enableSkipCredits = usePreferencesStore((s) => s.enableSkipCredits); const showingState = shouldShowNextEpisodeButton(time, duration); const status = usePlayerStore((s) => s.status); const setShouldStartFromBeginning = usePlayerStore( (s) => s.setShouldStartFromBeginning, ); const updateItem = useProgressStore((s) => s.updateItem); - const enableAutoplay = usePreferencesStore((s) => s.enableAutoplay); const isLastEpisode = !meta?.episode?.number || !meta?.episodes?.at(-1)?.number @@ -187,14 +188,25 @@ export function NextEpisodeButton(props: { useEffect(() => { if (!enableAutoplay || metaType !== "show") return; const onePercent = duration / 100; - const isEnding = time >= duration - onePercent && duration !== 0; + + // When skipCredits is enabled, use the 99% threshold; otherwise require 100% completion + const isEnding = enableSkipCredits + ? time >= duration - onePercent && duration !== 0 // 99% completion + : time >= duration && duration !== 0; // 100% completion if (duration === 0) hasAutoplayed.current = false; if (isEnding && isAutoplayAllowed() && !hasAutoplayed.current) { hasAutoplayed.current = true; loadNextEpisode(); } - }, [duration, enableAutoplay, loadNextEpisode, metaType, time]); + }, [ + duration, + enableAutoplay, + enableSkipCredits, + loadNextEpisode, + metaType, + time, + ]); if (!meta?.episode || !nextEp) return null; if (metaType !== "show") return null; diff --git a/src/hooks/useSettingsState.ts b/src/hooks/useSettingsState.ts index b52a63b5..cff14a51 100644 --- a/src/hooks/useSettingsState.ts +++ b/src/hooks/useSettingsState.ts @@ -58,6 +58,7 @@ export function useSettingsState( sourceOrder: string[], enableSourceOrder: boolean, proxyTmdb: boolean, + enableSkipCredits: boolean, ) { const [proxyUrlsState, setProxyUrls, resetProxyUrls, proxyUrlsChanged] = useDerived(proxyUrls); @@ -103,6 +104,12 @@ export function useSettingsState( resetEnableAutoplay, enableAutoplayChanged, ] = useDerived(enableAutoplay); + const [ + enableSkipCreditsState, + setEnableSkipCreditsState, + resetEnableSkipCredits, + enableSkipCreditsChanged, + ] = useDerived(enableSkipCredits); const [ enableDiscoverState, setEnableDiscoverState, @@ -142,6 +149,7 @@ export function useSettingsState( resetProfile(); resetEnableThumbnails(); resetEnableAutoplay(); + resetEnableSkipCredits(); resetEnableDiscover(); resetEnablePopDetails(); resetSourceOrder(); @@ -160,6 +168,7 @@ export function useSettingsState( profileChanged || enableThumbnailsChanged || enableAutoplayChanged || + enableSkipCreditsChanged || enableDiscoverChanged || enablePopDetailsChanged || sourceOrderChanged || @@ -219,6 +228,11 @@ export function useSettingsState( set: setEnableAutoplayState, changed: enableAutoplayChanged, }, + enableSkipCredits: { + state: enableSkipCreditsState, + set: setEnableSkipCreditsState, + changed: enableSkipCreditsChanged, + }, enableDiscover: { state: enableDiscoverState, set: setEnableDiscoverState, diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index e96ee5c1..550d191c 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -140,6 +140,11 @@ export function SettingsPage() { const enableAutoplay = usePreferencesStore((s) => s.enableAutoplay); const setEnableAutoplay = usePreferencesStore((s) => s.setEnableAutoplay); + const enableSkipCredits = usePreferencesStore((s) => s.enableSkipCredits); + const setEnableSkipCredits = usePreferencesStore( + (s) => s.setEnableSkipCredits, + ); + const sourceOrder = usePreferencesStore((s) => s.sourceOrder); const setSourceOrder = usePreferencesStore((s) => s.setSourceOrder); @@ -178,7 +183,7 @@ export function SettingsPage() { proxySet, backendUrlSetting, febboxToken, - account?.profile, + account ? account.profile : undefined, enableThumbnails, enableAutoplay, enableDiscover, @@ -186,6 +191,7 @@ export function SettingsPage() { sourceOrder, enableSourceOrder, proxyTmdb, + enableSkipCredits, ); const availableSources = useMemo(() => { @@ -256,6 +262,7 @@ export function SettingsPage() { setEnableThumbnails(state.enableThumbnails.state); setEnableAutoplay(state.enableAutoplay.state); + setEnableSkipCredits(state.enableSkipCredits.state); setEnableDiscover(state.enableDiscover.state); setEnablePopDetails(state.enablePopDetails.state); setSourceOrder(state.sourceOrder.state); @@ -289,6 +296,7 @@ export function SettingsPage() { setFebboxToken, state, setEnableAutoplay, + setEnableSkipCredits, setEnableDiscover, setEnablePopDetails, setSourceOrder, @@ -344,6 +352,8 @@ export function SettingsPage() { setEnableThumbnails={state.enableThumbnails.set} enableAutoplay={state.enableAutoplay.state} setEnableAutoplay={state.enableAutoplay.set} + enableSkipCredits={state.enableSkipCredits.state} + setEnableSkipCredits={state.enableSkipCredits.set} sourceOrder={availableSources} setSourceOrder={state.sourceOrder.set} enableSourceOrder={state.enableSourceOrder.state} diff --git a/src/pages/parts/settings/PreferencesPart.tsx b/src/pages/parts/settings/PreferencesPart.tsx index f8f1c0e9..204f5c27 100644 --- a/src/pages/parts/settings/PreferencesPart.tsx +++ b/src/pages/parts/settings/PreferencesPart.tsx @@ -21,6 +21,8 @@ export function PreferencesPart(props: { setEnableThumbnails: (v: boolean) => void; enableAutoplay: boolean; setEnableAutoplay: (v: boolean) => void; + enableSkipCredits: boolean; + setEnableSkipCredits: (v: boolean) => void; sourceOrder: string[]; setSourceOrder: (v: string[]) => void; enableSourceOrder: boolean; @@ -122,6 +124,29 @@ export function PreferencesPart(props: { {t("settings.preferences.autoplayLabel")}

+ + {/* Skip End Credits Preference */} + {props.enableAutoplay && allowAutoplay && ( +
+

+ {t("settings.preferences.skipCredits")} +

+

+ {t("settings.preferences.skipCreditsDescription")} +

+
+ props.setEnableSkipCredits(!props.enableSkipCredits) + } + className="bg-dropdown-background hover:bg-dropdown-hoverBackground select-none my-4 cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg" + > + +

+ {t("settings.preferences.skipCreditsLabel")} +

+
+
+ )} diff --git a/src/stores/preferences/index.tsx b/src/stores/preferences/index.tsx index dea61417..b8132c4e 100644 --- a/src/stores/preferences/index.tsx +++ b/src/stores/preferences/index.tsx @@ -5,6 +5,7 @@ import { immer } from "zustand/middleware/immer"; export interface PreferencesStore { enableThumbnails: boolean; enableAutoplay: boolean; + enableSkipCredits: boolean; enableDiscover: boolean; enablePopDetails: boolean; sourceOrder: string[]; @@ -13,6 +14,7 @@ export interface PreferencesStore { setEnableThumbnails(v: boolean): void; setEnableAutoplay(v: boolean): void; + setEnableSkipCredits(v: boolean): void; setEnableDiscover(v: boolean): void; setEnablePopDetails(v: boolean): void; setSourceOrder(v: string[]): void; @@ -25,6 +27,7 @@ export const usePreferencesStore = create( immer((set) => ({ enableThumbnails: false, enableAutoplay: true, + enableSkipCredits: true, enableDiscover: true, enablePopDetails: false, sourceOrder: [], @@ -40,6 +43,11 @@ export const usePreferencesStore = create( s.enableAutoplay = v; }); }, + setEnableSkipCredits(v) { + set((s) => { + s.enableSkipCredits = v; + }); + }, setEnableDiscover(v) { set((s) => { s.enableDiscover = v;