feishin/src/renderer/components/audio-player/index.tsx

196 lines
6.7 KiB
TypeScript
Raw Normal View History

2022-12-19 15:59:14 -08:00
import { useImperativeHandle, forwardRef, useRef, useState, useCallback, useEffect } from 'react';
import isElectron from 'is-electron';
import type { ReactPlayerProps } from 'react-player';
import ReactPlayer from 'react-player';
import type { Song } from '/@/renderer/api/types';
import {
2023-07-01 19:10:05 -07:00
crossfadeHandler,
gaplessHandler,
2022-12-19 15:59:14 -08:00
} from '/@/renderer/components/audio-player/utils/list-handlers';
import { useSettingsStore } from '/@/renderer/store/settings.store';
import type { CrossfadeStyle } from '/@/renderer/types';
import { PlaybackStyle, PlayerStatus } from '/@/renderer/types';
interface AudioPlayerProps extends ReactPlayerProps {
2023-07-01 19:10:05 -07:00
crossfadeDuration: number;
crossfadeStyle: CrossfadeStyle;
currentPlayer: 1 | 2;
playbackStyle: PlaybackStyle;
player1: Song;
player2: Song;
status: PlayerStatus;
volume: number;
2022-12-19 15:59:14 -08:00
}
export type AudioPlayerProgress = {
2023-07-01 19:10:05 -07:00
loaded: number;
loadedSeconds: number;
played: number;
playedSeconds: number;
2022-12-19 15:59:14 -08:00
};
const getDuration = (ref: any) => {
2023-07-01 19:10:05 -07:00
return ref.current?.player?.player?.player?.duration;
2022-12-19 15:59:14 -08:00
};
export const AudioPlayer = forwardRef(
2023-07-01 19:10:05 -07:00
(
{
status,
playbackStyle,
crossfadeStyle,
crossfadeDuration,
currentPlayer,
autoNext,
player1,
player2,
muted,
volume,
}: AudioPlayerProps,
ref: any,
) => {
const player1Ref = useRef<any>(null);
const player2Ref = useRef<any>(null);
const [isTransitioning, setIsTransitioning] = useState(false);
const audioDeviceId = useSettingsStore((state) => state.playback.audioDeviceId);
2022-12-19 15:59:14 -08:00
2023-07-01 19:10:05 -07:00
useImperativeHandle(ref, () => ({
get player1() {
return player1Ref?.current;
},
get player2() {
return player2Ref?.current;
},
}));
2022-12-19 15:59:14 -08:00
2023-07-01 19:10:05 -07:00
const handleOnEnded = () => {
autoNext();
setIsTransitioning(false);
};
2022-12-19 15:59:14 -08:00
2023-07-01 19:10:05 -07:00
useEffect(() => {
if (status === PlayerStatus.PLAYING) {
if (currentPlayer === 1) {
player1Ref.current?.getInternalPlayer()?.play();
} else {
player2Ref.current?.getInternalPlayer()?.play();
}
} else {
player1Ref.current?.getInternalPlayer()?.pause();
player2Ref.current?.getInternalPlayer()?.pause();
}
}, [currentPlayer, status]);
2022-12-19 15:59:14 -08:00
2023-07-01 19:10:05 -07:00
const handleCrossfade1 = useCallback(
(e: AudioPlayerProgress) => {
return crossfadeHandler({
currentPlayer,
currentPlayerRef: player1Ref,
currentTime: e.playedSeconds,
duration: getDuration(player1Ref),
fadeDuration: crossfadeDuration,
fadeType: crossfadeStyle,
isTransitioning,
nextPlayerRef: player2Ref,
player: 1,
setIsTransitioning,
volume,
});
},
[crossfadeDuration, crossfadeStyle, currentPlayer, isTransitioning, volume],
);
2022-12-19 15:59:14 -08:00
2023-07-01 19:10:05 -07:00
const handleCrossfade2 = useCallback(
(e: AudioPlayerProgress) => {
return crossfadeHandler({
currentPlayer,
currentPlayerRef: player2Ref,
currentTime: e.playedSeconds,
duration: getDuration(player2Ref),
fadeDuration: crossfadeDuration,
fadeType: crossfadeStyle,
isTransitioning,
nextPlayerRef: player1Ref,
player: 2,
setIsTransitioning,
volume,
});
},
[crossfadeDuration, crossfadeStyle, currentPlayer, isTransitioning, volume],
);
2022-12-19 15:59:14 -08:00
2023-07-01 19:10:05 -07:00
const handleGapless1 = useCallback(
(e: AudioPlayerProgress) => {
return gaplessHandler({
currentTime: e.playedSeconds,
duration: getDuration(player1Ref),
isFlac: player1?.container === 'flac',
isTransitioning,
nextPlayerRef: player2Ref,
setIsTransitioning,
});
},
[isTransitioning, player1?.container],
);
2022-12-19 15:59:14 -08:00
2023-07-01 19:10:05 -07:00
const handleGapless2 = useCallback(
(e: AudioPlayerProgress) => {
return gaplessHandler({
currentTime: e.playedSeconds,
duration: getDuration(player2Ref),
isFlac: player2?.container === 'flac',
isTransitioning,
nextPlayerRef: player1Ref,
setIsTransitioning,
});
},
[isTransitioning, player2?.container],
);
2022-12-19 15:59:14 -08:00
2023-07-01 19:10:05 -07:00
useEffect(() => {
if (isElectron()) {
if (audioDeviceId) {
player1Ref.current?.getInternalPlayer()?.setSinkId(audioDeviceId);
player2Ref.current?.getInternalPlayer()?.setSinkId(audioDeviceId);
} else {
player1Ref.current?.getInternalPlayer()?.setSinkId('');
player2Ref.current?.getInternalPlayer()?.setSinkId('');
}
}
}, [audioDeviceId]);
2022-12-19 15:59:14 -08:00
2023-07-01 19:10:05 -07:00
return (
<>
<ReactPlayer
ref={player1Ref}
height={0}
muted={muted}
playing={currentPlayer === 1 && status === PlayerStatus.PLAYING}
progressInterval={isTransitioning ? 10 : 250}
url={player1?.streamUrl}
volume={volume}
width={0}
onEnded={handleOnEnded}
onProgress={
playbackStyle === PlaybackStyle.GAPLESS ? handleGapless1 : handleCrossfade1
}
/>
<ReactPlayer
ref={player2Ref}
height={0}
muted={muted}
playing={currentPlayer === 2 && status === PlayerStatus.PLAYING}
progressInterval={isTransitioning ? 10 : 250}
url={player2?.streamUrl}
volume={volume}
width={0}
onEnded={handleOnEnded}
onProgress={
playbackStyle === PlaybackStyle.GAPLESS ? handleGapless2 : handleCrossfade2
}
/>
</>
);
},
2022-12-19 15:59:14 -08:00
);