Upgrade all dependencies
(1) react-native-track-player to v2 (2) react-navigation to v6 (3) react-native to v0.66.4
This commit is contained in:
43
src/utility/AddedTrackEvents.ts
Normal file
43
src/utility/AddedTrackEvents.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import TrackPlayer from 'react-native-track-player';
|
||||
import { useEffect } from 'react';
|
||||
import EventEmitter from 'events';
|
||||
|
||||
const eventName = 'track-added';
|
||||
const addedTrackEmitter = new EventEmitter();
|
||||
|
||||
/**
|
||||
* Emit the event that a track has been added
|
||||
*/
|
||||
export function emitTrackAdded() {
|
||||
addedTrackEmitter.emit(eventName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the callback whenever a track has been added to the queue
|
||||
*/
|
||||
export function onTrackAdded(callback: () => void) {
|
||||
addedTrackEmitter.addListener(eventName, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* A hook to manage the listeners for the added track function
|
||||
*/
|
||||
export function useOnTrackAdded(callback: () => void) {
|
||||
useEffect(() => {
|
||||
addedTrackEmitter.addListener(eventName, callback);
|
||||
return () => {
|
||||
addedTrackEmitter.removeListener(eventName, callback);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Monkey-patch the track-player to also emit track added events
|
||||
*/
|
||||
export function patchTrackPlayer() {
|
||||
const oldAddFunction = TrackPlayer.add;
|
||||
TrackPlayer.add = (...args: Parameters<typeof oldAddFunction>) => {
|
||||
emitTrackAdded();
|
||||
return oldAddFunction(...args);
|
||||
};
|
||||
}
|
||||
@@ -44,8 +44,8 @@ export function generateTrack(track: AlbumTrack, credentials: Credentials): Trac
|
||||
const url = encodeURI(`${credentials?.uri}/Audio/${track.Id}/universal?${trackParams}`);
|
||||
|
||||
return {
|
||||
id: track.Id,
|
||||
url,
|
||||
backendId: track.Id,
|
||||
title: track.Name,
|
||||
artist: track.Artists.join(', '),
|
||||
album: track.Album,
|
||||
|
||||
@@ -7,30 +7,30 @@
|
||||
* such as processing media buttons or analytics
|
||||
*/
|
||||
|
||||
import TrackPlayer from 'react-native-track-player';
|
||||
import TrackPlayer, { Event } from 'react-native-track-player';
|
||||
|
||||
export default async function() {
|
||||
TrackPlayer.addEventListener('remote-play', () => {
|
||||
TrackPlayer.addEventListener(Event.RemotePlay, () => {
|
||||
TrackPlayer.play();
|
||||
});
|
||||
|
||||
TrackPlayer.addEventListener('remote-pause', () => {
|
||||
TrackPlayer.addEventListener(Event.RemotePause, () => {
|
||||
TrackPlayer.pause();
|
||||
});
|
||||
|
||||
TrackPlayer.addEventListener('remote-next', () => {
|
||||
TrackPlayer.addEventListener(Event.RemoteNext, () => {
|
||||
TrackPlayer.skipToNext();
|
||||
});
|
||||
|
||||
TrackPlayer.addEventListener('remote-previous', () => {
|
||||
TrackPlayer.addEventListener(Event.RemotePrevious, () => {
|
||||
TrackPlayer.skipToPrevious();
|
||||
});
|
||||
|
||||
TrackPlayer.addEventListener('remote-stop', () => {
|
||||
TrackPlayer.addEventListener(Event.RemoteStop, () => {
|
||||
TrackPlayer.destroy();
|
||||
});
|
||||
|
||||
TrackPlayer.addEventListener('remote-seek', (event) => {
|
||||
TrackPlayer.addEventListener(Event.RemoteSeek, (event) => {
|
||||
TrackPlayer.seekTo(event.position);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
import { Track } from 'react-native-track-player';
|
||||
import { useTypedSelector } from 'store';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import TrackPlayer, { Event, Track, useTrackPlayerEvents } from 'react-native-track-player';
|
||||
|
||||
const idEqual = (left: Track | undefined, right: Track | undefined) => {
|
||||
return left?.id === right?.id;
|
||||
};
|
||||
interface CurrentTrackResponse {
|
||||
track: Track | undefined;
|
||||
index: number | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* This hook retrieves the current playing track from TrackPlayer
|
||||
*/
|
||||
export default function useCurrentTrack(): Track | undefined {
|
||||
const track = useTypedSelector(state => state.player.currentTrack, idEqual);
|
||||
export default function useCurrentTrack(): CurrentTrackResponse {
|
||||
const [track, setTrack] = useState<Track | undefined>();
|
||||
const [index, setIndex] = useState<number | undefined>();
|
||||
|
||||
// Retrieve the current track from the queue using the index
|
||||
const retrieveCurrentTrack = useCallback(async () => {
|
||||
const queue = await TrackPlayer.getQueue();
|
||||
const currentTrackIndex = await TrackPlayer.getCurrentTrack();
|
||||
setTrack(queue[currentTrackIndex]);
|
||||
setIndex(currentTrackIndex);
|
||||
}, [setTrack, setIndex]);
|
||||
|
||||
// Then execute the function on component mount and track changes
|
||||
useEffect(() => { retrieveCurrentTrack(); }, [retrieveCurrentTrack]);
|
||||
useTrackPlayerEvents([ Event.PlaybackTrackChanged ], retrieveCurrentTrack);
|
||||
|
||||
return track;
|
||||
return { track, index };
|
||||
}
|
||||
@@ -2,55 +2,27 @@ 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, play = true): Promise<TrackPlayer.Track[] | undefined> {
|
||||
return useCallback(async function playAlbum(albumId: string, play: boolean = true): Promise<Track[] | undefined> {
|
||||
const album = albums[albumId];
|
||||
const trackIds = album?.Tracks;
|
||||
const backendTrackIds = album?.Tracks;
|
||||
|
||||
// GUARD: Check that the album actually has tracks
|
||||
if (!album || !trackIds?.length) {
|
||||
// GUARD: Check if the album has songs
|
||||
if (!backendTrackIds?.length) {
|
||||
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) => {
|
||||
// Convert all backendTrackIds to the relevant format for react-native-track-player
|
||||
const newTracks = backendTrackIds.map((trackId) => {
|
||||
const track = tracks[trackId];
|
||||
if (!trackId || !track) {
|
||||
return;
|
||||
@@ -60,17 +32,14 @@ export default function usePlayAlbum() {
|
||||
}).filter((t): t is Track => typeof t !== 'undefined');
|
||||
|
||||
// Clear the queue and add all tracks
|
||||
await TrackPlayer.removeUpcomingTracks();
|
||||
await TrackPlayer.reset();
|
||||
await TrackPlayer.add(newTracks);
|
||||
|
||||
// Then, we'll dispatch the added track event
|
||||
dispatch(player.actions.addNewTrackToPlayer());
|
||||
|
||||
// Play the queue
|
||||
if (play) {
|
||||
await TrackPlayer.skip(trackIds[0]);
|
||||
await TrackPlayer.play();
|
||||
}
|
||||
|
||||
return newTracks;
|
||||
}, [credentials, albums, tracks, queue, dispatch]);
|
||||
}, [credentials, albums, tracks]);
|
||||
}
|
||||
@@ -3,20 +3,17 @@ 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, play = true, addToEnd = true) {
|
||||
return useCallback(async function playTrack(trackId: string, play: boolean = true, addToEnd: boolean = true) {
|
||||
// Get the relevant track
|
||||
const track = tracks[trackId];
|
||||
|
||||
@@ -25,22 +22,21 @@ export default function usePlayTrack() {
|
||||
return;
|
||||
}
|
||||
|
||||
// GUARD: Check if the track is already in the queue
|
||||
const trackInstances = queue.filter((t) => t.id.startsWith(trackId));
|
||||
|
||||
// Generate the new track for the queue
|
||||
const newTrack = {
|
||||
...(trackInstances.length ? trackInstances[0] : generateTrack(track, credentials)),
|
||||
id: `${trackId}_${trackInstances.length}`
|
||||
};
|
||||
const newTrack = generateTrack(track, credentials);
|
||||
|
||||
// Then, we'll need to check where to add the track
|
||||
if (addToEnd) {
|
||||
await TrackPlayer.add([ newTrack ]);
|
||||
|
||||
// Then we'll skip to it and play it
|
||||
if (play) {
|
||||
await TrackPlayer.skip(await (await TrackPlayer.getQueue()).length);
|
||||
await TrackPlayer.play();
|
||||
}
|
||||
} else {
|
||||
// Try and locate the current track
|
||||
const currentTrackId = await TrackPlayer.getCurrentTrack();
|
||||
const currentTrackIndex = queue.findIndex(track => track.id === currentTrackId);
|
||||
const currentTrackIndex = await TrackPlayer.getCurrentTrack();
|
||||
|
||||
// Since the argument is the id to insert the track BEFORE, we need
|
||||
// to get the current track + 1
|
||||
@@ -51,17 +47,13 @@ export default function usePlayTrack() {
|
||||
// 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
|
||||
if (play) {
|
||||
await TrackPlayer.skip(newTrack.id);
|
||||
await TrackPlayer.play();
|
||||
if (play) {
|
||||
await TrackPlayer.skip(currentTrackIndex + 1);
|
||||
await TrackPlayer.play();
|
||||
}
|
||||
}
|
||||
|
||||
return newTrack;
|
||||
}, [credentials, tracks, queue, dispatch]);
|
||||
}, [credentials, tracks, queue]);
|
||||
}
|
||||
@@ -1,18 +1,22 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import TrackPlayer, { usePlaybackState, Track } from 'react-native-track-player';
|
||||
import { useTypedSelector } from 'store';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import TrackPlayer, { Event, Track, useTrackPlayerEvents } from 'react-native-track-player';
|
||||
import { useOnTrackAdded } from './AddedTrackEvents';
|
||||
|
||||
/**
|
||||
* This hook retrieves the current playing track from TrackPlayer
|
||||
*/
|
||||
export default function useQueue(): Track[] {
|
||||
const state = usePlaybackState();
|
||||
const [queue, setQueue] = useState<Track[]>([]);
|
||||
const addedTrackCount = useTypedSelector(state => state.player.addedTrackCount);
|
||||
|
||||
useEffect(() => {
|
||||
TrackPlayer.getQueue().then(setQueue);
|
||||
}, [state, addedTrackCount]);
|
||||
// Define function that fetches the current queue
|
||||
const updateQueue = useCallback(() => TrackPlayer.getQueue().then(setQueue), [setQueue]);
|
||||
|
||||
// Then define the triggers for updating it
|
||||
useEffect(() => { updateQueue(); }, [updateQueue]);
|
||||
useTrackPlayerEvents([
|
||||
Event.PlaybackState,
|
||||
], updateQueue);
|
||||
useOnTrackAdded(updateQueue);
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user