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