From e9d8945f022de607a35ec93b8bf1c84cd113b6d3 Mon Sep 17 00:00:00 2001 From: Pas <74743263+Pasithea0@users.noreply.github.com> Date: Mon, 29 Sep 2025 16:03:07 -0600 Subject: [PATCH] Add small skip episode button to player --- src/components/player/Player.tsx | 1 + .../player/internals/SkipEpisodeButton.tsx | 63 +++++++++++++++++++ src/pages/parts/player/PlayerPart.tsx | 4 ++ 3 files changed, 68 insertions(+) create mode 100644 src/components/player/internals/SkipEpisodeButton.tsx diff --git a/src/components/player/Player.tsx b/src/components/player/Player.tsx index 7aa40a32..eebf459e 100644 --- a/src/components/player/Player.tsx +++ b/src/components/player/Player.tsx @@ -10,3 +10,4 @@ export * from "./base/CenterMobileControls"; export * from "./base/SubtitleView"; export * from "./internals/BookmarkButton"; export * from "./internals/InfoButton"; +export * from "./internals/SkipEpisodeButton"; diff --git a/src/components/player/internals/SkipEpisodeButton.tsx b/src/components/player/internals/SkipEpisodeButton.tsx new file mode 100644 index 00000000..71e4b96d --- /dev/null +++ b/src/components/player/internals/SkipEpisodeButton.tsx @@ -0,0 +1,63 @@ +import { useCallback } from "react"; +import { useTranslation } from "react-i18next"; + +import { Icons } from "@/components/Icon"; +import { usePlayerMeta } from "@/components/player/hooks/usePlayerMeta"; +import { VideoPlayerButton } from "@/components/player/internals/Button"; +import { PlayerMeta } from "@/stores/player/slices/source"; +import { usePlayerStore } from "@/stores/player/store"; +import { useProgressStore } from "@/stores/progress"; + +interface SkipEpisodeButtonProps { + inControl: boolean; + onChange?: (meta: PlayerMeta) => void; +} + +export function SkipEpisodeButton(props: SkipEpisodeButtonProps) { + const { t } = useTranslation(); + const meta = usePlayerStore((s) => s.meta); + const { setDirectMeta } = usePlayerMeta(); + const setShouldStartFromBeginning = usePlayerStore( + (s) => s.setShouldStartFromBeginning, + ); + const updateItem = useProgressStore((s) => s.updateItem); + + const nextEp = meta?.episodes?.find( + (v) => v.number === (meta?.episode?.number ?? 0) + 1, + ); + + const loadNextEpisode = useCallback(() => { + if (!meta || !nextEp) return; + const metaCopy = { ...meta }; + metaCopy.episode = nextEp; + setShouldStartFromBeginning(true); + setDirectMeta(metaCopy); + props.onChange?.(metaCopy); + const defaultProgress = { duration: 0, watched: 0 }; + updateItem({ + meta: metaCopy, + progress: defaultProgress, + }); + }, [ + setDirectMeta, + nextEp, + meta, + props, + setShouldStartFromBeginning, + updateItem, + ]); + + // Don't show button if not in control, not a show, or no next episode + if (!props.inControl) return null; + if (!meta?.episode || !nextEp) return null; + if (meta.type !== "show") return null; + + return ( + loadNextEpisode()} + icon={Icons.SKIP_EPISODE} + iconSizeClass="text-xl" + className="hover:bg-video-buttonBackground hover:bg-opacity-50" + /> + ); +} diff --git a/src/pages/parts/player/PlayerPart.tsx b/src/pages/parts/player/PlayerPart.tsx index 37a2c4a4..02415654 100644 --- a/src/pages/parts/player/PlayerPart.tsx +++ b/src/pages/parts/player/PlayerPart.tsx @@ -169,6 +169,10 @@ export function PlayerPart(props: PlayerPartProps) {
+ {status === playerStatus.PLAYING ? ( <>