2018-07-10 11:47:56 -04:00
|
|
|
import * as Channel from 'jschannel'
|
2020-05-06 05:54:33 -04:00
|
|
|
import { EventHandler, PeerTubeResolution, PeerTubeTextTrack, PlayerEventType } from './definitions'
|
2018-07-10 11:47:56 -04:00
|
|
|
import { EventRegistrar } from './events'
|
|
|
|
|
|
|
|
const PASSTHROUGH_EVENTS = [
|
2018-07-10 12:02:30 -04:00
|
|
|
'pause',
|
|
|
|
'play',
|
2018-07-10 11:47:56 -04:00
|
|
|
'playbackStatusUpdate',
|
|
|
|
'playbackStatusChange',
|
2019-12-17 08:20:43 -05:00
|
|
|
'resolutionUpdate',
|
|
|
|
'volumeChange'
|
2018-07-10 11:47:56 -04:00
|
|
|
]
|
|
|
|
|
|
|
|
/**
|
2018-07-10 12:02:30 -04:00
|
|
|
* Allows for programmatic control of a PeerTube embed running in an <iframe>
|
2018-07-10 11:47:56 -04:00
|
|
|
* within a web page.
|
|
|
|
*/
|
|
|
|
export class PeerTubePlayer {
|
2018-07-10 12:02:30 -04:00
|
|
|
|
2021-08-17 08:42:53 -04:00
|
|
|
private readonly eventRegistrar: EventRegistrar = new EventRegistrar()
|
2018-07-10 12:02:30 -04:00
|
|
|
private channel: Channel.MessagingChannel
|
|
|
|
private readyPromise: Promise<void>
|
|
|
|
|
2018-07-10 11:47:56 -04:00
|
|
|
/**
|
|
|
|
* Construct a new PeerTubePlayer for the given PeerTube embed iframe.
|
2018-07-10 12:02:30 -04:00
|
|
|
* Optionally provide a `scope` to ensure that messages are not crossed
|
|
|
|
* between multiple PeerTube embeds. The string passed here must match the
|
2018-07-10 11:47:56 -04:00
|
|
|
* `scope=` query parameter on the embed URL.
|
2018-07-10 12:02:30 -04:00
|
|
|
*
|
|
|
|
* @param embedElement
|
|
|
|
* @param scope
|
2018-07-10 11:47:56 -04:00
|
|
|
*/
|
2018-07-10 12:02:30 -04:00
|
|
|
constructor (
|
2021-08-17 08:42:53 -04:00
|
|
|
private readonly embedElement: HTMLIFrameElement,
|
|
|
|
private readonly scope?: string
|
2018-07-10 11:47:56 -04:00
|
|
|
) {
|
|
|
|
this.eventRegistrar.registerTypes(PASSTHROUGH_EVENTS)
|
|
|
|
|
|
|
|
this.constructChannel()
|
|
|
|
this.prepareToBeReady()
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destroy the player object and remove the associated player from the DOM.
|
|
|
|
*/
|
2018-07-10 12:02:30 -04:00
|
|
|
destroy () {
|
2018-07-10 11:47:56 -04:00
|
|
|
this.embedElement.remove()
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Listen to an event emitted by this player.
|
2018-07-10 12:02:30 -04:00
|
|
|
*
|
2018-07-10 11:47:56 -04:00
|
|
|
* @param event One of the supported event types
|
|
|
|
* @param handler A handler which will be passed an event object (or undefined if no event object is included)
|
|
|
|
*/
|
2018-07-10 12:02:30 -04:00
|
|
|
addEventListener (event: PlayerEventType, handler: EventHandler<any>): boolean {
|
2018-07-10 11:47:56 -04:00
|
|
|
return this.eventRegistrar.addListener(event, handler)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove an event listener previously added with addEventListener().
|
2018-07-10 12:02:30 -04:00
|
|
|
*
|
2018-07-10 11:47:56 -04:00
|
|
|
* @param event The name of the event previously listened to
|
2018-07-10 12:02:30 -04:00
|
|
|
* @param handler
|
2018-07-10 11:47:56 -04:00
|
|
|
*/
|
2018-07-10 12:02:30 -04:00
|
|
|
removeEventListener (event: PlayerEventType, handler: EventHandler<any>): boolean {
|
2018-07-10 11:47:56 -04:00
|
|
|
return this.eventRegistrar.removeListener(event, handler)
|
|
|
|
}
|
2018-07-10 12:02:30 -04:00
|
|
|
|
2018-07-10 11:47:56 -04:00
|
|
|
/**
|
|
|
|
* Promise resolves when the player is ready.
|
|
|
|
*/
|
2018-07-10 12:02:30 -04:00
|
|
|
get ready (): Promise<void> {
|
2018-07-10 11:47:56 -04:00
|
|
|
return this.readyPromise
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tell the embed to start/resume playback
|
|
|
|
*/
|
2018-07-10 12:02:30 -04:00
|
|
|
async play () {
|
2018-07-10 11:47:56 -04:00
|
|
|
await this.sendMessage('play')
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tell the embed to pause playback.
|
|
|
|
*/
|
2018-07-10 12:02:30 -04:00
|
|
|
async pause () {
|
2018-07-10 11:47:56 -04:00
|
|
|
await this.sendMessage('pause')
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tell the embed to change the audio volume
|
2021-08-17 08:42:53 -04:00
|
|
|
*
|
2018-07-10 11:47:56 -04:00
|
|
|
* @param value A number from 0 to 1
|
|
|
|
*/
|
2018-07-10 12:02:30 -04:00
|
|
|
async setVolume (value: number) {
|
2018-07-10 11:47:56 -04:00
|
|
|
await this.sendMessage('setVolume', value)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the current volume level in the embed.
|
2021-08-17 08:42:53 -04:00
|
|
|
*
|
2018-07-10 11:47:56 -04:00
|
|
|
* @param value A number from 0 to 1
|
|
|
|
*/
|
2018-07-10 12:02:30 -04:00
|
|
|
async getVolume (): Promise<number> {
|
2021-08-17 08:42:53 -04:00
|
|
|
return this.sendMessage<undefined, number>('getVolume')
|
2018-07-10 11:47:56 -04:00
|
|
|
}
|
|
|
|
|
2020-05-06 05:54:33 -04:00
|
|
|
/**
|
|
|
|
* Tell the embed to change the current caption
|
2021-08-17 08:42:53 -04:00
|
|
|
*
|
2020-05-06 05:54:33 -04:00
|
|
|
* @param value Caption id
|
|
|
|
*/
|
|
|
|
async setCaption (value: string) {
|
|
|
|
await this.sendMessage('setCaption', value)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get video captions
|
|
|
|
*/
|
|
|
|
async getCaptions (): Promise<PeerTubeTextTrack[]> {
|
2021-08-17 08:42:53 -04:00
|
|
|
return this.sendMessage<undefined, PeerTubeTextTrack[]>('getCaptions')
|
2020-05-06 05:54:33 -04:00
|
|
|
}
|
|
|
|
|
2018-07-10 11:47:56 -04:00
|
|
|
/**
|
|
|
|
* Tell the embed to seek to a specific position (in seconds)
|
2021-08-17 08:42:53 -04:00
|
|
|
*
|
2018-07-10 12:02:30 -04:00
|
|
|
* @param seconds
|
2018-07-10 11:47:56 -04:00
|
|
|
*/
|
2018-07-10 12:02:30 -04:00
|
|
|
async seek (seconds: number) {
|
2018-07-10 11:47:56 -04:00
|
|
|
await this.sendMessage('seek', seconds)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tell the embed to switch resolutions to the resolution identified
|
|
|
|
* by the given ID.
|
2018-07-10 12:02:30 -04:00
|
|
|
*
|
2018-07-10 11:47:56 -04:00
|
|
|
* @param resolutionId The ID of the resolution as found with getResolutions()
|
|
|
|
*/
|
2018-07-10 12:02:30 -04:00
|
|
|
async setResolution (resolutionId: any) {
|
2018-07-10 11:47:56 -04:00
|
|
|
await this.sendMessage('setResolution', resolutionId)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-07-10 12:02:30 -04:00
|
|
|
* Retrieve a list of the available resolutions. This may change later, listen to the
|
2018-07-10 11:47:56 -04:00
|
|
|
* `resolutionUpdate` event with `addEventListener` in order to be updated as the available
|
|
|
|
* resolutions change.
|
|
|
|
*/
|
2018-07-10 12:02:30 -04:00
|
|
|
async getResolutions (): Promise<PeerTubeResolution[]> {
|
2021-08-17 08:42:53 -04:00
|
|
|
return this.sendMessage<undefined, PeerTubeResolution[]>('getResolutions')
|
2018-07-10 11:47:56 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-07-10 12:02:30 -04:00
|
|
|
* Retrieve a list of available playback rates.
|
2018-07-10 11:47:56 -04:00
|
|
|
*/
|
2018-07-10 12:02:30 -04:00
|
|
|
async getPlaybackRates (): Promise<number[]> {
|
2021-08-17 08:42:53 -04:00
|
|
|
return this.sendMessage<undefined, number[]>('getPlaybackRates')
|
2018-07-10 11:47:56 -04:00
|
|
|
}
|
2018-07-10 12:02:30 -04:00
|
|
|
|
2018-07-10 11:47:56 -04:00
|
|
|
/**
|
|
|
|
* Get the current playback rate. Defaults to 1 (1x playback rate).
|
|
|
|
*/
|
2018-07-10 12:02:30 -04:00
|
|
|
async getPlaybackRate (): Promise<number> {
|
2021-08-17 08:42:53 -04:00
|
|
|
return this.sendMessage<undefined, number>('getPlaybackRate')
|
2018-07-10 11:47:56 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the playback rate. Should be one of the options returned by getPlaybackRates().
|
|
|
|
* Passing 0.5 means half speed, 1 means normal, 2 means 2x speed, etc.
|
2018-07-10 12:02:30 -04:00
|
|
|
*
|
|
|
|
* @param rate
|
2018-07-10 11:47:56 -04:00
|
|
|
*/
|
2018-07-10 12:02:30 -04:00
|
|
|
async setPlaybackRate (rate: number) {
|
2018-07-10 11:47:56 -04:00
|
|
|
await this.sendMessage('setPlaybackRate', rate)
|
|
|
|
}
|
|
|
|
|
2020-08-05 08:16:39 -04:00
|
|
|
/**
|
|
|
|
* Play next video in playlist
|
|
|
|
*/
|
|
|
|
async playNextVideo () {
|
|
|
|
await this.sendMessage('playNextVideo')
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Play previous video in playlist
|
|
|
|
*/
|
|
|
|
async playPreviousVideo () {
|
|
|
|
await this.sendMessage('playPreviousVideo')
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get video position currently played (starts from 1)
|
|
|
|
*/
|
|
|
|
async getCurrentPosition () {
|
2021-08-17 08:42:53 -04:00
|
|
|
return this.sendMessage<undefined, number>('getCurrentPosition')
|
2020-08-05 08:16:39 -04:00
|
|
|
}
|
|
|
|
|
2018-07-10 12:02:30 -04:00
|
|
|
private constructChannel () {
|
2018-07-10 11:47:56 -04:00
|
|
|
this.channel = Channel.build({
|
|
|
|
window: this.embedElement.contentWindow,
|
|
|
|
origin: '*',
|
|
|
|
scope: this.scope || 'peertube'
|
|
|
|
})
|
|
|
|
this.eventRegistrar.bindToChannel(this.channel)
|
|
|
|
}
|
2018-07-10 12:02:30 -04:00
|
|
|
|
|
|
|
private prepareToBeReady () {
|
2021-08-17 08:42:53 -04:00
|
|
|
let readyResolve: () => void
|
|
|
|
let readyReject: () => void
|
2018-07-10 12:02:30 -04:00
|
|
|
|
2018-07-10 11:47:56 -04:00
|
|
|
this.readyPromise = new Promise<void>((res, rej) => {
|
|
|
|
readyResolve = res
|
|
|
|
readyReject = rej
|
|
|
|
})
|
2018-07-10 12:02:30 -04:00
|
|
|
|
2018-07-10 11:47:56 -04:00
|
|
|
this.channel.bind('ready', success => success ? readyResolve() : readyReject())
|
|
|
|
this.channel.call({
|
|
|
|
method: 'isReady',
|
|
|
|
success: isReady => isReady ? readyResolve() : null
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-07-10 12:02:30 -04:00
|
|
|
private sendMessage<TIn, TOut> (method: string, params?: TIn): Promise<TOut> {
|
2018-07-10 11:47:56 -04:00
|
|
|
return new Promise<TOut>((resolve, reject) => {
|
|
|
|
this.channel.call({
|
2021-08-17 08:42:53 -04:00
|
|
|
method,
|
|
|
|
params,
|
2018-07-10 11:47:56 -04:00
|
|
|
success: result => resolve(result),
|
|
|
|
error: error => reject(error)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// put it on the window as well as the export
|
2021-08-17 08:42:53 -04:00
|
|
|
(window['PeerTubePlayer'] as any) = PeerTubePlayer
|