API Docs for: 0.2.41
Show:

File: src/playbacks/flash/flash.js

// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import {seekStringToSeconds} from '../../base/utils'

import BaseFlashPlayback from '../../playbacks/base_flash_playback'
import Browser from '../../components/browser'
import Mediator from '../../components/mediator'
import template from '../../base/template'
import $ from 'clappr-zepto'
import Events from '../../base/events'
import Playback from '../../base/playback'
import flashSwf from './public/Player.swf'

const MAX_ATTEMPTS = 60

export default class Flash extends BaseFlashPlayback {
  get name() { return 'flash' }
  get swfPath() { return template(flashSwf)({baseUrl: this._baseUrl}) }

  /**
   * Determine if the playback has ended.
   * @property ended
   * @type Boolean
   */
  get ended() {
    return this._currentState === 'ENDED'
  }

  /**
   * Determine if the playback is buffering.
   * This is related to the PLAYBACK_BUFFERING and PLAYBACK_BUFFERFULL events
   * @property buffering
   * @type Boolean
   */
  get buffering() {
    return !!this._bufferingState && this._currentState !== 'ENDED'
  }

  constructor(...args) {
    super(...args)
    this._src = this.options.src
    this._baseUrl = this.options.baseUrl
    this._autoPlay = this.options.autoPlay
    this.settings = {default: ['seekbar']}
    this.settings.left = ['playpause', 'position', 'duration']
    this.settings.right = ['fullscreen', 'volume']
    this.settings.seekEnabled = true
    this._isReadyState = false
    this._addListeners()
  }


  _bootstrap() {
    if (this.el.playerPlay) {
      this.el.width = '100%'
      this.el.height = '100%'
      if (this._currentState === 'PLAYING') {
        this._firstPlay()
      } else {
        this._currentState = 'IDLE'
        this._autoPlay && this.play()
      }
      $('<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%" />').insertAfter(this.$el)
      if (this.getDuration() > 0) {
        this._metadataLoaded()
      } else {
        Mediator.once(this.uniqueId + ':timeupdate', this._metadataLoaded, this)
      }
    } else {
      this._attempts = this._attempts || 0
      if (++this._attempts <= MAX_ATTEMPTS) {
        setTimeout(() => this._bootstrap(), 50)
      } else {
        this.trigger(Events.PLAYBACK_ERROR, {message: 'Max number of attempts reached'}, this.name)
      }
    }
  }

  _metadataLoaded() {
    this._isReadyState = true
    this.trigger(Events.PLAYBACK_READY, this.name)
    this.trigger(Events.PLAYBACK_SETTINGSUPDATE, this.name)
  }

  getPlaybackType() {
    return Playback.VOD
  }

  isHighDefinitionInUse() {
    return false
  }

  _updateTime() {
    this.trigger(Events.PLAYBACK_TIMEUPDATE, {current: this.el.getPosition(), total: this.el.getDuration()}, this.name)
  }

  _addListeners() {
    Mediator.on(this.uniqueId + ':progress', this._progress, this)
    Mediator.on(this.uniqueId + ':timeupdate', this._updateTime, this)
    Mediator.on(this.uniqueId + ':statechanged', this._checkState, this)
    Mediator.on(this.uniqueId + ':flashready', this._bootstrap, this)
  }

  stopListening() {
    super.stopListening()
    Mediator.off(this.uniqueId + ':progress')
    Mediator.off(this.uniqueId + ':timeupdate')
    Mediator.off(this.uniqueId + ':statechanged')
    Mediator.off(this.uniqueId + ':flashready')
  }

  _checkState() {
    if (this._isIdle || this._currentState === 'PAUSED') {
      return
    } else if (this._currentState !== 'PLAYING_BUFFERING' && this.el.getState() === 'PLAYING_BUFFERING') {
      this._bufferingState = true
      this.trigger(Events.PLAYBACK_BUFFERING, this.name)
      this._currentState = 'PLAYING_BUFFERING'
    } else if (this.el.getState() === 'PLAYING') {
      this._bufferingState = false
      this.trigger(Events.PLAYBACK_BUFFERFULL, this.name)
      this._currentState = 'PLAYING'
    } else if (this.el.getState() === 'IDLE') {
      this._currentState = 'IDLE'
    } else if (this.el.getState() === 'ENDED') {
      this.trigger(Events.PLAYBACK_ENDED, this.name)
      this.trigger(Events.PLAYBACK_TIMEUPDATE, {current: 0, total: this.el.getDuration()}, this.name)
      this._currentState = 'ENDED'
      this._isIdle = true
    }
  }

  _progress() {
    if (this._currentState !== 'IDLE' && this._currentState !== 'ENDED') {
      this.trigger(Events.PLAYBACK_PROGRESS,{
        start: 0,
        current: this.el.getBytesLoaded(),
        total: this.el.getBytesTotal()
      })
    }
  }

  _firstPlay() {
    if (this.el.playerPlay) {
      this._isIdle = false
      this.el.playerPlay(this._src)
      this.listenToOnce(this, Events.PLAYBACK_BUFFERFULL, () => this._checkInitialSeek())
      this._currentState = 'PLAYING'
    } else {
      this.listenToOnce(this, Events.PLAYBACK_READY, this._firstPlay)
    }
  }

  _checkInitialSeek() {
    let seekTime = seekStringToSeconds(window.location.href)
    if (seekTime !== 0) {
      this.seekSeconds(seekTime)
    }
  }

  play() {
    this.trigger(Events.PLAYBACK_PLAY_INTENT)
    if (this._currentState === 'PAUSED' || this._currentState === 'PLAYING_BUFFERING') {
      this._currentState = 'PLAYING'
      this.el.playerResume()
      this.trigger(Events.PLAYBACK_PLAY, this.name)
    } else if (this._currentState !== 'PLAYING') {
      this._firstPlay()
      this.trigger(Events.PLAYBACK_PLAY, this.name)
    }
  }

  volume(value) {
    if (this.isReady) {
      this.el.playerVolume(value)
    } else {
      this.listenToOnce(this, Events.PLAYBACK_BUFFERFULL, () => this.volume(value))
    }
  }

  pause() {
    this._currentState = 'PAUSED'
    this.el.playerPause()
    this.trigger(Events.PLAYBACK_PAUSE, this.name)
  }

  stop() {
    this.el.playerStop()
    this.trigger(Events.PLAYBACK_STOP)
    this.trigger(Events.PLAYBACK_TIMEUPDATE, {current: 0, total: 0}, this.name)
  }

  isPlaying() {
    return !!(this.isReady && this._currentState.indexOf('PLAYING') > -1)
  }

  get isReady(){
    return this._isReadyState
  }

  getDuration() {
    return this.el.getDuration()
  }

  seekPercentage(percentage) {
    if (this.el.getDuration() > 0) {
      let seekSeconds = this.el.getDuration() * (percentage / 100)
      this.seek(seekSeconds)
    } else {
      this.listenToOnce(this, Events.PLAYBACK_BUFFERFULL, () => this.seekPercentage(percentage))
    }
  }

  seek(time) {
    if (this.isReady && this.el.playerSeek) {
      this.el.playerSeek(time)
      this.trigger(Events.PLAYBACK_TIMEUPDATE, {current: time, total: this.el.getDuration()}, this.name)
      if (this._currentState === 'PAUSED') {
        this.el.playerPause()
      }
    } else {
      this.listenToOnce(this, Events.PLAYBACK_BUFFERFULL, () => this.seek(time))
    }
  }

  destroy() {
    clearInterval(this.bootstrapId)
    super.stopListening()
    this.$el.remove()
  }
}

Flash.canPlay = function(resource) {
  if (!Browser.hasFlash || !resource || resource.constructor !== String) {
    return false
  } else {
    const resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || []
    return resourceParts.length > 1 && !Browser.isMobile && resourceParts[1].toLowerCase().match(/^(mp4|mov|f4v|3gpp|3gp)$/)

  }
}