Bugfix/large playlist crash (#111)

* get all song coverArt as they are rendered

doing it all up front was too heavy
temporarily disabled mapping artwork in setQueue, need to fix this

* use cache data for track artwork when available

* fix round art in context menu for songs

* set only the first artwork at play time

then set the rest in the playback service

* handle both cached images and fetching images

* remove commented code

* fix shuffle

fix first thumbnail not being updated on shuffle for now playing background
This commit is contained in:
austinried
2022-04-21 14:58:35 +09:00
committed by GitHub
parent abb7ae3207
commit fea8940f00
18 changed files with 400 additions and 299 deletions
+150 -3
View File
@@ -1,8 +1,14 @@
import { getCurrentTrack, getPlayerState, trackPlayerCommands } from '@app/state/trackplayer'
import TrackPlayer, { Event, State } from 'react-native-track-player'
import { useStore } from './state/store'
import { unstable_batchedUpdates } from 'react-native'
import NetInfo, { NetInfoStateType } from '@react-native-community/netinfo'
import _ from 'lodash'
import { unstable_batchedUpdates } from 'react-native'
import TrackPlayer, { Event, State } from 'react-native-track-player'
import { fetchAlbum, FetchExisingFileOptions, fetchExistingFile, fetchFile, FetchFileOptions } from './hooks/fetch'
import qk from './hooks/queryKeys'
import queryClient from './queryClient'
import queueService from './queueservice'
import { useStore } from './state/store'
import { ReturnedPromiseResolvedType } from './util/types'
const reset = () => {
unstable_batchedUpdates(() => {
@@ -34,12 +40,81 @@ const rebuildQueue = (forcePlay?: boolean) => {
})
}
const updateQueue = () => {
unstable_batchedUpdates(() => {
useStore.getState().updateQueue()
})
}
const setDuckPaused = (duckPaused: boolean) => {
unstable_batchedUpdates(() => {
useStore.getState().setDuckPaused(duckPaused)
})
}
const setQueryDataAlbum = (queryKey: any, data: ReturnedPromiseResolvedType<typeof fetchAlbum>) => {
unstable_batchedUpdates(() => {
queryClient.setQueryData(queryKey, data)
})
}
const setQueryDataExistingFiles = (queryKey: any, data: ReturnedPromiseResolvedType<typeof fetchExistingFile>) => {
unstable_batchedUpdates(() => {
queryClient.setQueryData(queryKey, data)
})
}
const setQueryDataCoverArt = (queryKey: any, data: ReturnedPromiseResolvedType<typeof fetchFile>) => {
unstable_batchedUpdates(() => {
queryClient.setQueryData(queryKey, data)
})
}
function getClient() {
const client = useStore.getState().client
if (!client) {
throw new Error('no client!')
}
return client
}
async function getAlbum(id: string) {
try {
const res = await fetchAlbum(id, getClient())
setQueryDataAlbum(qk.album(id), res)
return res
} catch {}
}
async function getCoverArtThumbExisting(coverArt: string) {
const serverId = useStore.getState().settings.activeServerId
const options: FetchExisingFileOptions = { itemType: 'coverArtThumb', itemId: coverArt }
try {
const res = await fetchExistingFile(options, serverId)
setQueryDataExistingFiles(qk.existingFiles(options.itemType, options.itemId), res)
return res
} catch {}
}
async function getCoverArtThumb(coverArt: string) {
const serverId = useStore.getState().settings.activeServerId
const fromUrl = getClient().getCoverArtUri({ id: coverArt, size: '256' })
const options: FetchFileOptions = {
itemType: 'coverArtThumb',
itemId: coverArt,
fromUrl,
expectedContentType: 'image',
}
try {
const res = await fetchFile(options, serverId)
setQueryDataCoverArt(qk.coverArt(coverArt, 'thumbnail'), res)
return res
} catch {}
}
let serviceCreated = false
const createService = async () => {
@@ -142,6 +217,78 @@ const createService = async () => {
rebuildQueue(true)
}
})
queueService.addListener('set', async ({ queue }) => {
const contextId = useStore.getState().queueContextId
const throwIfQueueChanged = () => {
if (contextId !== useStore.getState().queueContextId) {
throw 'queue-changed'
}
}
const albumIds = _.uniq(queue.map(s => s.albumId)).filter((id): id is string => id !== undefined)
const albumIdImagePath: { [albumId: string]: string | undefined } = {}
for (const albumId of albumIds) {
let coverArt = queryClient.getQueryData<string>(qk.albumCoverArt(albumId))
if (!coverArt) {
throwIfQueueChanged()
console.log('no cached coverArt for album', albumId, 'getting album...')
coverArt = (await getAlbum(albumId))?.album.coverArt
if (!coverArt) {
continue
}
}
let imagePath =
queryClient.getQueryData<string>(qk.existingFiles('coverArtThumb', coverArt)) ||
queryClient.getQueryData<string>(qk.coverArt(coverArt, 'thumbnail'))
if (!imagePath) {
throwIfQueueChanged()
console.log('no cached image for', coverArt, 'getting file...')
imagePath = (await getCoverArtThumbExisting(coverArt)) || (await getCoverArtThumb(coverArt))
if (!imagePath) {
continue
}
}
albumIdImagePath[albumId] = imagePath
}
for (let i = 0; i < queue.length; i++) {
const track = queue[i]
if (typeof track.artwork === 'string') {
continue
}
if (!track.albumId) {
continue
}
let imagePath = albumIdImagePath[track.albumId]
if (!imagePath) {
continue
}
try {
throwIfQueueChanged()
let trackIdx = i
const shuffleOrder = useStore.getState().shuffleOrder
if (shuffleOrder) {
trackIdx = shuffleOrder.indexOf(i)
}
await TrackPlayer.updateMetadataForTrack(trackIdx, { ...track, artwork: `file://${imagePath}` })
} catch {
break
}
}
await trackPlayerCommands.enqueue(async () => {
updateQueue()
})
})
}
module.exports = async function () {