Search code examples
javascriptvue.jsvimeo

Vimeo SDK : Unknown Player Error after Initialization (VueJS)


I would like to display a bunch of Videos on a Vue-Website using a Vimeo player.

For this purpose, I created a VideoPlayer-Component which is created for each Video:

<template>

  <div class="video-element__container">
    <div class="video-element__player" ref="player" @click="$emit('shrink')"></div>


    <div class="video-element__play-button" @click="play()"></div>
       
  </div>


</template>

<script>
import Player from '@vimeo/player'

export default {
  name: 'VideoPlayer',
  props: ['isActive', 'id'],
  data () {
    return {
      player: null,
    }
  },
  mounted() {
    this.initPlayer()
  },
  components: {
  },
  methods:
  {
    async play () {
      try {
        await this.player.play()
        this.isPlaying = true
      } catch (e) {
        console.log(e)
      }
    },
    pause() {
      this.player.pause()
      this.isPlaying = false
    },

    initPlayer () {
      let options = {
        url: 'https://vimeo.com/393632283',
        loop: false,
        controls: false,
        muted: true,
      };

      let iframe = this.$refs.player
      this.player = new Player(iframe, options);
    
    }
  }
}
</script>

However, whenever I click 'play' I get the following error:

Uncaught (in promise) Error: Unknown player. Probably unloaded.
    at readyPromise (player.es.js?84c9:1458)
    at new Promise (<anonymous>)
    at Proxy.ready (player.es.js?84c9:1457)
    at eval (player.es.js?84c9:1311)
    at new Promise (<anonymous>)
    at Proxy.get (player.es.js?84c9:1306)
    at Proxy.getDuration (player.es.js?84c9:2052)
    at Proxy.initPlayer (VideoPlayer.vue?5912:93)
    at Proxy.play (VideoPlayer.vue?5912:53)
    at Object.onClick._cache.<computed>._cache.<computed> (VideoPlayer.vue?5912:17)

Yet, when I use the Vimeo controls it works just fine. Can someone see the issue here? Thanks!


Solution

  • Even tho Triet Doan's answer is now accepted, I do not like it. It takes whole OP code written in Options API and rewrote it into Composition API without explaining why it is needed...

    Well it is NOT needed at all! In fact, simplest thing to fix original code is to comment out player: null in data() (as can be seen in demo below)

    Why

    Vue reactivity can be a little intruding. Both getters/setters created with Object.defineProperty() (used for reactivity in Vue 2) and proxies (used for reactivity in Vue 3) can change the behavior especially for complex data types/classes as Vimeo Player is.

    To workaround such problems, just do not put such complex objects into data() or do not wrap them in ref() or reactive(). In most cases you don't need your Vue template to be reactive to some internal state of such complex objects anyway....

    Instead just add them to the instance as this.player = new ... in mounted hook or declare them as ordinary variables (without ref() or reactive()) in setup()

    const app = Vue.createApp({
      name: 'VideoPlayer',
      data() {
        return {
          // player: null, <-- THIS LITTLE CHANGE IS NEEDED
        }
      },
      mounted() {
        this.initPlayer()
      },
      methods: {
        async play() {
          try {
            await this.player.play()
            this.isPlaying = true
          } catch (e) {
            console.log(e)
          }
        },
        pause() {
          this.player.pause()
          this.isPlaying = false
        },
    
        initPlayer() {
          let options = {
            url: 'https://vimeo.com/393632283',
            loop: false,
            controls: false,
            muted: true,
          };
    
          let element = this.$refs.player
          this.player = new Vimeo.Player(element, options);
    
        }
      }
    })
    app.mount('#app')
    <script src="https://unpkg.com/[email protected]/dist/vue.global.js"></script>
    <script src="https://player.vimeo.com/api/player.js"></script>
    
    <div id="app">
      <div class="video-element__container">
        <div class="video-element__player" ref="player" @click="$emit('shrink')"></div>
        <div class="video-element__play-button" @click="play()"></div>
      </div>
    </div>

    Warning

    On the other hand, I completely agree with the suggestion of using existing wrapper - vue-vimeo-player. Because it handles some edge cases and more importantly the unloading of the player when the component is destroyed. Without it, you are creating a memory leak in your app