fix(test): fix playbackService tests

This commit is contained in:
Phan An 2022-07-25 20:23:30 +02:00
parent 235362ba30
commit be68021723
No known key found for this signature in database
GPG key ID: A81E4477F0BB6FDC
5 changed files with 127 additions and 359 deletions

View file

@ -103,8 +103,11 @@ export default abstract class UnitTestCase {
}
protected setReadOnlyProperty<T> (obj: T, prop: keyof T, value: any) {
return Object.defineProperty(obj, prop, {
get: () => value
return Object.defineProperties(obj, {
[prop]: {
value,
configurable: true
}
})
}

View file

@ -1,221 +0,0 @@
import factory from '@/__tests__/factory'
const currentUser = factory<User>('user', {
id: 1,
name: 'Phan An',
email: 'me@phanan.net',
is_admin: true
})
const unknownArtist = factory<Artist>('artist', { id: 1, name: 'Unknown Artist' })
const variousArtist = factory<Artist>('artist', { id: 2, name: 'Various Artist' })
const all4One = factory<Artist>('artist', { id: 3, name: 'All-4-One' })
const bobDylan = factory<Artist>('artist', { id: 4, name: 'Bob Dylan' })
const jamesBlunt = factory<Artist>('artist', { id: 5, name: 'James Blunt' })
const all4OneAlbum = factory<Album>('album', {
id: 1193,
artist_id: 3,
name: 'All-4-One',
cover: '/img/covers/565c0f7067425.jpeg'
})
const musicSpeaks = factory<Album>('album', {
id: 1194,
artist_id: 3,
name: 'And The Music Speaks',
cover: '/img/covers/unknown-album.png'
})
const spaceJam = factory<Album>('album', {
id: 1195,
artist_id: 3,
name: 'Space Jam',
cover: '/img/covers/565c0f7115e0f.png'
})
const highway = factory<Album>('album', {
id: 1217,
artist_id: 4,
name: 'Highway 61 Revisited',
cover: '/img/covers/565c0f76dc6e8.jpeg'
})
const patGarrett = factory<Album>('album', {
id: 1218,
artist_id: 4,
name: 'Pat Garrett & Billy the Kid',
cover: '/img/covers/unknown-album.png'
})
const theTimes = factory<Album>('album', {
id: 1219,
artist_id: 4,
name: 'The Times They Are A-Changin',
cover: '/img/covers/unknown-album.png'
})
const backToBedlam = factory<Album>('album', {
id: 1268,
artist_id: 5,
name: 'Back To Bedlam',
cover: '/img/covers/unknown-album.png'
})
export default {
artists: [unknownArtist, variousArtist, all4One, bobDylan, jamesBlunt],
albums: [
all4OneAlbum,
musicSpeaks,
spaceJam,
highway,
patGarrett,
theTimes,
backToBedlam
],
songs: [
factory<Song>('song', {
id: '39189f4545f9d5671fb3dc964f0080a0',
album_id: all4OneAlbum.id,
artist_id: all4One.id,
title: 'I Swear',
length: 259.92,
play_count: 4
}),
factory<Song>('song', {
id: 'a6a550f7d950d2a2520f9bf1a60f025a',
album_id: musicSpeaks.id,
artist_id: all4One.id,
title: 'I can love you like that',
length: 262.61,
play_count: 2
}),
factory<Song>('song', {
id: 'd86c30fd34f13c1aff8db59b7fc9c610',
album_id: spaceJam.id,
artist_id: all4One.id,
title: 'I turn to you',
length: 293.04
}),
factory<Song>('song', {
id: 'e6d3977f3ffa147801ca5d1fdf6fa55e',
album_id: highway.id,
artist_id: bobDylan.id,
title: 'Like a rolling stone',
length: 373.63
}),
factory<Song>('song', {
id: 'aa16bbef6a9710eb9a0f41ecc534fad5',
album_id: patGarrett.id,
artist_id: bobDylan.id,
title: 'Knockin\' on heaven\'s door',
length: 151.9
}),
factory<Song>('song', {
id: 'cb7edeac1f097143e65b1b2cde102482',
album_id: theTimes.id,
artist_id: bobDylan.id,
title: 'The times they are a-changin\'',
length: 196
}),
factory<Song>('song', {
id: '0ba9fb128427b32683b9eb9140912a70',
album_id: backToBedlam.id,
artist_id: jamesBlunt.id,
title: 'No bravery',
length: 243.12
}),
factory<Song>('song', {
id: '123fd1ad32240ecab28a4e86ed5173',
album_id: backToBedlam.id,
artist_id: jamesBlunt.id,
title: 'So long, Jimmy',
length: 265.04
}),
factory<Song>('song', {
id: '6a54c674d8b16732f26df73f59c63e21',
album_id: backToBedlam.id,
artist_id: jamesBlunt.id,
title: 'Wisemen',
length: 223.14
}),
factory<Song>('song', {
id: '6df7d82a9a8701e40d1c291cf14a16bc',
album_id: backToBedlam.id,
artist_id: jamesBlunt.id,
title: 'Goodbye my lover',
length: 258.61
}),
factory<Song>('song', {
id: '74a2000d343e4587273d3ad14e2fd741',
album_id: backToBedlam.id,
artist_id: jamesBlunt.id,
title: 'High',
length: 245.86
}),
factory<Song>('song', {
id: '7900ab518f51775fe6cf06092c074ee5',
album_id: backToBedlam.id,
artist_id: jamesBlunt.id,
title: 'You\'re beautiful',
length: 213.29
}),
factory<Song>('song', {
id: '803910a51f9893347e087af851e38777',
album_id: backToBedlam.id,
artist_id: jamesBlunt.id,
title: 'Cry',
length: 246.91
}),
factory<Song>('song', {
id: 'd82b0d4d4803ebbcb61000a5b6a868f5',
album_id: backToBedlam.id,
artist_id: jamesBlunt.id,
title: 'Tears and rain',
length: 244.45
})
],
interactions: [
{
id: 1,
song_id: '7900ab518f51775fe6cf06092c074ee5',
liked: false,
play_count: 1
},
{
id: 2,
song_id: '95c0ffc33c08c8c14ea5de0a44d5df3c',
liked: false,
play_count: 2
},
{
id: 3,
song_id: 'c83b201502eb36f1084f207761fa195c',
liked: false,
play_count: 1
},
{
id: 4,
song_id: 'cb7edeac1f097143e65b1b2cde102482',
liked: true,
play_count: 3
},
{
id: 5,
song_id: 'ccc38cc14bb95aefdf6da4b34adcf548',
liked: false,
play_count: 4
}
] as Interaction[],
currentUser,
users: [
currentUser,
factory<User>('user', {
id: 2,
name: 'John Doe',
email: 'john@doe.tld',
is_admin: false
})
]
}

View file

@ -5,7 +5,7 @@ import { eventBus, noop } from '@/utils'
import router from '@/router'
import factory from '@/__tests__/factory'
import UnitTestCase from '@/__tests__/UnitTestCase'
import { nextTick } from 'vue'
import { nextTick, reactive } from 'vue'
import { socketService } from '@/services'
import { playbackService } from './playbackService'
@ -43,11 +43,22 @@ new class extends UnitTestCase {
super.beforeEach(() => this.setupEnvironment())
}
private setCurrentSong (song?: Song) {
song = reactive(song || factory<Song>('song', {
playback_state: 'Playing'
}))
queueStore.state.songs = reactive([song])
return song
}
protected test () {
it('only initializes once', () => {
const spy = vi.spyOn(plyr, 'setup')
playbackService.init()
expect(spy).toHaveBeenCalled()
playbackService.init()
expect(spy).toHaveBeenCalledTimes(1)
})
@ -60,23 +71,23 @@ new class extends UnitTestCase {
])(
'when playCountRegistered is %s, isTranscoding is %s, current media time is %d, media duration is %d, then registerPlay() should be call %d times',
(playCountRegistered, isTranscoding, currentTime, duration, numberOfCalls) => {
this.setCurrentSong(factory<Song>('song', {
play_count_registered: playCountRegistered,
playback_state: 'Playing'
}))
this.setReadOnlyProperty(playbackService, 'isTranscoding', isTranscoding)
playbackService.init()
const mediaElement = playbackService.player!.media
const mediaElement = playbackService.player.media
// we can't set mediaElement.currentTime|duration directly because they're read-only
Object.defineProperties(mediaElement, {
currentTime: {
value: currentTime,
configurable: true
},
duration: {
value: duration,
configurable: true
}
})
this.setReadOnlyProperty(mediaElement, 'currentTime', currentTime)
this.setReadOnlyProperty(mediaElement, 'duration', duration)
const registerPlayMock = this.mock(playbackService, 'registerPlay')
mediaElement.dispatchEvent(new Event('timeupdate'))
expect(registerPlayMock).toHaveBeenCalledTimes(numberOfCalls)
})
@ -89,11 +100,11 @@ new class extends UnitTestCase {
it('scrobbles if current song ends', () => {
commonStore.state.use_last_fm = true
userStore.current = factory<User>('user', {
userStore.state.current = reactive(factory<User>('user', {
preferences: {
lastfm_session_key: 'foo'
}
})
}))
playbackService.init()
const scrobbleMock = this.mock(songStore, 'scrobble')
@ -109,7 +120,9 @@ new class extends UnitTestCase {
playbackService.init()
const restartMock = this.mock(playbackService, 'restart')
const playNextMock = this.mock(playbackService, 'playNext')
playbackService.player!.media.dispatchEvent(new Event('ended'))
expect(restartMock).toHaveBeenCalledTimes(restartCalls)
expect(playNextMock).toHaveBeenCalledTimes(playNextCalls)
})
@ -128,16 +141,8 @@ new class extends UnitTestCase {
const mediaElement = playbackService.player!.media
Object.defineProperties(mediaElement, {
currentTime: {
value: currentTime,
configurable: true
},
duration: {
value: duration,
configurable: true
}
})
this.setReadOnlyProperty(mediaElement, 'currentTime', currentTime)
this.setReadOnlyProperty(mediaElement, 'duration', duration)
const preloadMock = this.mock(playbackService, 'preload')
mediaElement.dispatchEvent(new Event('timeupdate'))
@ -148,14 +153,12 @@ new class extends UnitTestCase {
it('registers play', () => {
const recentlyPlayedStoreAddMock = this.mock(recentlyPlayedStore, 'add')
const recentlyPlayedStoreFetchAllMock = this.mock(recentlyPlayedStore, 'fetch')
const registerPlayMock = this.mock(songStore, 'registerPlay')
const song = factory<Song>('song')
playbackService.registerPlay(song)
expect(recentlyPlayedStoreAddMock).toHaveBeenCalledWith(song)
expect(recentlyPlayedStoreFetchAllMock).toHaveBeenCalled()
expect(registerPlayMock).toHaveBeenCalledWith(song)
expect(song.play_count_registered).toBe(true)
})
@ -180,13 +183,11 @@ new class extends UnitTestCase {
})
it('restarts a song', async () => {
const song = factory<Song>('song')
const song = this.setCurrentSong()
this.mock(Math, 'floor', 1000)
const emitMock = this.mock(eventBus, 'emit')
const broadcastMock = this.mock(socketService, 'broadcast')
const showNotificationMock = this.mock(playbackService, 'showNotification')
const dataToBroadcast = {}
this.mock(songStore, 'generateDataToBroadcast', dataToBroadcast)
const restartMock = this.mock(playbackService.player!, 'restart')
const playMock = this.mock(window.HTMLMediaElement.prototype, 'play')
@ -195,7 +196,7 @@ new class extends UnitTestCase {
expect(song.play_start_time).toEqual(1000)
expect(song.play_count_registered).toBe(false)
expect(emitMock).toHaveBeenCalledWith('SONG_STARTED', song)
expect(broadcastMock).toHaveBeenCalledWith('SOCKET_SONG', dataToBroadcast)
expect(broadcastMock).toHaveBeenCalledWith('SOCKET_SONG', song)
expect(showNotificationMock).toHaveBeenCalled()
expect(restartMock).toHaveBeenCalled()
expect(playMock).toHaveBeenCalled()
@ -279,23 +280,22 @@ new class extends UnitTestCase {
})
it('pauses playback', () => {
const currentSong = factory<Song>('song')
const dataToBroadcast = {}
this.mock(songStore, 'generateDataToBroadcast', dataToBroadcast)
const song = this.setCurrentSong()
const pauseMock = this.mock(playbackService.player!, 'pause')
const broadcastMock = this.mock(socketService, 'broadcast')
playbackService.pause()
expect(currentSong.playback_state).toEqual('Paused')
expect(broadcastMock).toHaveBeenCalledWith('SOCKET_SONG', dataToBroadcast)
expect(song.playback_state).toEqual('Paused')
expect(broadcastMock).toHaveBeenCalledWith('SOCKET_SONG', song)
expect(pauseMock).toHaveBeenCalled()
})
it('resumes playback', async () => {
const currentSong = factory<Song>('song')
const dataToBroadcast = {}
this.mock(songStore, 'generateDataToBroadcast', dataToBroadcast)
const song = this.setCurrentSong(factory<Song>('song', {
playback_state: 'Paused'
}))
const playMock = this.mock(window.HTMLMediaElement.prototype, 'play')
const broadcastMock = this.mock(socketService, 'broadcast')
const emitMock = this.mock(eventBus, 'emit')
@ -304,12 +304,13 @@ new class extends UnitTestCase {
await playbackService.resume()
expect(queueStore.current?.playback_state).toEqual('Playing')
expect(broadcastMock).toHaveBeenCalledWith('SOCKET_SONG', dataToBroadcast)
expect(broadcastMock).toHaveBeenCalledWith('SOCKET_SONG', song)
expect(playMock).toHaveBeenCalled()
expect(emitMock).toHaveBeenCalledWith('SONG_STARTED', currentSong)
expect(emitMock).toHaveBeenCalledWith('SONG_STARTED', song)
})
it('plays first in queue if toggled when there is no current song', async () => {
queueStore.clear()
const playFirstInQueueMock = this.mock(playbackService, 'playFirstInQueue')
await playbackService.toggle()
@ -318,10 +319,10 @@ new class extends UnitTestCase {
})
it.each<[MethodOf<typeof playbackService>, PlaybackState]>([
['resume', 'Stopped'],
['resume', 'Paused'],
['pause', 'Playing']
])('%ss playback if toggled when current song playback state is %s', async (action, playbackState) => {
this.setCurrentSong(factory<Song>('song', { playback_state: playbackState }))
const actionMock = this.mock(playbackService, action)
await playbackService.toggle()
@ -375,14 +376,5 @@ new class extends UnitTestCase {
expect(playMock).toHaveBeenCalledWith(songs[0])
})
it('playFirstInQueue triggers queueAndPlay if queue is empty', async () => {
queueStore.all = []
const queueAndPlayMock = this.mock(playbackService, 'queueAndPlay')
await playbackService.playFirstInQueue()
expect(queueAndPlayMock).toHaveBeenCalled()
})
}
}

View file

@ -22,20 +22,20 @@ import router from '@/router'
const PRELOAD_BUFFER = 30
const DEFAULT_VOLUME_VALUE = 7
const VOLUME_INPUT_SELECTOR = '#volumeInput'
const REPEAT_MODES: RepeatMode[] = ['NO_REPEAT', 'REPEAT_ALL', 'REPEAT_ONE']
export const playbackService = {
player: null as Plyr | null,
volumeInput: null as unknown as HTMLInputElement,
repeatModes: REPEAT_MODES,
initialized: false,
class PlaybackService {
public player: Plyr
private volumeInput: HTMLInputElement
private repeatModes: RepeatMode[] = ['NO_REPEAT', 'REPEAT_ALL', 'REPEAT_ONE']
private initialized = false
init () {
// We don't need to init this service twice, or the media events will be duplicated.
public init () {
if (this.initialized) {
return
}
this.initialized = true
this.player = plyr.setup('.plyr', {
controls: []
})[0]
@ -54,10 +54,9 @@ export const playbackService = {
}
this.setMediaSessionActionHandlers()
this.initialized = true
},
}
setMediaSessionActionHandlers () {
private setMediaSessionActionHandlers () {
if (!navigator.mediaSession) {
return
}
@ -66,9 +65,9 @@ export const playbackService = {
navigator.mediaSession.setActionHandler('pause', () => this.pause())
navigator.mediaSession.setActionHandler('previoustrack', () => this.playPrev())
navigator.mediaSession.setActionHandler('nexttrack', () => this.playNext())
},
}
listenToMediaEvents (mediaElement: HTMLMediaElement) {
private listenToMediaEvents (mediaElement: HTMLMediaElement) {
mediaElement.addEventListener('error', () => this.playNext(), true)
mediaElement.addEventListener('ended', () => {
@ -104,29 +103,25 @@ export const playbackService = {
}
if (process.env.NODE_ENV !== 'test') {
timeUpdateHandler = throttle(timeUpdateHandler, 3000)
timeUpdateHandler = throttle(timeUpdateHandler, 1000)
}
mediaElement.addEventListener('timeupdate', timeUpdateHandler)
},
}
get isTranscoding () {
return isMobile.any && preferences.transcodeOnMobile
},
registerPlay (song: Song) {
public registerPlay (song: Song) {
recentlyPlayedStore.add(song)
songStore.registerPlay(song)
song.play_count_registered = true
},
}
preload (song: Song) {
public preload (song: Song) {
const audioElement = document.createElement('audio')
audioElement.setAttribute('src', songStore.getSourceUrl(song))
audioElement.setAttribute('preload', 'auto')
audioElement.load()
song.preloaded = true
},
}
/**
* Play a song. Because
@ -136,9 +131,9 @@ export const playbackService = {
* So many dreams swinging out of the blue
* We'll let them come true
*/
async play (song: Song) {
public async play (song: Song) {
document.title = `${song.title} ♫ Koel`
this.player!.media.setAttribute('title', `${song.artist_name} - ${song.title}`)
this.player.media.setAttribute('title', `${song.artist_name} - ${song.title}`)
if (queueStore.current) {
queueStore.current.playback_state = 'Stopped'
@ -148,7 +143,7 @@ export const playbackService = {
// Manually set the `src` attribute of the audio to prevent plyr from resetting
// the audio media object and cause our equalizer to malfunction.
this.getPlayer().media.src = songStore.getSourceUrl(song)
this.player.media.src = songStore.getSourceUrl(song)
// We'll just "restart" playing the song, which will handle notification, scrobbling etc.
// Fixes #898
@ -157,9 +152,9 @@ export const playbackService = {
}
await this.restart()
},
}
showNotification (song: Song) {
public showNotification (song: Song) {
if (!window.Notification || !preferences.notify) {
return
}
@ -191,9 +186,9 @@ export const playbackService = {
}
]
})
},
}
async restart () {
public async restart () {
const song = queueStore.current!
this.showNotification(song)
@ -205,21 +200,25 @@ export const playbackService = {
eventBus.emit('SONG_STARTED', song)
socketService.broadcast('SOCKET_SONG', song)
this.getPlayer().restart()
this.player.restart()
try {
await this.getPlayer().media.play()
await this.player.media.play()
} catch (error) {
// convert this into a warning, as an error will cause Cypress to fail the tests entirely
logger.warn(error)
}
},
}
public get isTranscoding () {
return isMobile.any && preferences.transcodeOnMobile
}
/**
* The next song in the queue.
* If we're in REPEAT_ALL mode and there's no next song, just get the first song.
*/
get next () {
public get next () {
if (queueStore.next) {
return queueStore.next
}
@ -227,13 +226,13 @@ export const playbackService = {
if (preferences.repeatMode === 'REPEAT_ALL') {
return queueStore.first
}
},
}
/**
* The previous song in the queue.
* If we're in REPEAT_ALL mode and there's no prev song, get the last song.
*/
get previous () {
public get previous () {
if (queueStore.previous) {
return queueStore.previous
}
@ -241,13 +240,13 @@ export const playbackService = {
if (preferences.repeatMode === 'REPEAT_ALL') {
return queueStore.last
}
},
}
/**
* Circle through the repeat mode.
* The selected mode will be stored into local storage as well.
*/
changeRepeatMode () {
public changeRepeatMode () {
let index = this.repeatModes.indexOf(preferences.repeatMode) + 1
if (index >= this.repeatModes.length) {
@ -255,17 +254,17 @@ export const playbackService = {
}
preferences.repeatMode = this.repeatModes[index]
},
}
/**
* Play the prev song in the queue, if one is found.
* If the prev song is not found and the current mode is NO_REPEAT, we stop completely.
*/
async playPrev () {
public async playPrev () {
// If the song's duration is greater than 5 seconds and we've passed 5 seconds into it,
// restart playing instead.
if (this.getPlayer().media.currentTime > 5 && queueStore.current!.length > 5) {
this.getPlayer().restart()
if (this.player.media.currentTime > 5 && queueStore.current!.length > 5) {
this.player.restart()
return
}
@ -275,66 +274,64 @@ export const playbackService = {
} else {
this.previous && await this.play(this.previous)
}
},
}
/**
* Play the next song in the queue, if one is found.
* If the next song is not found and the current mode is NO_REPEAT, we stop completely.
*/
async playNext () {
public async playNext () {
if (!this.next && preferences.repeatMode === 'NO_REPEAT') {
await this.stop() // Nothing lasts forever, even cold November rain.
} else {
this.next && await this.play(this.next)
}
},
}
getVolume: () => preferences.volume,
public getVolume () {
return preferences.volume
}
/**
* @param {Number} volume 0-10
* @param {Boolean=true} persist Whether the volume should be saved into local storage
*/
setVolume (volume: number, persist = true) {
this.getPlayer().setVolume(volume)
if (persist) {
preferences.volume = volume
}
public setVolume (volume: number, persist = true) {
this.player.setVolume(volume)
persist && (preferences.volume = volume)
this.volumeInput.value = String(volume)
},
}
mute () {
public mute () {
this.setVolume(0, false)
},
}
unmute () {
public unmute () {
preferences.volume = preferences.volume || DEFAULT_VOLUME_VALUE
this.setVolume(preferences.volume)
},
}
async stop () {
public async stop () {
document.title = 'Koel'
this.getPlayer().pause()
this.getPlayer().seek(0)
this.player.pause()
this.player.seek(0)
if (queueStore.current) {
queueStore.current.playback_state = 'Stopped'
}
socketService.broadcast('SOCKET_PLAYBACK_STOPPED')
},
}
pause () {
this.getPlayer().pause()
public pause () {
this.player.pause()
queueStore.current!.playback_state = 'Paused'
socketService.broadcast('SOCKET_SONG', queueStore.current)
},
}
async resume () {
public async resume () {
try {
await this.getPlayer().media.play()
await this.player.media.play()
} catch (error) {
logger.error(error)
}
@ -342,9 +339,9 @@ export const playbackService = {
queueStore.current!.playback_state = 'Playing'
eventBus.emit('SONG_STARTED', queueStore.current)
socketService.broadcast('SOCKET_SONG', queueStore.current)
},
}
async toggle () {
public async toggle () {
if (!queueStore.current) {
await this.playFirstInQueue()
return
@ -356,7 +353,7 @@ export const playbackService = {
}
this.pause()
},
}
/**
* Queue up songs (replace them into the queue) and start playing right away.
@ -364,7 +361,7 @@ export const playbackService = {
* @param {?Song[]} songs An array of song objects. Defaults to all songs if null.
* @param {Boolean=false} shuffled Whether to shuffle the songs before playing.
*/
async queueAndPlay (songs: Song[], shuffled = false) {
public async queueAndPlay (songs: Song[], shuffled = false) {
if (shuffled) {
songs = shuffle(songs)
}
@ -376,13 +373,11 @@ export const playbackService = {
await nextTick()
router.go('queue')
await this.play(queueStore.first)
},
}
getPlayer () {
return this.player!
},
async playFirstInQueue () {
public async playFirstInQueue () {
queueStore.all.length && await this.play(queueStore.first)
}
}
export const playbackService = new PlaybackService()

View file

@ -1,13 +1,12 @@
import { reactive } from 'vue'
import { differenceBy, shuffle, union, unionBy } from 'lodash'
import { differenceBy, shuffle, unionBy } from 'lodash'
import { arrayify } from '@/utils'
import { httpService } from '@/services'
import { songStore } from '@/stores'
export const queueStore = {
state: reactive({
songs: [] as Song[],
current: null as Song
songs: [] as Song[]
}),
init () {
@ -40,7 +39,7 @@ export const queueStore = {
},
set all (songs: Song[]) {
this.state.songs = songs
this.state.songs = reactive(songs)
},
get first () {
@ -52,7 +51,7 @@ export const queueStore = {
},
contains (song: Song) {
return this.all.includes(song)
return this.all.includes(reactive(song))
},
/**
@ -74,7 +73,7 @@ export const queueStore = {
},
replaceQueueWith (songs: Song | Song[]) {
this.state.songs = arrayify(songs)
this.state.songs = reactive(arrayify(songs))
},
queueAfterCurrent (songs: Song | Song[]) {
@ -88,7 +87,7 @@ export const queueStore = {
this.unqueue(songs)
const head = this.all.splice(0, this.indexOf(this.current) + 1)
this.all = head.concat(songs, this.all)
this.all = head.concat(reactive(songs), this.all)
},
unqueue (songs: Song | Song[]) {
@ -104,7 +103,7 @@ export const queueStore = {
movedSongs.forEach(song => {
this.all.splice(this.indexOf(song), 1)
this.all.splice(targetIndex, 0, song)
this.all.splice(targetIndex, 0, reactive(song))
})
},
@ -113,7 +112,7 @@ export const queueStore = {
},
indexOf (song: Song) {
return this.all.indexOf(song)
return this.all.indexOf(reactive(song))
},
get next () {