From a78d917fd27cd86f869839e42a2136d45945d3a2 Mon Sep 17 00:00:00 2001 From: jeffvli Date: Sun, 16 Nov 2025 21:37:07 -0800 Subject: [PATCH] move timestamp update to separate effect --- .../audio-player/engine/mpv-player-engine.tsx | 14 ++++-- .../audio-player/engine/web-player-engine.tsx | 2 +- .../player/audio-player/mpv-player.tsx | 39 +++++++++++---- .../player/audio-player/web-player.tsx | 48 ++++++++++++------- 4 files changed, 74 insertions(+), 29 deletions(-) diff --git a/src/renderer/features/player/audio-player/engine/mpv-player-engine.tsx b/src/renderer/features/player/audio-player/engine/mpv-player-engine.tsx index 38a1806a7..4d77813f3 100644 --- a/src/renderer/features/player/audio-player/engine/mpv-player-engine.tsx +++ b/src/renderer/features/player/audio-player/engine/mpv-player-engine.tsx @@ -17,7 +17,7 @@ interface MpvPlayerEngineProps { nextSrc: string | undefined; onEnded: () => void; onProgress: (e: PlayerOnProgressProps) => void; - playerRef: RefObject; + playerRef: RefObject; playerStatus: PlayerStatus; speed?: number; volume: number; @@ -109,7 +109,9 @@ export const MpvPlayerEngine = (props: MpvPlayerEngineProps) => { } const vol = volume / 100 || 0; - setInternalVolume(vol); + queueMicrotask(() => { + setInternalVolume(vol); + }); mpvPlayer.volume(volume); }, [volume]); @@ -147,11 +149,15 @@ export const MpvPlayerEngine = (props: MpvPlayerEngineProps) => { if (currentSrc) { // Set current song at position 0 and next song at position 1 mpvPlayer.setQueue(currentSrc, nextSrc, playerStatus !== PlayerStatus.PLAYING); - setPreviousCurrentSrc(currentSrc); + setTimeout(() => { + setPreviousCurrentSrc(currentSrc); + }, 0); } else { // Clear queue if no current song mpvPlayer.setQueue(undefined, undefined, true); - setPreviousCurrentSrc(undefined); + setTimeout(() => { + setPreviousCurrentSrc(undefined); + }, 0); } } else { // If currentSrc hasn't changed but nextSrc has, update position 1 diff --git a/src/renderer/features/player/audio-player/engine/web-player-engine.tsx b/src/renderer/features/player/audio-player/engine/web-player-engine.tsx index 3f11948cb..8cc22ca99 100644 --- a/src/renderer/features/player/audio-player/engine/web-player-engine.tsx +++ b/src/renderer/features/player/audio-player/engine/web-player-engine.tsx @@ -26,7 +26,7 @@ interface WebPlayerEngineProps { onProgressPlayer1: (e: PlayerOnProgressProps) => void; onProgressPlayer2: (e: PlayerOnProgressProps) => void; playerNum: number; - playerRef: RefObject; + playerRef: RefObject; playerStatus: PlayerStatus; speed?: number; src1: string | undefined; diff --git a/src/renderer/features/player/audio-player/mpv-player.tsx b/src/renderer/features/player/audio-player/mpv-player.tsx index 532f29ba1..358be3f7f 100644 --- a/src/renderer/features/player/audio-player/mpv-player.tsx +++ b/src/renderer/features/player/audio-player/mpv-player.tsx @@ -1,10 +1,10 @@ -import { useCallback, useRef, useState } from 'react'; +import isElectron from 'is-electron'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { MpvPlayerEngine, MpvPlayerEngineHandle } from './engine/mpv-player-engine'; import { useMainPlayerListener } from '/@/renderer/features/player/audio-player/hooks/use-main-player-listener'; import { usePlayerEvents } from '/@/renderer/features/player/audio-player/hooks/use-player-events'; -import { PlayerOnProgressProps } from '/@/renderer/features/player/audio-player/types'; import { usePlayerActions, usePlayerData, @@ -17,6 +17,8 @@ import { PlayerStatus } from '/@/shared/types/types'; const PLAY_PAUSE_FADE_DURATION = 300; const PLAY_PAUSE_FADE_INTERVAL = 10; +const mpvPlayer = isElectron() ? window.api.mpvPlayer : null; + export function MpvPlayer() { const playerRef = useRef(null); const { currentSong, nextSong, status } = usePlayerData(); @@ -64,12 +66,10 @@ export function MpvPlayer() { [isTransitioning], ); - const onProgress = useCallback( - (e: PlayerOnProgressProps) => { - setTimestamp(Number(e.playedSeconds.toFixed(0))); - }, - [setTimestamp], - ); + const onProgress = useCallback(() => { + // Progress callback is now only used for transition logic + // Timestamp updates are handled separately in useEffect + }, []); const handleOnEnded = useCallback(() => { // When mpv auto-advances to the next song (position 1 becomes position 0), @@ -107,6 +107,29 @@ export function MpvPlayer() { [volume, isTransitioning, fadeAndSetStatus], ); + useEffect(() => { + if (localPlayerStatus !== PlayerStatus.PLAYING) { + return; + } + + const interval = setInterval(async () => { + if (!mpvPlayer) { + return; + } + + try { + const time = await mpvPlayer.getCurrentTime(); + if (time !== undefined) { + setTimestamp(Number(time.toFixed(0))); + } + } catch { + // Do nothing + } + }, 500); + + return () => clearInterval(interval); + }, [localPlayerStatus, setTimestamp]); + useMainPlayerListener(); return ( diff --git a/src/renderer/features/player/audio-player/web-player.tsx b/src/renderer/features/player/audio-player/web-player.tsx index 2d7ba6f9b..70d89fef3 100644 --- a/src/renderer/features/player/audio-player/web-player.tsx +++ b/src/renderer/features/player/audio-player/web-player.tsx @@ -1,7 +1,7 @@ import type { Dispatch } from 'react'; import type ReactPlayer from 'react-player'; -import { useCallback, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { WebPlayerEngine, @@ -23,7 +23,7 @@ const PLAY_PAUSE_FADE_DURATION = 300; const PLAY_PAUSE_FADE_INTERVAL = 10; export function WebPlayer() { - const playerRef = useRef(null); + const playerRef = useRef(null); const { num, player1, player2, status } = usePlayerData(); const { mediaAutoNext, setTimestamp } = usePlayerActions(); const { crossfadeDuration, speed, transitionType } = usePlayerProperties(); @@ -71,12 +71,6 @@ export function WebPlayer() { const onProgressPlayer1 = useCallback( (e: PlayerOnProgressProps) => { - if (transitionType === 'crossfade' && num === 1) { - setTimestamp(Number(e.playedSeconds.toFixed(0))); - } else if (transitionType === 'gapless') { - setTimestamp(Number(e.playedSeconds.toFixed(0))); - } - if (!playerRef.current?.player1()) { return; } @@ -108,17 +102,11 @@ export function WebPlayer() { break; } }, - [crossfadeDuration, isTransitioning, num, setTimestamp, transitionType, volume], + [crossfadeDuration, isTransitioning, num, transitionType, volume], ); const onProgressPlayer2 = useCallback( (e: PlayerOnProgressProps) => { - if (transitionType === PlayerStyle.CROSSFADE && num === 2) { - setTimestamp(Number(e.playedSeconds.toFixed(0))); - } else if (transitionType === PlayerStyle.GAPLESS) { - setTimestamp(Number(e.playedSeconds.toFixed(0))); - } - if (!playerRef.current?.player2()) { return; } @@ -150,7 +138,7 @@ export function WebPlayer() { break; } }, - [crossfadeDuration, isTransitioning, num, setTimestamp, transitionType, volume], + [crossfadeDuration, isTransitioning, num, transitionType, volume], ); const handleOnEndedPlayer1 = useCallback(() => { @@ -205,6 +193,34 @@ export function WebPlayer() { [volume, num, isTransitioning], ); + useEffect(() => { + if (localPlayerStatus !== PlayerStatus.PLAYING) { + return; + } + + const interval = setInterval(() => { + const activePlayer = + num === 1 ? playerRef.current?.player1() : playerRef.current?.player2(); + const internalPlayer = + activePlayer?.ref?.getInternalPlayer() as HTMLAudioElement | null; + + if (!internalPlayer) { + return; + } + + const currentTime = internalPlayer.currentTime; + + if ( + transitionType === PlayerStyle.CROSSFADE || + transitionType === PlayerStyle.GAPLESS + ) { + setTimestamp(Number(currentTime.toFixed(0))); + } + }, 500); + + return () => clearInterval(interval); + }, [localPlayerStatus, num, setTimestamp, transitionType]); + useMainPlayerListener(); return (