2022-04-20 09:37:22 +00:00
|
|
|
import { reactive } from 'vue'
|
2024-01-11 19:30:43 +00:00
|
|
|
import { differenceBy, unionBy } from 'lodash'
|
2024-05-19 05:49:42 +00:00
|
|
|
import { arrayify, isSong, logger, moveItemsInList } from '@/utils'
|
2022-09-15 09:07:25 +00:00
|
|
|
import { http } from '@/services'
|
2022-06-10 10:47:46 +00:00
|
|
|
import { songStore } from '@/stores'
|
2022-04-23 22:31:40 +00:00
|
|
|
|
2022-04-15 14:24:30 +00:00
|
|
|
export const queueStore = {
|
2024-05-19 05:49:42 +00:00
|
|
|
state: reactive<{ playables: Playable[] }>({
|
|
|
|
playables: []
|
2022-04-20 09:37:22 +00:00
|
|
|
}),
|
2022-04-15 14:24:30 +00:00
|
|
|
|
2024-01-01 11:40:21 +00:00
|
|
|
init (savedState: QueueState) {
|
|
|
|
// don't set this.all here, as it would trigger saving state
|
2024-05-19 05:49:42 +00:00
|
|
|
this.state.playables = songStore.syncWithVault(savedState.songs)
|
2024-01-01 11:40:21 +00:00
|
|
|
|
2024-05-19 05:49:42 +00:00
|
|
|
if (!this.state.playables.length) {
|
2024-01-01 11:40:21 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (savedState.current_song) {
|
|
|
|
songStore.syncWithVault(savedState.current_song)[0].playback_state = 'Paused'
|
|
|
|
} else {
|
|
|
|
this.all[0].playback_state = 'Paused'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2022-04-23 22:31:40 +00:00
|
|
|
get all () {
|
2024-05-19 05:49:42 +00:00
|
|
|
return this.state.playables
|
2022-04-15 14:24:30 +00:00
|
|
|
},
|
|
|
|
|
2024-05-19 05:49:42 +00:00
|
|
|
set all (playables: Playable[]) {
|
|
|
|
this.state.playables = playables
|
|
|
|
songStore.syncWithVault(playables.filter(isSong))
|
2024-01-01 11:40:21 +00:00
|
|
|
this.saveState()
|
2022-04-15 14:24:30 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
get first () {
|
|
|
|
return this.all[0]
|
|
|
|
},
|
|
|
|
|
|
|
|
get last () {
|
|
|
|
return this.all[this.all.length - 1]
|
|
|
|
},
|
|
|
|
|
2024-05-19 05:49:42 +00:00
|
|
|
contains (playable: Playable) {
|
|
|
|
return this.all.includes(reactive(playable))
|
2022-04-15 14:24:30 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2022-04-23 22:31:40 +00:00
|
|
|
* Add song(s) to the end of the current queue.
|
2022-04-15 14:24:30 +00:00
|
|
|
*/
|
2024-05-19 05:49:42 +00:00
|
|
|
queue (playables: MaybeArray<Playable>) {
|
|
|
|
this.unqueue(playables)
|
|
|
|
this.all = unionBy(this.all, arrayify(playables), 'id')
|
2022-04-15 14:24:30 +00:00
|
|
|
},
|
|
|
|
|
2024-05-19 05:49:42 +00:00
|
|
|
queueIfNotQueued (playable: Playable) {
|
|
|
|
if (!this.contains(playable)) {
|
|
|
|
this.queueAfterCurrent(playable)
|
2022-04-21 18:12:11 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2024-05-19 05:49:42 +00:00
|
|
|
queueToTop (playables: MaybeArray<Playable>) {
|
|
|
|
this.all = unionBy(arrayify(playables), this.all, 'id')
|
2022-04-15 14:24:30 +00:00
|
|
|
},
|
|
|
|
|
2024-05-19 05:49:42 +00:00
|
|
|
replaceQueueWith (playables: MaybeArray<Playable>) {
|
|
|
|
this.all = arrayify(playables)
|
2022-04-15 14:24:30 +00:00
|
|
|
},
|
|
|
|
|
2024-05-19 05:49:42 +00:00
|
|
|
queueAfterCurrent (playables: MaybeArray<Playable>) {
|
|
|
|
playables = arrayify(playables)
|
2022-04-15 14:24:30 +00:00
|
|
|
|
|
|
|
if (!this.current || !this.all.length) {
|
2024-05-19 05:49:42 +00:00
|
|
|
return this.queue(playables)
|
2022-04-15 14:24:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// First we unqueue the songs to make sure there are no duplicates.
|
2024-05-19 05:49:42 +00:00
|
|
|
this.unqueue(playables)
|
2022-04-15 14:24:30 +00:00
|
|
|
|
|
|
|
const head = this.all.splice(0, this.indexOf(this.current) + 1)
|
2024-05-19 05:49:42 +00:00
|
|
|
this.all = head.concat(reactive(playables), this.all)
|
2022-04-15 14:24:30 +00:00
|
|
|
},
|
|
|
|
|
2024-06-02 17:15:31 +00:00
|
|
|
unqueue (playables: MaybeArray<Playable>) {
|
2024-05-19 05:49:42 +00:00
|
|
|
playables = arrayify(playables)
|
|
|
|
playables.forEach(song => (song.playback_state = 'Stopped'))
|
|
|
|
this.all = differenceBy(this.all, playables, 'id')
|
2022-04-15 14:24:30 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move some songs to after a target.
|
|
|
|
*/
|
2024-06-02 17:15:31 +00:00
|
|
|
move (playables: MaybeArray<Playable>, target: Playable, type: MoveType) {
|
2024-05-19 05:49:42 +00:00
|
|
|
this.state.playables = moveItemsInList(this.state.playables, playables, target, type)
|
2024-01-01 11:40:21 +00:00
|
|
|
this.saveState()
|
2022-04-15 14:24:30 +00:00
|
|
|
},
|
|
|
|
|
2022-04-21 18:12:11 +00:00
|
|
|
clear () {
|
2022-04-15 14:24:30 +00:00
|
|
|
this.all = []
|
|
|
|
},
|
|
|
|
|
2024-01-11 19:26:38 +00:00
|
|
|
/**
|
|
|
|
* Clear the queue without saving the state.
|
|
|
|
*/
|
|
|
|
clearSilently () {
|
2024-05-19 05:49:42 +00:00
|
|
|
this.state.playables = []
|
2024-01-11 19:26:38 +00:00
|
|
|
},
|
|
|
|
|
2024-05-19 05:49:42 +00:00
|
|
|
indexOf (playable: Playable) {
|
|
|
|
return this.all.indexOf(reactive(playable))
|
2022-04-15 14:24:30 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
get next () {
|
|
|
|
if (!this.current) {
|
|
|
|
return this.first
|
|
|
|
}
|
|
|
|
|
2022-12-22 15:46:51 +00:00
|
|
|
const index = this.indexOf(this.current) + 1
|
2022-04-15 14:24:30 +00:00
|
|
|
|
|
|
|
return index >= this.all.length ? undefined : this.all[index]
|
|
|
|
},
|
|
|
|
|
|
|
|
get previous () {
|
|
|
|
if (!this.current) {
|
|
|
|
return this.last
|
|
|
|
}
|
|
|
|
|
2022-12-22 15:46:51 +00:00
|
|
|
const index = this.indexOf(this.current) - 1
|
2022-04-15 14:24:30 +00:00
|
|
|
|
|
|
|
return index < 0 ? undefined : this.all[index]
|
|
|
|
},
|
|
|
|
|
|
|
|
get current () {
|
2024-01-24 22:39:47 +00:00
|
|
|
return this.all.find(({ playback_state }) => playback_state !== 'Stopped')
|
2022-04-15 14:24:30 +00:00
|
|
|
},
|
|
|
|
|
2022-06-10 10:47:46 +00:00
|
|
|
async fetchRandom (limit = 500) {
|
2024-01-01 11:40:21 +00:00
|
|
|
this.all = await http.get<Song[]>(`queue/fetch?order=rand&limit=${limit}`)
|
|
|
|
return this.all
|
2022-06-10 10:47:46 +00:00
|
|
|
},
|
|
|
|
|
2024-05-19 05:49:42 +00:00
|
|
|
async fetchInOrder (sortField: PlayableListSortField, order: SortOrder, limit = 500) {
|
2024-01-01 11:40:21 +00:00
|
|
|
this.all = await http.get<Song[]>(`queue/fetch?order=${order}&sort=${sortField}&limit=${limit}`)
|
|
|
|
return this.all
|
|
|
|
},
|
|
|
|
|
|
|
|
saveState () {
|
|
|
|
try {
|
2024-05-19 05:49:42 +00:00
|
|
|
http.silently.put('queue/state', { songs: this.state.playables.map(({ id }) => id) })
|
2024-04-23 11:24:29 +00:00
|
|
|
} catch (error: unknown) {
|
|
|
|
logger.error(error)
|
2024-01-01 11:40:21 +00:00
|
|
|
}
|
2022-04-15 14:24:30 +00:00
|
|
|
}
|
|
|
|
}
|