Skip to content
Snippets Groups Projects
Track.vue 3.71 KiB
Newer Older
<template>
  <audio
    ref="audio"
    @error="errored"
    @loadeddata="loaded"
    @durationchange="updateDuration"
    @timeupdate="updateProgress"
    @ended="ended"
    preload>
    <source
      @error="sourceErrored"
      v-for="src in srcs"
      :src="src.url"
  </audio>
</template>

<script>
import {mapState} from 'vuex'
import url from '@/utils/url'
import formats from '@/audio/formats'
import _ from 'lodash'
// import logger from '@/logging'

export default {
  props: {
    track: {type: Object},
    isCurrent: {type: Boolean, default: false},
    startTime: {type: Number, default: 0},
    autoplay: {type: Boolean, default: false}
  data () {
    return {
      sourceErrors: 0
    }
  },
  computed: {
    ...mapState({
      playing: state => state.player.playing,
      currentTime: state => state.player.currentTime,
      duration: state => state.player.duration,
      volume: state => state.player.volume,
      looping: state => state.player.looping
    }),
    srcs: function () {
      let file = this.track.files[0]
      if (!file) {
        this.$store.dispatch('player/trackErrored')
      let sources = [
        {type: file.mimetype, url: file.path}
      ]
      formats.formats.forEach(f => {
        if (f !== file.mimetype) {
          let format = formats.formatsMap[f]
          let url = `/api/v1/trackfiles/transcode/?track_file=${file.id}&to=${format}`
          sources.push({type: f, url: url})
        }
      })
      if (this.$store.state.auth.authenticated) {
        // we need to send the token directly in url
        // so authentication can be checked by the backend
        // because for audio files we cannot use the regular Authentication
        // header
        sources.forEach(e => {
          e.url = url.updateQueryString(e.url, 'jwt', this.$store.state.auth.token)
        })
      return sources
    }
  },
  methods: {
    errored: function () {
      this.$store.dispatch('player/trackErrored')
    },
    sourceErrored: function () {
      this.sourceErrors += 1
      if (this.sourceErrors >= this.srcs.length) {
        // all sources failed
        this.errored()
      }
    },
    updateDuration: function (e) {
      this.$store.commit('player/duration', this.$refs.audio.duration)
      this.$refs.audio.volume = this.volume
      this.$store.commit('player/resetErrorCount')
      if (this.isCurrent) {
        this.$store.commit('player/duration', this.$refs.audio.duration)
        if (this.startTime) {
          this.setCurrentTime(this.startTime)
        }
        if (this.autoplay) {
          this.$store.commit('player/playing', true)
          this.$refs.audio.play()
        }
    updateProgress: _.throttle(function () {
      if (this.$refs.audio) {
        this.$store.dispatch('player/updateProgress', this.$refs.audio.currentTime)
      }
      let onlyTrack = this.$store.state.queue.tracks.length === 1
      if (this.looping === 1 || (onlyTrack && this.looping === 2)) {
        this.setCurrentTime(0)
        this.$refs.audio.play()
      } else {
        this.$store.dispatch('player/trackEnded', this.track)
      }
    },
    setCurrentTime (t) {
      if (t < 0 | t > this.duration) {
        return
      }
      this.updateProgress(t)
      this.$refs.audio.currentTime = t
    }
  },
  watch: {
    playing: function (newValue) {
      if (newValue === true) {
        this.$refs.audio.play()
      } else {
        this.$refs.audio.pause()
      }
    },
    volume: function (newValue) {
      this.$refs.audio.volume = newValue
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>