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
(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")}
+ {t("settings.preferences.skipCredits")} +
++ {t("settings.preferences.skipCreditsDescription")} +
++ {t("settings.preferences.skipCreditsLabel")} +
+