diff --git a/src/components/TrackPlayerState.tsx b/src/components/TrackPlayerState.tsx
index 91b6f14..e20f175 100644
--- a/src/components/TrackPlayerState.tsx
+++ b/src/components/TrackPlayerState.tsx
@@ -2,8 +2,14 @@ import { useAppState } from '@react-native-community/hooks'
import { useAtomValue, useUpdateAtom } from 'jotai/utils'
import React, { useEffect } from 'react'
import { View } from 'react-native'
-import TrackPlayer, { Event, useTrackPlayerEvents } from 'react-native-track-player'
-import { currentTrackAtom, getQueue, getTrack, playerStateAtom, queueWriteAtom } from '../state/trackplayer'
+import TrackPlayer, { Event, State, useTrackPlayerEvents } from 'react-native-track-player'
+import {
+ currentTrackAtom,
+ playerStateAtom,
+ queueWriteAtom,
+ useRefreshCurrentTrack,
+ useRefreshQueue,
+} from '../state/trackplayer'
const AppActiveResponder: React.FC<{
update: () => void
@@ -32,23 +38,16 @@ const TrackPlayerEventResponder: React.FC<{
const CurrentTrackState = () => {
const setCurrentTrack = useUpdateAtom(currentTrackAtom)
+ const refreshCurrentTrack = useRefreshCurrentTrack()
const update = async (payload?: Payload) => {
- if (payload?.type === Event.PlaybackQueueEnded && 'track' in payload) {
+ const queueEnded = payload?.type === Event.PlaybackQueueEnded && 'track' in payload
+ const remoteStop = payload?.type === Event.RemoteStop
+ if (queueEnded || remoteStop) {
setCurrentTrack(undefined)
return
}
-
- const index = await TrackPlayer.getCurrentTrack()
- if (index !== null && index >= 0) {
- const track = await getTrack(index)
- if (track !== null) {
- setCurrentTrack(track)
- return
- }
- }
-
- setCurrentTrack(undefined)
+ await refreshCurrentTrack()
}
return (
@@ -70,31 +69,36 @@ const PlayerState = () => {
const setPlayerState = useUpdateAtom(playerStateAtom)
const update = async (payload?: Payload) => {
+ if (payload?.type === Event.RemoteStop) {
+ setPlayerState(State.None)
+ return
+ }
setPlayerState(payload?.state || (await TrackPlayer.getState()))
}
- return
+ return
}
const QueueState = () => {
const setQueue = useUpdateAtom(queueWriteAtom)
+ const refreshQueue = useRefreshQueue()
const update = async (payload?: Payload) => {
if (payload) {
setQueue([])
return
}
- setQueue(await getQueue())
+ await refreshQueue()
}
return
}
const Debug = () => {
- const value = useAtomValue(queueWriteAtom)
+ const value = useAtomValue(currentTrackAtom)
useEffect(() => {
- console.log(value.map(t => t.title))
+ // ToastAndroid.show(value?.title || 'undefined', 1)
}, [value])
return <>>
diff --git a/src/state/trackplayer.ts b/src/state/trackplayer.ts
index 3711cd3..ee31564 100644
--- a/src/state/trackplayer.ts
+++ b/src/state/trackplayer.ts
@@ -3,6 +3,7 @@ import TrackPlayer, { State, Track } from 'react-native-track-player'
import equal from 'fast-deep-equal'
import { useUpdateAtom } from 'jotai/utils'
import { Song } from '../models/music'
+import { PromiseQueue } from '../util'
type TrackExt = Track & {
id: string
@@ -36,7 +37,7 @@ export const queueAtom = atom(get => get(_queue))
export const queueWriteAtom = atom(
get => get(_queue),
(get, set, update) => {
- if (get(_queue) !== update) {
+ if (!equal(get(_queue), update)) {
set(_queue, update)
}
},
@@ -50,19 +51,44 @@ export const queueNameAtom = atom(get => {
return undefined
})
-export const getQueue = async (): Promise => {
+const trackPlayerCommands = new PromiseQueue(1)
+
+const getQueue = async (): Promise => {
return ((await TrackPlayer.getQueue()) as TrackExt[]) || []
}
-export const getTrack = async (index: number): Promise => {
+const getTrack = async (index: number): Promise => {
return ((await TrackPlayer.getTrack(index)) as TrackExt) || undefined
}
+export const useRefreshQueue = () => {
+ const setQueue = useUpdateAtom(queueWriteAtom)
+
+ return () =>
+ trackPlayerCommands.enqueue(async () => {
+ setQueue(await getQueue())
+ })
+}
+
+export const useRefreshCurrentTrack = () => {
+ const setCurrentTrack = useUpdateAtom(currentTrackAtom)
+
+ return () =>
+ trackPlayerCommands.enqueue(async () => {
+ const index = await TrackPlayer.getCurrentTrack()
+ if (typeof index === 'number' && index >= 0) {
+ setCurrentTrack(await getTrack(index))
+ } else {
+ setCurrentTrack(undefined)
+ }
+ })
+}
+
export const usePrevious = () => {
const setCurrentTrack = useUpdateAtom(currentTrackAtom)
- return async () => {
- try {
+ return () =>
+ trackPlayerCommands.enqueue(async () => {
const [current, queue] = await Promise.all([await TrackPlayer.getCurrentTrack(), await getQueue()])
if (current > 0) {
await TrackPlayer.skipToPrevious()
@@ -71,15 +97,14 @@ export const usePrevious = () => {
await TrackPlayer.seekTo(0)
}
await TrackPlayer.play()
- } catch {}
- }
+ })
}
export const useNext = () => {
const setCurrentTrack = useUpdateAtom(currentTrackAtom)
- return async () => {
- try {
+ return () =>
+ trackPlayerCommands.enqueue(async () => {
const [current, queue] = await Promise.all([await TrackPlayer.getCurrentTrack(), await getQueue()])
if (current >= queue.length - 1) {
await TrackPlayer.skip(0)
@@ -90,64 +115,68 @@ export const useNext = () => {
setCurrentTrack(queue[current + 1])
await TrackPlayer.play()
}
- } catch {}
- }
+ })
}
export const useAdd = () => {
const setQueue = useUpdateAtom(queueWriteAtom)
const setCurrentTrack = useUpdateAtom(currentTrackAtom)
- return async (tracks: TrackExt | TrackExt[], insertBeforeindex?: number) => {
- await TrackPlayer.add(tracks, insertBeforeindex)
+ return (tracks: TrackExt | TrackExt[], insertBeforeindex?: number) =>
+ trackPlayerCommands.enqueue(async () => {
+ await TrackPlayer.add(tracks, insertBeforeindex)
- const queue = await getQueue()
- setQueue(queue)
- setCurrentTrack(queue.length > 0 ? queue[await TrackPlayer.getCurrentTrack()] : undefined)
- }
+ const queue = await getQueue()
+ setQueue(queue)
+ setCurrentTrack(queue.length > 0 ? queue[await TrackPlayer.getCurrentTrack()] : undefined)
+ })
}
-export const useReset = () => {
+export const useReset = (enqueue = true) => {
const setQueue = useUpdateAtom(queueWriteAtom)
const setCurrentTrack = useUpdateAtom(currentTrackAtom)
- return async () => {
+ const reset = async () => {
await TrackPlayer.reset()
setQueue([])
setCurrentTrack(undefined)
}
+
+ return enqueue ? () => trackPlayerCommands.enqueue(reset) : reset
}
export const useSetQueue = () => {
const setCurrentTrack = useUpdateAtom(currentTrackAtom)
const setQueue = useUpdateAtom(queueWriteAtom)
+ const reset = useReset(false)
- return async (songs: Song[], name: string, playId?: string) => {
- await TrackPlayer.reset()
- const tracks = songs.map(s => mapSongToTrack(s, name))
+ return async (songs: Song[], name: string, playId?: string) =>
+ trackPlayerCommands.enqueue(async () => {
+ await reset()
+ const tracks = songs.map(s => mapSongToTrack(s, name))
- if (playId) {
- setCurrentTrack(tracks.find(t => t.id === playId))
- }
+ if (playId) {
+ setCurrentTrack(tracks.find(t => t.id === playId))
+ }
- if (!playId) {
- await TrackPlayer.add(tracks)
- } else if (playId === tracks[0].id) {
- await TrackPlayer.add(tracks)
- await TrackPlayer.play()
- } else {
- const playIndex = tracks.findIndex(t => t.id === playId)
- const tracks1 = tracks.slice(0, playIndex)
- const tracks2 = tracks.slice(playIndex)
+ if (!playId) {
+ await TrackPlayer.add(tracks)
+ } else if (playId === tracks[0].id) {
+ await TrackPlayer.add(tracks)
+ await TrackPlayer.play()
+ } else {
+ const playIndex = tracks.findIndex(t => t.id === playId)
+ const tracks1 = tracks.slice(0, playIndex)
+ const tracks2 = tracks.slice(playIndex)
- await TrackPlayer.add(tracks2)
- await TrackPlayer.play()
+ await TrackPlayer.add(tracks2)
+ await TrackPlayer.play()
- await TrackPlayer.add(tracks1, 0)
- }
+ await TrackPlayer.add(tracks1, 0)
+ }
- setQueue(await getQueue())
- }
+ setQueue(await getQueue())
+ })
}
function mapSongToTrack(song: Song, queueName: string): TrackExt {
diff --git a/src/subsonic/api.ts b/src/subsonic/api.ts
index bb209ec..5837058 100644
--- a/src/subsonic/api.ts
+++ b/src/subsonic/api.ts
@@ -26,6 +26,7 @@ import {
} from './responses'
import { Server } from '../models/settings'
import paths from '../paths'
+import { PromiseQueue } from '../util'
export class SubsonicApiError extends Error {
method: string
@@ -42,37 +43,7 @@ export class SubsonicApiError extends Error {
}
}
-type QueuePromise = () => Promise
-
-class Queue {
- maxSimultaneously: number
-
- private active = 0
- private queue: QueuePromise[] = []
-
- constructor(maxSimultaneously = 1) {
- this.maxSimultaneously = maxSimultaneously
- }
-
- async enqueue(func: QueuePromise) {
- if (++this.active > this.maxSimultaneously) {
- await new Promise(resolve => this.queue.push(resolve as QueuePromise))
- }
-
- try {
- return await func()
- } catch (err) {
- throw err
- } finally {
- this.active--
- if (this.queue.length) {
- this.queue.shift()?.()
- }
- }
- }
-}
-
-const downloadQueue = new Queue(1)
+const downloadQueue = new PromiseQueue(1)
export class SubsonicApiClient {
address: string
diff --git a/src/util.ts b/src/util.ts
index fd73faa..4ed7106 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -9,3 +9,33 @@ export function formatDuration(seconds: number): string {
}
return time
}
+
+type QueuedPromise = () => Promise
+
+export class PromiseQueue {
+ maxSimultaneously: number
+
+ private active = 0
+ private queue: QueuedPromise[] = []
+
+ constructor(maxSimultaneously = 1) {
+ this.maxSimultaneously = maxSimultaneously
+ }
+
+ async enqueue(func: () => Promise) {
+ if (++this.active > this.maxSimultaneously) {
+ await new Promise(resolve => this.queue.push(resolve as QueuedPromise))
+ }
+
+ try {
+ return await func()
+ } catch (err) {
+ throw err
+ } finally {
+ this.active--
+ if (this.queue.length) {
+ this.queue.shift()?.()
+ }
+ }
+ }
+}