]>
Commit | Line | Data |
---|---|---|
99941732 WL |
1 | import * as Channel from 'jschannel' |
2 | import { EventRegistrar } from './events' | |
3 | import { EventHandler, PlayerEventType, PeerTubeResolution } from './definitions' | |
4 | ||
5 | const PASSTHROUGH_EVENTS = [ | |
902aa3a0 C |
6 | 'pause', |
7 | 'play', | |
99941732 WL |
8 | 'playbackStatusUpdate', |
9 | 'playbackStatusChange', | |
10 | 'resolutionUpdate' | |
11 | ] | |
12 | ||
13 | /** | |
902aa3a0 | 14 | * Allows for programmatic control of a PeerTube embed running in an <iframe> |
99941732 WL |
15 | * within a web page. |
16 | */ | |
17 | export class PeerTubePlayer { | |
902aa3a0 C |
18 | |
19 | private eventRegistrar: EventRegistrar = new EventRegistrar() | |
20 | private channel: Channel.MessagingChannel | |
21 | private readyPromise: Promise<void> | |
22 | ||
99941732 WL |
23 | /** |
24 | * Construct a new PeerTubePlayer for the given PeerTube embed iframe. | |
902aa3a0 C |
25 | * Optionally provide a `scope` to ensure that messages are not crossed |
26 | * between multiple PeerTube embeds. The string passed here must match the | |
99941732 | 27 | * `scope=` query parameter on the embed URL. |
902aa3a0 C |
28 | * |
29 | * @param embedElement | |
30 | * @param scope | |
99941732 | 31 | */ |
902aa3a0 C |
32 | constructor ( |
33 | private embedElement: HTMLIFrameElement, | |
34 | private scope?: string | |
99941732 WL |
35 | ) { |
36 | this.eventRegistrar.registerTypes(PASSTHROUGH_EVENTS) | |
37 | ||
38 | this.constructChannel() | |
39 | this.prepareToBeReady() | |
40 | } | |
41 | ||
99941732 WL |
42 | /** |
43 | * Destroy the player object and remove the associated player from the DOM. | |
44 | */ | |
902aa3a0 | 45 | destroy () { |
99941732 WL |
46 | this.embedElement.remove() |
47 | } | |
48 | ||
49 | /** | |
50 | * Listen to an event emitted by this player. | |
902aa3a0 | 51 | * |
99941732 WL |
52 | * @param event One of the supported event types |
53 | * @param handler A handler which will be passed an event object (or undefined if no event object is included) | |
54 | */ | |
902aa3a0 | 55 | addEventListener (event: PlayerEventType, handler: EventHandler<any>): boolean { |
99941732 WL |
56 | return this.eventRegistrar.addListener(event, handler) |
57 | } | |
58 | ||
59 | /** | |
60 | * Remove an event listener previously added with addEventListener(). | |
902aa3a0 | 61 | * |
99941732 | 62 | * @param event The name of the event previously listened to |
902aa3a0 | 63 | * @param handler |
99941732 | 64 | */ |
902aa3a0 | 65 | removeEventListener (event: PlayerEventType, handler: EventHandler<any>): boolean { |
99941732 WL |
66 | return this.eventRegistrar.removeListener(event, handler) |
67 | } | |
902aa3a0 | 68 | |
99941732 WL |
69 | /** |
70 | * Promise resolves when the player is ready. | |
71 | */ | |
902aa3a0 | 72 | get ready (): Promise<void> { |
99941732 WL |
73 | return this.readyPromise |
74 | } | |
75 | ||
76 | /** | |
77 | * Tell the embed to start/resume playback | |
78 | */ | |
902aa3a0 | 79 | async play () { |
99941732 WL |
80 | await this.sendMessage('play') |
81 | } | |
82 | ||
83 | /** | |
84 | * Tell the embed to pause playback. | |
85 | */ | |
902aa3a0 | 86 | async pause () { |
99941732 WL |
87 | await this.sendMessage('pause') |
88 | } | |
89 | ||
90 | /** | |
91 | * Tell the embed to change the audio volume | |
92 | * @param value A number from 0 to 1 | |
93 | */ | |
902aa3a0 | 94 | async setVolume (value: number) { |
99941732 WL |
95 | await this.sendMessage('setVolume', value) |
96 | } | |
97 | ||
98 | /** | |
99 | * Get the current volume level in the embed. | |
100 | * @param value A number from 0 to 1 | |
101 | */ | |
902aa3a0 C |
102 | async getVolume (): Promise<number> { |
103 | return this.sendMessage<void, number>('setVolume') | |
99941732 WL |
104 | } |
105 | ||
106 | /** | |
107 | * Tell the embed to seek to a specific position (in seconds) | |
902aa3a0 | 108 | * @param seconds |
99941732 | 109 | */ |
902aa3a0 | 110 | async seek (seconds: number) { |
99941732 WL |
111 | await this.sendMessage('seek', seconds) |
112 | } | |
113 | ||
114 | /** | |
115 | * Tell the embed to switch resolutions to the resolution identified | |
116 | * by the given ID. | |
902aa3a0 | 117 | * |
99941732 WL |
118 | * @param resolutionId The ID of the resolution as found with getResolutions() |
119 | */ | |
902aa3a0 | 120 | async setResolution (resolutionId: any) { |
99941732 WL |
121 | await this.sendMessage('setResolution', resolutionId) |
122 | } | |
123 | ||
124 | /** | |
902aa3a0 | 125 | * Retrieve a list of the available resolutions. This may change later, listen to the |
99941732 WL |
126 | * `resolutionUpdate` event with `addEventListener` in order to be updated as the available |
127 | * resolutions change. | |
128 | */ | |
902aa3a0 C |
129 | async getResolutions (): Promise<PeerTubeResolution[]> { |
130 | return this.sendMessage<void, PeerTubeResolution[]>('getResolutions') | |
99941732 WL |
131 | } |
132 | ||
133 | /** | |
902aa3a0 | 134 | * Retrieve a list of available playback rates. |
99941732 | 135 | */ |
902aa3a0 C |
136 | async getPlaybackRates (): Promise<number[]> { |
137 | return this.sendMessage<void, number[]>('getPlaybackRates') | |
99941732 | 138 | } |
902aa3a0 | 139 | |
99941732 WL |
140 | /** |
141 | * Get the current playback rate. Defaults to 1 (1x playback rate). | |
142 | */ | |
902aa3a0 C |
143 | async getPlaybackRate (): Promise<number> { |
144 | return this.sendMessage<void, number>('getPlaybackRate') | |
99941732 WL |
145 | } |
146 | ||
147 | /** | |
148 | * Set the playback rate. Should be one of the options returned by getPlaybackRates(). | |
149 | * Passing 0.5 means half speed, 1 means normal, 2 means 2x speed, etc. | |
902aa3a0 C |
150 | * |
151 | * @param rate | |
99941732 | 152 | */ |
902aa3a0 | 153 | async setPlaybackRate (rate: number) { |
99941732 WL |
154 | await this.sendMessage('setPlaybackRate', rate) |
155 | } | |
156 | ||
902aa3a0 | 157 | private constructChannel () { |
99941732 WL |
158 | this.channel = Channel.build({ |
159 | window: this.embedElement.contentWindow, | |
160 | origin: '*', | |
161 | scope: this.scope || 'peertube' | |
162 | }) | |
163 | this.eventRegistrar.bindToChannel(this.channel) | |
164 | } | |
902aa3a0 C |
165 | |
166 | private prepareToBeReady () { | |
167 | let readyResolve: Function | |
168 | let readyReject: Function | |
169 | ||
99941732 WL |
170 | this.readyPromise = new Promise<void>((res, rej) => { |
171 | readyResolve = res | |
172 | readyReject = rej | |
173 | }) | |
902aa3a0 | 174 | |
99941732 WL |
175 | this.channel.bind('ready', success => success ? readyResolve() : readyReject()) |
176 | this.channel.call({ | |
177 | method: 'isReady', | |
178 | success: isReady => isReady ? readyResolve() : null | |
179 | }) | |
180 | } | |
181 | ||
902aa3a0 | 182 | private sendMessage<TIn, TOut> (method: string, params?: TIn): Promise<TOut> { |
99941732 WL |
183 | return new Promise<TOut>((resolve, reject) => { |
184 | this.channel.call({ | |
185 | method, params, | |
186 | success: result => resolve(result), | |
187 | error: error => reject(error) | |
188 | }) | |
189 | }) | |
190 | } | |
191 | } | |
192 | ||
193 | // put it on the window as well as the export | |
902aa3a0 | 194 | window[ 'PeerTubePlayer' ] = PeerTubePlayer |