See #272: updated front-end for transcoding and new API results, improved error handling in player
This commit is contained in:
parent
d3f8fb6cb0
commit
b757ca4616
|
@ -79,10 +79,14 @@ export default {
|
|||
return true
|
||||
}
|
||||
if (this.track) {
|
||||
return this.track.is_playable
|
||||
return this.track.uploads && this.track.uploads.length > 0
|
||||
} else if (this.artist) {
|
||||
return this.albums.filter((a) => {
|
||||
return a.is_playable === true
|
||||
}).length > 0
|
||||
} else if (this.tracks) {
|
||||
return this.tracks.filter((t) => {
|
||||
return t.is_playable
|
||||
return t.uploads && t.uploads.length > 0
|
||||
}).length > 0
|
||||
}
|
||||
return false
|
||||
|
@ -139,7 +143,7 @@ export default {
|
|||
self.isLoading = false
|
||||
}, 250)
|
||||
return tracks.filter(e => {
|
||||
return e.is_playable === true
|
||||
return e.uploads && e.uploads.length > 0
|
||||
})
|
||||
})
|
||||
},
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<audio-track
|
||||
ref="currentAudio"
|
||||
v-if="currentTrack"
|
||||
@errored="handleError"
|
||||
:is-current="true"
|
||||
:start-time="$store.state.player.currentTime"
|
||||
:autoplay="$store.state.player.playing"
|
||||
|
@ -41,13 +42,13 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress-area" v-if="currentTrack">
|
||||
<div class="progress-area" v-if="currentTrack && !errored">
|
||||
<div class="ui grid">
|
||||
<div class="left floated four wide column">
|
||||
<p class="timer start" @click="updateProgress(0)">{{currentTimeFormatted}}</p>
|
||||
</div>
|
||||
|
||||
<div class="right floated four wide column">
|
||||
<div v-if="!isLoadingAudio" class="right floated four wide column">
|
||||
<p class="timer total">{{durationFormatted}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -59,7 +60,18 @@
|
|||
<div class="bar" :data-percent="progress" :style="{ 'width': progress + '%' }"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ui small warning message" v-if="currentTrack && errored">
|
||||
<div class="header">
|
||||
<translate>We cannot load this track</translate>
|
||||
</div>
|
||||
<p v-if="hasNext && playing && $store.state.player.errorCount < $store.state.player.maxConsecutiveErrors">
|
||||
<translate>The next track will play automatically in a few seconds...</translate>
|
||||
<i class="loading spinner icon"></i>
|
||||
</p>
|
||||
<p>
|
||||
<translate>You may have a connectivity issue.</translate>
|
||||
</p>
|
||||
</div>
|
||||
<div class="two wide column controls ui grid">
|
||||
<a
|
||||
href
|
||||
|
@ -299,6 +311,10 @@ export default {
|
|||
}
|
||||
let image = this.$refs.cover
|
||||
this.ambiantColors = ColorThief.prototype.getPalette(image, 4).slice(0, 4)
|
||||
},
|
||||
handleError ({sound, error}) {
|
||||
this.$store.commit('player/isLoadingAudio', false)
|
||||
this.$store.dispatch('player/trackErrored')
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -310,6 +326,7 @@ export default {
|
|||
looping: state => state.player.looping,
|
||||
duration: state => state.player.duration,
|
||||
bufferProgress: state => state.player.bufferProgress,
|
||||
errored: state => state.player.errored,
|
||||
queue: state => state.queue
|
||||
}),
|
||||
...mapGetters({
|
||||
|
|
|
@ -46,12 +46,17 @@ export default {
|
|||
onload: function () {
|
||||
self.$store.commit('player/isLoadingAudio', false)
|
||||
self.$store.commit('player/resetErrorCount')
|
||||
self.$store.commit('player/errored', false)
|
||||
self.$store.commit('player/duration', self.sound.duration())
|
||||
let node = self.sound._sounds[0]._node;
|
||||
node.addEventListener('progress', () => {
|
||||
self.updateBuffer(node)
|
||||
})
|
||||
}
|
||||
},
|
||||
onloaderror: function (sound, error) {
|
||||
console.log('Error while playing:', sound, error)
|
||||
self.$emit('errored', {sound, error})
|
||||
},
|
||||
})
|
||||
if (this.autoplay) {
|
||||
self.$store.commit('player/isLoadingAudio', true)
|
||||
|
@ -73,14 +78,23 @@ export default {
|
|||
looping: state => state.player.looping
|
||||
}),
|
||||
srcs: function () {
|
||||
// let file = this.track.files[0]
|
||||
// if (!file) {
|
||||
// this.$store.dispatch('player/trackErrored')
|
||||
// return []
|
||||
// }
|
||||
let sources = [
|
||||
{type: 'mp3', url: this.$store.getters['instance/absoluteUrl'](this.track.listen_url)}
|
||||
]
|
||||
let sources = this.track.uploads.map(u => {
|
||||
return {
|
||||
type: u.extension,
|
||||
url: this.$store.getters['instance/absoluteUrl'](u.listen_url),
|
||||
}
|
||||
})
|
||||
// We always add a transcoded MP3 src at the end
|
||||
// because transcoding is expensive, but we want browsers that do
|
||||
// not support other codecs to be able to play it :)
|
||||
sources.push({
|
||||
type: 'mp3',
|
||||
url: url.updateQueryString(
|
||||
this.$store.getters['instance/absoluteUrl'](this.track.listen_url),
|
||||
'to',
|
||||
'mp3'
|
||||
)
|
||||
})
|
||||
if (this.$store.state.auth.authenticated) {
|
||||
// we need to send the token directly in url
|
||||
// so authentication can be checked by the backend
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
{{ track.album.title }}
|
||||
</router-link>
|
||||
</td>
|
||||
<td colspan="4" v-if="track.duration">
|
||||
{{ time.parse(track.duration) }}
|
||||
<td colspan="4" v-if="track.uploads && track.uploads.length > 0">
|
||||
{{ time.parse(track.uploads[0].duration) }}
|
||||
</td>
|
||||
<td colspan="4" v-else>
|
||||
<translate>N/A</translate>
|
||||
|
|
|
@ -44,13 +44,13 @@
|
|||
<i class="external icon"></i>
|
||||
<translate>View on MusicBrainz</translate>
|
||||
</a>
|
||||
<a v-if="track.is_playable" :href="downloadUrl" target="_blank" class="ui button">
|
||||
<a v-if="upload" :href="downloadUrl" target="_blank" class="ui button">
|
||||
<i class="download icon"></i>
|
||||
<translate>Download</translate>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui vertical stripe center aligned segment">
|
||||
<div class="ui vertical stripe center aligned segment" v-if="upload">
|
||||
<h2 class="ui header"><translate>Track information</translate></h2>
|
||||
<table class="ui very basic collapsing celled center aligned table">
|
||||
<tbody>
|
||||
|
@ -58,8 +58,8 @@
|
|||
<td>
|
||||
<translate>Duration</translate>
|
||||
</td>
|
||||
<td v-if="track.duration">
|
||||
{{ time.parse(track.duration) }}
|
||||
<td v-if="upload.duration">
|
||||
{{ time.parse(upload.duration) }}
|
||||
</td>
|
||||
<td v-else>
|
||||
<translate>N/A</translate>
|
||||
|
@ -69,8 +69,8 @@
|
|||
<td>
|
||||
<translate>Size</translate>
|
||||
</td>
|
||||
<td v-if="track.size">
|
||||
{{ track.size | humanSize }}
|
||||
<td v-if="upload.size">
|
||||
{{ upload.size | humanSize }}
|
||||
</td>
|
||||
<td v-else>
|
||||
<translate>N/A</translate>
|
||||
|
@ -80,8 +80,8 @@
|
|||
<td>
|
||||
<translate>Bitrate</translate>
|
||||
</td>
|
||||
<td v-if="track.bitrate">
|
||||
{{ track.bitrate | humanSize }}/s
|
||||
<td v-if="upload.bitrate">
|
||||
{{ upload.bitrate | humanSize }}/s
|
||||
</td>
|
||||
<td v-else>
|
||||
<translate>N/A</translate>
|
||||
|
@ -91,8 +91,8 @@
|
|||
<td>
|
||||
<translate>Type</translate>
|
||||
</td>
|
||||
<td v-if="track.mimetype">
|
||||
{{ track.mimetype }}
|
||||
<td v-if="upload.extension">
|
||||
{{ upload.extension }}
|
||||
</td>
|
||||
<td v-else>
|
||||
<translate>N/A</translate>
|
||||
|
@ -195,6 +195,11 @@ export default {
|
|||
title: this.$gettext('Track')
|
||||
}
|
||||
},
|
||||
upload () {
|
||||
if (this.track.uploads) {
|
||||
return this.track.uploads[0]
|
||||
}
|
||||
},
|
||||
wikipediaUrl () {
|
||||
return 'https://en.wikipedia.org/w/index.php?search=' + encodeURI(this.track.title + ' ' + this.track.artist.name)
|
||||
},
|
||||
|
@ -204,7 +209,7 @@ export default {
|
|||
}
|
||||
},
|
||||
downloadUrl () {
|
||||
let u = this.$store.getters['instance/absoluteUrl'](this.track.listen_url)
|
||||
let u = this.$store.getters['instance/absoluteUrl'](this.upload.listen_url)
|
||||
if (this.$store.state.auth.authenticated) {
|
||||
u = url.updateQueryString(u, 'jwt', encodeURI(this.$store.state.auth.token))
|
||||
}
|
||||
|
|
|
@ -79,6 +79,7 @@ export default new Vuex.Store({
|
|||
id: track.id,
|
||||
title: track.title,
|
||||
mbid: track.mbid,
|
||||
uploads: track.uploads,
|
||||
listen_url: track.listen_url,
|
||||
album: {
|
||||
id: track.album.id,
|
||||
|
|
|
@ -95,10 +95,19 @@ export default {
|
|||
incrementVolume ({commit, state}, value) {
|
||||
commit('volume', state.volume + value)
|
||||
},
|
||||
stop (context) {
|
||||
stop ({commit}) {
|
||||
commit('errored', false)
|
||||
commit('resetErrorCount')
|
||||
},
|
||||
togglePlay ({commit, state}) {
|
||||
togglePlay ({commit, state, dispatch}) {
|
||||
commit('playing', !state.playing)
|
||||
if (state.errored && state.errorCount < state.maxConsecutiveErrors) {
|
||||
setTimeout(() => {
|
||||
if (state.playing) {
|
||||
dispatch('queue/next', null, {root: true})
|
||||
}
|
||||
}, 3000)
|
||||
}
|
||||
},
|
||||
trackListened ({commit, rootState}, track) {
|
||||
if (!rootState.auth.authenticated) {
|
||||
|
@ -121,7 +130,13 @@ export default {
|
|||
trackErrored ({commit, dispatch, state}) {
|
||||
commit('errored', true)
|
||||
commit('incrementErrorCount')
|
||||
dispatch('queue/next', null, {root: true})
|
||||
if (state.errorCount < state.maxConsecutiveErrors) {
|
||||
setTimeout(() => {
|
||||
if (state.playing) {
|
||||
dispatch('queue/next', null, {root: true})
|
||||
}
|
||||
}, 3000)
|
||||
}
|
||||
},
|
||||
updateProgress ({commit}, t) {
|
||||
commit('currentTime', t)
|
||||
|
|
|
@ -142,7 +142,6 @@ export default {
|
|||
commit('ended', false)
|
||||
commit('player/currentTime', 0, {root: true})
|
||||
commit('player/playing', true, {root: true})
|
||||
commit('player/errored', false, {root: true})
|
||||
commit('currentIndex', index)
|
||||
if (state.tracks.length - index <= 2 && rootState.radios.running) {
|
||||
dispatch('radios/populateQueue', null, {root: true})
|
||||
|
|
|
@ -267,7 +267,6 @@ describe('store/queue', () => {
|
|||
{ type: 'ended', payload: false },
|
||||
{ type: 'player/currentTime', payload: 0, options: {root: true} },
|
||||
{ type: 'player/playing', payload: true, options: {root: true} },
|
||||
{ type: 'player/errored', payload: false, options: {root: true} },
|
||||
{ type: 'currentIndex', payload: 1 }
|
||||
]
|
||||
})
|
||||
|
@ -281,7 +280,6 @@ describe('store/queue', () => {
|
|||
{ type: 'ended', payload: false },
|
||||
{ type: 'player/currentTime', payload: 0, options: {root: true} },
|
||||
{ type: 'player/playing', payload: true, options: {root: true} },
|
||||
{ type: 'player/errored', payload: false, options: {root: true} },
|
||||
{ type: 'currentIndex', payload: 1 }
|
||||
]
|
||||
})
|
||||
|
@ -295,7 +293,6 @@ describe('store/queue', () => {
|
|||
{ type: 'ended', payload: false },
|
||||
{ type: 'player/currentTime', payload: 0, options: {root: true} },
|
||||
{ type: 'player/playing', payload: true, options: {root: true} },
|
||||
{ type: 'player/errored', payload: false, options: {root: true} },
|
||||
{ type: 'currentIndex', payload: 1 }
|
||||
],
|
||||
expectedActions: [
|
||||
|
|
Loading…
Reference in New Issue