Fixed #310: current track restart/hiccup when shuffling queue, deleting track from queue or reordering

This commit is contained in:
Eliot Berriot 2018-06-22 23:29:54 +02:00
parent b2a876b4df
commit 39b473f1e6
No known key found for this signature in database
GPG Key ID: DD6965E2476E5C27
7 changed files with 54 additions and 28 deletions

View File

@ -0,0 +1 @@
Fixed current track restart/hiccup when shuffling queue, deleting track from queue or reordering (#310)

View File

@ -116,8 +116,8 @@
</div> </div>
<div class="ui bottom attached tab" data-tab="queue"> <div class="ui bottom attached tab" data-tab="queue">
<table class="ui compact inverted very basic fixed single line unstackable table"> <table class="ui compact inverted very basic fixed single line unstackable table">
<draggable v-model="queue.tracks" element="tbody" @update="reorder"> <draggable v-model="tracks" element="tbody" @update="reorder">
<tr @click="$store.dispatch('queue/currentIndex', index)" v-for="(track, index) in queue.tracks" :key="index" :class="[{'active': index === queue.currentIndex}]"> <tr @click="$store.dispatch('queue/currentIndex', index)" v-for="(track, index) in tracks" :key="index" :class="[{'active': index === queue.currentIndex}]">
<td class="right aligned">{{ index + 1}}</td> <td class="right aligned">{{ index + 1}}</td>
<td class="center aligned"> <td class="center aligned">
<img class="ui mini image" v-if="track.album.cover" :src="$store.getters['instance/absoluteUrl'](track.album.cover)"> <img class="ui mini image" v-if="track.album.cover" :src="$store.getters['instance/absoluteUrl'](track.album.cover)">
@ -176,6 +176,7 @@ export default {
return { return {
selectedTab: 'library', selectedTab: 'library',
backend: backend, backend: backend,
tracksChangeBuffer: null,
isCollapsed: true, isCollapsed: true,
fetchInterval: null fetchInterval: null
} }
@ -207,6 +208,14 @@ export default {
return adminPermissions.filter(e => { return adminPermissions.filter(e => {
return e return e
}).length > 0 }).length > 0
},
tracks: {
get () {
return this.$store.state.queue.tracks
},
set (value) {
this.tracksChangeBuffer = value
}
} }
}, },
methods: { methods: {
@ -219,7 +228,7 @@ export default {
}, },
reorder: function (event) { reorder: function (event) {
this.$store.commit('queue/reorder', { this.$store.commit('queue/reorder', {
oldIndex: event.oldIndex, newIndex: event.newIndex}) tracks: this.tracksChangeBuffer, oldIndex: event.oldIndex, newIndex: event.newIndex})
}, },
scrollToCurrent () { scrollToCurrent () {
let current = $(this.$el).find('[data-tab="queue"] .active')[0] let current = $(this.$el).find('[data-tab="queue"] .active')[0]

View File

@ -1,16 +1,16 @@
<template> <template>
<div class="ui inverted segment player-wrapper" :style="style"> <div class="ui inverted segment player-wrapper" :style="style">
<div class="player"> <div class="player">
<audio-track <keep-alive>
ref="currentAudio" <audio-track
v-if="renderAudio && currentTrack" ref="currentAudio"
:key="currentTrack.id" v-if="renderAudio && currentTrack"
:is-current="true" :is-current="true"
:start-time="$store.state.player.currentTime" :start-time="$store.state.player.currentTime"
:autoplay="$store.state.player.playing" :autoplay="$store.state.player.playing"
:track="currentTrack"> :track="currentTrack">
</audio-track> </audio-track>
</keep-alive>
<div v-if="currentTrack" class="track-area ui unstackable items"> <div v-if="currentTrack" class="track-area ui unstackable items">
<div class="ui inverted item"> <div class="ui inverted item">
<div class="ui tiny image"> <div class="ui tiny image">

View File

@ -4,7 +4,7 @@
@error="errored" @error="errored"
@loadeddata="loaded" @loadeddata="loaded"
@durationchange="updateDuration" @durationchange="updateDuration"
@timeupdate="updateProgress" @timeupdate="updateProgressThrottled"
@ended="ended" @ended="ended"
preload> preload>
<source <source
@ -30,6 +30,7 @@ export default {
}, },
data () { data () {
return { return {
realTrack: this.track,
sourceErrors: 0, sourceErrors: 0,
isUpdatingTime: false isUpdatingTime: false
} }
@ -43,7 +44,7 @@ export default {
looping: state => state.player.looping looping: state => state.player.looping
}), }),
srcs: function () { srcs: function () {
let file = this.track.files[0] let file = this.realTrack.files[0]
if (!file) { if (!file) {
this.$store.dispatch('player/trackErrored') this.$store.dispatch('player/trackErrored')
return [] return []
@ -61,6 +62,9 @@ export default {
}) })
} }
return sources return sources
},
updateProgressThrottled () {
return _.throttle(this.updateProgress, 250)
} }
}, },
methods: { methods: {
@ -100,30 +104,40 @@ export default {
} }
} }
}, },
updateProgress: _.throttle(function () { updateProgress: function () {
this.isUpdatingTime = true this.isUpdatingTime = true
if (this.$refs.audio) { if (this.$refs.audio) {
this.$store.dispatch('player/updateProgress', this.$refs.audio.currentTime) this.$store.dispatch('player/updateProgress', this.$refs.audio.currentTime)
} }
}, 250), },
ended: function () { ended: function () {
let onlyTrack = this.$store.state.queue.tracks.length === 1 let onlyTrack = this.$store.state.queue.tracks.length === 1
if (this.looping === 1 || (onlyTrack && this.looping === 2)) { if (this.looping === 1 || (onlyTrack && this.looping === 2)) {
this.setCurrentTime(0) this.setCurrentTime(0)
this.$refs.audio.play() this.$refs.audio.play()
} else { } else {
this.$store.dispatch('player/trackEnded', this.track) this.$store.dispatch('player/trackEnded', this.realTrack)
} }
}, },
setCurrentTime (t) { setCurrentTime (t) {
if (t < 0 | t > this.duration) { if (t < 0 | t > this.duration) {
return return
} }
this.updateProgress(t) if (t === this.$refs.audio.currentTime) {
return
}
if (t === 0) {
this.updateProgressThrottled.cancel()
}
this.$refs.audio.currentTime = t this.$refs.audio.currentTime = t
} }
}, },
watch: { watch: {
track: _.debounce(function (newValue) {
this.realTrack = newValue
this.setCurrentTime(0)
this.$refs.audio.load()
}, 1000, {leading: true, trailing: true}),
playing: function (newValue) { playing: function (newValue) {
if (newValue === true) { if (newValue === true) {
this.$refs.audio.play() this.$refs.audio.play()
@ -131,6 +145,11 @@ export default {
this.$refs.audio.pause() this.$refs.audio.pause()
} }
}, },
'$store.state.queue.currentIndex' () {
if (this.$store.state.player.playing) {
this.$refs.audio.play()
}
},
volume: function (newValue) { volume: function (newValue) {
this.$refs.audio.volume = newValue this.$refs.audio.volume = newValue
}, },

View File

@ -31,9 +31,10 @@ export default {
insert (state, {track, index}) { insert (state, {track, index}) {
state.tracks.splice(index, 0, track) state.tracks.splice(index, 0, track)
}, },
reorder (state, {oldIndex, newIndex}) { reorder (state, {tracks, oldIndex, newIndex}) {
// called when the user uses drag / drop to reorder // called when the user uses drag / drop to reorder
// tracks in queue // tracks in queue
state.tracks = tracks
if (oldIndex === state.currentIndex) { if (oldIndex === state.currentIndex) {
state.currentIndex = newIndex state.currentIndex = newIndex
return return
@ -102,7 +103,7 @@ export default {
} }
if (current) { if (current) {
// we play next track, which now have the same index // we play next track, which now have the same index
dispatch('currentIndex', index) commit('currentIndex', index)
} }
if (state.currentIndex + 1 === state.tracks.length) { if (state.currentIndex + 1 === state.tracks.length) {
dispatch('radios/populateQueue', null, {root: true}) dispatch('radios/populateQueue', null, {root: true})
@ -156,7 +157,6 @@ export default {
let toKeep = state.tracks.slice(0, state.currentIndex + 1) let toKeep = state.tracks.slice(0, state.currentIndex + 1)
let toShuffle = state.tracks.slice(state.currentIndex + 1) let toShuffle = state.tracks.slice(state.currentIndex + 1)
let shuffled = toKeep.concat(_.shuffle(toShuffle)) let shuffled = toKeep.concat(_.shuffle(toShuffle))
commit('player/currentTime', 0, {root: true})
commit('tracks', []) commit('tracks', [])
let params = {tracks: shuffled} let params = {tracks: shuffled}
if (callback) { if (callback) {

View File

@ -169,11 +169,11 @@ describe('store/queue', () => {
payload: 2, payload: 2,
params: {state: {currentIndex: 2}}, params: {state: {currentIndex: 2}},
expectedMutations: [ expectedMutations: [
{ type: 'splice', payload: {start: 2, size: 1} } { type: 'splice', payload: {start: 2, size: 1} },
{ type: 'currentIndex', payload: 2 }
], ],
expectedActions: [ expectedActions: [
{ type: 'player/stop', payload: null, options: {root: true} }, { type: 'player/stop', payload: null, options: {root: true} }
{ type: 'currentIndex', payload: 2 }
] ]
}, done) }, done)
}) })
@ -324,7 +324,6 @@ describe('store/queue', () => {
action: store.actions.shuffle, action: store.actions.shuffle,
params: {state: {currentIndex: 1, tracks: tracks}}, params: {state: {currentIndex: 1, tracks: tracks}},
expectedMutations: [ expectedMutations: [
{ type: 'player/currentTime', payload: 0, options: {root: true} },
{ type: 'tracks', payload: [] } { type: 'tracks', payload: [] }
], ],
expectedActions: [ expectedActions: [

View File

@ -1,7 +1,5 @@
import store from '@/store/ui' import store from '@/store/ui'
import { testAction } from '../../utils'
describe('store/ui', () => { describe('store/ui', () => {
describe('mutations', () => { describe('mutations', () => {
it('addMessage', () => { it('addMessage', () => {