From c9f7f71194105944f051e7329b88956e4c863976 Mon Sep 17 00:00:00 2001 From: Lei Nelissen Date: Thu, 25 Jul 2024 15:45:26 +0200 Subject: [PATCH] fix: further limit extraneous events from playback reporting --- .../Music/stacks/components/TrackListView.tsx | 5 +-- src/utility/JellyfinApi/playback.ts | 7 ++-- src/utility/PlaybackService.ts | 5 +-- src/utility/usePlayTracks.ts | 32 +++++++++++++++++-- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/screens/Music/stacks/components/TrackListView.tsx b/src/screens/Music/stacks/components/TrackListView.tsx index 55bd037..f5b596e 100644 --- a/src/screens/Music/stacks/components/TrackListView.tsx +++ b/src/screens/Music/stacks/components/TrackListView.tsx @@ -6,7 +6,6 @@ import { useNavigation } from '@react-navigation/native'; import { useAppDispatch, useTypedSelector } from '@/store'; import TouchableHandler from '@/components/TouchableHandler'; import useCurrentTrack from '@/utility/useCurrentTrack'; -import TrackPlayer from 'react-native-track-player'; import Play from '@/assets/icons/play.svg'; import Shuffle from '@/assets/icons/shuffle.svg'; import useDefaultStyles from '@/components/Colors'; @@ -150,9 +149,7 @@ const TrackListView: React.FC = ({ const playEntity = useCallback(() => { playTracks(trackIds); }, [playTracks, trackIds]); const shuffleEntity = useCallback(() => { playTracks(trackIds, { shuffle: true }); }, [playTracks, trackIds]); const selectTrack = useCallback(async (index: number) => { - await playTracks(trackIds, { play: false }); - await TrackPlayer.skip(index); - await TrackPlayer.play(); + await playTracks(trackIds, { playIndex: index }); }, [playTracks, trackIds]); const longPressTrack = useCallback((index: number) => { navigation.navigate('TrackPopupMenu', { trackId: trackIds[index].toString() }); diff --git a/src/utility/JellyfinApi/playback.ts b/src/utility/JellyfinApi/playback.ts index 047448d..542d735 100644 --- a/src/utility/JellyfinApi/playback.ts +++ b/src/utility/JellyfinApi/playback.ts @@ -31,6 +31,9 @@ export async function sendPlaybackEvent( TrackPlayer.getPlaybackState(), ]); + // GUARD: Ensure that no empty events are sent out + if (!activeTrack?.backendId) return; + // Generate a payload from the gathered data const payload = { VolumeLevel: volume * 100, @@ -41,8 +44,8 @@ export async function sendPlaybackEvent( PositionTicks: Math.round((lastPosition || currentPosition) * 10_000_000), PlaybackRate: 1, PlayMethod: 'transcode', - MediaSourceId: activeTrack?.backendId || null, - ItemId: activeTrack?.backendId || null, + MediaSourceId: activeTrack.backendId, + ItemId: activeTrack.backendId, CanSeek: true, PlaybackStartTimeTicks: null, }; diff --git a/src/utility/PlaybackService.ts b/src/utility/PlaybackService.ts index 51a7833..f171dbd 100644 --- a/src/utility/PlaybackService.ts +++ b/src/utility/PlaybackService.ts @@ -40,15 +40,16 @@ export default async function() { TrackPlayer.addEventListener(Event.PlaybackActiveTrackChanged, async (e) => { // Retrieve the current settings from the Redux store const settings = store.getState().settings; + console.log('TrackChanged', e?.track?.title); // GUARD: Only report playback when the settings is enabled if (settings.enablePlaybackReporting && 'track' in e) { // GUARD: End the previous track if it's about to end if (e.lastTrack) { - await sendPlaybackEvent('/Sessions/Playing/Stopped', e.lastTrack, e.lastPosition); + sendPlaybackEvent('/Sessions/Playing/Stopped', e.lastTrack, e.lastPosition); } - await sendPlaybackEvent('/Sessions/Playing', e.track); + sendPlaybackEvent('/Sessions/Playing', e.track); } }); diff --git a/src/utility/usePlayTracks.ts b/src/utility/usePlayTracks.ts index 6139dd8..15d4fc8 100644 --- a/src/utility/usePlayTracks.ts +++ b/src/utility/usePlayTracks.ts @@ -8,6 +8,14 @@ interface PlayOptions { play: boolean; shuffle: boolean; method: 'add-to-end' | 'add-after-currently-playing' | 'replace'; + /** + * The index for the track that should start out playing. This ensures that + * no intermediate tracks are played (however briefly) while the queue skips + * to this index. + * + * NOTE: This option is only available with the `replace` method. + */ + playIndex?: number; } const defaults: PlayOptions = { @@ -103,11 +111,29 @@ export default function usePlayTracks() { break; } case 'replace': { + // Reset the queue first await TrackPlayer.reset(); - await TrackPlayer.add(newTracks); - if (play) { - await TrackPlayer.play(); + // GUARD: Check if we need to skip to a particular index + if (options.playIndex) { + // If so, we'll split the tracks into tracks before the + // index that should be played, and the queue of tracks that + // will start playing + const before = newTracks.slice(0, options.playIndex); + const current = newTracks.slice(options.playIndex); + + // First, we'll add the current queue and (optionally) force + // it to start playing. + await TrackPlayer.add(current); + if (play) await TrackPlayer.play(); + + // Then, we'll insert the "previous" tracks after the queue + // has started playing. This ensures that these tracks won't + // trigger any events on the track player. + await TrackPlayer.add(before, options.playIndex); + } else { + await TrackPlayer.add(newTracks); + if (play) await TrackPlayer.play(); } break;