diff options
Diffstat (limited to 'client/src/standalone')
-rw-r--r-- | client/src/standalone/player/definitions.ts | 24 | ||||
-rw-r--r-- | client/src/standalone/player/events.ts | 56 | ||||
-rw-r--r-- | client/src/standalone/player/player.ts | 104 | ||||
-rw-r--r-- | client/src/standalone/videos/embed.ts | 120 | ||||
-rw-r--r-- | client/src/standalone/videos/test-embed.ts | 32 |
5 files changed, 165 insertions, 171 deletions
diff --git a/client/src/standalone/player/definitions.ts b/client/src/standalone/player/definitions.ts index 6920672a7..7f9ef9b6f 100644 --- a/client/src/standalone/player/definitions.ts +++ b/client/src/standalone/player/definitions.ts | |||
@@ -1,18 +1,16 @@ | |||
1 | |||
2 | export interface EventHandler<T> { | 1 | export interface EventHandler<T> { |
3 | (ev : T) : void | 2 | (ev: T): void |
4 | } | 3 | } |
5 | 4 | ||
6 | export type PlayerEventType = | 5 | export type PlayerEventType = |
7 | 'pause' | 'play' | | 6 | 'pause' | 'play' | |
8 | 'playbackStatusUpdate' | | 7 | 'playbackStatusUpdate' | |
9 | 'playbackStatusChange' | | 8 | 'playbackStatusChange' | |
10 | 'resolutionUpdate' | 9 | 'resolutionUpdate' |
11 | ; | ||
12 | 10 | ||
13 | export interface PeerTubeResolution { | 11 | export interface PeerTubeResolution { |
14 | id : any | 12 | id: any |
15 | label : string | 13 | label: string |
16 | src : string | 14 | src: string |
17 | active : boolean | 15 | active: boolean |
18 | } \ No newline at end of file | 16 | } |
diff --git a/client/src/standalone/player/events.ts b/client/src/standalone/player/events.ts index c01328352..f1639ef19 100644 --- a/client/src/standalone/player/events.ts +++ b/client/src/standalone/player/events.ts | |||
@@ -1,48 +1,48 @@ | |||
1 | import { EventHandler } from "./definitions" | 1 | import { EventHandler } from './definitions' |
2 | 2 | ||
3 | interface PlayerEventRegistrar { | 3 | interface PlayerEventRegistrar { |
4 | registrations : Function[] | 4 | registrations: Function[] |
5 | } | 5 | } |
6 | 6 | ||
7 | interface PlayerEventRegistrationMap { | 7 | interface PlayerEventRegistrationMap { |
8 | [name : string] : PlayerEventRegistrar | 8 | [ name: string ]: PlayerEventRegistrar |
9 | } | 9 | } |
10 | 10 | ||
11 | export class EventRegistrar { | 11 | export class EventRegistrar { |
12 | 12 | ||
13 | private eventRegistrations : PlayerEventRegistrationMap = {} | 13 | private eventRegistrations: PlayerEventRegistrationMap = {} |
14 | 14 | ||
15 | public bindToChannel(channel : Channel.MessagingChannel) { | 15 | public bindToChannel (channel: Channel.MessagingChannel) { |
16 | for (let name of Object.keys(this.eventRegistrations)) | 16 | for (let name of Object.keys(this.eventRegistrations)) { |
17 | channel.bind(name, (txn, params) => this.fire(name, params)) | 17 | channel.bind(name, (txn, params) => this.fire(name, params)) |
18 | } | 18 | } |
19 | } | ||
19 | 20 | ||
20 | public registerTypes(names : string[]) { | 21 | public registerTypes (names: string[]) { |
21 | for (let name of names) | 22 | for (let name of names) { |
22 | this.eventRegistrations[name] = { registrations: [] } | 23 | this.eventRegistrations[ name ] = { registrations: [] } |
23 | } | 24 | } |
25 | } | ||
24 | 26 | ||
25 | public fire<T>(name : string, event : T) { | 27 | public fire<T> (name: string, event: T) { |
26 | this.eventRegistrations[name].registrations.forEach(x => x(event)) | 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 | ||
27 | } | 35 | } |
28 | 36 | ||
29 | public addListener<T>(name : string, handler : EventHandler<T>) { | 37 | this.eventRegistrations[ name ].registrations.push(handler) |
30 | if (!this.eventRegistrations[name]) { | 38 | return true |
31 | console.warn(`PeerTube: addEventListener(): The event '${name}' is not supported`) | 39 | } |
32 | return false | ||
33 | } | ||
34 | 40 | ||
35 | this.eventRegistrations[name].registrations.push(handler) | 41 | public removeListener<T> (name: string, handler: EventHandler<T>) { |
36 | return true | 42 | if (!this.eventRegistrations[ name ]) return false |
37 | } | ||
38 | 43 | ||
39 | public removeListener<T>(name : string, handler : EventHandler<T>) { | 44 | this.eventRegistrations[ name ].registrations = this.eventRegistrations[ name ].registrations.filter(x => x === handler) |
40 | if (!this.eventRegistrations[name]) | ||
41 | return false | ||
42 | 45 | ||
43 | this.eventRegistrations[name].registrations = | 46 | return true |
44 | this.eventRegistrations[name].registrations.filter(x => x === handler) | 47 | } |
45 | |||
46 | return true | ||
47 | } | ||
48 | } | 48 | } |
diff --git a/client/src/standalone/player/player.ts b/client/src/standalone/player/player.ts index 9fc648d25..91a5e73f3 100644 --- a/client/src/standalone/player/player.ts +++ b/client/src/standalone/player/player.ts | |||
@@ -3,29 +3,35 @@ import { EventRegistrar } from './events' | |||
3 | import { EventHandler, PlayerEventType, PeerTubeResolution } from './definitions' | 3 | import { EventHandler, PlayerEventType, PeerTubeResolution } from './definitions' |
4 | 4 | ||
5 | const PASSTHROUGH_EVENTS = [ | 5 | const PASSTHROUGH_EVENTS = [ |
6 | 'pause', 'play', | 6 | 'pause', |
7 | 'play', | ||
7 | 'playbackStatusUpdate', | 8 | 'playbackStatusUpdate', |
8 | 'playbackStatusChange', | 9 | 'playbackStatusChange', |
9 | 'resolutionUpdate' | 10 | 'resolutionUpdate' |
10 | ] | 11 | ] |
11 | 12 | ||
12 | /** | 13 | /** |
13 | * Allows for programmatic control of a PeerTube embed running in an <iframe> | 14 | * Allows for programmatic control of a PeerTube embed running in an <iframe> |
14 | * within a web page. | 15 | * within a web page. |
15 | */ | 16 | */ |
16 | export class PeerTubePlayer { | 17 | export class PeerTubePlayer { |
18 | |||
19 | private eventRegistrar: EventRegistrar = new EventRegistrar() | ||
20 | private channel: Channel.MessagingChannel | ||
21 | private readyPromise: Promise<void> | ||
22 | |||
17 | /** | 23 | /** |
18 | * Construct a new PeerTubePlayer for the given PeerTube embed iframe. | 24 | * Construct a new PeerTubePlayer for the given PeerTube embed iframe. |
19 | * Optionally provide a `scope` to ensure that messages are not crossed | 25 | * Optionally provide a `scope` to ensure that messages are not crossed |
20 | * between multiple PeerTube embeds. The string passed here must match the | 26 | * between multiple PeerTube embeds. The string passed here must match the |
21 | * `scope=` query parameter on the embed URL. | 27 | * `scope=` query parameter on the embed URL. |
22 | * | 28 | * |
23 | * @param embedElement | 29 | * @param embedElement |
24 | * @param scope | 30 | * @param scope |
25 | */ | 31 | */ |
26 | constructor( | 32 | constructor ( |
27 | private embedElement : HTMLIFrameElement, | 33 | private embedElement: HTMLIFrameElement, |
28 | private scope? : string | 34 | private scope?: string |
29 | ) { | 35 | ) { |
30 | this.eventRegistrar.registerTypes(PASSTHROUGH_EVENTS) | 36 | this.eventRegistrar.registerTypes(PASSTHROUGH_EVENTS) |
31 | 37 | ||
@@ -33,55 +39,51 @@ export class PeerTubePlayer { | |||
33 | this.prepareToBeReady() | 39 | this.prepareToBeReady() |
34 | } | 40 | } |
35 | 41 | ||
36 | private eventRegistrar : EventRegistrar = new EventRegistrar() | ||
37 | private channel : Channel.MessagingChannel | ||
38 | private readyPromise : Promise<void> | ||
39 | |||
40 | /** | 42 | /** |
41 | * Destroy the player object and remove the associated player from the DOM. | 43 | * Destroy the player object and remove the associated player from the DOM. |
42 | */ | 44 | */ |
43 | destroy() { | 45 | destroy () { |
44 | this.embedElement.remove() | 46 | this.embedElement.remove() |
45 | } | 47 | } |
46 | 48 | ||
47 | /** | 49 | /** |
48 | * Listen to an event emitted by this player. | 50 | * Listen to an event emitted by this player. |
49 | * | 51 | * |
50 | * @param event One of the supported event types | 52 | * @param event One of the supported event types |
51 | * @param handler A handler which will be passed an event object (or undefined if no event object is included) | 53 | * @param handler A handler which will be passed an event object (or undefined if no event object is included) |
52 | */ | 54 | */ |
53 | addEventListener(event : PlayerEventType, handler : EventHandler<any>): boolean { | 55 | addEventListener (event: PlayerEventType, handler: EventHandler<any>): boolean { |
54 | return this.eventRegistrar.addListener(event, handler) | 56 | return this.eventRegistrar.addListener(event, handler) |
55 | } | 57 | } |
56 | 58 | ||
57 | /** | 59 | /** |
58 | * Remove an event listener previously added with addEventListener(). | 60 | * Remove an event listener previously added with addEventListener(). |
59 | * | 61 | * |
60 | * @param event The name of the event previously listened to | 62 | * @param event The name of the event previously listened to |
61 | * @param handler | 63 | * @param handler |
62 | */ | 64 | */ |
63 | removeEventListener(event : PlayerEventType, handler : EventHandler<any>): boolean { | 65 | removeEventListener (event: PlayerEventType, handler: EventHandler<any>): boolean { |
64 | return this.eventRegistrar.removeListener(event, handler) | 66 | return this.eventRegistrar.removeListener(event, handler) |
65 | } | 67 | } |
66 | 68 | ||
67 | /** | 69 | /** |
68 | * Promise resolves when the player is ready. | 70 | * Promise resolves when the player is ready. |
69 | */ | 71 | */ |
70 | get ready(): Promise<void> { | 72 | get ready (): Promise<void> { |
71 | return this.readyPromise | 73 | return this.readyPromise |
72 | } | 74 | } |
73 | 75 | ||
74 | /** | 76 | /** |
75 | * Tell the embed to start/resume playback | 77 | * Tell the embed to start/resume playback |
76 | */ | 78 | */ |
77 | async play() { | 79 | async play () { |
78 | await this.sendMessage('play') | 80 | await this.sendMessage('play') |
79 | } | 81 | } |
80 | 82 | ||
81 | /** | 83 | /** |
82 | * Tell the embed to pause playback. | 84 | * Tell the embed to pause playback. |
83 | */ | 85 | */ |
84 | async pause() { | 86 | async pause () { |
85 | await this.sendMessage('pause') | 87 | await this.sendMessage('pause') |
86 | } | 88 | } |
87 | 89 | ||
@@ -89,7 +91,7 @@ export class PeerTubePlayer { | |||
89 | * Tell the embed to change the audio volume | 91 | * Tell the embed to change the audio volume |
90 | * @param value A number from 0 to 1 | 92 | * @param value A number from 0 to 1 |
91 | */ | 93 | */ |
92 | async setVolume(value : number) { | 94 | async setVolume (value: number) { |
93 | await this.sendMessage('setVolume', value) | 95 | await this.sendMessage('setVolume', value) |
94 | } | 96 | } |
95 | 97 | ||
@@ -97,62 +99,62 @@ export class PeerTubePlayer { | |||
97 | * Get the current volume level in the embed. | 99 | * Get the current volume level in the embed. |
98 | * @param value A number from 0 to 1 | 100 | * @param value A number from 0 to 1 |
99 | */ | 101 | */ |
100 | async getVolume(): Promise<number> { | 102 | async getVolume (): Promise<number> { |
101 | return await this.sendMessage<void, number>('setVolume') | 103 | return this.sendMessage<void, number>('setVolume') |
102 | } | 104 | } |
103 | 105 | ||
104 | /** | 106 | /** |
105 | * Tell the embed to seek to a specific position (in seconds) | 107 | * Tell the embed to seek to a specific position (in seconds) |
106 | * @param seconds | 108 | * @param seconds |
107 | */ | 109 | */ |
108 | async seek(seconds : number) { | 110 | async seek (seconds: number) { |
109 | await this.sendMessage('seek', seconds) | 111 | await this.sendMessage('seek', seconds) |
110 | } | 112 | } |
111 | 113 | ||
112 | /** | 114 | /** |
113 | * Tell the embed to switch resolutions to the resolution identified | 115 | * Tell the embed to switch resolutions to the resolution identified |
114 | * by the given ID. | 116 | * by the given ID. |
115 | * | 117 | * |
116 | * @param resolutionId The ID of the resolution as found with getResolutions() | 118 | * @param resolutionId The ID of the resolution as found with getResolutions() |
117 | */ | 119 | */ |
118 | async setResolution(resolutionId : any) { | 120 | async setResolution (resolutionId: any) { |
119 | await this.sendMessage('setResolution', resolutionId) | 121 | await this.sendMessage('setResolution', resolutionId) |
120 | } | 122 | } |
121 | 123 | ||
122 | /** | 124 | /** |
123 | * Retrieve a list of the available resolutions. This may change later, listen to the | 125 | * Retrieve a list of the available resolutions. This may change later, listen to the |
124 | * `resolutionUpdate` event with `addEventListener` in order to be updated as the available | 126 | * `resolutionUpdate` event with `addEventListener` in order to be updated as the available |
125 | * resolutions change. | 127 | * resolutions change. |
126 | */ | 128 | */ |
127 | async getResolutions(): Promise<PeerTubeResolution[]> { | 129 | async getResolutions (): Promise<PeerTubeResolution[]> { |
128 | return await this.sendMessage<void, PeerTubeResolution[]>('getResolutions') | 130 | return this.sendMessage<void, PeerTubeResolution[]>('getResolutions') |
129 | } | 131 | } |
130 | 132 | ||
131 | /** | 133 | /** |
132 | * Retrieve a list of available playback rates. | 134 | * Retrieve a list of available playback rates. |
133 | */ | 135 | */ |
134 | async getPlaybackRates() : Promise<number[]> { | 136 | async getPlaybackRates (): Promise<number[]> { |
135 | return await this.sendMessage<void, number[]>('getPlaybackRates') | 137 | return this.sendMessage<void, number[]>('getPlaybackRates') |
136 | } | 138 | } |
137 | 139 | ||
138 | /** | 140 | /** |
139 | * Get the current playback rate. Defaults to 1 (1x playback rate). | 141 | * Get the current playback rate. Defaults to 1 (1x playback rate). |
140 | */ | 142 | */ |
141 | async getPlaybackRate() : Promise<number> { | 143 | async getPlaybackRate (): Promise<number> { |
142 | return await this.sendMessage<void, number>('getPlaybackRate') | 144 | return this.sendMessage<void, number>('getPlaybackRate') |
143 | } | 145 | } |
144 | 146 | ||
145 | /** | 147 | /** |
146 | * Set the playback rate. Should be one of the options returned by getPlaybackRates(). | 148 | * Set the playback rate. Should be one of the options returned by getPlaybackRates(). |
147 | * Passing 0.5 means half speed, 1 means normal, 2 means 2x speed, etc. | 149 | * Passing 0.5 means half speed, 1 means normal, 2 means 2x speed, etc. |
148 | * | 150 | * |
149 | * @param rate | 151 | * @param rate |
150 | */ | 152 | */ |
151 | async setPlaybackRate(rate : number) { | 153 | async setPlaybackRate (rate: number) { |
152 | await this.sendMessage('setPlaybackRate', rate) | 154 | await this.sendMessage('setPlaybackRate', rate) |
153 | } | 155 | } |
154 | 156 | ||
155 | private constructChannel() { | 157 | private constructChannel () { |
156 | this.channel = Channel.build({ | 158 | this.channel = Channel.build({ |
157 | window: this.embedElement.contentWindow, | 159 | window: this.embedElement.contentWindow, |
158 | origin: '*', | 160 | origin: '*', |
@@ -160,14 +162,16 @@ export class PeerTubePlayer { | |||
160 | }) | 162 | }) |
161 | this.eventRegistrar.bindToChannel(this.channel) | 163 | this.eventRegistrar.bindToChannel(this.channel) |
162 | } | 164 | } |
163 | 165 | ||
164 | private prepareToBeReady() { | 166 | private prepareToBeReady () { |
165 | let readyResolve, readyReject | 167 | let readyResolve: Function |
168 | let readyReject: Function | ||
169 | |||
166 | this.readyPromise = new Promise<void>((res, rej) => { | 170 | this.readyPromise = new Promise<void>((res, rej) => { |
167 | readyResolve = res | 171 | readyResolve = res |
168 | readyReject = rej | 172 | readyReject = rej |
169 | }) | 173 | }) |
170 | 174 | ||
171 | this.channel.bind('ready', success => success ? readyResolve() : readyReject()) | 175 | this.channel.bind('ready', success => success ? readyResolve() : readyReject()) |
172 | this.channel.call({ | 176 | this.channel.call({ |
173 | method: 'isReady', | 177 | method: 'isReady', |
@@ -175,7 +179,7 @@ export class PeerTubePlayer { | |||
175 | }) | 179 | }) |
176 | } | 180 | } |
177 | 181 | ||
178 | private sendMessage<TIn, TOut>(method : string, params? : TIn): Promise<TOut> { | 182 | private sendMessage<TIn, TOut> (method: string, params?: TIn): Promise<TOut> { |
179 | return new Promise<TOut>((resolve, reject) => { | 183 | return new Promise<TOut>((resolve, reject) => { |
180 | this.channel.call({ | 184 | this.channel.call({ |
181 | method, params, | 185 | method, params, |
@@ -187,4 +191,4 @@ export class PeerTubePlayer { | |||
187 | } | 191 | } |
188 | 192 | ||
189 | // put it on the window as well as the export | 193 | // put it on the window as well as the export |
190 | window['PeerTubePlayer'] = PeerTubePlayer \ No newline at end of file | 194 | window[ 'PeerTubePlayer' ] = PeerTubePlayer |
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index e9baf64d0..a4196600a 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts | |||
@@ -22,23 +22,21 @@ import * as Channel from 'jschannel' | |||
22 | 22 | ||
23 | import { VideoDetails } from '../../../../shared' | 23 | import { VideoDetails } from '../../../../shared' |
24 | import { addContextMenu, getVideojsOptions, loadLocale } from '../../assets/player/peertube-player' | 24 | import { addContextMenu, getVideojsOptions, loadLocale } from '../../assets/player/peertube-player' |
25 | import { PeerTubeResolution } from '../player/definitions'; | 25 | import { PeerTubeResolution } from '../player/definitions' |
26 | 26 | ||
27 | /** | 27 | /** |
28 | * Embed API exposes control of the embed player to the outside world via | 28 | * Embed API exposes control of the embed player to the outside world via |
29 | * JSChannels and window.postMessage | 29 | * JSChannels and window.postMessage |
30 | */ | 30 | */ |
31 | class PeerTubeEmbedApi { | 31 | class PeerTubeEmbedApi { |
32 | constructor( | 32 | private channel: Channel.MessagingChannel |
33 | private embed : PeerTubeEmbed | ||
34 | ) { | ||
35 | } | ||
36 | |||
37 | private channel : Channel.MessagingChannel | ||
38 | private isReady = false | 33 | private isReady = false |
39 | private resolutions : PeerTubeResolution[] = null | 34 | private resolutions: PeerTubeResolution[] = null |
35 | |||
36 | constructor (private embed: PeerTubeEmbed) { | ||
37 | } | ||
40 | 38 | ||
41 | initialize() { | 39 | initialize () { |
42 | this.constructChannel() | 40 | this.constructChannel() |
43 | this.setupStateTracking() | 41 | this.setupStateTracking() |
44 | 42 | ||
@@ -46,14 +44,14 @@ class PeerTubeEmbedApi { | |||
46 | 44 | ||
47 | this.notifyReady() | 45 | this.notifyReady() |
48 | } | 46 | } |
49 | 47 | ||
50 | private get element() { | 48 | private get element () { |
51 | return this.embed.videoElement | 49 | return this.embed.videoElement |
52 | } | 50 | } |
53 | 51 | ||
54 | private constructChannel() { | 52 | private constructChannel () { |
55 | let channel = Channel.build({ window: window.parent, origin: '*', scope: this.embed.scope }) | 53 | let channel = Channel.build({ window: window.parent, origin: '*', scope: this.embed.scope }) |
56 | 54 | ||
57 | channel.bind('play', (txn, params) => this.embed.player.play()) | 55 | channel.bind('play', (txn, params) => this.embed.player.play()) |
58 | channel.bind('pause', (txn, params) => this.embed.player.pause()) | 56 | channel.bind('pause', (txn, params) => this.embed.player.pause()) |
59 | channel.bind('seek', (txn, time) => this.embed.player.currentTime(time)) | 57 | channel.bind('seek', (txn, time) => this.embed.player.currentTime(time)) |
@@ -69,9 +67,8 @@ class PeerTubeEmbedApi { | |||
69 | this.channel = channel | 67 | this.channel = channel |
70 | } | 68 | } |
71 | 69 | ||
72 | private setResolution(resolutionId : number) { | 70 | private setResolution (resolutionId: number) { |
73 | if (resolutionId === -1 && this.embed.player.peertube().isAutoResolutionForbidden()) | 71 | if (resolutionId === -1 && this.embed.player.peertube().isAutoResolutionForbidden()) return |
74 | return | ||
75 | 72 | ||
76 | // Auto resolution | 73 | // Auto resolution |
77 | if (resolutionId === -1) { | 74 | if (resolutionId === -1) { |
@@ -86,14 +83,13 @@ class PeerTubeEmbedApi { | |||
86 | /** | 83 | /** |
87 | * Let the host know that we're ready to go! | 84 | * Let the host know that we're ready to go! |
88 | */ | 85 | */ |
89 | private notifyReady() { | 86 | private notifyReady () { |
90 | this.isReady = true | 87 | this.isReady = true |
91 | this.channel.notify({ method: 'ready', params: true }) | 88 | this.channel.notify({ method: 'ready', params: true }) |
92 | } | 89 | } |
93 | 90 | ||
94 | private setupStateTracking() { | 91 | private setupStateTracking () { |
95 | 92 | let currentState: 'playing' | 'paused' | 'unstarted' = 'unstarted' | |
96 | let currentState : 'playing' | 'paused' | 'unstarted' = 'unstarted' | ||
97 | 93 | ||
98 | setInterval(() => { | 94 | setInterval(() => { |
99 | let position = this.element.currentTime | 95 | let position = this.element.currentTime |
@@ -104,7 +100,7 @@ class PeerTubeEmbedApi { | |||
104 | params: { | 100 | params: { |
105 | position, | 101 | position, |
106 | volume, | 102 | volume, |
107 | playbackState: currentState, | 103 | playbackState: currentState |
108 | } | 104 | } |
109 | }) | 105 | }) |
110 | }, 500) | 106 | }, 500) |
@@ -125,7 +121,7 @@ class PeerTubeEmbedApi { | |||
125 | this.embed.player.peertube().on('videoFileUpdate', () => this.loadResolutions()) | 121 | this.embed.player.peertube().on('videoFileUpdate', () => this.loadResolutions()) |
126 | } | 122 | } |
127 | 123 | ||
128 | private loadResolutions() { | 124 | private loadResolutions () { |
129 | let resolutions = [] | 125 | let resolutions = [] |
130 | let currentResolutionId = this.embed.player.peertube().getCurrentResolutionId() | 126 | let currentResolutionId = this.embed.player.peertube().getCurrentResolutionId() |
131 | 127 | ||
@@ -152,30 +148,28 @@ class PeerTubeEmbedApi { | |||
152 | } | 148 | } |
153 | 149 | ||
154 | class PeerTubeEmbed { | 150 | class PeerTubeEmbed { |
155 | constructor( | 151 | videoElement: HTMLVideoElement |
156 | private videoContainerId : string | 152 | player: any |
157 | ) { | 153 | playerOptions: any |
158 | this.videoElement = document.getElementById(videoContainerId) as HTMLVideoElement | 154 | api: PeerTubeEmbedApi = null |
159 | } | 155 | autoplay = false |
160 | 156 | controls = true | |
161 | videoElement : HTMLVideoElement | 157 | muted = false |
162 | player : any | 158 | loop = false |
163 | playerOptions : any | 159 | enableApi = false |
164 | api : PeerTubeEmbedApi = null | 160 | startTime = 0 |
165 | autoplay : boolean = false | 161 | scope = 'peertube' |
166 | controls : boolean = true | 162 | |
167 | muted : boolean = false | 163 | static async main () { |
168 | loop : boolean = false | ||
169 | enableApi : boolean = false | ||
170 | startTime : number = 0 | ||
171 | scope : string = 'peertube' | ||
172 | |||
173 | static async main() { | ||
174 | const videoContainerId = 'video-container' | 164 | const videoContainerId = 'video-container' |
175 | const embed = new PeerTubeEmbed(videoContainerId) | 165 | const embed = new PeerTubeEmbed(videoContainerId) |
176 | await embed.init() | 166 | await embed.init() |
177 | } | 167 | } |
178 | 168 | ||
169 | constructor (private videoContainerId: string) { | ||
170 | this.videoElement = document.getElementById(videoContainerId) as HTMLVideoElement | ||
171 | } | ||
172 | |||
179 | getVideoUrl (id: string) { | 173 | getVideoUrl (id: string) { |
180 | return window.location.origin + '/api/v1/videos/' + id | 174 | return window.location.origin + '/api/v1/videos/' + id |
181 | } | 175 | } |
@@ -219,15 +213,7 @@ class PeerTubeEmbed { | |||
219 | return params.has(name) ? params.get(name) : defaultValue | 213 | return params.has(name) ? params.get(name) : defaultValue |
220 | } | 214 | } |
221 | 215 | ||
222 | private initializeApi() { | 216 | async init () { |
223 | if (!this.enableApi) | ||
224 | return | ||
225 | |||
226 | this.api = new PeerTubeEmbedApi(this) | ||
227 | this.api.initialize() | ||
228 | } | ||
229 | |||
230 | async init() { | ||
231 | try { | 217 | try { |
232 | await this.initCore() | 218 | await this.initCore() |
233 | } catch (e) { | 219 | } catch (e) { |
@@ -235,7 +221,14 @@ class PeerTubeEmbed { | |||
235 | } | 221 | } |
236 | } | 222 | } |
237 | 223 | ||
238 | private loadParams() { | 224 | private initializeApi () { |
225 | if (!this.enableApi) return | ||
226 | |||
227 | this.api = new PeerTubeEmbedApi(this) | ||
228 | this.api.initialize() | ||
229 | } | ||
230 | |||
231 | private loadParams () { | ||
239 | try { | 232 | try { |
240 | let params = new URL(window.location.toString()).searchParams | 233 | let params = new URL(window.location.toString()).searchParams |
241 | 234 | ||
@@ -248,24 +241,23 @@ class PeerTubeEmbed { | |||
248 | 241 | ||
249 | const startTimeParamString = params.get('start') | 242 | const startTimeParamString = params.get('start') |
250 | const startTimeParamNumber = parseInt(startTimeParamString, 10) | 243 | const startTimeParamNumber = parseInt(startTimeParamString, 10) |
251 | if (isNaN(startTimeParamNumber) === false) | 244 | |
252 | this.startTime = startTimeParamNumber | 245 | if (isNaN(startTimeParamNumber) === false) this.startTime = startTimeParamNumber |
253 | } catch (err) { | 246 | } catch (err) { |
254 | console.error('Cannot get params from URL.', err) | 247 | console.error('Cannot get params from URL.', err) |
255 | } | 248 | } |
256 | } | 249 | } |
257 | 250 | ||
258 | private async initCore() { | 251 | private async initCore () { |
259 | const urlParts = window.location.href.split('/') | 252 | const urlParts = window.location.href.split('/') |
260 | const lastPart = urlParts[urlParts.length - 1] | 253 | const lastPart = urlParts[ urlParts.length - 1 ] |
261 | const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[0] | 254 | const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[ 0 ] |
262 | 255 | ||
263 | await loadLocale(window.location.origin, vjs, navigator.language) | 256 | await loadLocale(window.location.origin, vjs, navigator.language) |
264 | let response = await this.loadVideoInfo(videoId) | 257 | let response = await this.loadVideoInfo(videoId) |
265 | 258 | ||
266 | if (!response.ok) { | 259 | if (!response.ok) { |
267 | if (response.status === 404) | 260 | if (response.status === 404) return this.videoNotFound(this.videoElement) |
268 | return this.videoNotFound(this.videoElement) | ||
269 | 261 | ||
270 | return this.videoFetchError(this.videoElement) | 262 | return this.videoFetchError(this.videoElement) |
271 | } | 263 | } |
@@ -279,7 +271,7 @@ class PeerTubeEmbed { | |||
279 | controls: this.controls, | 271 | controls: this.controls, |
280 | muted: this.muted, | 272 | muted: this.muted, |
281 | loop: this.loop, | 273 | loop: this.loop, |
282 | startTime : this.startTime, | 274 | startTime: this.startTime, |
283 | 275 | ||
284 | inactivityTimeout: 1500, | 276 | inactivityTimeout: 1500, |
285 | videoViewUrl: this.getVideoUrl(videoId) + '/views', | 277 | videoViewUrl: this.getVideoUrl(videoId) + '/views', |
@@ -295,14 +287,15 @@ class PeerTubeEmbed { | |||
295 | this.playerOptions = videojsOptions | 287 | this.playerOptions = videojsOptions |
296 | this.player = vjs(this.videoContainerId, videojsOptions, () => { | 288 | this.player = vjs(this.videoContainerId, videojsOptions, () => { |
297 | 289 | ||
298 | window['videojsPlayer'] = this.player | 290 | window[ 'videojsPlayer' ] = this.player |
299 | 291 | ||
300 | if (this.controls) { | 292 | if (this.controls) { |
301 | (this.player as any).dock({ | 293 | this.player.dock({ |
302 | title: videoInfo.name, | 294 | title: videoInfo.name, |
303 | description: this.player.localize('Uses P2P, others may know your IP is downloading this video.') | 295 | description: this.player.localize('Uses P2P, others may know your IP is downloading this video.') |
304 | }) | 296 | }) |
305 | } | 297 | } |
298 | |||
306 | addContextMenu(this.player, window.location.origin + videoInfo.embedPath) | 299 | addContextMenu(this.player, window.location.origin + videoInfo.embedPath) |
307 | this.initializeApi() | 300 | this.initializeApi() |
308 | }) | 301 | }) |
@@ -310,3 +303,4 @@ class PeerTubeEmbed { | |||
310 | } | 303 | } |
311 | 304 | ||
312 | PeerTubeEmbed.main() | 305 | PeerTubeEmbed.main() |
306 | .catch(err => console.error('Cannot init embed.', err)) | ||
diff --git a/client/src/standalone/videos/test-embed.ts b/client/src/standalone/videos/test-embed.ts index 721514488..c0181ed28 100644 --- a/client/src/standalone/videos/test-embed.ts +++ b/client/src/standalone/videos/test-embed.ts | |||
@@ -1,49 +1,47 @@ | |||
1 | import './test-embed.scss' | 1 | import './test-embed.scss' |
2 | import { PeerTubePlayer } from '../player/player'; | 2 | import { PeerTubePlayer } from '../player/player' |
3 | import { PlayerEventType } from '../player/definitions'; | 3 | import { PlayerEventType } from '../player/definitions' |
4 | 4 | ||
5 | window.addEventListener('load', async () => { | 5 | window.addEventListener('load', async () => { |
6 | |||
7 | const urlParts = window.location.href.split('/') | 6 | const urlParts = window.location.href.split('/') |
8 | const lastPart = urlParts[urlParts.length - 1] | 7 | const lastPart = urlParts[ urlParts.length - 1 ] |
9 | const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[0] | 8 | const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[ 0 ] |
10 | 9 | ||
11 | let iframe = document.createElement('iframe') | 10 | let iframe = document.createElement('iframe') |
12 | iframe.src = `/videos/embed/${videoId}?autoplay=1&controls=0&api=1` | 11 | iframe.src = `/videos/embed/${videoId}?autoplay=1&controls=0&api=1` |
13 | let mainElement = document.querySelector('#host') | 12 | let mainElement = document.querySelector('#host') |
14 | mainElement.appendChild(iframe); | 13 | mainElement.appendChild(iframe) |
15 | 14 | ||
16 | console.log(`Document finished loading.`) | 15 | console.log(`Document finished loading.`) |
17 | let player = new PeerTubePlayer(document.querySelector('iframe')) | 16 | let player = new PeerTubePlayer(document.querySelector('iframe')) |
18 | 17 | ||
19 | window['player'] = player | 18 | window[ 'player' ] = player |
20 | 19 | ||
21 | console.log(`Awaiting player ready...`) | 20 | console.log(`Awaiting player ready...`) |
22 | await player.ready | 21 | await player.ready |
23 | console.log(`Player is ready.`) | 22 | console.log(`Player is ready.`) |
24 | 23 | ||
25 | let monitoredEvents = [ | 24 | let monitoredEvents = [ |
26 | 'pause', 'play', | 25 | 'pause', |
27 | 'playbackStatusUpdate', | 26 | 'play', |
27 | 'playbackStatusUpdate', | ||
28 | 'playbackStatusChange' | 28 | 'playbackStatusChange' |
29 | ] | 29 | ] |
30 | 30 | ||
31 | monitoredEvents.forEach(e => { | 31 | monitoredEvents.forEach(e => { |
32 | player.addEventListener(<PlayerEventType>e, () => console.log(`PLAYER: event '${e}' received`)) | 32 | player.addEventListener(e as PlayerEventType, () => console.log(`PLAYER: event '${e}' received`)) |
33 | console.log(`PLAYER: now listening for event '${e}'`) | 33 | console.log(`PLAYER: now listening for event '${e}'`) |
34 | }) | 34 | }) |
35 | 35 | ||
36 | let playbackRates = [] | 36 | let playbackRates: number[] = [] |
37 | let activeRate = 1 | ||
38 | let currentRate = await player.getPlaybackRate() | 37 | let currentRate = await player.getPlaybackRate() |
39 | 38 | ||
40 | let updateRates = async () => { | 39 | let updateRates = async () => { |
41 | |||
42 | let rateListEl = document.querySelector('#rate-list') | 40 | let rateListEl = document.querySelector('#rate-list') |
43 | rateListEl.innerHTML = '' | 41 | rateListEl.innerHTML = '' |
44 | 42 | ||
45 | playbackRates.forEach(rate => { | 43 | playbackRates.forEach(rate => { |
46 | if (currentRate == rate) { | 44 | if (currentRate === rate) { |
47 | let itemEl = document.createElement('strong') | 45 | let itemEl = document.createElement('strong') |
48 | itemEl.innerText = `${rate} (active)` | 46 | itemEl.innerText = `${rate} (active)` |
49 | itemEl.style.display = 'block' | 47 | itemEl.style.display = 'block' |
@@ -93,6 +91,6 @@ window.addEventListener('load', async () => { | |||
93 | 91 | ||
94 | player.getResolutions().then( | 92 | player.getResolutions().then( |
95 | resolutions => updateResolutions(resolutions)) | 93 | resolutions => updateResolutions(resolutions)) |
96 | player.addEventListener('resolutionUpdate', | 94 | player.addEventListener('resolutionUpdate', |
97 | resolutions => updateResolutions(resolutions)) | 95 | resolutions => updateResolutions(resolutions)) |
98 | }) \ No newline at end of file | 96 | }) |