aboutsummaryrefslogblamecommitdiffhomepage
path: root/client/src/assets/player/upnext/upnext-plugin.ts
blob: a3747b25fae5e21c7c711a40aa39557fdc2bc64d (plain) (tree)



















                                                                                                                                                                   
                                                                        







                                                   



                                          

                    




                           
                               



                                                      
 
                                                           

                                    
                                            





                                                 
                              




















                                                                                           
                                                                                       














                                                                                     
 

                                                                                   
 
                                                   















                                            



                                                                                                                          
 










                                                                                                 


                             

                                            

                                                              


                                          















                                                                     


                                                                      













                                              
// @ts-ignore
import * as videojs from 'video.js'
import { VideoJSComponentInterface } from '../peertube-videojs-typings'

function getMainTemplate (options: any) {
  return `
    <div class="vjs-upnext-top">
      <span class="vjs-upnext-headtext">${options.headText}</span>
      <div class="vjs-upnext-title"></div>
    </div>
    <div class="vjs-upnext-autoplay-icon">
      <svg height="100%" version="1.1" viewbox="0 0 98 98" width="100%">
        <circle class="vjs-upnext-svg-autoplay-circle" cx="49" cy="49" fill="#000" fill-opacity="0.8" r="48"></circle>
        <circle class="vjs-upnext-svg-autoplay-ring" cx="-49" cy="49" fill-opacity="0" r="46.5" stroke="#FFFFFF" stroke-width="4" transform="rotate(-90)"></circle>
        <polygon class="vjs-upnext-svg-autoplay-triangle" fill="#fff" points="32,27 72,49 32,71"></polygon></svg>
    </div>
    <span class="vjs-upnext-bottom">
      <span class="vjs-upnext-cancel">
        <button class="vjs-upnext-cancel-button" tabindex="0" aria-label="Cancel autoplay">${options.cancelText}</button>
      </span>
      <span class="vjs-upnext-suspended">${options.suspendedText}</span>
    </span>
  `
}

// @ts-ignore-start
const Component = videojs.getComponent('Component')
class EndCard extends Component {
  options_: any
  dashOffsetTotal = 586
  dashOffsetStart = 293
  interval = 50
  upNextEvents = new videojs.EventTarget()
  ticks = 0
  totalTicks: number

  container: HTMLElement
  title: HTMLElement
  autoplayRing: HTMLElement
  cancelButton: HTMLElement
  suspendedMessage: HTMLElement
  nextButton: HTMLElement

  constructor (player: videojs.Player, options: any) {
    super(player, options)

    this.totalTicks = this.options_.timeout / this.interval

    player.on('ended', (_: any) => {
      if (!this.options_.condition()) return

      player.addClass('vjs-upnext--showing')
      this.showCard((canceled: boolean) => {
        player.removeClass('vjs-upnext--showing')
        this.container.style.display = 'none'
        if (!canceled) {
          this.options_.next()
        }
      })
    })

    player.on('playing', () => {
      this.upNextEvents.trigger('playing')
    })
  }

  createEl () {
    const container = super.createEl('div', {
      className: 'vjs-upnext-content',
      innerHTML: getMainTemplate(this.options_)
    })

    this.container = container
    container.style.display = 'none'

    this.autoplayRing = container.getElementsByClassName('vjs-upnext-svg-autoplay-ring')[0]
    this.title = container.getElementsByClassName('vjs-upnext-title')[0]
    this.cancelButton = container.getElementsByClassName('vjs-upnext-cancel-button')[0]
    this.suspendedMessage = container.getElementsByClassName('vjs-upnext-suspended')[0]
    this.nextButton = container.getElementsByClassName('vjs-upnext-autoplay-icon')[0]

    this.cancelButton.onclick = () => {
      this.upNextEvents.trigger('cancel')
    }

    this.nextButton.onclick = () => {
      this.upNextEvents.trigger('next')
    }

    return container
  }

  showCard (cb: Function) {
    let timeout: any

    this.autoplayRing.setAttribute('stroke-dasharray', '' + this.dashOffsetStart)
    this.autoplayRing.setAttribute('stroke-dashoffset', '' + -this.dashOffsetStart)

    this.title.innerHTML = this.options_.getTitle()

    this.upNextEvents.one('cancel', () => {
      clearTimeout(timeout)
      cb(true)
    })

    this.upNextEvents.one('playing', () => {
      clearTimeout(timeout)
      cb(true)
    })

    this.upNextEvents.one('next', () => {
      clearTimeout(timeout)
      cb(false)
    })

    const goToPercent = (percent: number) => {
      const newOffset = Math.max(-this.dashOffsetTotal, - this.dashOffsetStart - percent * this.dashOffsetTotal / 2 / 100)
      this.autoplayRing.setAttribute('stroke-dashoffset', '' + newOffset)
    }

    const tick = () => {
      goToPercent((this.ticks++) * 100 / this.totalTicks)
    }

    const update = () => {
      if (this.options_.suspended()) {
        this.suspendedMessage.innerText = this.options_.suspendedText
        goToPercent(0)
        this.ticks = 0
        timeout = setTimeout(update.bind(this), 300) // checks once supsended can be a bit longer
      } else if (this.ticks >= this.totalTicks) {
        clearTimeout(timeout)
        cb(false)
      } else {
        this.suspendedMessage.innerText = ''
        tick()
        timeout = setTimeout(update.bind(this), this.interval)
      }
    }

    this.container.style.display = 'block'
    timeout = setTimeout(update.bind(this), this.interval)
  }
}
// @ts-ignore-end

videojs.registerComponent('EndCard', EndCard)

const Plugin: VideoJSComponentInterface = videojs.getPlugin('plugin')
class UpNextPlugin extends Plugin {
  constructor (player: videojs.Player, options: any = {}) {
    const settings = {
      next: options.next,
      getTitle: options.getTitle,
      timeout: options.timeout || 5000,
      cancelText: options.cancelText || 'Cancel',
      headText: options.headText || 'Up Next',
      suspendedText: options.suspendedText || 'Autoplay is suspended',
      condition: options.condition,
      suspended: options.suspended
    }

    super(player, settings)

    this.player.ready(() => {
      player.addClass('vjs-upnext')
    })

    player.addChild('EndCard', settings)
  }
}

videojs.registerPlugin('upnext', UpNextPlugin)
export { UpNextPlugin }