mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-29 09:29:29 +01:00
impl async storage for extra queue data
finally fixes shuffle (ugh)
This commit is contained in:
parent
f859be51a5
commit
4f69b36c7b
@ -135,10 +135,10 @@ const ProgressState = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Debug = () => {
|
const Debug = () => {
|
||||||
const value = useAtomValue(currentTrackAtom)
|
const value = useAtomValue(queueAtom)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// ToastAndroid.show(value?.title || 'undefined', 1)
|
console.log(value.map(t => t.title))
|
||||||
}, [value])
|
}, [value])
|
||||||
|
|
||||||
return <></>
|
return <></>
|
||||||
|
|||||||
@ -12,11 +12,13 @@ import {
|
|||||||
currentTrackAtom,
|
currentTrackAtom,
|
||||||
playerStateAtom,
|
playerStateAtom,
|
||||||
queueNameAtom,
|
queueNameAtom,
|
||||||
|
queueShuffledAtom,
|
||||||
useNext,
|
useNext,
|
||||||
usePause,
|
usePause,
|
||||||
usePlay,
|
usePlay,
|
||||||
usePrevious,
|
usePrevious,
|
||||||
useProgress,
|
useProgress,
|
||||||
|
useToggleShuffle,
|
||||||
} from '@app/state/trackplayer'
|
} from '@app/state/trackplayer'
|
||||||
import colors from '@app/styles/colors'
|
import colors from '@app/styles/colors'
|
||||||
import font from '@app/styles/font'
|
import font from '@app/styles/font'
|
||||||
@ -206,6 +208,8 @@ const PlayerControls = () => {
|
|||||||
const pause = usePause()
|
const pause = usePause()
|
||||||
const next = useNext()
|
const next = useNext()
|
||||||
const previous = usePrevious()
|
const previous = usePrevious()
|
||||||
|
const shuffle = useAtomValue(queueShuffledAtom)
|
||||||
|
const toggleShuffle = useToggleShuffle()
|
||||||
|
|
||||||
let playPauseIcon: string
|
let playPauseIcon: string
|
||||||
let playPauseAction: undefined | (() => void)
|
let playPauseAction: undefined | (() => void)
|
||||||
@ -253,8 +257,8 @@ const PlayerControls = () => {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={controlsStyles.center}>
|
<View style={controlsStyles.center}>
|
||||||
<PressableOpacity onPress={undefined} disabled={disabled}>
|
<PressableOpacity onPress={() => toggleShuffle()} disabled={disabled}>
|
||||||
<Icon name="shuffle" size={26} color="white" />
|
<Icon name="shuffle" size={26} color={shuffle ? colors.accent : 'white'} />
|
||||||
</PressableOpacity>
|
</PressableOpacity>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
|
import { Song } from '@app/models/music'
|
||||||
|
import atomWithAsyncStorage from '@app/storage/atomWithAsyncStorage'
|
||||||
|
import PromiseQueue from '@app/util/PromiseQueue'
|
||||||
import equal from 'fast-deep-equal'
|
import equal from 'fast-deep-equal'
|
||||||
import { atom } from 'jotai'
|
import { atom } from 'jotai'
|
||||||
import { useAtomCallback, useAtomValue, useUpdateAtom } from 'jotai/utils'
|
import { useAtomCallback, useAtomValue, useUpdateAtom } from 'jotai/utils'
|
||||||
import { useEffect } from 'react'
|
import { useCallback, useEffect } from 'react'
|
||||||
import TrackPlayer, { State, Track } from 'react-native-track-player'
|
import TrackPlayer, { State, Track } from 'react-native-track-player'
|
||||||
import { Song } from '@app/models/music'
|
|
||||||
import PromiseQueue from '@app/util/PromiseQueue'
|
|
||||||
|
|
||||||
type TrackExt = Track & {
|
type TrackExt = Track & {
|
||||||
id: string
|
id: string
|
||||||
queueName: string
|
|
||||||
queueIndex?: number
|
|
||||||
artworkThumb?: string
|
artworkThumb?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,6 +20,42 @@ type Progress = {
|
|||||||
buffered: number
|
buffered: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type QueueExt = {
|
||||||
|
name?: string
|
||||||
|
shuffleOrder?: number[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const queueExtAtom = atomWithAsyncStorage<QueueExt>('@queue', {})
|
||||||
|
|
||||||
|
export const queueNameAtom = atom<string | undefined>(get => get(queueExtAtom).name)
|
||||||
|
export const queueShuffledAtom = atom<boolean>(get => get(queueExtAtom).shuffleOrder !== undefined)
|
||||||
|
|
||||||
|
const queueShuffleOrderAtom = atom<number[] | undefined, number[] | undefined>(
|
||||||
|
get => get(queueExtAtom).shuffleOrder,
|
||||||
|
(get, set, update) => {
|
||||||
|
const queueExt = get(queueExtAtom)
|
||||||
|
if (!equal(queueExt.shuffleOrder, update)) {
|
||||||
|
set(queueExtAtom, {
|
||||||
|
...queueExt,
|
||||||
|
shuffleOrder: update,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const queueNameWriteAtom = atom<string | undefined, string | undefined>(
|
||||||
|
get => get(queueExtAtom).name,
|
||||||
|
(get, set, update) => {
|
||||||
|
const queueExt = get(queueExtAtom)
|
||||||
|
if (!equal(queueExt.name, update)) {
|
||||||
|
set(queueExtAtom, {
|
||||||
|
...queueExt,
|
||||||
|
name: update,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
const playerState = atom<State>(State.None)
|
const playerState = atom<State>(State.None)
|
||||||
export const playerStateAtom = atom<State, State>(
|
export const playerStateAtom = atom<State, State>(
|
||||||
get => get(playerState),
|
get => get(playerState),
|
||||||
@ -51,26 +86,6 @@ export const queueAtom = atom<TrackExt[], TrackExt[]>(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
export const queueNameAtom = atom<string | undefined>(get => {
|
|
||||||
const queue = get(_queue)
|
|
||||||
return queue.length > 0 ? queue[0].queueName : undefined
|
|
||||||
})
|
|
||||||
|
|
||||||
export const queueShuffledAtom = atom<boolean>(get => {
|
|
||||||
const queue = get(_queue)
|
|
||||||
return queue.length > 0 ? queue[0].queueIndex !== undefined : false
|
|
||||||
})
|
|
||||||
|
|
||||||
export const orderedQueueAtom = atom<TrackExt[]>(get => {
|
|
||||||
const queue = get(_queue)
|
|
||||||
|
|
||||||
if (queue.length === 0 || queue[0].queueIndex === undefined) {
|
|
||||||
return queue
|
|
||||||
}
|
|
||||||
|
|
||||||
return queue.map(t => t.queueIndex as number).map(i => queue[i])
|
|
||||||
})
|
|
||||||
|
|
||||||
const _progress = atom<Progress>({ position: 0, duration: 0, buffered: 0 })
|
const _progress = atom<Progress>({ position: 0, duration: 0, buffered: 0 })
|
||||||
export const progressAtom = atom<Progress, Progress>(
|
export const progressAtom = atom<Progress, Progress>(
|
||||||
get => get(_progress),
|
get => get(_progress),
|
||||||
@ -100,6 +115,11 @@ const getTrack = async (index: number): Promise<TrackExt> => {
|
|||||||
return ((await TrackPlayer.getTrack(index)) as TrackExt) || undefined
|
return ((await TrackPlayer.getTrack(index)) as TrackExt) || undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getCurrentTrack = async (): Promise<number | undefined> => {
|
||||||
|
const current = await TrackPlayer.getCurrentTrack()
|
||||||
|
return typeof current === 'number' ? current : undefined
|
||||||
|
}
|
||||||
|
|
||||||
const getPlayerState = async (): Promise<State> => {
|
const getPlayerState = async (): Promise<State> => {
|
||||||
return (await TrackPlayer.getState()) || State.None
|
return (await TrackPlayer.getState()) || State.None
|
||||||
}
|
}
|
||||||
@ -200,92 +220,132 @@ export const useNext = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// export const useAdd = () => {
|
|
||||||
// const setQueue = useUpdateAtom(queueAtom)
|
|
||||||
// const setCurrentTrack = useUpdateAtom(currentTrackAtom)
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
export const useReset = (enqueue = true) => {
|
export const useReset = (enqueue = true) => {
|
||||||
const setQueue = useUpdateAtom(queueAtom)
|
const setQueue = useUpdateAtom(queueAtom)
|
||||||
|
const setQueueExt = useUpdateAtom(queueExtAtom)
|
||||||
const setCurrentTrack = useUpdateAtom(currentTrackAtom)
|
const setCurrentTrack = useUpdateAtom(currentTrackAtom)
|
||||||
|
|
||||||
const reset = async () => {
|
const reset = async () => {
|
||||||
await TrackPlayer.reset()
|
await TrackPlayer.reset()
|
||||||
setQueue([])
|
setQueue([])
|
||||||
|
setQueueExt({})
|
||||||
setCurrentTrack(undefined)
|
setCurrentTrack(undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
return enqueue ? () => trackPlayerCommands.enqueue(reset) : reset
|
return enqueue ? () => trackPlayerCommands.enqueue(reset) : reset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shuffleTracks(tracks: TrackExt[], firstTrack?: number) {
|
||||||
|
if (tracks.length === 0) {
|
||||||
|
return { tracks, shuffleOrder: [] }
|
||||||
|
}
|
||||||
|
|
||||||
|
const trackIndexes = tracks.map((_t, i) => i)
|
||||||
|
let shuffleOrder: number[] = []
|
||||||
|
|
||||||
|
for (let i = trackIndexes.length; i--; i > 0) {
|
||||||
|
const randi = Math.floor(Math.random() * (i + 1))
|
||||||
|
shuffleOrder.push(trackIndexes.splice(randi, 1)[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstTrack !== undefined) {
|
||||||
|
shuffleOrder.splice(shuffleOrder.indexOf(firstTrack), 1)
|
||||||
|
shuffleOrder = [firstTrack, ...shuffleOrder]
|
||||||
|
}
|
||||||
|
|
||||||
|
tracks = shuffleOrder.map(i => tracks[i])
|
||||||
|
|
||||||
|
return { tracks, shuffleOrder }
|
||||||
|
}
|
||||||
|
|
||||||
|
function unshuffleTracks(tracks: TrackExt[], shuffleOrder: number[]): TrackExt[] {
|
||||||
|
if (tracks.length === 0 || shuffleOrder.length === 0) {
|
||||||
|
return tracks
|
||||||
|
}
|
||||||
|
|
||||||
|
return shuffleOrder.map((_v, i) => tracks[shuffleOrder.indexOf(i)])
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useToggleShuffle = () => {
|
||||||
|
const setQueue = useUpdateAtom(queueAtom)
|
||||||
|
const setQueueShuffleOrder = useUpdateAtom(queueShuffleOrderAtom)
|
||||||
|
const getQueueShuffleOrder = useAtomCallback(useCallback(get => get(queueShuffleOrderAtom), []))
|
||||||
|
|
||||||
|
return async () => {
|
||||||
|
return trackPlayerCommands.enqueue(async () => {
|
||||||
|
const queue = await getQueue()
|
||||||
|
const current = await getCurrentTrack()
|
||||||
|
const queueShuffleOrder = await getQueueShuffleOrder()
|
||||||
|
|
||||||
|
await TrackPlayer.remove(queue.map((_t, i) => i).filter(i => i !== current))
|
||||||
|
|
||||||
|
if (queueShuffleOrder === undefined) {
|
||||||
|
let { tracks, shuffleOrder } = shuffleTracks(queue, current)
|
||||||
|
if (tracks.length > 0) {
|
||||||
|
tracks = tracks.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
await TrackPlayer.add(tracks)
|
||||||
|
setQueueShuffleOrder(shuffleOrder)
|
||||||
|
} else {
|
||||||
|
const tracks = unshuffleTracks(queue, queueShuffleOrder)
|
||||||
|
|
||||||
|
if (current !== undefined) {
|
||||||
|
const shuffledCurrent = queueShuffleOrder[current]
|
||||||
|
const tracks1 = tracks.slice(0, shuffledCurrent)
|
||||||
|
const tracks2 = tracks.slice(shuffledCurrent + 1)
|
||||||
|
|
||||||
|
await TrackPlayer.add(tracks2)
|
||||||
|
await TrackPlayer.add(tracks1, 0)
|
||||||
|
} else {
|
||||||
|
await TrackPlayer.add(tracks)
|
||||||
|
}
|
||||||
|
|
||||||
|
setQueueShuffleOrder(undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
setQueue(await getQueue())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const useSetQueue = () => {
|
export const useSetQueue = () => {
|
||||||
const setCurrentTrack = useUpdateAtom(currentTrackAtom)
|
const setCurrentTrack = useUpdateAtom(currentTrackAtom)
|
||||||
const setQueue = useUpdateAtom(queueAtom)
|
const setQueue = useUpdateAtom(queueAtom)
|
||||||
|
const setQueueShuffleOrder = useUpdateAtom(queueShuffleOrderAtom)
|
||||||
|
const setQueueName = useUpdateAtom(queueNameWriteAtom)
|
||||||
const reset = useReset(false)
|
const reset = useReset(false)
|
||||||
const getShuffled = useAtomCallback(get => get(queueShuffledAtom))
|
const getQueueShuffled = useAtomCallback(useCallback(get => get(queueShuffledAtom), []))
|
||||||
|
|
||||||
return async (songs: Song[], name: string, playTrack?: number, shuffle?: boolean) =>
|
return async (songs: Song[], name: string, playTrack?: number, shuffle?: boolean) =>
|
||||||
trackPlayerCommands.enqueue(async () => {
|
trackPlayerCommands.enqueue(async () => {
|
||||||
const shuffleTracks = shuffle !== undefined ? shuffle : await getShuffled()
|
const shuffled = shuffle !== undefined ? shuffle : await getQueueShuffled()
|
||||||
|
|
||||||
await TrackPlayer.setupPlayer()
|
await TrackPlayer.setupPlayer()
|
||||||
await reset()
|
await reset()
|
||||||
|
|
||||||
if (songs.length === 0) {
|
if (songs.length === 0) {
|
||||||
setCurrentTrack(undefined)
|
|
||||||
setQueue([])
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let tracks = songs.map(s => mapSongToTrack(s, name))
|
let queue = songs.map(mapSongToTrack)
|
||||||
console.log(tracks.map(t => t.title))
|
|
||||||
|
|
||||||
if (shuffleTracks) {
|
if (shuffled) {
|
||||||
let trackIndexes = tracks.map((_t, i) => i)
|
const { tracks, shuffleOrder } = shuffleTracks(queue, playTrack)
|
||||||
const shuffleOrder: number[] = []
|
setQueueShuffleOrder(shuffleOrder)
|
||||||
|
queue = tracks
|
||||||
for (let i = trackIndexes.length; i--; i > 0) {
|
|
||||||
tracks[i].queueIndex = i
|
|
||||||
|
|
||||||
const randi = Math.floor(Math.random() * (i + 1))
|
|
||||||
shuffleOrder.push(trackIndexes[randi])
|
|
||||||
trackIndexes.splice(randi, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
tracks = shuffleOrder.map(i => tracks[i])
|
|
||||||
|
|
||||||
if (playTrack !== undefined) {
|
|
||||||
tracks = [
|
|
||||||
tracks.splice(
|
|
||||||
tracks.findIndex(t => t.queueIndex === playTrack),
|
|
||||||
1,
|
|
||||||
)[0],
|
|
||||||
...tracks,
|
|
||||||
]
|
|
||||||
playTrack = 0
|
playTrack = 0
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
console.log(tracks.map(t => t.title))
|
|
||||||
|
|
||||||
playTrack = playTrack || 0
|
playTrack = playTrack || 0
|
||||||
setCurrentTrack(tracks[playTrack])
|
setCurrentTrack(queue[playTrack])
|
||||||
|
|
||||||
if (playTrack === 0) {
|
if (playTrack === 0) {
|
||||||
await TrackPlayer.add(tracks)
|
await TrackPlayer.add(queue)
|
||||||
await TrackPlayer.play()
|
await TrackPlayer.play()
|
||||||
} else {
|
} else {
|
||||||
const tracks1 = tracks.slice(0, playTrack)
|
const tracks1 = queue.slice(0, playTrack)
|
||||||
const tracks2 = tracks.slice(playTrack)
|
const tracks2 = queue.slice(playTrack)
|
||||||
|
|
||||||
await TrackPlayer.add(tracks2)
|
await TrackPlayer.add(tracks2)
|
||||||
await TrackPlayer.play()
|
await TrackPlayer.play()
|
||||||
@ -294,6 +354,7 @@ export const useSetQueue = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setQueue(await getQueue())
|
setQueue(await getQueue())
|
||||||
|
setQueueName(name)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,10 +371,9 @@ export const useProgress = () => {
|
|||||||
return progress
|
return progress
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapSongToTrack(song: Song, queueName: string): TrackExt {
|
function mapSongToTrack(song: Song): TrackExt {
|
||||||
return {
|
return {
|
||||||
id: song.id,
|
id: song.id,
|
||||||
queueName,
|
|
||||||
title: song.title,
|
title: song.title,
|
||||||
artist: song.artist || 'Unknown Artist',
|
artist: song.artist || 'Unknown Artist',
|
||||||
album: song.album || 'Unknown Album',
|
album: song.album || 'Unknown Album',
|
||||||
|
|||||||
@ -4145,9 +4145,9 @@ joi@^17.2.1:
|
|||||||
"@sideway/pinpoint" "^2.0.0"
|
"@sideway/pinpoint" "^2.0.0"
|
||||||
|
|
||||||
jotai@^1.1.0:
|
jotai@^1.1.0:
|
||||||
version "1.1.2"
|
version "1.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/jotai/-/jotai-1.1.2.tgz#3f211e0c03c74e95ea6fd7a69c1d2b65731009bf"
|
resolved "https://registry.yarnpkg.com/jotai/-/jotai-1.2.2.tgz#631fd7ad44e9ac26cdf9874d52282c1cfe032807"
|
||||||
integrity sha512-dni4wtgYGG+s9YbOJN7lcfrrhxiD6bH1SN00Pnl0F2htgOXmjxqkGlFzw02OK0Rw35wGNzBfDTJVtbGD9wHOhg==
|
integrity sha512-iqkkUdWsH2Mk4HY1biba/8kA77+8liVBy8E0d8Nce29qow4h9mzdDhpTasAruuFYPycw6JvfZgL5RB0JJuIZjw==
|
||||||
|
|
||||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user