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:
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user