Commit b757ca46 authored by Eliot Berriot's avatar Eliot Berriot 💬

See #272: updated front-end for transcoding and new API results, improved error handling in player

parent d3f8fb6c
......@@ -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: [
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment