diff options
Diffstat (limited to 'client/src/standalone')
-rw-r--r-- | client/src/standalone/videos/embed-api.ts | 46 | ||||
-rw-r--r-- | client/src/standalone/videos/embed.ts | 29 | ||||
-rw-r--r-- | client/src/standalone/videos/shared/auth-http.ts | 30 | ||||
-rw-r--r-- | client/src/standalone/videos/shared/peertube-plugin.ts | 1 | ||||
-rw-r--r-- | client/src/standalone/videos/shared/player-manager-options.ts | 12 | ||||
-rw-r--r-- | client/src/standalone/videos/shared/video-fetcher.ts | 17 |
6 files changed, 100 insertions, 35 deletions
diff --git a/client/src/standalone/videos/embed-api.ts b/client/src/standalone/videos/embed-api.ts index 2124b4711..a99f1edae 100644 --- a/client/src/standalone/videos/embed-api.ts +++ b/client/src/standalone/videos/embed-api.ts | |||
@@ -13,6 +13,12 @@ export class PeerTubeEmbedApi { | |||
13 | private isReady = false | 13 | private isReady = false |
14 | private resolutions: PeerTubeResolution[] = [] | 14 | private resolutions: PeerTubeResolution[] = [] |
15 | 15 | ||
16 | private oldVideoElement: HTMLVideoElement | ||
17 | private videoElPlayListener: () => void | ||
18 | private videoElPauseListener: () => void | ||
19 | private videoElEndedListener: () => void | ||
20 | private videoElInterval: any | ||
21 | |||
16 | constructor (private readonly embed: PeerTubeEmbed) { | 22 | constructor (private readonly embed: PeerTubeEmbed) { |
17 | 23 | ||
18 | } | 24 | } |
@@ -26,6 +32,11 @@ export class PeerTubeEmbedApi { | |||
26 | this.notifyReady() | 32 | this.notifyReady() |
27 | } | 33 | } |
28 | 34 | ||
35 | reInit () { | ||
36 | this.disposeStateTracking() | ||
37 | this.setupStateTracking() | ||
38 | } | ||
39 | |||
29 | private get element () { | 40 | private get element () { |
30 | return this.embed.getPlayerElement() | 41 | return this.embed.getPlayerElement() |
31 | } | 42 | } |
@@ -101,7 +112,7 @@ export class PeerTubeEmbedApi { | |||
101 | private setupStateTracking () { | 112 | private setupStateTracking () { |
102 | let currentState: 'playing' | 'paused' | 'unstarted' | 'ended' = 'unstarted' | 113 | let currentState: 'playing' | 'paused' | 'unstarted' | 'ended' = 'unstarted' |
103 | 114 | ||
104 | setInterval(() => { | 115 | this.videoElInterval = setInterval(() => { |
105 | const position = this.element.currentTime | 116 | const position = this.element.currentTime |
106 | const volume = this.element.volume | 117 | const volume = this.element.volume |
107 | 118 | ||
@@ -116,20 +127,29 @@ export class PeerTubeEmbedApi { | |||
116 | }) | 127 | }) |
117 | }, 500) | 128 | }, 500) |
118 | 129 | ||
119 | this.element.addEventListener('play', ev => { | 130 | // --------------------------------------------------------------------------- |
131 | |||
132 | this.videoElPlayListener = () => { | ||
120 | currentState = 'playing' | 133 | currentState = 'playing' |
121 | this.channel.notify({ method: 'playbackStatusChange', params: 'playing' }) | 134 | this.channel.notify({ method: 'playbackStatusChange', params: 'playing' }) |
122 | }) | 135 | } |
136 | this.element.addEventListener('play', this.videoElPlayListener) | ||
123 | 137 | ||
124 | this.element.addEventListener('pause', ev => { | 138 | this.videoElPauseListener = () => { |
125 | currentState = 'paused' | 139 | currentState = 'paused' |
126 | this.channel.notify({ method: 'playbackStatusChange', params: 'paused' }) | 140 | this.channel.notify({ method: 'playbackStatusChange', params: 'paused' }) |
127 | }) | 141 | } |
142 | this.element.addEventListener('pause', this.videoElPauseListener) | ||
128 | 143 | ||
129 | this.element.addEventListener('ended', ev => { | 144 | this.videoElEndedListener = () => { |
130 | currentState = 'ended' | 145 | currentState = 'ended' |
131 | this.channel.notify({ method: 'playbackStatusChange', params: 'ended' }) | 146 | this.channel.notify({ method: 'playbackStatusChange', params: 'ended' }) |
132 | }) | 147 | } |
148 | this.element.addEventListener('ended', this.videoElEndedListener) | ||
149 | |||
150 | this.oldVideoElement = this.element | ||
151 | |||
152 | // --------------------------------------------------------------------------- | ||
133 | 153 | ||
134 | // PeerTube specific capabilities | 154 | // PeerTube specific capabilities |
135 | this.embed.player.peertubeResolutions().on('resolutionsAdded', () => this.loadResolutions()) | 155 | this.embed.player.peertubeResolutions().on('resolutionsAdded', () => this.loadResolutions()) |
@@ -145,6 +165,18 @@ export class PeerTubeEmbedApi { | |||
145 | }) | 165 | }) |
146 | } | 166 | } |
147 | 167 | ||
168 | private disposeStateTracking () { | ||
169 | if (!this.oldVideoElement) return | ||
170 | |||
171 | this.oldVideoElement.removeEventListener('play', this.videoElPlayListener) | ||
172 | this.oldVideoElement.removeEventListener('pause', this.videoElPauseListener) | ||
173 | this.oldVideoElement.removeEventListener('ended', this.videoElEndedListener) | ||
174 | |||
175 | clearInterval(this.videoElInterval) | ||
176 | |||
177 | this.oldVideoElement = undefined | ||
178 | } | ||
179 | |||
148 | private loadResolutions () { | 180 | private loadResolutions () { |
149 | this.resolutions = this.embed.player.peertubeResolutions().getResolutions() | 181 | this.resolutions = this.embed.player.peertubeResolutions().getResolutions() |
150 | .map(r => ({ | 182 | .map(r => ({ |
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index 451e54840..356f149c0 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts | |||
@@ -3,10 +3,10 @@ import '../../assets/player/shared/dock/peertube-dock-component' | |||
3 | import '../../assets/player/shared/dock/peertube-dock-plugin' | 3 | import '../../assets/player/shared/dock/peertube-dock-plugin' |
4 | import videojs from 'video.js' | 4 | import videojs from 'video.js' |
5 | import { peertubeTranslate } from '../../../../shared/core-utils/i18n' | 5 | import { peertubeTranslate } from '../../../../shared/core-utils/i18n' |
6 | import { HTMLServerConfig, LiveVideo, ResultList, VideoDetails, VideoPlaylist, VideoPlaylistElement } from '../../../../shared/models' | 6 | import { HTMLServerConfig, ResultList, VideoDetails, VideoPlaylist, VideoPlaylistElement } from '../../../../shared/models' |
7 | import { PeertubePlayerManager } from '../../assets/player' | 7 | import { PeertubePlayerManager } from '../../assets/player' |
8 | import { TranslationsManager } from '../../assets/player/translations-manager' | 8 | import { TranslationsManager } from '../../assets/player/translations-manager' |
9 | import { getParamString, logger } from '../../root-helpers' | 9 | import { getParamString, logger, videoRequiresAuth } from '../../root-helpers' |
10 | import { PeerTubeEmbedApi } from './embed-api' | 10 | import { PeerTubeEmbedApi } from './embed-api' |
11 | import { AuthHTTP, LiveManager, PeerTubePlugin, PlayerManagerOptions, PlaylistFetcher, PlaylistTracker, VideoFetcher } from './shared' | 11 | import { AuthHTTP, LiveManager, PeerTubePlugin, PlayerManagerOptions, PlaylistFetcher, PlaylistTracker, VideoFetcher } from './shared' |
12 | import { PlayerHTML } from './shared/player-html' | 12 | import { PlayerHTML } from './shared/player-html' |
@@ -117,6 +117,11 @@ export class PeerTubeEmbed { | |||
117 | 117 | ||
118 | private initializeApi () { | 118 | private initializeApi () { |
119 | if (this.playerManagerOptions.hasAPIEnabled()) { | 119 | if (this.playerManagerOptions.hasAPIEnabled()) { |
120 | if (this.api) { | ||
121 | this.api.reInit() | ||
122 | return | ||
123 | } | ||
124 | |||
120 | this.api = new PeerTubeEmbedApi(this) | 125 | this.api = new PeerTubeEmbedApi(this) |
121 | this.api.initialize() | 126 | this.api.initialize() |
122 | } | 127 | } |
@@ -167,22 +172,25 @@ export class PeerTubeEmbed { | |||
167 | private async buildVideoPlayer (videoResponse: Response, captionsPromise: Promise<Response>) { | 172 | private async buildVideoPlayer (videoResponse: Response, captionsPromise: Promise<Response>) { |
168 | const alreadyHadPlayer = this.resetPlayerElement() | 173 | const alreadyHadPlayer = this.resetPlayerElement() |
169 | 174 | ||
170 | const videoInfoPromise: Promise<{ video: VideoDetails, live?: LiveVideo }> = videoResponse.json() | 175 | const videoInfoPromise = videoResponse.json() |
171 | .then((videoInfo: VideoDetails) => { | 176 | .then(async (videoInfo: VideoDetails) => { |
172 | this.playerManagerOptions.loadParams(this.config, videoInfo) | 177 | this.playerManagerOptions.loadParams(this.config, videoInfo) |
173 | 178 | ||
174 | if (!alreadyHadPlayer && !this.playerManagerOptions.hasAutoplay()) { | 179 | if (!alreadyHadPlayer && !this.playerManagerOptions.hasAutoplay()) { |
175 | this.playerHTML.buildPlaceholder(videoInfo) | 180 | this.playerHTML.buildPlaceholder(videoInfo) |
176 | } | 181 | } |
182 | const live = videoInfo.isLive | ||
183 | ? await this.videoFetcher.loadLive(videoInfo) | ||
184 | : undefined | ||
177 | 185 | ||
178 | if (!videoInfo.isLive) { | 186 | const videoFileToken = videoRequiresAuth(videoInfo) |
179 | return { video: videoInfo } | 187 | ? await this.videoFetcher.loadVideoToken(videoInfo) |
180 | } | 188 | : undefined |
181 | 189 | ||
182 | return this.videoFetcher.loadVideoWithLive(videoInfo) | 190 | return { live, video: videoInfo, videoFileToken } |
183 | }) | 191 | }) |
184 | 192 | ||
185 | const [ { video, live }, translations, captionsResponse, PeertubePlayerManagerModule ] = await Promise.all([ | 193 | const [ { video, live, videoFileToken }, translations, captionsResponse, PeertubePlayerManagerModule ] = await Promise.all([ |
186 | videoInfoPromise, | 194 | videoInfoPromise, |
187 | this.translationsPromise, | 195 | this.translationsPromise, |
188 | captionsPromise, | 196 | captionsPromise, |
@@ -200,6 +208,9 @@ export class PeerTubeEmbed { | |||
200 | translations, | 208 | translations, |
201 | serverConfig: this.config, | 209 | serverConfig: this.config, |
202 | 210 | ||
211 | authorizationHeader: () => this.http.getHeaderTokenValue(), | ||
212 | videoFileToken: () => videoFileToken, | ||
213 | |||
203 | onVideoUpdate: (uuid: string) => this.loadVideoAndBuildPlayer(uuid), | 214 | onVideoUpdate: (uuid: string) => this.loadVideoAndBuildPlayer(uuid), |
204 | 215 | ||
205 | playlistTracker: this.playlistTracker, | 216 | playlistTracker: this.playlistTracker, |
diff --git a/client/src/standalone/videos/shared/auth-http.ts b/client/src/standalone/videos/shared/auth-http.ts index 0356ab8a6..95e3b029e 100644 --- a/client/src/standalone/videos/shared/auth-http.ts +++ b/client/src/standalone/videos/shared/auth-http.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { HttpStatusCode, OAuth2ErrorCode, UserRefreshToken } from '../../../../../shared/models' | 1 | import { HttpStatusCode, OAuth2ErrorCode, UserRefreshToken } from '../../../../../shared/models' |
2 | import { objectToUrlEncoded, UserTokens } from '../../../root-helpers' | 2 | import { OAuthUserTokens, objectToUrlEncoded } from '../../../root-helpers' |
3 | import { peertubeLocalStorage } from '../../../root-helpers/peertube-web-storage' | 3 | import { peertubeLocalStorage } from '../../../root-helpers/peertube-web-storage' |
4 | 4 | ||
5 | export class AuthHTTP { | 5 | export class AuthHTTP { |
@@ -8,30 +8,32 @@ export class AuthHTTP { | |||
8 | CLIENT_SECRET: 'client_secret' | 8 | CLIENT_SECRET: 'client_secret' |
9 | } | 9 | } |
10 | 10 | ||
11 | private userTokens: UserTokens | 11 | private userOAuthTokens: OAuthUserTokens |
12 | 12 | ||
13 | private headers = new Headers() | 13 | private headers = new Headers() |
14 | 14 | ||
15 | constructor () { | 15 | constructor () { |
16 | this.userTokens = UserTokens.getUserTokens(peertubeLocalStorage) | 16 | this.userOAuthTokens = OAuthUserTokens.getUserTokens(peertubeLocalStorage) |
17 | 17 | ||
18 | if (this.userTokens) this.setHeadersFromTokens() | 18 | if (this.userOAuthTokens) this.setHeadersFromTokens() |
19 | } | 19 | } |
20 | 20 | ||
21 | fetch (url: string, { optionalAuth }: { optionalAuth: boolean }) { | 21 | fetch (url: string, { optionalAuth, method }: { optionalAuth: boolean, method?: string }) { |
22 | const refreshFetchOptions = optionalAuth | 22 | const refreshFetchOptions = optionalAuth |
23 | ? { headers: this.headers } | 23 | ? { headers: this.headers } |
24 | : {} | 24 | : {} |
25 | 25 | ||
26 | return this.refreshFetch(url.toString(), refreshFetchOptions) | 26 | return this.refreshFetch(url.toString(), { ...refreshFetchOptions, method }) |
27 | } | 27 | } |
28 | 28 | ||
29 | getHeaderTokenValue () { | 29 | getHeaderTokenValue () { |
30 | return `${this.userTokens.tokenType} ${this.userTokens.accessToken}` | 30 | if (!this.userOAuthTokens) return null |
31 | |||
32 | return `${this.userOAuthTokens.tokenType} ${this.userOAuthTokens.accessToken}` | ||
31 | } | 33 | } |
32 | 34 | ||
33 | isLoggedIn () { | 35 | isLoggedIn () { |
34 | return !!this.userTokens | 36 | return !!this.userOAuthTokens |
35 | } | 37 | } |
36 | 38 | ||
37 | private refreshFetch (url: string, options?: RequestInit) { | 39 | private refreshFetch (url: string, options?: RequestInit) { |
@@ -47,7 +49,7 @@ export class AuthHTTP { | |||
47 | headers.set('Content-Type', 'application/x-www-form-urlencoded') | 49 | headers.set('Content-Type', 'application/x-www-form-urlencoded') |
48 | 50 | ||
49 | const data = { | 51 | const data = { |
50 | refresh_token: this.userTokens.refreshToken, | 52 | refresh_token: this.userOAuthTokens.refreshToken, |
51 | client_id: clientId, | 53 | client_id: clientId, |
52 | client_secret: clientSecret, | 54 | client_secret: clientSecret, |
53 | response_type: 'code', | 55 | response_type: 'code', |
@@ -64,15 +66,15 @@ export class AuthHTTP { | |||
64 | return res.json() | 66 | return res.json() |
65 | }).then((obj: UserRefreshToken & { code?: OAuth2ErrorCode }) => { | 67 | }).then((obj: UserRefreshToken & { code?: OAuth2ErrorCode }) => { |
66 | if (!obj || obj.code === OAuth2ErrorCode.INVALID_GRANT) { | 68 | if (!obj || obj.code === OAuth2ErrorCode.INVALID_GRANT) { |
67 | UserTokens.flushLocalStorage(peertubeLocalStorage) | 69 | OAuthUserTokens.flushLocalStorage(peertubeLocalStorage) |
68 | this.removeTokensFromHeaders() | 70 | this.removeTokensFromHeaders() |
69 | 71 | ||
70 | return resolve() | 72 | return resolve() |
71 | } | 73 | } |
72 | 74 | ||
73 | this.userTokens.accessToken = obj.access_token | 75 | this.userOAuthTokens.accessToken = obj.access_token |
74 | this.userTokens.refreshToken = obj.refresh_token | 76 | this.userOAuthTokens.refreshToken = obj.refresh_token |
75 | UserTokens.saveToLocalStorage(peertubeLocalStorage, this.userTokens) | 77 | OAuthUserTokens.saveToLocalStorage(peertubeLocalStorage, this.userOAuthTokens) |
76 | 78 | ||
77 | this.setHeadersFromTokens() | 79 | this.setHeadersFromTokens() |
78 | 80 | ||
@@ -84,7 +86,7 @@ export class AuthHTTP { | |||
84 | 86 | ||
85 | return refreshingTokenPromise | 87 | return refreshingTokenPromise |
86 | .catch(() => { | 88 | .catch(() => { |
87 | UserTokens.flushLocalStorage(peertubeLocalStorage) | 89 | OAuthUserTokens.flushLocalStorage(peertubeLocalStorage) |
88 | 90 | ||
89 | this.removeTokensFromHeaders() | 91 | this.removeTokensFromHeaders() |
90 | }).then(() => fetch(url, { | 92 | }).then(() => fetch(url, { |
diff --git a/client/src/standalone/videos/shared/peertube-plugin.ts b/client/src/standalone/videos/shared/peertube-plugin.ts index 968854ce8..daf6f2b03 100644 --- a/client/src/standalone/videos/shared/peertube-plugin.ts +++ b/client/src/standalone/videos/shared/peertube-plugin.ts | |||
@@ -43,6 +43,7 @@ export class PeerTubePlugin { | |||
43 | return { | 43 | return { |
44 | getBaseStaticRoute: unimplemented, | 44 | getBaseStaticRoute: unimplemented, |
45 | getBaseRouterRoute: unimplemented, | 45 | getBaseRouterRoute: unimplemented, |
46 | getBaseWebSocketRoute: unimplemented, | ||
46 | getBasePluginClientPath: unimplemented, | 47 | getBasePluginClientPath: unimplemented, |
47 | 48 | ||
48 | getSettings: () => { | 49 | getSettings: () => { |
diff --git a/client/src/standalone/videos/shared/player-manager-options.ts b/client/src/standalone/videos/shared/player-manager-options.ts index eed821994..87a84975b 100644 --- a/client/src/standalone/videos/shared/player-manager-options.ts +++ b/client/src/standalone/videos/shared/player-manager-options.ts | |||
@@ -17,7 +17,8 @@ import { | |||
17 | isP2PEnabled, | 17 | isP2PEnabled, |
18 | logger, | 18 | logger, |
19 | peertubeLocalStorage, | 19 | peertubeLocalStorage, |
20 | UserLocalStorageKeys | 20 | UserLocalStorageKeys, |
21 | videoRequiresAuth | ||
21 | } from '../../../root-helpers' | 22 | } from '../../../root-helpers' |
22 | import { PeerTubePlugin } from './peertube-plugin' | 23 | import { PeerTubePlugin } from './peertube-plugin' |
23 | import { PlayerHTML } from './player-html' | 24 | import { PlayerHTML } from './player-html' |
@@ -154,6 +155,9 @@ export class PlayerManagerOptions { | |||
154 | captionsResponse: Response | 155 | captionsResponse: Response |
155 | live?: LiveVideo | 156 | live?: LiveVideo |
156 | 157 | ||
158 | authorizationHeader: () => string | ||
159 | videoFileToken: () => string | ||
160 | |||
157 | serverConfig: HTMLServerConfig | 161 | serverConfig: HTMLServerConfig |
158 | 162 | ||
159 | alreadyHadPlayer: boolean | 163 | alreadyHadPlayer: boolean |
@@ -169,9 +173,11 @@ export class PlayerManagerOptions { | |||
169 | video, | 173 | video, |
170 | captionsResponse, | 174 | captionsResponse, |
171 | alreadyHadPlayer, | 175 | alreadyHadPlayer, |
176 | videoFileToken, | ||
172 | translations, | 177 | translations, |
173 | playlistTracker, | 178 | playlistTracker, |
174 | live, | 179 | live, |
180 | authorizationHeader, | ||
175 | serverConfig | 181 | serverConfig |
176 | } = options | 182 | } = options |
177 | 183 | ||
@@ -227,6 +233,10 @@ export class PlayerManagerOptions { | |||
227 | embedUrl: window.location.origin + video.embedPath, | 233 | embedUrl: window.location.origin + video.embedPath, |
228 | embedTitle: video.name, | 234 | embedTitle: video.name, |
229 | 235 | ||
236 | requiresAuth: videoRequiresAuth(video), | ||
237 | authorizationHeader, | ||
238 | videoFileToken, | ||
239 | |||
230 | errorNotifier: () => { | 240 | errorNotifier: () => { |
231 | // Empty, we don't have a notifier in the embed | 241 | // Empty, we don't have a notifier in the embed |
232 | }, | 242 | }, |
diff --git a/client/src/standalone/videos/shared/video-fetcher.ts b/client/src/standalone/videos/shared/video-fetcher.ts index b42d622f9..cf6d12831 100644 --- a/client/src/standalone/videos/shared/video-fetcher.ts +++ b/client/src/standalone/videos/shared/video-fetcher.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { HttpStatusCode, LiveVideo, VideoDetails } from '../../../../../shared/models' | 1 | import { HttpStatusCode, LiveVideo, VideoDetails, VideoToken } from '../../../../../shared/models' |
2 | import { logger } from '../../../root-helpers' | 2 | import { logger } from '../../../root-helpers' |
3 | import { AuthHTTP } from './auth-http' | 3 | import { AuthHTTP } from './auth-http' |
4 | 4 | ||
@@ -36,10 +36,15 @@ export class VideoFetcher { | |||
36 | return { captionsPromise, videoResponse } | 36 | return { captionsPromise, videoResponse } |
37 | } | 37 | } |
38 | 38 | ||
39 | loadVideoWithLive (video: VideoDetails) { | 39 | loadLive (video: VideoDetails) { |
40 | return this.http.fetch(this.getLiveUrl(video.uuid), { optionalAuth: true }) | 40 | return this.http.fetch(this.getLiveUrl(video.uuid), { optionalAuth: true }) |
41 | .then(res => res.json()) | 41 | .then(res => res.json() as Promise<LiveVideo>) |
42 | .then((live: LiveVideo) => ({ video, live })) | 42 | } |
43 | |||
44 | loadVideoToken (video: VideoDetails) { | ||
45 | return this.http.fetch(this.getVideoTokenUrl(video.uuid), { optionalAuth: true, method: 'POST' }) | ||
46 | .then(res => res.json() as Promise<VideoToken>) | ||
47 | .then(token => token.files.token) | ||
43 | } | 48 | } |
44 | 49 | ||
45 | getVideoViewsUrl (videoUUID: string) { | 50 | getVideoViewsUrl (videoUUID: string) { |
@@ -61,4 +66,8 @@ export class VideoFetcher { | |||
61 | private getLiveUrl (videoId: string) { | 66 | private getLiveUrl (videoId: string) { |
62 | return window.location.origin + '/api/v1/videos/live/' + videoId | 67 | return window.location.origin + '/api/v1/videos/live/' + videoId |
63 | } | 68 | } |
69 | |||
70 | private getVideoTokenUrl (id: string) { | ||
71 | return this.getVideoUrl(id) + '/token' | ||
72 | } | ||
64 | } | 73 | } |