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