1 import * as Channel from 'jschannel'
2 import { EventHandler, PeerTubeResolution, PeerTubeTextTrack, PlayerEventType } from './definitions'
3 import { EventRegistrar } from './events'
5 const PASSTHROUGH_EVENTS = [
8 'playbackStatusUpdate',
9 'playbackStatusChange',
15 * Allows for programmatic control of a PeerTube embed running in an <iframe>
18 export class PeerTubePlayer {
20 private readonly eventRegistrar: EventRegistrar = new EventRegistrar()
21 private channel: Channel.MessagingChannel
22 private readyPromise: Promise<void>
25 * Construct a new PeerTubePlayer for the given PeerTube embed iframe.
26 * Optionally provide a `scope` to ensure that messages are not crossed
27 * between multiple PeerTube embeds. The string passed here must match the
28 * `scope=` query parameter on the embed URL.
34 private readonly embedElement: HTMLIFrameElement,
35 private readonly scope?: string
37 this.eventRegistrar.registerTypes(PASSTHROUGH_EVENTS)
39 this.constructChannel()
40 this.prepareToBeReady()
44 * Destroy the player object and remove the associated player from the DOM.
47 this.embedElement.remove()
51 * Listen to an event emitted by this player.
53 * @param event One of the supported event types
54 * @param handler A handler which will be passed an event object (or undefined if no event object is included)
56 addEventListener (event: PlayerEventType, handler: EventHandler<any>): boolean {
57 return this.eventRegistrar.addListener(event, handler)
61 * Remove an event listener previously added with addEventListener().
63 * @param event The name of the event previously listened to
66 removeEventListener (event: PlayerEventType, handler: EventHandler<any>): boolean {
67 return this.eventRegistrar.removeListener(event, handler)
71 * Promise resolves when the player is ready.
73 get ready (): Promise<void> {
74 return this.readyPromise
78 * Tell the embed to start/resume playback
81 await this.sendMessage('play')
85 * Tell the embed to pause playback.
88 await this.sendMessage('pause')
92 * Tell the embed to change the audio volume
94 * @param value A number from 0 to 1
96 async setVolume (value: number) {
97 await this.sendMessage('setVolume', value)
101 * Get the current volume level in the embed.
103 * @param value A number from 0 to 1
105 async getVolume (): Promise<number> {
106 return this.sendMessage<undefined, number>('getVolume')
110 * Tell the embed to change the current caption
112 * @param value Caption id
114 async setCaption (value: string) {
115 await this.sendMessage('setCaption', value)
121 async getCaptions (): Promise<PeerTubeTextTrack[]> {
122 return this.sendMessage<undefined, PeerTubeTextTrack[]>('getCaptions')
126 * Tell the embed to seek to a specific position (in seconds)
130 async seek (seconds: number) {
131 await this.sendMessage('seek', seconds)
135 * Tell the embed to switch resolutions to the resolution identified
138 * @param resolutionId The ID of the resolution as found with getResolutions()
140 async setResolution (resolutionId: any) {
141 await this.sendMessage('setResolution', resolutionId)
145 * Retrieve a list of the available resolutions. This may change later, listen to the
146 * `resolutionUpdate` event with `addEventListener` in order to be updated as the available
147 * resolutions change.
149 async getResolutions (): Promise<PeerTubeResolution[]> {
150 return this.sendMessage<undefined, PeerTubeResolution[]>('getResolutions')
154 * Retrieve a list of available playback rates.
156 async getPlaybackRates (): Promise<number[]> {
157 return this.sendMessage<undefined, number[]>('getPlaybackRates')
161 * Get the current playback rate. Defaults to 1 (1x playback rate).
163 async getPlaybackRate (): Promise<number> {
164 return this.sendMessage<undefined, number>('getPlaybackRate')
168 * Set the playback rate. Should be one of the options returned by getPlaybackRates().
169 * Passing 0.5 means half speed, 1 means normal, 2 means 2x speed, etc.
173 async setPlaybackRate (rate: number) {
174 await this.sendMessage('setPlaybackRate', rate)
178 * Play next video in playlist
180 async playNextVideo () {
181 await this.sendMessage('playNextVideo')
185 * Play previous video in playlist
187 async playPreviousVideo () {
188 await this.sendMessage('playPreviousVideo')
192 * Get video position currently played (starts from 1)
194 async getCurrentPosition () {
195 return this.sendMessage<undefined, number>('getCurrentPosition')
198 private constructChannel () {
199 this.channel = Channel.build({
200 window: this.embedElement.contentWindow,
202 scope: this.scope || 'peertube'
204 this.eventRegistrar.bindToChannel(this.channel)
207 private prepareToBeReady () {
208 let readyResolve: () => void
209 let readyReject: () => void
211 this.readyPromise = new Promise<void>((res, rej) => {
216 this.channel.bind('ready', success => success ? readyResolve() : readyReject())
219 success: isReady => isReady ? readyResolve() : null
223 private sendMessage<TIn, TOut> (method: string, params?: TIn): Promise<TOut> {
224 return new Promise<TOut>((resolve, reject) => {
228 success: result => resolve(result),
229 error: error => reject(error)
235 // put it on the window as well as the export
236 (window['PeerTubePlayer'] as any) = PeerTubePlayer