aboutsummaryrefslogblamecommitdiffhomepage
path: root/client/src/standalone/videos/shared/player-manager-options.ts
blob: 3c7521bc2e5b5026e1f966a2f5822311d74a5629 (plain) (tree)
1
2
3
4
5
6
7
8
9



                                                                         
             



                       
             







                                                                                                                        
         
                       
                       
                       


















                                                    
                                       







































































                                                                                



                                                                                                       















                                                                                       
                                                                













                                                                                                                            
                                                      







                                                                                


                                 

                    

                          


                                     


                               

                                  
                                      










                                         
                                
                     

                       
                   
                    
                      
           
                          
                          
                  

               



                                                             



                                                         
                                                                   
                      











                                             

                   






                                                              

                                        


                                                                     
                                  
                                                                        










                                                               
 
                                        

                                                 







                                                           
                                                       


                            


                         































                                                                   











                                                                                             










































































                                                                                                                   
import { peertubeTranslate } from '../../../../../shared/core-utils/i18n'
import {
  HTMLServerConfig,
  LiveVideo,
  Storyboard,
  Video,
  VideoCaption,
  VideoDetails,
  VideoPlaylistElement,
  VideoState,
  VideoStreamingPlaylistType
} from '../../../../../shared/models'
import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode, VideoJSCaption } from '../../../assets/player'
import {
  getBoolOrDefault,
  getParamString,
  getParamToggle,
  isP2PEnabled,
  logger,
  peertubeLocalStorage,
  UserLocalStorageKeys,
  videoRequiresUserAuth
} from '../../../root-helpers'
import { PeerTubePlugin } from './peertube-plugin'
import { PlayerHTML } from './player-html'
import { PlaylistTracker } from './playlist-tracker'
import { Translations } from './translations'
import { VideoFetcher } from './video-fetcher'

export class PlayerManagerOptions {
  private autoplay: boolean

  private controls: boolean
  private controlBar: boolean

  private muted: boolean
  private loop: boolean
  private subtitle: string
  private enableApi = false
  private startTime: number | string = 0
  private stopTime: number | string
  private playbackRate: number | string

  private title: boolean
  private warningTitle: boolean
  private peertubeLink: boolean
  private p2pEnabled: boolean
  private bigPlayBackgroundColor: string
  private foregroundColor: string

  private mode: PlayerMode
  private scope = 'peertube'

  constructor (
    private readonly playerHTML: PlayerHTML,
    private readonly videoFetcher: VideoFetcher,
    private readonly peertubePlugin: PeerTubePlugin
  ) {}

  hasAPIEnabled () {
    return this.enableApi
  }

  hasAutoplay () {
    return this.autoplay
  }

  hasControls () {
    return this.controls
  }

  hasTitle () {
    return this.title
  }

  hasWarningTitle () {
    return this.warningTitle
  }

  hasP2PEnabled () {
    return !!this.p2pEnabled
  }

  hasBigPlayBackgroundColor () {
    return !!this.bigPlayBackgroundColor
  }

  getBigPlayBackgroundColor () {
    return this.bigPlayBackgroundColor
  }

  hasForegroundColor () {
    return !!this.foregroundColor
  }

  getForegroundColor () {
    return this.foregroundColor
  }

  getMode () {
    return this.mode
  }

  getScope () {
    return this.scope
  }

  // ---------------------------------------------------------------------------

  loadParams (config: HTMLServerConfig, video: VideoDetails) {
    try {
      const params = new URL(window.location.toString()).searchParams

      this.autoplay = getParamToggle(params, 'autoplay', false)
      // Disable auto play on live videos that are not streamed
      if (video.state.id === VideoState.LIVE_ENDED || video.state.id === VideoState.WAITING_FOR_LIVE) {
        this.autoplay = false
      }

      this.controls = getParamToggle(params, 'controls', true)
      this.controlBar = getParamToggle(params, 'controlBar', true)

      this.muted = getParamToggle(params, 'muted', undefined)
      this.loop = getParamToggle(params, 'loop', false)
      this.title = getParamToggle(params, 'title', true)
      this.enableApi = getParamToggle(params, 'api', this.enableApi)
      this.warningTitle = getParamToggle(params, 'warningTitle', true)
      this.peertubeLink = getParamToggle(params, 'peertubeLink', true)
      this.p2pEnabled = getParamToggle(params, 'p2p', this.isP2PEnabled(config, video))

      this.scope = getParamString(params, 'scope', this.scope)
      this.subtitle = getParamString(params, 'subtitle')
      this.startTime = getParamString(params, 'start')
      this.stopTime = getParamString(params, 'stop')
      this.playbackRate = getParamString(params, 'playbackRate')

      this.bigPlayBackgroundColor = getParamString(params, 'bigPlayBackgroundColor')
      this.foregroundColor = getParamString(params, 'foregroundColor')

      const modeParam = getParamString(params, 'mode')

      if (modeParam) {
        if (modeParam === 'p2p-media-loader') this.mode = 'p2p-media-loader'
        else this.mode = 'webtorrent'
      } else {
        if (Array.isArray(video.streamingPlaylists) && video.streamingPlaylists.length !== 0) this.mode = 'p2p-media-loader'
        else this.mode = 'webtorrent'
      }
    } catch (err) {
      logger.error('Cannot get params from URL.', err)
    }
  }

  // ---------------------------------------------------------------------------

  async getPlayerOptions (options: {
    video: VideoDetails
    captionsResponse: Response

    storyboardsResponse: Response

    live?: LiveVideo

    forceAutoplay: boolean

    authorizationHeader: () => string
    videoFileToken: () => string

    videoPassword: () => string
    requiresPassword: boolean

    serverConfig: HTMLServerConfig

    autoplayFromPreviousVideo: boolean

    translations: Translations

    playlistTracker?: PlaylistTracker
    playNextPlaylistVideo?: () => any
    playPreviousPlaylistVideo?: () => any
    onVideoUpdate?: (uuid: string) => any
  }) {
    const {
      video,
      captionsResponse,
      autoplayFromPreviousVideo,
      videoFileToken,
      videoPassword,
      requiresPassword,
      translations,
      forceAutoplay,
      playlistTracker,
      live,
      storyboardsResponse,
      authorizationHeader,
      serverConfig
    } = options

    const [ videoCaptions, storyboard ] = await Promise.all([
      this.buildCaptions(captionsResponse, translations),
      this.buildStoryboard(storyboardsResponse)
    ])

    const playerOptions: PeertubePlayerManagerOptions = {
      common: {
        // Autoplay in playlist mode
        autoplay: autoplayFromPreviousVideo ? true : this.autoplay,
        forceAutoplay,

        controls: this.controls,
        controlBar: this.controlBar,

        muted: this.muted,
        loop: this.loop,

        p2pEnabled: this.p2pEnabled,

        captions: videoCaptions.length !== 0,
        subtitle: this.subtitle,

        storyboard,

        startTime: playlistTracker
          ? playlistTracker.getCurrentElement().startTimestamp
          : this.startTime,
        stopTime: playlistTracker
          ? playlistTracker.getCurrentElement().stopTimestamp
          : this.stopTime,

        playbackRate: this.playbackRate,

        videoCaptions,
        inactivityTimeout: 2500,
        videoViewUrl: this.videoFetcher.getVideoViewsUrl(video.uuid),
        videoViewIntervalMs: 5000,
        metricsUrl: window.location.origin + '/api/v1/metrics/playback',

        videoShortUUID: video.shortUUID,
        videoUUID: video.uuid,

        playerElement: this.playerHTML.getPlayerElement(),
        onPlayerElementChange: (element: HTMLVideoElement) => {
          this.playerHTML.setPlayerElement(element)
        },

        videoDuration: video.duration,
        enableHotkeys: true,

        peertubeLink: this.peertubeLink,
        instanceName: serverConfig.instance.name,

        poster: window.location.origin + video.previewPath,
        theaterButton: false,

        serverUrl: window.location.origin,
        language: navigator.language,
        embedUrl: window.location.origin + video.embedPath,
        embedTitle: video.name,

        requiresUserAuth: videoRequiresUserAuth(video),
        authorizationHeader,
        videoFileToken,

        requiresPassword,
        videoPassword,

        errorNotifier: () => {
          // Empty, we don't have a notifier in the embed
        },

        ...this.buildLiveOptions(video, live),

        ...this.buildPlaylistOptions(options)
      },

      webtorrent: {
        videoFiles: video.files
      },

      ...this.buildP2PMediaLoaderOptions(video),

      pluginsManager: this.peertubePlugin.getPluginsManager()
    }

    return playerOptions
  }

  private buildLiveOptions (video: VideoDetails, live: LiveVideo) {
    if (!video.isLive) return { isLive: false }

    return {
      isLive: true,
      liveOptions: {
        latencyMode: live.latencyMode
      }
    }
  }

  private async buildStoryboard (storyboardsResponse: Response) {
    const { storyboards } = await storyboardsResponse.json() as { storyboards: Storyboard[] }
    if (!storyboards || storyboards.length === 0) return undefined

    return {
      url: window.location.origin + storyboards[0].storyboardPath,
      height: storyboards[0].spriteHeight,
      width: storyboards[0].spriteWidth,
      interval: storyboards[0].spriteDuration
    }
  }

  private buildPlaylistOptions (options: {
    playlistTracker?: PlaylistTracker
    playNextPlaylistVideo?: () => any
    playPreviousPlaylistVideo?: () => any
    onVideoUpdate?: (uuid: string) => any
  }) {
    const { playlistTracker, playNextPlaylistVideo, playPreviousPlaylistVideo, onVideoUpdate } = options

    if (!playlistTracker) return {}

    return {
      playlist: {
        elements: playlistTracker.getPlaylistElements(),
        playlist: playlistTracker.getPlaylist(),

        getCurrentPosition: () => playlistTracker.getCurrentPosition(),

        onItemClicked: (videoPlaylistElement: VideoPlaylistElement) => {
          playlistTracker.setCurrentElement(videoPlaylistElement)

          onVideoUpdate(videoPlaylistElement.video.uuid)
        }
      },

      nextVideo: () => playNextPlaylistVideo(),
      hasNextVideo: () => playlistTracker.hasNextPlaylistElement(),

      previousVideo: () => playPreviousPlaylistVideo(),
      hasPreviousVideo: () => playlistTracker.hasPreviousPlaylistElement()
    }
  }

  private buildP2PMediaLoaderOptions (video: VideoDetails) {
    if (this.mode !== 'p2p-media-loader') return {}

    const hlsPlaylist = video.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)

    return {
      p2pMediaLoader: {
        playlistUrl: hlsPlaylist.playlistUrl,
        segmentsSha256Url: hlsPlaylist.segmentsSha256Url,
        redundancyBaseUrls: hlsPlaylist.redundancies.map(r => r.baseUrl),
        trackerAnnounce: video.trackerUrls,
        videoFiles: hlsPlaylist.files
      } as P2PMediaLoaderOptions
    }
  }

  // ---------------------------------------------------------------------------

  private async buildCaptions (captionsResponse: Response, translations: Translations): Promise<VideoJSCaption[]> {
    if (captionsResponse.ok) {
      const { data } = await captionsResponse.json()

      return data.map((c: VideoCaption) => ({
        label: peertubeTranslate(c.language.label, translations),
        language: c.language.id,
        src: window.location.origin + c.captionPath
      }))
    }

    return []
  }

  // ---------------------------------------------------------------------------

  private isP2PEnabled (config: HTMLServerConfig, video: Video) {
    const userP2PEnabled = getBoolOrDefault(
      peertubeLocalStorage.getItem(UserLocalStorageKeys.P2P_ENABLED),
      config.defaults.p2p.embed.enabled
    )

    return isP2PEnabled(video, config, userP2PEnabled)
  }
}