From 684d606ab03129740e7e4eae4e72c149506b1139 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Thu, 15 Feb 2024 16:13:46 -0800 Subject: [PATCH] Revert "[C-3561] Improve playback delay when playing track on mobile (#7267)" This reverts commit 589ed010203d01b61a165ed2c3f38eb7ec86034b. --- .../src/components/audio/AudioPlayer.tsx | 150 ++++++++---------- 1 file changed, 65 insertions(+), 85 deletions(-) diff --git a/packages/mobile/src/components/audio/AudioPlayer.tsx b/packages/mobile/src/components/audio/AudioPlayer.tsx index 6853edcd105..9e781791b52 100644 --- a/packages/mobile/src/components/audio/AudioPlayer.tsx +++ b/packages/mobile/src/components/audio/AudioPlayer.tsx @@ -2,8 +2,9 @@ import { useRef, useEffect, useCallback, useState } from 'react' import { useAppContext } from '@audius/common/context' import { SquareSizes } from '@audius/common/models' -import type { Track } from '@audius/common/models' +import type { ID, Track } from '@audius/common/models' import { FeatureFlags } from '@audius/common/services' +import type { QueryParams } from '@audius/common/services' import { accountSelectors, cacheTracksSelectors, @@ -289,6 +290,38 @@ export const AudioPlayer = () => { } }, [reset]) + // Map of user signature for gated tracks + const [gatedQueryParamsMap, setGatedQueryParamsMap] = useState<{ + [trackId: ID]: QueryParams + }>({}) + + const handleGatedQueryParams = useCallback( + async (tracks: QueueableTrack[]) => { + const queryParamsMap: { [trackId: ID]: QueryParams } = {} + + for (const { track } of tracks) { + if (!track) { + continue + } + const trackId = track.track_id + + if (gatedQueryParamsMap[trackId]) { + queryParamsMap[trackId] = gatedQueryParamsMap[trackId] + } else { + const nftAccessSignature = nftAccessSignatureMap[trackId]?.mp3 ?? null + queryParamsMap[trackId] = await getQueryParams({ + audiusBackendInstance, + nftAccessSignature + }) + } + } + + setGatedQueryParamsMap(queryParamsMap) + return queryParamsMap + }, + [nftAccessSignatureMap, gatedQueryParamsMap, setGatedQueryParamsMap] + ) + useTrackPlayerEvents(playerEvents, async (event) => { const duration = (await TrackPlayer.getProgress()).duration const position = (await TrackPlayer.getProgress()).position @@ -491,13 +524,8 @@ export const AudioPlayer = () => { // Ref to keep track of the queue in the track player vs the queue in state const queueListRef = useRef([]) - - // A ref to the enqueue task to await before either requeing or appending to queue - const enqueueTracksJobRef = useRef>() - // A way to abort the enqeue tracks job if a new lineup is played - const abortEnqueueControllerRef = useRef(new AbortController()) - // The ref of trackQueryParams to avoid re-generating query params for the same track - const trackQueryParams = useRef({}) + // Ref to ensure that we do not try to update while we are already updating + const updatingQueueRef = useRef(false) const handleQueueChange = useCallback(async () => { const refUids = queueListRef.current @@ -508,6 +536,7 @@ export const AudioPlayer = () => { return } + updatingQueueRef.current = true queueListRef.current = queueTrackUids // Checks to allow for continuous playback while making queue updates @@ -516,18 +545,6 @@ export const AudioPlayer = () => { refUids.length > 0 && isEqual(queueTrackUids.slice(0, refUids.length), refUids) - // If not an append, cancel the enqueue task first - if (!isQueueAppend) { - abortEnqueueControllerRef.current.abort() - } - // wait for enqueue task to either shut down or finish - if (enqueueTracksJobRef.current) { - await enqueueTracksJobRef.current - } - - // Re-init the abort controller now that the enqueue job is done - abortEnqueueControllerRef.current = new AbortController() - // TODO: Queue removal logic was firing too often previously and causing playback issues when at the end of queues. Need to fix // Check if we are removing from the end of the queue // const isQueueRemoval = @@ -551,7 +568,11 @@ export const AudioPlayer = () => { ? queueTracks.slice(refUids.length) : queueTracks - const makeTrackData = async ({ track, isPreview }: QueueableTrack) => { + const queryParamsMap = isReachable + ? await handleGatedQueryParams(newQueueTracks) + : null + + const newTrackData = newQueueTracks.map(({ track, isPreview }) => { if (!track) { return unlistedTrackFallbackTrackData } @@ -566,19 +587,10 @@ export const AudioPlayer = () => { const audioFilePath = getLocalAudioPath(trackId) url = `file://${audioFilePath}` } else { - let queryParams = trackQueryParams.current[trackId] - - if (!queryParams) { - const nftAccessSignature = nftAccessSignatureMap[trackId]?.mp3 ?? null - queryParams = await getQueryParams({ - audiusBackendInstance, - nftAccessSignature - }) - trackQueryParams.current[trackId] = queryParams + const queryParams = { + ...queryParamsMap?.[track.track_id], + preview: isPreview } - - queryParams = { ...queryParams, preview: isPreview } - url = apiClient.makeUrl( `/tracks/${encodeHashId(track.track_id)}/stream`, queryParams @@ -612,76 +624,44 @@ export const AudioPlayer = () => { artwork: imageUrl, duration: isPreview ? getTrackPreviewDuration(track) : track.duration } - } - - // Enqueue tracks using 'middle-out' to ensure user can ready skip forward or backwards - const enqueueTracks = async ( - queuableTracks: QueueableTrack[], - queueIndex = 0 - ) => { - let currentPivot = 1 - while ( - queueIndex - currentPivot > 0 || - queueIndex + currentPivot < queueTracks.length - ) { - if (abortEnqueueControllerRef.current.signal.aborted) { - return - } - - const nextTrack = queuableTracks[queueIndex + currentPivot] - if (nextTrack) { - await TrackPlayer.add(await makeTrackData(nextTrack)) - } - - const previousTrack = queuableTracks[queueIndex - currentPivot] - if (previousTrack) { - await TrackPlayer.add(await makeTrackData(previousTrack), 0) - } - currentPivot++ - } - } + }) if (isQueueAppend) { - enqueueTracksJobRef.current = enqueueTracks(newQueueTracks) - await enqueueTracksJobRef.current - enqueueTracksJobRef.current = undefined + await TrackPlayer.add(newTrackData) } else { + // New queue, reset before adding new tracks + // NOTE: Should only happen when the user selects a new lineup so reset should never be called in the background and cause an error await TrackPlayer.reset() - - const firstTrack = newQueueTracks[queueIndex] - if (!firstTrack) return - await TrackPlayer.add(await makeTrackData(firstTrack)) - - if (playing) { - await TrackPlayer.play() + await TrackPlayer.add(newTrackData) + if (queueIndex < newQueueTracks.length) { + await TrackPlayer.skip(queueIndex) } - - enqueueTracksJobRef.current = enqueueTracks(newQueueTracks, queueIndex) - await enqueueTracksJobRef.current - enqueueTracksJobRef.current = undefined } + + if (playing) await TrackPlayer.play() + updatingQueueRef.current = false }, [ + isNotReachable, + isOfflineModeEnabled, + offlineAvailabilityByTrackId, + playing, queueIndex, + queueTrackOwnersMap, queueTrackUids, - didOfflineToggleChange, queueTracks, - queueTrackOwnersMap, - isOfflineModeEnabled, - offlineAvailabilityByTrackId, + didOfflineToggleChange, isCollectionMarkedForDownload, - isNotReachable, - storageNodeSelector, - nftAccessSignatureMap, - playing + handleGatedQueryParams, + isReachable, + storageNodeSelector ]) const handleQueueIdxChange = useCallback(async () => { const playerIdx = await TrackPlayer.getActiveTrackIndex() const queue = await TrackPlayer.getQueue() - await enqueueTracksJobRef.current - if ( + !updatingQueueRef.current && queueIndex !== -1 && queueIndex !== playerIdx && queueIndex < queue.length