From 5d2d2f506e4186f0ff9d82c0af161a59937921d1 Mon Sep 17 00:00:00 2001
From: Eliot Berriot <contact@eliotberriot.com>
Date: Tue, 1 Oct 2019 19:20:44 +0200
Subject: [PATCH] Fix #925: fallback to next available format when browser
 fails to decode audio

---
 changes/changelog.d/925.bugfix        |  1 +
 front/src/components/audio/Player.vue | 31 +++++++++++++++++++++------
 2 files changed, 26 insertions(+), 6 deletions(-)
 create mode 100644 changes/changelog.d/925.bugfix

diff --git a/changes/changelog.d/925.bugfix b/changes/changelog.d/925.bugfix
new file mode 100644
index 00000000..b1e464a4
--- /dev/null
+++ b/changes/changelog.d/925.bugfix
@@ -0,0 +1 @@
+Fallback to next available format when browser fails to decode audio (#925)
diff --git a/front/src/components/audio/Player.vue b/front/src/components/audio/Player.vue
index 7fda9520..c70f2575 100644
--- a/front/src/components/audio/Player.vue
+++ b/front/src/components/audio/Player.vue
@@ -262,7 +262,7 @@ export default {
       sourceErrors: 0,
       progressInterval: null,
       maxPreloaded: 3,
-      preloadDelay: 15,
+      preloadDelay: 5,
       soundsCache: [],
       soundId: null,
       playTimeout: null,
@@ -374,12 +374,18 @@ export default {
       this.$store.commit("player/isLoadingAudio", false)
       this.$store.dispatch("player/trackErrored")
     },
-    getSound (trackData) {
+    getSound (trackData, skippedFormats = []) {
       let cached = this.getSoundFromCache(trackData)
       if (cached) {
         return cached.sound
       }
-      let srcs = this.getSrcs(trackData)
+      let srcs = this.getSrcs(trackData).filter((s) => {
+        return skippedFormats.indexOf(s.type) === -1
+      })
+      let srcsByUrl = {}
+      srcs.forEach(s => {
+        srcsByUrl[s.url] = s
+      })
       let self = this
       let sound = new Howl({
         src: srcs.map((s) => { return s.url }),
@@ -413,12 +419,23 @@ export default {
           self.$store.commit('player/errored', false)
           self.$store.commit('player/duration', this.duration())
         },
-        onloaderror: function (sound, error) {
+        onloaderror: async function (sound, error) {
           self.removeFromCache(this)
           if (this != self.currentSound) {
             return
           }
+          if (error === 4) {
+            console.log('Error while decoding:', sound, error)
+            if (skippedFormats.length === 0 && srcs.length > 1) {
+              skippedFormats.push(srcs[0].type)
+              console.log(`Playing ${srcs[0].type} failed, loading next format ${srcs[1].type}`)
+              await self.loadSound(trackData, null, skippedFormats)
+              console.log('Replacing current sound with alternative format')
+              return
+            }
+          } else {
           console.log('Error while playing:', sound, error)
+          }
           self.handleError({sound, error})
         },
       })
@@ -579,9 +596,11 @@ export default {
         if (toKeep.length < self.maxPreloaded) {
           toKeep.push(e)
         } else {
+          if (e.sound) {
           let src = e.sound._src
           e.sound.unload()
         }
+        }
       })
       this.soundsCache = _.reverse(toKeep)
     },
@@ -596,7 +615,7 @@ export default {
       })
       this.soundsCache = toKeep
     },
-    async loadSound (newValue, oldValue) {
+    async loadSound (newValue, oldValue, skippedFormats = []) {
       let trackData = newValue
       let oldSound = this.currentSound
       if (oldSound && trackData !== oldValue) {
@@ -611,7 +630,7 @@ export default {
         if (trackData === null) {
           this.handleError({})
         }
-        this.currentSound = this.getSound(trackData)
+        this.currentSound = this.getSound(trackData, skippedFormats)
         this.$store.commit('player/isLoadingAudio', true)
         if (this.playing) {
           this.soundId = this.currentSound.play()
-- 
GitLab