mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-06 20:10:12 +02:00
fix: sleep timer end-of-song mode (#1706)
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
|||||||
usePlayerData,
|
usePlayerData,
|
||||||
usePlayerMuted,
|
usePlayerMuted,
|
||||||
usePlayerProperties,
|
usePlayerProperties,
|
||||||
|
usePlayerStoreBase,
|
||||||
usePlayerVolume,
|
usePlayerVolume,
|
||||||
} from '/@/renderer/store';
|
} from '/@/renderer/store';
|
||||||
import { toast } from '/@/shared/components/toast/toast';
|
import { toast } from '/@/shared/components/toast/toast';
|
||||||
@@ -180,7 +181,15 @@ export function WebPlayer() {
|
|||||||
|
|
||||||
promise.then(() => {
|
promise.then(() => {
|
||||||
playerRef.current?.player1()?.ref?.getInternalPlayer().pause();
|
playerRef.current?.player1()?.ref?.getInternalPlayer().pause();
|
||||||
playerRef.current?.setVolume(volume);
|
|
||||||
|
// If mediaAutoNext resulted in a paused state (e.g. end of queue,
|
||||||
|
// or pauseOnNextSongEnd flag), stop all audio instead of restoring volume.
|
||||||
|
const currentStatus = usePlayerStoreBase.getState().player.status;
|
||||||
|
if (currentStatus === PlayerStatus.PAUSED) {
|
||||||
|
playerRef.current?.pause();
|
||||||
|
} else {
|
||||||
|
playerRef.current?.setVolume(volume);
|
||||||
|
}
|
||||||
setIsTransitioning(false);
|
setIsTransitioning(false);
|
||||||
});
|
});
|
||||||
}, [mediaAutoNext, volume]);
|
}, [mediaAutoNext, volume]);
|
||||||
@@ -193,7 +202,13 @@ export function WebPlayer() {
|
|||||||
|
|
||||||
promise.then(() => {
|
promise.then(() => {
|
||||||
playerRef.current?.player2()?.ref?.getInternalPlayer().pause();
|
playerRef.current?.player2()?.ref?.getInternalPlayer().pause();
|
||||||
playerRef.current?.setVolume(volume);
|
|
||||||
|
const currentStatus = usePlayerStoreBase.getState().player.status;
|
||||||
|
if (currentStatus === PlayerStatus.PAUSED) {
|
||||||
|
playerRef.current?.pause();
|
||||||
|
} else {
|
||||||
|
playerRef.current?.setVolume(volume);
|
||||||
|
}
|
||||||
setIsTransitioning(false);
|
setIsTransitioning(false);
|
||||||
});
|
});
|
||||||
}, [mediaAutoNext, volume]);
|
}, [mediaAutoNext, volume]);
|
||||||
@@ -527,6 +542,11 @@ function crossfadeHandler(args: {
|
|||||||
|
|
||||||
if (!isTransitioning) {
|
if (!isTransitioning) {
|
||||||
if (duration > 0 && currentTime > duration - crossfadeDuration) {
|
if (duration > 0 && currentTime > duration - crossfadeDuration) {
|
||||||
|
// Skip pre-starting next player if pauseOnNextSongEnd is set
|
||||||
|
if (usePlayerStoreBase.getState().player.pauseOnNextSongEnd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
nextPlayer.setVolume(0);
|
nextPlayer.setVolume(0);
|
||||||
nextPlayer.ref?.getInternalPlayer().play();
|
nextPlayer.ref?.getInternalPlayer().play();
|
||||||
return setIsTransitioning(player);
|
return setIsTransitioning(player);
|
||||||
@@ -616,6 +636,11 @@ function gaplessHandler(args: {
|
|||||||
const durationPadding = getDurationPadding(isFlac);
|
const durationPadding = getDurationPadding(isFlac);
|
||||||
|
|
||||||
if (currentTime + durationPadding >= duration) {
|
if (currentTime + durationPadding >= duration) {
|
||||||
|
// Skip pre-starting next player if pauseOnNextSongEnd is set
|
||||||
|
if (usePlayerStoreBase.getState().player.pauseOnNextSongEnd) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return nextPlayer.ref
|
return nextPlayer.ref
|
||||||
?.getInternalPlayer()
|
?.getInternalPlayer()
|
||||||
?.play()
|
?.play()
|
||||||
|
|||||||
@@ -96,24 +96,19 @@ const useSleepTimer = () => {
|
|||||||
[handleOnCurrentSongChange, handleOnPlayerProgress],
|
[handleOnCurrentSongChange, handleOnPlayerProgress],
|
||||||
);
|
);
|
||||||
|
|
||||||
// End-of-song mode: subscribe to player index changes
|
// End-of-song mode: set the pauseOnNextSongEnd flag so that
|
||||||
|
// mediaAutoNext returns PAUSED status when the current song ends.
|
||||||
|
// This is a generic player mechanism — the web player handles it
|
||||||
|
// without needing to know about the sleep timer.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!active || mode !== 'endOfSong') return;
|
if (!active || mode !== 'endOfSong') return;
|
||||||
|
|
||||||
const initialIndex = usePlayerStoreBase.getState().player.index;
|
usePlayerStoreBase.getState().setPauseOnNextSongEnd(true);
|
||||||
|
|
||||||
const unsub = usePlayerStoreBase.subscribe(
|
return () => {
|
||||||
(state) => state.player.index,
|
usePlayerStoreBase.getState().setPauseOnNextSongEnd(false);
|
||||||
(index) => {
|
};
|
||||||
if (index !== initialIndex) {
|
}, [active, mode]);
|
||||||
cancelTimer();
|
|
||||||
mediaPauseRef.current();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return () => unsub();
|
|
||||||
}, [active, mode, cancelTimer]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SleepTimerHookInner = () => {
|
export const SleepTimerHookInner = () => {
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ interface Actions {
|
|||||||
moveSelectedToTop: (items: QueueSong[]) => void;
|
moveSelectedToTop: (items: QueueSong[]) => void;
|
||||||
setCrossfadeDuration: (duration: number) => void;
|
setCrossfadeDuration: (duration: number) => void;
|
||||||
setCrossfadeStyle: (style: CrossfadeStyle) => void;
|
setCrossfadeStyle: (style: CrossfadeStyle) => void;
|
||||||
|
setPauseOnNextSongEnd: (value: boolean) => void;
|
||||||
setQueue: (data: Song[], index?: number, position?: number) => void;
|
setQueue: (data: Song[], index?: number, position?: number) => void;
|
||||||
setRepeat: (repeat: PlayerRepeat) => void;
|
setRepeat: (repeat: PlayerRepeat) => void;
|
||||||
setShuffle: (shuffle: PlayerShuffle) => void;
|
setShuffle: (shuffle: PlayerShuffle) => void;
|
||||||
@@ -91,6 +92,7 @@ interface State {
|
|||||||
crossfadeStyle: CrossfadeStyle;
|
crossfadeStyle: CrossfadeStyle;
|
||||||
index: number;
|
index: number;
|
||||||
muted: boolean;
|
muted: boolean;
|
||||||
|
pauseOnNextSongEnd: boolean;
|
||||||
playerNum: 1 | 2;
|
playerNum: 1 | 2;
|
||||||
repeat: PlayerRepeat;
|
repeat: PlayerRepeat;
|
||||||
seekToTimestamp: string;
|
seekToTimestamp: string;
|
||||||
@@ -295,6 +297,7 @@ const initialState: State = {
|
|||||||
crossfadeStyle: CrossfadeStyle.EQUAL_POWER,
|
crossfadeStyle: CrossfadeStyle.EQUAL_POWER,
|
||||||
index: -1,
|
index: -1,
|
||||||
muted: false,
|
muted: false,
|
||||||
|
pauseOnNextSongEnd: false,
|
||||||
playerNum: 1,
|
playerNum: 1,
|
||||||
repeat: PlayerRepeat.NONE,
|
repeat: PlayerRepeat.NONE,
|
||||||
seekToTimestamp: uniqueSeekToTimestamp(0),
|
seekToTimestamp: uniqueSeekToTimestamp(0),
|
||||||
@@ -882,13 +885,19 @@ export const usePlayerStoreBase = createWithEqualityFn<PlayerState>()(
|
|||||||
playbackLength,
|
playbackLength,
|
||||||
repeat,
|
repeat,
|
||||||
);
|
);
|
||||||
const newStatus = shouldPause ? PlayerStatus.PAUSED : PlayerStatus.PLAYING;
|
const pauseOnNext = player.pauseOnNextSongEnd;
|
||||||
|
const newStatus =
|
||||||
|
shouldPause || pauseOnNext ? PlayerStatus.PAUSED : PlayerStatus.PLAYING;
|
||||||
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.player.index = nextPlaybackIndex;
|
state.player.index = nextPlaybackIndex;
|
||||||
state.player.playerNum = newPlayerNum;
|
state.player.playerNum = newPlayerNum;
|
||||||
setTimestampStore(0);
|
setTimestampStore(0);
|
||||||
state.player.status = newStatus;
|
state.player.status = newStatus;
|
||||||
|
|
||||||
|
if (pauseOnNext) {
|
||||||
|
state.player.pauseOnNextSongEnd = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (repeat === PlayerRepeat.ONE && nextPlaybackIndex === currentIndex) {
|
if (repeat === PlayerRepeat.ONE && nextPlaybackIndex === currentIndex) {
|
||||||
@@ -1315,6 +1324,11 @@ export const usePlayerStoreBase = createWithEqualityFn<PlayerState>()(
|
|||||||
state.player.crossfadeStyle = style;
|
state.player.crossfadeStyle = style;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
setPauseOnNextSongEnd: (value: boolean) => {
|
||||||
|
set((state) => {
|
||||||
|
state.player.pauseOnNextSongEnd = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
setRepeat: (repeat: PlayerRepeat) => {
|
setRepeat: (repeat: PlayerRepeat) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.player.repeat = repeat;
|
state.player.repeat = repeat;
|
||||||
@@ -1633,6 +1647,7 @@ export const usePlayerActions = () => {
|
|||||||
moveSelectedToTop: state.moveSelectedToTop,
|
moveSelectedToTop: state.moveSelectedToTop,
|
||||||
setCrossfadeDuration: state.setCrossfadeDuration,
|
setCrossfadeDuration: state.setCrossfadeDuration,
|
||||||
setCrossfadeStyle: state.setCrossfadeStyle,
|
setCrossfadeStyle: state.setCrossfadeStyle,
|
||||||
|
setPauseOnNextSongEnd: state.setPauseOnNextSongEnd,
|
||||||
setQueue: state.setQueue,
|
setQueue: state.setQueue,
|
||||||
setRepeat: state.setRepeat,
|
setRepeat: state.setRepeat,
|
||||||
setShuffle: state.setShuffle,
|
setShuffle: state.setShuffle,
|
||||||
|
|||||||
Reference in New Issue
Block a user