impl shuffle, controls are temp

This commit is contained in:
austinried 2021-07-20 13:02:42 +09:00
parent df783a074b
commit f859be51a5
5 changed files with 89 additions and 41 deletions

View File

@ -8,7 +8,7 @@ import {
playerStateAtom, playerStateAtom,
progressAtom, progressAtom,
progressSubsAtom, progressSubsAtom,
queueWriteAtom, queueAtom,
useRefreshCurrentTrack, useRefreshCurrentTrack,
useRefreshPlayerState, useRefreshPlayerState,
useRefreshProgress, useRefreshProgress,
@ -85,7 +85,7 @@ const PlayerState = () => {
} }
const QueueState = () => { const QueueState = () => {
const setQueue = useUpdateAtom(queueWriteAtom) const setQueue = useUpdateAtom(queueAtom)
const refreshQueue = useRefreshQueue() const refreshQueue = useRefreshQueue()
const update = async (payload?: Payload) => { const update = async (payload?: Payload) => {

View File

@ -26,7 +26,8 @@ const AlbumDetails: React.FC<{
const Songs = () => ( const Songs = () => (
<> <>
<View style={styles.controls}> <View style={styles.controls}>
<Button title="Play Album" onPress={() => setQueue(album.songs, album.name, album.songs[0].id)} /> <Button title="Play Album" onPress={() => setQueue(album.songs, album.name, undefined, false)} />
<Button title="Shuffle" onPress={() => setQueue(album.songs, album.name, undefined, true)} />
</View> </View>
<View style={styles.songs}> <View style={styles.songs}>
{album.songs {album.songs
@ -37,8 +38,8 @@ const AlbumDetails: React.FC<{
return a.title.localeCompare(b.title) return a.title.localeCompare(b.title)
} }
}) })
.map(s => ( .map((s, i) => (
<SongItem key={s.id} song={s} onPress={() => setQueue(album.songs, album.name, s.id)} /> <SongItem key={i} song={s} onPress={() => setQueue(album.songs, album.name, i)} />
))} ))}
</View> </View>
</> </>

View File

@ -48,13 +48,13 @@ const ArtistDetails: React.FC<{ id: string }> = ({ id }) => {
const TopSongs = () => ( const TopSongs = () => (
<> <>
<Text style={styles.header}>Top Songs</Text> <Text style={styles.header}>Top Songs</Text>
{artist.topSongs.map(s => ( {artist.topSongs.map((s, i) => (
<SongItem <SongItem
key={s.id} key={i}
song={s} song={s}
showArt={true} showArt={true}
subtitle="album" subtitle="album"
onPress={() => setQueue(artist.topSongs, `Top Songs: ${artist.name}`, s.id)} onPress={() => setQueue(artist.topSongs, `Top Songs: ${artist.name}`, i)}
/> />
))} ))}
</> </>

View File

@ -26,11 +26,11 @@ const PlaylistDetails: React.FC<{
const Songs = () => ( const Songs = () => (
<> <>
<View style={styles.controls}> <View style={styles.controls}>
<Button title="Play Playlist" onPress={() => setQueue(playlist.songs, playlist.name, playlist.songs[0].id)} /> <Button title="Play Playlist" onPress={() => setQueue(playlist.songs, playlist.name)} />
</View> </View>
<View style={styles.songs}> <View style={styles.songs}>
{playlist.songs.map((s, index) => ( {playlist.songs.map((s, i) => (
<SongItem key={index} song={s} showArt={true} onPress={() => setQueue(playlist.songs, playlist.name, s.id)} /> <SongItem key={i} song={s} showArt={true} onPress={() => setQueue(playlist.songs, playlist.name, i)} />
))} ))}
</View> </View>
</> </>

View File

@ -1,6 +1,6 @@
import equal from 'fast-deep-equal' import equal from 'fast-deep-equal'
import { atom } from 'jotai' import { atom } from 'jotai'
import { useAtomValue, useUpdateAtom } from 'jotai/utils' import { useAtomCallback, useAtomValue, useUpdateAtom } from 'jotai/utils'
import { useEffect } from 'react' import { 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 { Song } from '@app/models/music'
@ -9,6 +9,7 @@ import PromiseQueue from '@app/util/PromiseQueue'
type TrackExt = Track & { type TrackExt = Track & {
id: string id: string
queueName: string queueName: string
queueIndex?: number
artworkThumb?: string artworkThumb?: string
} }
@ -41,8 +42,7 @@ export const currentTrackAtom = atom<OptionalTrackExt, OptionalTrackExt>(
) )
const _queue = atom<TrackExt[]>([]) const _queue = atom<TrackExt[]>([])
export const queueReadAtom = atom<TrackExt[]>(get => get(_queue)) export const queueAtom = atom<TrackExt[], TrackExt[]>(
export const queueWriteAtom = atom<TrackExt[], TrackExt[]>(
get => get(_queue), get => get(_queue),
(get, set, update) => { (get, set, update) => {
if (!equal(get(_queue), update)) { if (!equal(get(_queue), update)) {
@ -53,10 +53,22 @@ export const queueWriteAtom = atom<TrackExt[], TrackExt[]>(
export const queueNameAtom = atom<string | undefined>(get => { export const queueNameAtom = atom<string | undefined>(get => {
const queue = get(_queue) const queue = get(_queue)
if (queue.length > 0) { return queue.length > 0 ? queue[0].queueName : undefined
return queue[0].queueName })
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 undefined
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 })
@ -106,7 +118,7 @@ const getProgress = async (): Promise<Progress> => {
} }
export const useRefreshQueue = () => { export const useRefreshQueue = () => {
const setQueue = useUpdateAtom(queueWriteAtom) const setQueue = useUpdateAtom(queueAtom)
return () => return () =>
trackPlayerCommands.enqueue(async () => { trackPlayerCommands.enqueue(async () => {
@ -188,22 +200,22 @@ export const useNext = () => {
}) })
} }
export const useAdd = () => { // export const useAdd = () => {
const setQueue = useUpdateAtom(queueWriteAtom) // const setQueue = useUpdateAtom(queueAtom)
const setCurrentTrack = useUpdateAtom(currentTrackAtom) // const setCurrentTrack = useUpdateAtom(currentTrackAtom)
return (tracks: TrackExt | TrackExt[], insertBeforeindex?: number) => // return (tracks: TrackExt | TrackExt[], insertBeforeindex?: number) =>
trackPlayerCommands.enqueue(async () => { // trackPlayerCommands.enqueue(async () => {
await TrackPlayer.add(tracks, insertBeforeindex) // await TrackPlayer.add(tracks, insertBeforeindex)
const queue = await getQueue() // const queue = await getQueue()
setQueue(queue) // setQueue(queue)
setCurrentTrack(queue.length > 0 ? queue[await TrackPlayer.getCurrentTrack()] : undefined) // setCurrentTrack(queue.length > 0 ? queue[await TrackPlayer.getCurrentTrack()] : undefined)
}) // })
} // }
export const useReset = (enqueue = true) => { export const useReset = (enqueue = true) => {
const setQueue = useUpdateAtom(queueWriteAtom) const setQueue = useUpdateAtom(queueAtom)
const setCurrentTrack = useUpdateAtom(currentTrackAtom) const setCurrentTrack = useUpdateAtom(currentTrackAtom)
const reset = async () => { const reset = async () => {
@ -217,28 +229,63 @@ export const useReset = (enqueue = true) => {
export const useSetQueue = () => { export const useSetQueue = () => {
const setCurrentTrack = useUpdateAtom(currentTrackAtom) const setCurrentTrack = useUpdateAtom(currentTrackAtom)
const setQueue = useUpdateAtom(queueWriteAtom) const setQueue = useUpdateAtom(queueAtom)
const reset = useReset(false) const reset = useReset(false)
const getShuffled = useAtomCallback(get => get(queueShuffledAtom))
return async (songs: Song[], name: string, playId?: string) => return async (songs: Song[], name: string, playTrack?: number, shuffle?: boolean) =>
trackPlayerCommands.enqueue(async () => { trackPlayerCommands.enqueue(async () => {
const shuffleTracks = shuffle !== undefined ? shuffle : await getShuffled()
await TrackPlayer.setupPlayer() await TrackPlayer.setupPlayer()
await reset() await reset()
const tracks = songs.map(s => mapSongToTrack(s, name))
if (playId) { if (songs.length === 0) {
setCurrentTrack(tracks.find(t => t.id === playId)) setCurrentTrack(undefined)
setQueue([])
return
} }
if (!playId) { let tracks = songs.map(s => mapSongToTrack(s, name))
await TrackPlayer.add(tracks) console.log(tracks.map(t => t.title))
} else if (playId === tracks[0].id) {
if (shuffleTracks) {
let trackIndexes = tracks.map((_t, i) => i)
const shuffleOrder: number[] = []
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
}
}
console.log(tracks.map(t => t.title))
playTrack = playTrack || 0
setCurrentTrack(tracks[playTrack])
if (playTrack === 0) {
await TrackPlayer.add(tracks) await TrackPlayer.add(tracks)
await TrackPlayer.play() await TrackPlayer.play()
} else { } else {
const playIndex = tracks.findIndex(t => t.id === playId) const tracks1 = tracks.slice(0, playTrack)
const tracks1 = tracks.slice(0, playIndex) const tracks2 = tracks.slice(playTrack)
const tracks2 = tracks.slice(playIndex)
await TrackPlayer.add(tracks2) await TrackPlayer.add(tracks2)
await TrackPlayer.play() await TrackPlayer.play()