Browse Source

Fix player break in fade when nav to NowPlaying

- Add better handling to pause/play when the current index changes
- Add better audio handling when switching songs
master
jeffvli 3 years ago
parent
commit
15cf9de9c2
  1. 10
      src/components/card/Card.tsx
  2. 28
      src/components/player/Player.tsx
  3. 148
      src/components/shared/ContextMenu.tsx
  4. 3
      src/components/viewtypes/ListViewTable.tsx

10
src/components/card/Card.tsx

@ -77,17 +77,15 @@ const Card = ({
if (playClick.type === 'playlist') { if (playClick.type === 'playlist') {
const res = await getPlaylist(playClick.id); const res = await getPlaylist(playClick.id);
dispatch(appendPlayQueue({ entries: res.song })); dispatch(appendPlayQueue({ entries: res.song }));
if (playQueue.entry.length < 1) {
dispatch(setStatus('PLAYING'));
}
} }
if (playClick.type === 'album' || playClick.type === 'artist') { if (playClick.type === 'album' || playClick.type === 'artist') {
const res = await getAlbum(playClick.id); const res = await getAlbum(playClick.id);
dispatch(appendPlayQueue({ entries: res.song })); dispatch(appendPlayQueue({ entries: res.song }));
if (playQueue.entry.length < 1) { }
dispatch(setStatus('PLAYING'));
} if (playQueue.entry.length < 1) {
dispatch(setStatus('PLAYING'));
} }
}; };

28
src/components/player/Player.tsx

@ -235,7 +235,6 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
const cacheSongs = settings.getSync('cacheSongs'); const cacheSongs = settings.getSync('cacheSongs');
const [debug, setDebug] = useState(playQueue.showDebugWindow); const [debug, setDebug] = useState(playQueue.showDebugWindow);
const [title] = useState(''); const [title] = useState('');
const [srcIsSet, setSrcIsSet] = useState(false);
const [cachePath] = useState(path.join(getSongCachePath(), '/')); const [cachePath] = useState(path.join(getSongCachePath(), '/'));
const [fadeDuration, setFadeDuration] = useState(playQueue.fadeDuration); const [fadeDuration, setFadeDuration] = useState(playQueue.fadeDuration);
const [fadeType, setFadeType] = useState(playQueue.fadeType); const [fadeType, setFadeType] = useState(playQueue.fadeType);
@ -305,10 +304,9 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
}, [playQueue.currentPlayer, player.status]); }, [playQueue.currentPlayer, player.status]);
useEffect(() => { useEffect(() => {
/* Adding a small delay when setting the track src helps to not break the player when we're modifying // Adding a small delay when setting the track src helps to not break the player when we're modifying
the currentSongIndex such as when sorting the table, shuffling, or drag and dropping rows. // the currentSongIndex such as when sorting the table, shuffling, or drag and dropping rows.
It can also prevent loading unneeded tracks when rapidly incrementing/decrementing the player. */ // It can also prevent loading unneeded tracks when rapidly incrementing/decrementing the player.
if (playQueue[currentEntryList].length > 0 && !playQueue.isFading) { if (playQueue[currentEntryList].length > 0 && !playQueue.isFading) {
const timer1 = setTimeout(() => { const timer1 = setTimeout(() => {
dispatch(setPlayerSrc({ player: 1, src: getSrc1() })); dispatch(setPlayerSrc({ player: 1, src: getSrc1() }));
@ -325,15 +323,17 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
} }
if (playQueue[currentEntryList].length > 0) { if (playQueue[currentEntryList].length > 0) {
/* If fading, just instantly switch the track, otherwise the player breaks // If fading, just instantly switch the track, otherwise the player breaks
from the timeout due to the listen handlers that run during the fade */ // from the timeout due to the listen handlers that run during the fade
// If switching to the NowPlayingView while on player1 and fading, dispatching
// the src for player1 will cause the player to break
dispatch(setPlayerSrc({ player: 1, src: getSrc1() })); dispatch(setPlayerSrc({ player: 1, src: getSrc1() }));
dispatch(setPlayerSrc({ player: 2, src: getSrc2() })); dispatch(setPlayerSrc({ player: 2, src: getSrc2() }));
setSrcIsSet(true);
} }
return undefined; return undefined;
}, [currentEntryList, dispatch, getSrc1, getSrc2, playQueue, srcIsSet]); }, [currentEntryList, dispatch, getSrc1, getSrc2, playQueue]);
useEffect(() => { useEffect(() => {
// Update playback settings when changed in redux store // Update playback settings when changed in redux store
@ -381,6 +381,7 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
}; };
const handleOnEndedPlayer1 = () => { const handleOnEndedPlayer1 = () => {
player1Ref.current.audioEl.current.currentTime = 0;
if (cacheSongs) { if (cacheSongs) {
cacheSong( cacheSong(
`${playQueue[currentEntryList][playQueue.player1.index].id}.mp3`, `${playQueue[currentEntryList][playQueue.player1.index].id}.mp3`,
@ -422,10 +423,10 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
dispatch(setAutoIncremented(false)); dispatch(setAutoIncremented(false));
} }
} }
setSrcIsSet(false);
}; };
const handleOnEndedPlayer2 = () => { const handleOnEndedPlayer2 = () => {
player2Ref.current.audioEl.current.currentTime = 0;
if (cacheSongs) { if (cacheSongs) {
cacheSong( cacheSong(
`${playQueue[currentEntryList][playQueue.player2.index].id}.mp3`, `${playQueue[currentEntryList][playQueue.player2.index].id}.mp3`,
@ -466,7 +467,6 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
dispatch(setAutoIncremented(false)); dispatch(setAutoIncremented(false));
} }
} }
setSrcIsSet(false);
}; };
const handleGaplessPlayer1 = () => { const handleGaplessPlayer1 = () => {
@ -509,7 +509,8 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
volume={playQueue.player1.volume} volume={playQueue.player1.volume}
autoPlay={ autoPlay={
playQueue.player1.index === playQueue.currentIndex && playQueue.player1.index === playQueue.currentIndex &&
playQueue.currentPlayer === 1 playQueue.currentPlayer === 1 &&
player.status === 'PLAYING'
} }
onError={(e: any) => { onError={(e: any) => {
if (playQueue[currentEntryList].length > 0) { if (playQueue[currentEntryList].length > 0) {
@ -534,7 +535,8 @@ const Player = ({ currentEntryList, children }: any, ref: any) => {
volume={playQueue.player2.volume} volume={playQueue.player2.volume}
autoPlay={ autoPlay={
playQueue.player2.index === playQueue.currentIndex && playQueue.player2.index === playQueue.currentIndex &&
playQueue.currentPlayer === 2 playQueue.currentPlayer === 2 &&
player.status === 'PLAYING'
} }
onError={(e: any) => { onError={(e: any) => {
if (playQueue[currentEntryList].length > 0) { if (playQueue[currentEntryList].length > 0) {

148
src/components/shared/ContextMenu.tsx

@ -96,16 +96,6 @@ export const GlobalContextMenu = () => {
dispatch(fixPlayer2Index()); dispatch(fixPlayer2Index());
} }
dispatch(setContextMenu({ show: false })); dispatch(setContextMenu({ show: false }));
// If the currently playing song is removed, then the player will autostart because
// the player src changed. If that happens, automatically set the playing status
if (
_.map(multiSelect.selected, 'uniqueId').includes(
playQueue.current.uniqueId
)
) {
setTimeout(() => dispatch(setStatus('PLAYING')), 50);
}
}; };
const handleAddToPlaylist = async () => { const handleAddToPlaylist = async () => {
@ -208,80 +198,78 @@ export const GlobalContextMenu = () => {
return ( return (
<> <>
{misc.contextMenu.show && misc.contextMenu.type === 'nowPlaying' && ( {misc.contextMenu.show && misc.contextMenu.type === 'nowPlaying' && (
<> <ContextMenu
<ContextMenu xPos={misc.contextMenu.xPos}
xPos={misc.contextMenu.xPos} yPos={misc.contextMenu.yPos}
yPos={misc.contextMenu.yPos} width={190}
width={190} numOfButtons={7}
numOfButtons={7} numOfDividers={3}
numOfDividers={3} >
<ContextMenuButton
text={`Selected: ${multiSelect.selected.length}`}
/>
<ContextMenuDivider />
<ContextMenuButton text="Add to queue" onClick={handleAddToQueue} />
<ContextMenuButton
text="Remove from current"
onClick={handleRemoveFromQueue}
/>
<ContextMenuDivider />
<Whisper
ref={playlistTriggerRef}
enterable
placement="autoHorizontalStart"
trigger="none"
speaker={
<Popover>
<StyledInputPicker
data={playlists}
placement="autoVerticalStart"
virtualized
labelKey="name"
valueKey="id"
width={200}
onChange={(e: any) => setSelectedPlaylistId(e)}
/>
<StyledButton
disabled={
!selectedPlaylistId ||
misc.isProcessingPlaylist.includes(selectedPlaylistId)
}
loading={misc.isProcessingPlaylist.includes(
selectedPlaylistId
)}
onClick={handleAddToPlaylist}
>
Add
</StyledButton>
</Popover>
}
> >
<ContextMenuButton <ContextMenuButton
text={`Selected: ${multiSelect.selected.length}`} text="Add to playlist"
/> onClick={() =>
<ContextMenuDivider /> playlistTriggerRef.current.state.isOverlayShown
<ContextMenuButton text="Add to queue" onClick={handleAddToQueue} /> ? playlistTriggerRef.current.close()
<ContextMenuButton : playlistTriggerRef.current.open()
text="Remove from current"
onClick={handleRemoveFromQueue}
/>
<ContextMenuDivider />
<Whisper
ref={playlistTriggerRef}
enterable
placement="autoHorizontalStart"
trigger="none"
speaker={
<Popover>
<StyledInputPicker
data={playlists}
placement="autoVerticalStart"
virtualized
labelKey="name"
valueKey="id"
width={200}
onChange={(e: any) => setSelectedPlaylistId(e)}
/>
<StyledButton
disabled={
!selectedPlaylistId ||
misc.isProcessingPlaylist.includes(selectedPlaylistId)
}
loading={misc.isProcessingPlaylist.includes(
selectedPlaylistId
)}
onClick={handleAddToPlaylist}
>
Add
</StyledButton>
</Popover>
} }
>
<ContextMenuButton
text="Add to playlist"
onClick={() =>
playlistTriggerRef.current.state.isOverlayShown
? playlistTriggerRef.current.close()
: playlistTriggerRef.current.open()
}
/>
</Whisper>
<ContextMenuDivider />
<ContextMenuButton
text="Add to favorites"
onClick={() => handleFavorite(false)}
/>
<ContextMenuButton
text="Add to favorites (ordered)"
onClick={() => handleFavorite(true)}
/>
<ContextMenuButton
text="Remove from favorites"
onClick={handleUnfavorite}
/> />
</ContextMenu> </Whisper>
</> <ContextMenuDivider />
<ContextMenuButton
text="Add to favorites"
onClick={() => handleFavorite(false)}
/>
<ContextMenuButton
text="Add to favorites (ordered)"
onClick={() => handleFavorite(true)}
/>
<ContextMenuButton
text="Remove from favorites"
onClick={handleUnfavorite}
/>
</ContextMenu>
)} )}
</> </>
); );

3
src/components/viewtypes/ListViewTable.tsx

@ -186,7 +186,7 @@ const ListViewTable = ({
}) })
); );
} }
if (playQueue.currentPlayer === 1) { if (playQueue.currentPlayer === 1 && !playQueue.isFading) {
dispatch(fixPlayer2Index()); dispatch(fixPlayer2Index());
} }
} }
@ -195,6 +195,7 @@ const ListViewTable = ({
dispatch, dispatch,
nowPlaying, nowPlaying,
playQueue.currentPlayer, playQueue.currentPlayer,
playQueue.isFading,
playQueue.sortColumn, playQueue.sortColumn,
playQueue.sortType, playQueue.sortType,
sortedData, sortedData,

Loading…
Cancel
Save