Add toggle for skipping at 99%

This commit is contained in:
Pas 2025-03-15 21:42:30 -07:00
parent 81138d5d90
commit 1aa47cfe7c
6 changed files with 76 additions and 4 deletions

View file

@ -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 <bold>extension</bold> is required for that source. <br><br> <strong>(The default order is best for most users)</strong>",
"title": "Preferences",

View file

@ -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;

View file

@ -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,

View file

@ -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}

View file

@ -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")}
</p>
</div>
{/* Skip End Credits Preference */}
{props.enableAutoplay && allowAutoplay && (
<div className="pt-4">
<p className="text-white font-bold mb-3">
{t("settings.preferences.skipCredits")}
</p>
<p className="max-w-[25rem] font-medium">
{t("settings.preferences.skipCreditsDescription")}
</p>
<div
onClick={() =>
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"
>
<Toggle enabled={props.enableSkipCredits} />
<p className="flex-1 text-white font-bold">
{t("settings.preferences.skipCreditsLabel")}
</p>
</div>
</div>
)}
</div>
</div>

View file

@ -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<PreferencesStore>((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;