add half-speed playback toggle for all three video sources

Uses a ref to avoid stale closure issues with YouTube's state change callback,
ensuring the playback rate applies to both local recordings and the YouTube video.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-08 19:57:52 -05:00
parent fc190b72b3
commit 1770aadf99
2 changed files with 49 additions and 0 deletions

View File

@@ -390,6 +390,20 @@
background-color: #3b82f6; background-color: #3b82f6;
} }
.speed-btn {
background-color: #3b3b3b;
min-width: 60px;
}
.speed-btn.active {
background-color: #f59e0b;
color: #000;
}
.speed-btn.active:hover:not(:disabled) {
background-color: #fbbf24;
}
.loading-text { .loading-text {
color: #888; color: #888;
font-style: italic; font-style: italic;

View File

@@ -42,6 +42,8 @@ export default function YouTubePlayer() {
const localVideo1Ref = useRef(null); const localVideo1Ref = useRef(null);
const localVideo2Ref = useRef(null); const localVideo2Ref = useRef(null);
const [isPlaying, setIsPlaying] = useState(false); const [isPlaying, setIsPlaying] = useState(false);
const [playbackRate, setPlaybackRate] = useState(1);
const playbackRateRef = useRef(1);
const [isReady, setIsReady] = useState(false); const [isReady, setIsReady] = useState(false);
const [duration, setDuration] = useState(0); const [duration, setDuration] = useState(0);
const [currentTime, setCurrentTime] = useState(0); const [currentTime, setCurrentTime] = useState(0);
@@ -326,13 +328,35 @@ export default function YouTubePlayer() {
}; };
const applyPlaybackRate = useCallback((rate) => {
if (youtubePlayerRef.current && youtubePlayerRef.current.setPlaybackRate) {
youtubePlayerRef.current.setPlaybackRate(rate);
}
if (localVideo1Ref.current) {
localVideo1Ref.current.playbackRate = rate;
}
if (localVideo2Ref.current) {
localVideo2Ref.current.playbackRate = rate;
}
}, []);
const handleToggleSpeed = () => {
const newRate = playbackRateRef.current === 1 ? 0.5 : 1;
playbackRateRef.current = newRate;
setPlaybackRate(newRate);
applyPlaybackRate(newRate);
};
const playAllLocalVideos = () => { const playAllLocalVideos = () => {
const rate = playbackRateRef.current;
if (localVideo1Ref.current) { if (localVideo1Ref.current) {
localVideo1Ref.current.muted = true; localVideo1Ref.current.muted = true;
localVideo1Ref.current.playbackRate = rate;
localVideo1Ref.current.play(); localVideo1Ref.current.play();
} }
if (localVideo2Ref.current) { if (localVideo2Ref.current) {
localVideo2Ref.current.muted = true; localVideo2Ref.current.muted = true;
localVideo2Ref.current.playbackRate = rate;
localVideo2Ref.current.play(); localVideo2Ref.current.play();
} }
}; };
@@ -425,6 +449,9 @@ export default function YouTubePlayer() {
// Play all videos // Play all videos
if (youtubePlayerRef.current && isReady) { if (youtubePlayerRef.current && isReady) {
youtubePlayerRef.current.playVideo(); youtubePlayerRef.current.playVideo();
if (youtubePlayerRef.current.setPlaybackRate) {
youtubePlayerRef.current.setPlaybackRate(playbackRateRef.current);
}
} }
playAllLocalVideos(); playAllLocalVideos();
}; };
@@ -810,6 +837,14 @@ export default function YouTubePlayer() {
> >
{isPlaying ? '⏸ Pause' : '▶ Play'} {isPlaying ? '⏸ Pause' : '▶ Play'}
</button> </button>
<button
className={`control-btn speed-btn${playbackRate === 0.5 ? ' active' : ''}`}
onClick={handleToggleSpeed}
disabled={!isReady || isPlaybackBlocked}
title="Toggle half speed"
>
{playbackRate === 0.5 ? '0.5x' : '1x'}
</button>
</div> </div>
{isProcessingPlayback && ( {isProcessingPlayback && (
<p className="loading-text">Processing video for mobile playback. This may take a few minutes for long recordings.</p> <p className="loading-text">Processing video for mobile playback. This may take a few minutes for long recordings.</p>