(1) Play whole album when selecting a single track
(2) Create popup window on track long-press in which the track can be added to the end or front of the queue (3) Add Redux counter for added tracks so that the queue is properly updated
This commit is contained in:
@@ -2,17 +2,22 @@ import { useTypedSelector } from 'store';
|
||||
import { useCallback } from 'react';
|
||||
import TrackPlayer, { Track } from 'react-native-track-player';
|
||||
import { generateTrack } from './JellyfinApi';
|
||||
import useQueue from './useQueue';
|
||||
import player from 'store/player';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
/**
|
||||
* Generate a callback function that starts playing a full album given its
|
||||
* supplied id.
|
||||
*/
|
||||
export default function usePlayAlbum() {
|
||||
const dispatch = useDispatch();
|
||||
const credentials = useTypedSelector(state => state.settings.jellyfin);
|
||||
const albums = useTypedSelector(state => state.music.albums.entities);
|
||||
const tracks = useTypedSelector(state => state.music.tracks.entities);
|
||||
const queue = useQueue();
|
||||
|
||||
return useCallback(async function playAlbum(albumId: string) {
|
||||
return useCallback(async function playAlbum(albumId: string, play = true): Promise<TrackPlayer.Track[] | undefined> {
|
||||
const album = albums[albumId];
|
||||
const trackIds = album?.Tracks;
|
||||
|
||||
@@ -21,6 +26,29 @@ export default function usePlayAlbum() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the queue already contains the consecutive track listing
|
||||
// that is described as part of the album
|
||||
const queuedAlbum = queue.reduce<TrackPlayer.Track[]>((sum, track) => {
|
||||
if (track.id.startsWith(trackIds[sum.length])) {
|
||||
sum.push(track);
|
||||
} else {
|
||||
sum = [];
|
||||
}
|
||||
|
||||
return sum;
|
||||
}, []);
|
||||
|
||||
// If the entire album is already in the queue, we can just return those
|
||||
// tracks, rather than adding it to the queue again.
|
||||
if (queuedAlbum.length === trackIds.length) {
|
||||
if (play) {
|
||||
await TrackPlayer.skip(trackIds[0]);
|
||||
await TrackPlayer.play();
|
||||
}
|
||||
|
||||
return queuedAlbum;
|
||||
}
|
||||
|
||||
// Convert all trackIds to the relevant format for react-native-track-player
|
||||
const newTracks = trackIds.map((trackId) => {
|
||||
const track = tracks[trackId];
|
||||
@@ -34,7 +62,15 @@ export default function usePlayAlbum() {
|
||||
// Clear the queue and add all tracks
|
||||
await TrackPlayer.removeUpcomingTracks();
|
||||
await TrackPlayer.add(newTracks);
|
||||
await TrackPlayer.skip(trackIds[0]);
|
||||
TrackPlayer.play();
|
||||
}, [credentials, albums, tracks]);
|
||||
|
||||
// Then, we'll dispatch the added track event
|
||||
dispatch(player.actions.addNewTrackToPlayer());
|
||||
|
||||
if (play) {
|
||||
await TrackPlayer.skip(trackIds[0]);
|
||||
await TrackPlayer.play();
|
||||
}
|
||||
|
||||
return newTracks;
|
||||
}, [credentials, albums, tracks, queue, dispatch]);
|
||||
}
|
||||
@@ -3,17 +3,20 @@ import TrackPlayer from 'react-native-track-player';
|
||||
import { useTypedSelector } from 'store';
|
||||
import { generateTrack } from './JellyfinApi';
|
||||
import useQueue from './useQueue';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import player from 'store/player';
|
||||
|
||||
/**
|
||||
* A hook that generates a callback that can setup and start playing a
|
||||
* particular trackId in the player.
|
||||
*/
|
||||
export default function usePlayTrack() {
|
||||
const dispatch = useDispatch();
|
||||
const credentials = useTypedSelector(state => state.settings.jellyfin);
|
||||
const tracks = useTypedSelector(state => state.music.tracks.entities);
|
||||
const queue = useQueue();
|
||||
|
||||
return useCallback(async function playTrack(trackId: string) {
|
||||
return useCallback(async function playTrack(trackId: string, play = true, addToEnd = true) {
|
||||
// Get the relevant track
|
||||
const track = tracks[trackId];
|
||||
|
||||
@@ -30,10 +33,35 @@ export default function usePlayTrack() {
|
||||
...(trackInstances.length ? trackInstances[0] : generateTrack(track, credentials)),
|
||||
id: `${trackId}_${trackInstances.length}`
|
||||
};
|
||||
await TrackPlayer.add([ newTrack ]);
|
||||
|
||||
// Then, we'll need to check where to add the track
|
||||
if (addToEnd) {
|
||||
await TrackPlayer.add([ newTrack ]);
|
||||
} else {
|
||||
// Try and locate the current track
|
||||
const currentTrackId = await TrackPlayer.getCurrentTrack();
|
||||
const currentTrackIndex = queue.findIndex(track => track.id === currentTrackId);
|
||||
|
||||
// Since the argument is the id to insert the track BEFORE, we need
|
||||
// to get the current track + 1
|
||||
const targetTrack = currentTrackIndex >= 0 && queue.length > 1
|
||||
? queue[currentTrackIndex + 1].id
|
||||
: undefined;
|
||||
|
||||
// Depending on whether this track exists, we either add it there,
|
||||
// or at the end of the queue.
|
||||
await TrackPlayer.add([ newTrack ], targetTrack);
|
||||
}
|
||||
|
||||
// Then, we'll dispatch the added track event
|
||||
dispatch(player.actions.addNewTrackToPlayer());
|
||||
|
||||
// Then we'll skip to it and play it
|
||||
await TrackPlayer.skip(newTrack.id);
|
||||
TrackPlayer.play();
|
||||
}, [credentials, tracks, queue]);
|
||||
if (play) {
|
||||
await TrackPlayer.skip(newTrack.id);
|
||||
await TrackPlayer.play();
|
||||
}
|
||||
|
||||
return newTrack;
|
||||
}, [credentials, tracks, queue, dispatch]);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import TrackPlayer, { usePlaybackState, Track } from 'react-native-track-player';
|
||||
import { useTypedSelector } from 'store';
|
||||
|
||||
/**
|
||||
* This hook retrieves the current playing track from TrackPlayer
|
||||
@@ -7,10 +8,11 @@ import TrackPlayer, { usePlaybackState, Track } from 'react-native-track-player'
|
||||
export default function useQueue(): Track[] {
|
||||
const state = usePlaybackState();
|
||||
const [queue, setQueue] = useState<Track[]>([]);
|
||||
const addedTrackCount = useTypedSelector(state => state.player);
|
||||
|
||||
useEffect(() => {
|
||||
TrackPlayer.getQueue().then(setQueue);
|
||||
}, [state]);
|
||||
}, [state, addedTrackCount]);
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user