aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/standalone/embed-player-api
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2023-07-10 16:08:53 +0200
committerChocobozzz <me@florianbigard.com>2023-07-10 16:08:53 +0200
commit8953f055c86ca74f145d7ac5ac93bb6104d73af9 (patch)
tree4fd67ba6c2ba32f45bcc92e931cffe2fa57c12a4 /client/src/standalone/embed-player-api
parenta1bd2b77d99cec5c27d38501f5f12f9dc339de17 (diff)
downloadPeerTube-8953f055c86ca74f145d7ac5ac93bb6104d73af9.tar.gz
PeerTube-8953f055c86ca74f145d7ac5ac93bb6104d73af9.tar.zst
PeerTube-8953f055c86ca74f145d7ac5ac93bb6104d73af9.zip
Rename player embed api
Diffstat (limited to 'client/src/standalone/embed-player-api')
-rw-r--r--client/src/standalone/embed-player-api/.npmignore3
-rw-r--r--client/src/standalone/embed-player-api/README.md3
-rw-r--r--client/src/standalone/embed-player-api/definitions.ts25
-rw-r--r--client/src/standalone/embed-player-api/events.ts48
-rw-r--r--client/src/standalone/embed-player-api/package.json28
-rw-r--r--client/src/standalone/embed-player-api/player.ts236
-rw-r--r--client/src/standalone/embed-player-api/tsconfig.json19
-rw-r--r--client/src/standalone/embed-player-api/webpack.config.js12
8 files changed, 374 insertions, 0 deletions
diff --git a/client/src/standalone/embed-player-api/.npmignore b/client/src/standalone/embed-player-api/.npmignore
new file mode 100644
index 000000000..870b6315b
--- /dev/null
+++ b/client/src/standalone/embed-player-api/.npmignore
@@ -0,0 +1,3 @@
1tsconfig.json
2*.ts
3webpack.config.ts
diff --git a/client/src/standalone/embed-player-api/README.md b/client/src/standalone/embed-player-api/README.md
new file mode 100644
index 000000000..7b47e8f02
--- /dev/null
+++ b/client/src/standalone/embed-player-api/README.md
@@ -0,0 +1,3 @@
1# @peertube/embed-api
2
3See https://docs.joinpeertube.org/api/embed-player
diff --git a/client/src/standalone/embed-player-api/definitions.ts b/client/src/standalone/embed-player-api/definitions.ts
new file mode 100644
index 000000000..495f1a98c
--- /dev/null
+++ b/client/src/standalone/embed-player-api/definitions.ts
@@ -0,0 +1,25 @@
1export type EventHandler<T> = (ev: T) => void
2
3export type PlayerEventType =
4 'pause' | 'play' |
5 'playbackStatusUpdate' |
6 'playbackStatusChange' |
7 'resolutionUpdate' |
8 'volumeChange'
9
10export interface PeerTubeResolution {
11 id: any
12 label: string
13 active: boolean
14 height: number
15
16 src?: string
17 width?: number
18}
19
20export type PeerTubeTextTrack = {
21 id: string
22 label: string
23 src: string
24 mode: TextTrackMode
25}
diff --git a/client/src/standalone/embed-player-api/events.ts b/client/src/standalone/embed-player-api/events.ts
new file mode 100644
index 000000000..77d21c78c
--- /dev/null
+++ b/client/src/standalone/embed-player-api/events.ts
@@ -0,0 +1,48 @@
1import { EventHandler } from './definitions'
2
3interface PlayerEventRegistrar {
4 registrations: EventHandler<any>[]
5}
6
7interface PlayerEventRegistrationMap {
8 [ name: string ]: PlayerEventRegistrar
9}
10
11export class EventRegistrar {
12
13 private eventRegistrations: PlayerEventRegistrationMap = {}
14
15 public bindToChannel (channel: Channel.MessagingChannel) {
16 for (const name of Object.keys(this.eventRegistrations)) {
17 channel.bind(name, (txn, params) => this.fire(name, params))
18 }
19 }
20
21 public registerTypes (names: string[]) {
22 for (const name of names) {
23 this.eventRegistrations[name] = { registrations: [] }
24 }
25 }
26
27 public fire<T> (name: string, event: T) {
28 this.eventRegistrations[name].registrations.forEach(x => x(event))
29 }
30
31 public addListener<T> (name: string, handler: EventHandler<T>) {
32 if (!this.eventRegistrations[name]) {
33 console.warn(`PeerTube: addEventListener(): The event '${name}' is not supported`)
34 return false
35 }
36
37 this.eventRegistrations[name].registrations.push(handler)
38 return true
39 }
40
41 public removeListener<T> (name: string, handler: EventHandler<T>) {
42 if (!this.eventRegistrations[name]) return false
43
44 this.eventRegistrations[name].registrations = this.eventRegistrations[name].registrations.filter(x => x !== handler)
45
46 return true
47 }
48}
diff --git a/client/src/standalone/embed-player-api/package.json b/client/src/standalone/embed-player-api/package.json
new file mode 100644
index 000000000..b549fbf52
--- /dev/null
+++ b/client/src/standalone/embed-player-api/package.json
@@ -0,0 +1,28 @@
1{
2 "name": "@peertube/embed-api",
3 "private": false,
4 "version": "0.0.6",
5 "description": "API to communicate with the PeerTube player embed",
6 "scripts": {
7 "build": "../../../node_modules/.bin/tsc && ../../../node_modules/.bin/webpack --mode production --config ./webpack.config.js"
8 },
9 "repository": {
10 "type": "git",
11 "url": "git+https://github.com/Chocobozzz/PeerTube.git"
12 },
13 "keywords": [
14 "peertube",
15 "embed"
16 ],
17 "main": "./dist/player.js",
18 "types": "./dist/player.d.ts",
19 "author": "Chocobozzz",
20 "license": "AGPL-3.0",
21 "bugs": {
22 "url": "https://github.com/Chocobozzz/PeerTube/issues"
23 },
24 "homepage": "https://github.com/Chocobozzz/PeerTube#readme",
25 "dependencies": {
26 "jschannel": "^1.0.2"
27 }
28}
diff --git a/client/src/standalone/embed-player-api/player.ts b/client/src/standalone/embed-player-api/player.ts
new file mode 100644
index 000000000..75487258b
--- /dev/null
+++ b/client/src/standalone/embed-player-api/player.ts
@@ -0,0 +1,236 @@
1import * as Channel from 'jschannel'
2import { EventHandler, PeerTubeResolution, PeerTubeTextTrack, PlayerEventType } from './definitions'
3import { EventRegistrar } from './events'
4
5const 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 */
18export class PeerTubePlayer {
19
20 private readonly 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 readonly embedElement: HTMLIFrameElement,
35 private readonly 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 *
94 * @param value A number from 0 to 1
95 */
96 async setVolume (value: number) {
97 await this.sendMessage('setVolume', value)
98 }
99
100 /**
101 * Get the current volume level in the embed.
102 *
103 * @param value A number from 0 to 1
104 */
105 async getVolume (): Promise<number> {
106 return this.sendMessage<undefined, number>('getVolume')
107 }
108
109 /**
110 * Tell the embed to change the current caption
111 *
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[]> {
122 return this.sendMessage<undefined, PeerTubeTextTrack[]>('getCaptions')
123 }
124
125 /**
126 * Tell the embed to seek to a specific position (in seconds)
127 *
128 * @param seconds
129 */
130 async seek (seconds: number) {
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.
137 *
138 * @param resolutionId The ID of the resolution as found with getResolutions()
139 */
140 async setResolution (resolutionId: any) {
141 await this.sendMessage('setResolution', resolutionId)
142 }
143
144 /**
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.
148 */
149 async getResolutions (): Promise<PeerTubeResolution[]> {
150 return this.sendMessage<undefined, PeerTubeResolution[]>('getResolutions')
151 }
152
153 /**
154 * Retrieve a list of available playback rates.
155 */
156 async getPlaybackRates (): Promise<number[]> {
157 return this.sendMessage<undefined, number[]>('getPlaybackRates')
158 }
159
160 /**
161 * Get the current playback rate. Defaults to 1 (1x playback rate).
162 */
163 async getPlaybackRate (): Promise<number> {
164 return this.sendMessage<undefined, number>('getPlaybackRate')
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.
170 *
171 * @param rate
172 */
173 async setPlaybackRate (rate: number) {
174 await this.sendMessage('setPlaybackRate', rate)
175 }
176
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 () {
195 return this.sendMessage<undefined, number>('getCurrentPosition')
196 }
197
198 private constructChannel () {
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 }
206
207 private prepareToBeReady () {
208 let readyResolve: () => void
209 let readyReject: () => void
210
211 this.readyPromise = new Promise<void>((res, rej) => {
212 readyResolve = res
213 readyReject = rej
214 })
215
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
223 private sendMessage<TIn, TOut> (method: string, params?: TIn): Promise<TOut> {
224 return new Promise<TOut>((resolve, reject) => {
225 this.channel.call({
226 method,
227 params,
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
236(window as any)['PeerTubePlayer'] = PeerTubePlayer
diff --git a/client/src/standalone/embed-player-api/tsconfig.json b/client/src/standalone/embed-player-api/tsconfig.json
new file mode 100644
index 000000000..eecc63dfb
--- /dev/null
+++ b/client/src/standalone/embed-player-api/tsconfig.json
@@ -0,0 +1,19 @@
1{
2 "compilerOptions": {
3 "module": "commonjs",
4 "removeComments": true,
5 "sourceMap": false,
6 "typeRoots": [
7 "../../../node_modules/@types"
8 ],
9 "outDir": "./dist",
10 "declaration": true,
11 "target": "es5",
12 "types": [],
13 "lib": [
14 "es2018",
15 "dom"
16 ]
17 },
18 "files": [ "./player.ts" ]
19}
diff --git a/client/src/standalone/embed-player-api/webpack.config.js b/client/src/standalone/embed-player-api/webpack.config.js
new file mode 100644
index 000000000..48d350edf
--- /dev/null
+++ b/client/src/standalone/embed-player-api/webpack.config.js
@@ -0,0 +1,12 @@
1const path = require('path')
2
3module.exports = [
4 {
5 mode: 'production',
6 entry: './dist/player.js',
7 output: {
8 filename: 'player.min.js',
9 path: path.resolve(__dirname, 'build')
10 }
11 }
12]