From 52f008d7eb638143e1319d140d7058bfa1766674 Mon Sep 17 00:00:00 2001 From: Pinapelz Date: Sun, 7 Jun 2026 03:19:34 -0700 Subject: make unlimited mode start at random point in video --- src/components/MiniYouTubePlayer/index.tsx | 1 - src/components/YTPlayer/index.tsx | 149 +++++++++++++++++++++++------ 2 files changed, 122 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/components/MiniYouTubePlayer/index.tsx b/src/components/MiniYouTubePlayer/index.tsx index 5aa2d57..7c8e86a 100644 --- a/src/components/MiniYouTubePlayer/index.tsx +++ b/src/components/MiniYouTubePlayer/index.tsx @@ -1,4 +1,3 @@ -import React from "react"; import { default as YouTubePlayer } from "react-youtube"; interface Props { diff --git a/src/components/YTPlayer/index.tsx b/src/components/YTPlayer/index.tsx index 1aac9ac..9910351 100644 --- a/src/components/YTPlayer/index.tsx +++ b/src/components/YTPlayer/index.tsx @@ -9,81 +9,160 @@ interface Props { currentTry: number; } +const DEFAULT_VOLUME = 0.7; + +const loadVolume = () => { + try { + const storedVolume = localStorage.getItem("playerVolume"); + if (storedVolume === null) return DEFAULT_VOLUME; + + const parsedVolume = Number(storedVolume); + if (!Number.isFinite(parsedVolume)) return DEFAULT_VOLUME; + + return Math.max(0, Math.min(1, parsedVolume)); + } catch { + return DEFAULT_VOLUME; + } +}; + export function Player({ id, currentTry }: Props) { const opts = { width: "0", height: "0", }; - // react-youtube doesn't export types for this // eslint-disable-next-line @typescript-eslint/no-explicit-any const playerRef = React.useRef(null); + const MIN_REMAINING_SEC = 17; const currentPlayTime = playTimes[currentTry]; + const totalPlayTime = playTimes[playTimes.length - 1]; + const progressDurationSec = totalPlayTime / 1000; - const [play, setPlay] = React.useState(false); - - const [currentTime, setCurrentTime] = React.useState(0); + const [play, setPlay] = React.useState(false); + const [startTime, setStartTime] = React.useState(0); + const [currentTime, setCurrentTime] = React.useState(0); + const [isReady, setIsReady] = React.useState(false); + const [volume, setVolume] = React.useState(loadVolume); - const [isReady, setIsReady] = React.useState(false); + const progressValue = Math.min( + progressDurationSec, + Math.max(0, currentTime - startTime), + ); React.useEffect(() => { - setInterval(() => { + const interval = setInterval(() => { playerRef.current?.internalPlayer - .getCurrentTime() - .then((time: number) => { - setCurrentTime(time); - }); + ?.getCurrentTime() + .then((time: number) => setCurrentTime(time)); }, 250); + + return () => clearInterval(interval); }, []); React.useEffect(() => { - if (play) { - if (currentTime * 1000 >= currentPlayTime) { - playerRef.current?.internalPlayer.pauseVideo(); - playerRef.current?.internalPlayer.seekTo(0); - setPlay(false); - } + if (!play) return; + + const elapsedMs = Math.max(0, (currentTime - startTime) * 1000); + + if (elapsedMs >= currentPlayTime) { + playerRef.current?.internalPlayer.pauseVideo(); + playerRef.current?.internalPlayer.seekTo(startTime, true); + setPlay(false); } - }, [play, currentTime]); + }, [play, currentTime, startTime, currentPlayTime]); - // don't call play video each time currentTime changes const startPlayback = React.useCallback(() => { - playerRef.current?.internalPlayer.playVideo(); + const player = playerRef.current?.internalPlayer; + if (!player) return; + + player.seekTo(startTime, true); + player.playVideo(); setPlay(true); - }, []); + }, [startTime]); + + const updateVolume = React.useCallback( + (event: React.ChangeEvent) => { + setVolume(Number(event.target.value)); + }, + [], + ); + + const setReady = React.useCallback(async () => { - const setReady = React.useCallback(() => { + const player = playerRef.current?.internalPlayer; + if (!player) return; + + const duration = await player.getDuration(); + + const maxStart = Math.max(0, duration - MIN_REMAINING_SEC); + const randomStart = Math.random() * maxStart; + + setStartTime(randomStart); + player.setVolume(volume * 100); + player.seekTo(randomStart, true); setIsReady(true); - }, []); + }, [volume]); + + React.useEffect(() => { + if (!isReady) return; + + const player = playerRef.current?.internalPlayer; + if (!player) return; + + player.getDuration().then((duration: number) => { + const maxStart = Math.max(0, duration - MIN_REMAINING_SEC); + const randomStart = Math.random() * maxStart; + + setStartTime(randomStart); + setPlay(false); + setCurrentTime(0); + + player.seekTo(randomStart, true); + }); + }, [id, isReady]); + + React.useEffect(() => { + if (!isReady) return; + + playerRef.current?.internalPlayer?.setVolume(volume * 100); + + try { + localStorage.setItem("playerVolume", String(volume)); + } catch { + } + }, [isReady, volume]); return ( <> + {isReady ? ( <> - {currentTime !== 0 && } + + {playTimes.map((playTime) => ( ))} + 1s 16s - {!play && ( + + {!play ? ( - )} - {play && ( + ) : ( )} + + + + Volume {Math.round(volume * 100)}% + + + ) : (

Loading player...

-- cgit v1.2.3