mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-16 21:50:35 +02:00
add cancellation to player status fade
This commit is contained in:
@@ -31,28 +31,39 @@ export function MpvPlayer() {
|
|||||||
|
|
||||||
const [localPlayerStatus, setLocalPlayerStatus] = useState<PlayerStatus>(status);
|
const [localPlayerStatus, setLocalPlayerStatus] = useState<PlayerStatus>(status);
|
||||||
const [isTransitioning, setIsTransitioning] = useState(false);
|
const [isTransitioning, setIsTransitioning] = useState(false);
|
||||||
|
const fadeIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
const fadeAndSetStatus = useCallback(
|
const fadeAndSetStatus = useCallback(
|
||||||
async (startVolume: number, endVolume: number, duration: number, status: PlayerStatus) => {
|
async (startVolume: number, endVolume: number, duration: number, status: PlayerStatus) => {
|
||||||
if (isTransitioning) {
|
// Cancel any in-progress fade
|
||||||
return setLocalPlayerStatus(status);
|
if (fadeIntervalRef.current) {
|
||||||
|
clearInterval(fadeIntervalRef.current);
|
||||||
|
fadeIntervalRef.current = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set initial volume immediately to ensure we start from the correct position
|
||||||
|
// This is especially important when cancelling a previous fade
|
||||||
|
playerRef.current?.setVolume(startVolume);
|
||||||
|
|
||||||
const steps = duration / PLAY_PAUSE_FADE_INTERVAL;
|
const steps = duration / PLAY_PAUSE_FADE_INTERVAL;
|
||||||
const volumeStep = (endVolume - startVolume) / steps;
|
const volumeStep = (endVolume - startVolume) / steps;
|
||||||
let currentStep = 0;
|
let currentStep = 0;
|
||||||
|
|
||||||
const promise = new Promise((resolve) => {
|
const promise = new Promise<void>((resolve) => {
|
||||||
const interval = setInterval(() => {
|
fadeIntervalRef.current = setInterval(() => {
|
||||||
currentStep++;
|
currentStep++;
|
||||||
const newVolume = startVolume + volumeStep * currentStep;
|
const newVolume = startVolume + volumeStep * currentStep;
|
||||||
|
|
||||||
playerRef.current?.setVolume(newVolume);
|
playerRef.current?.setVolume(newVolume);
|
||||||
|
|
||||||
if (currentStep >= steps) {
|
if (currentStep >= steps) {
|
||||||
clearInterval(interval);
|
if (fadeIntervalRef.current) {
|
||||||
setIsTransitioning(false);
|
clearInterval(fadeIntervalRef.current);
|
||||||
resolve(true);
|
fadeIntervalRef.current = null;
|
||||||
|
}
|
||||||
|
// Ensure final volume is exactly the target
|
||||||
|
playerRef.current?.setVolume(endVolume);
|
||||||
|
resolve();
|
||||||
}
|
}
|
||||||
}, PLAY_PAUSE_FADE_INTERVAL);
|
}, PLAY_PAUSE_FADE_INTERVAL);
|
||||||
});
|
});
|
||||||
@@ -65,7 +76,7 @@ export function MpvPlayer() {
|
|||||||
await promise;
|
await promise;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[isTransitioning],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onProgress = useCallback(() => {
|
const onProgress = useCallback(() => {
|
||||||
@@ -106,9 +117,19 @@ export function MpvPlayer() {
|
|||||||
playerRef.current?.setVolume(volume);
|
playerRef.current?.setVolume(volume);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[volume, isTransitioning, fadeAndSetStatus],
|
[volume, fadeAndSetStatus],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Cleanup fade interval on unmount
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (fadeIntervalRef.current) {
|
||||||
|
clearInterval(fadeIntervalRef.current);
|
||||||
|
fadeIntervalRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (localPlayerStatus !== PlayerStatus.PLAYING) {
|
if (localPlayerStatus !== PlayerStatus.PLAYING) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -40,31 +40,42 @@ export function WebPlayer() {
|
|||||||
|
|
||||||
const [localPlayerStatus, setLocalPlayerStatus] = useState<PlayerStatus>(status);
|
const [localPlayerStatus, setLocalPlayerStatus] = useState<PlayerStatus>(status);
|
||||||
const [isTransitioning, setIsTransitioning] = useState<boolean | string>(false);
|
const [isTransitioning, setIsTransitioning] = useState<boolean | string>(false);
|
||||||
|
const fadeIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
const [player1Source, setPlayer1Source] = useState<MediaElementAudioSourceNode | null>(null);
|
const [player1Source, setPlayer1Source] = useState<MediaElementAudioSourceNode | null>(null);
|
||||||
const [player2Source, setPlayer2Source] = useState<MediaElementAudioSourceNode | null>(null);
|
const [player2Source, setPlayer2Source] = useState<MediaElementAudioSourceNode | null>(null);
|
||||||
|
|
||||||
const fadeAndSetStatus = useCallback(
|
const fadeAndSetStatus = useCallback(
|
||||||
async (startVolume: number, endVolume: number, duration: number, status: PlayerStatus) => {
|
async (startVolume: number, endVolume: number, duration: number, status: PlayerStatus) => {
|
||||||
if (isTransitioning) {
|
// Cancel any in-progress fade
|
||||||
return setLocalPlayerStatus(status);
|
if (fadeIntervalRef.current) {
|
||||||
|
clearInterval(fadeIntervalRef.current);
|
||||||
|
fadeIntervalRef.current = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set initial volume immediately to ensure we start from the correct position
|
||||||
|
// This is especially important when cancelling a previous fade
|
||||||
|
playerRef.current?.setVolume(startVolume);
|
||||||
|
|
||||||
const steps = duration / PLAY_PAUSE_FADE_INTERVAL;
|
const steps = duration / PLAY_PAUSE_FADE_INTERVAL;
|
||||||
const volumeStep = (endVolume - startVolume) / steps;
|
const volumeStep = (endVolume - startVolume) / steps;
|
||||||
let currentStep = 0;
|
let currentStep = 0;
|
||||||
|
|
||||||
const promise = new Promise((resolve) => {
|
const promise = new Promise<void>((resolve) => {
|
||||||
const interval = setInterval(() => {
|
fadeIntervalRef.current = setInterval(() => {
|
||||||
currentStep++;
|
currentStep++;
|
||||||
const newVolume = startVolume + volumeStep * currentStep;
|
const newVolume = startVolume + volumeStep * currentStep;
|
||||||
|
|
||||||
playerRef.current?.setVolume(newVolume);
|
playerRef.current?.setVolume(newVolume);
|
||||||
|
|
||||||
if (currentStep >= steps) {
|
if (currentStep >= steps) {
|
||||||
clearInterval(interval);
|
if (fadeIntervalRef.current) {
|
||||||
setIsTransitioning(false);
|
clearInterval(fadeIntervalRef.current);
|
||||||
resolve(true);
|
fadeIntervalRef.current = null;
|
||||||
|
}
|
||||||
|
// Ensure final volume is exactly the target
|
||||||
|
playerRef.current?.setVolume(endVolume);
|
||||||
|
resolve();
|
||||||
}
|
}
|
||||||
}, PLAY_PAUSE_FADE_INTERVAL);
|
}, PLAY_PAUSE_FADE_INTERVAL);
|
||||||
});
|
});
|
||||||
@@ -77,7 +88,7 @@ export function WebPlayer() {
|
|||||||
await promise;
|
await promise;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[isTransitioning],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onProgressPlayer1 = useCallback(
|
const onProgressPlayer1 = useCallback(
|
||||||
@@ -242,6 +253,16 @@ export function WebPlayer() {
|
|||||||
[volume, num, isTransitioning, transitionType],
|
[volume, num, isTransitioning, transitionType],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Cleanup fade interval on unmount
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (fadeIntervalRef.current) {
|
||||||
|
clearInterval(fadeIntervalRef.current);
|
||||||
|
fadeIntervalRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (localPlayerStatus !== PlayerStatus.PLAYING) {
|
if (localPlayerStatus !== PlayerStatus.PLAYING) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user