diff options
Diffstat (limited to 'client/src/app')
10 files changed, 117 insertions, 46 deletions
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts index 9ae6f9f12..b3818c8de 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.ts +++ b/client/src/app/+videos/+video-watch/video-watch.component.ts | |||
@@ -20,12 +20,12 @@ import { | |||
20 | } from '@app/core' | 20 | } from '@app/core' |
21 | import { HooksService } from '@app/core/plugins/hooks.service' | 21 | import { HooksService } from '@app/core/plugins/hooks.service' |
22 | import { isXPercentInViewport, scrollToTop } from '@app/helpers' | 22 | import { isXPercentInViewport, scrollToTop } from '@app/helpers' |
23 | import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main' | 23 | import { Video, VideoCaptionService, VideoDetails, VideoFileTokenService, VideoService } from '@app/shared/shared-main' |
24 | import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' | 24 | import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' |
25 | import { LiveVideoService } from '@app/shared/shared-video-live' | 25 | import { LiveVideoService } from '@app/shared/shared-video-live' |
26 | import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' | 26 | import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' |
27 | import { logger } from '@root-helpers/logger' | 27 | import { logger } from '@root-helpers/logger' |
28 | import { isP2PEnabled } from '@root-helpers/video' | 28 | import { isP2PEnabled, videoRequiresAuth } from '@root-helpers/video' |
29 | import { timeToInt } from '@shared/core-utils' | 29 | import { timeToInt } from '@shared/core-utils' |
30 | import { | 30 | import { |
31 | HTMLServerConfig, | 31 | HTMLServerConfig, |
@@ -78,6 +78,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
78 | private nextVideoUUID = '' | 78 | private nextVideoUUID = '' |
79 | private nextVideoTitle = '' | 79 | private nextVideoTitle = '' |
80 | 80 | ||
81 | private videoFileToken: string | ||
82 | |||
81 | private currentTime: number | 83 | private currentTime: number |
82 | 84 | ||
83 | private paramsSub: Subscription | 85 | private paramsSub: Subscription |
@@ -110,6 +112,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
110 | private pluginService: PluginService, | 112 | private pluginService: PluginService, |
111 | private peertubeSocket: PeerTubeSocket, | 113 | private peertubeSocket: PeerTubeSocket, |
112 | private screenService: ScreenService, | 114 | private screenService: ScreenService, |
115 | private videoFileTokenService: VideoFileTokenService, | ||
113 | private location: PlatformLocation, | 116 | private location: PlatformLocation, |
114 | @Inject(LOCALE_ID) private localeId: string | 117 | @Inject(LOCALE_ID) private localeId: string |
115 | ) { } | 118 | ) { } |
@@ -252,12 +255,19 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
252 | 'filter:api.video-watch.video.get.result' | 255 | 'filter:api.video-watch.video.get.result' |
253 | ) | 256 | ) |
254 | 257 | ||
255 | const videoAndLiveObs: Observable<{ video: VideoDetails, live?: LiveVideo }> = videoObs.pipe( | 258 | const videoAndLiveObs: Observable<{ video: VideoDetails, live?: LiveVideo, videoFileToken?: string }> = videoObs.pipe( |
256 | switchMap(video => { | 259 | switchMap(video => { |
257 | if (!video.isLive) return of({ video }) | 260 | if (!video.isLive) return of({ video, live: undefined }) |
258 | 261 | ||
259 | return this.liveVideoService.getVideoLive(video.uuid) | 262 | return this.liveVideoService.getVideoLive(video.uuid) |
260 | .pipe(map(live => ({ live, video }))) | 263 | .pipe(map(live => ({ live, video }))) |
264 | }), | ||
265 | |||
266 | switchMap(({ video, live }) => { | ||
267 | if (!videoRequiresAuth(video)) return of({ video, live, videoFileToken: undefined }) | ||
268 | |||
269 | return this.videoFileTokenService.getVideoFileToken(video.uuid) | ||
270 | .pipe(map(({ token }) => ({ video, live, videoFileToken: token }))) | ||
261 | }) | 271 | }) |
262 | ) | 272 | ) |
263 | 273 | ||
@@ -266,7 +276,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
266 | this.videoCaptionService.listCaptions(videoId), | 276 | this.videoCaptionService.listCaptions(videoId), |
267 | this.userService.getAnonymousOrLoggedUser() | 277 | this.userService.getAnonymousOrLoggedUser() |
268 | ]).subscribe({ | 278 | ]).subscribe({ |
269 | next: ([ { video, live }, captionsResult, loggedInOrAnonymousUser ]) => { | 279 | next: ([ { video, live, videoFileToken }, captionsResult, loggedInOrAnonymousUser ]) => { |
270 | const queryParams = this.route.snapshot.queryParams | 280 | const queryParams = this.route.snapshot.queryParams |
271 | 281 | ||
272 | const urlOptions = { | 282 | const urlOptions = { |
@@ -283,7 +293,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
283 | peertubeLink: false | 293 | peertubeLink: false |
284 | } | 294 | } |
285 | 295 | ||
286 | this.onVideoFetched({ video, live, videoCaptions: captionsResult.data, loggedInOrAnonymousUser, urlOptions }) | 296 | this.onVideoFetched({ video, live, videoCaptions: captionsResult.data, videoFileToken, loggedInOrAnonymousUser, urlOptions }) |
287 | .catch(err => this.handleGlobalError(err)) | 297 | .catch(err => this.handleGlobalError(err)) |
288 | }, | 298 | }, |
289 | 299 | ||
@@ -356,16 +366,19 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
356 | video: VideoDetails | 366 | video: VideoDetails |
357 | live: LiveVideo | 367 | live: LiveVideo |
358 | videoCaptions: VideoCaption[] | 368 | videoCaptions: VideoCaption[] |
369 | videoFileToken: string | ||
370 | |||
359 | urlOptions: URLOptions | 371 | urlOptions: URLOptions |
360 | loggedInOrAnonymousUser: User | 372 | loggedInOrAnonymousUser: User |
361 | }) { | 373 | }) { |
362 | const { video, live, videoCaptions, urlOptions, loggedInOrAnonymousUser } = options | 374 | const { video, live, videoCaptions, urlOptions, videoFileToken, loggedInOrAnonymousUser } = options |
363 | 375 | ||
364 | this.subscribeToLiveEventsIfNeeded(this.video, video) | 376 | this.subscribeToLiveEventsIfNeeded(this.video, video) |
365 | 377 | ||
366 | this.video = video | 378 | this.video = video |
367 | this.videoCaptions = videoCaptions | 379 | this.videoCaptions = videoCaptions |
368 | this.liveVideo = live | 380 | this.liveVideo = live |
381 | this.videoFileToken = videoFileToken | ||
369 | 382 | ||
370 | // Re init attributes | 383 | // Re init attributes |
371 | this.playerPlaceholderImgSrc = undefined | 384 | this.playerPlaceholderImgSrc = undefined |
@@ -414,6 +427,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
414 | video: this.video, | 427 | video: this.video, |
415 | videoCaptions: this.videoCaptions, | 428 | videoCaptions: this.videoCaptions, |
416 | liveVideo: this.liveVideo, | 429 | liveVideo: this.liveVideo, |
430 | videoFileToken: this.videoFileToken, | ||
417 | urlOptions, | 431 | urlOptions, |
418 | loggedInOrAnonymousUser, | 432 | loggedInOrAnonymousUser, |
419 | user: this.user | 433 | user: this.user |
@@ -561,11 +575,15 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
561 | video: VideoDetails | 575 | video: VideoDetails |
562 | liveVideo: LiveVideo | 576 | liveVideo: LiveVideo |
563 | videoCaptions: VideoCaption[] | 577 | videoCaptions: VideoCaption[] |
578 | |||
579 | videoFileToken: string | ||
580 | |||
564 | urlOptions: CustomizationOptions & { playerMode: PlayerMode } | 581 | urlOptions: CustomizationOptions & { playerMode: PlayerMode } |
582 | |||
565 | loggedInOrAnonymousUser: User | 583 | loggedInOrAnonymousUser: User |
566 | user?: AuthUser // Keep for plugins | 584 | user?: AuthUser // Keep for plugins |
567 | }) { | 585 | }) { |
568 | const { video, liveVideo, videoCaptions, urlOptions, loggedInOrAnonymousUser } = params | 586 | const { video, liveVideo, videoCaptions, videoFileToken, urlOptions, loggedInOrAnonymousUser } = params |
569 | 587 | ||
570 | const getStartTime = () => { | 588 | const getStartTime = () => { |
571 | const byUrl = urlOptions.startTime !== undefined | 589 | const byUrl = urlOptions.startTime !== undefined |
@@ -623,13 +641,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
623 | theaterButton: true, | 641 | theaterButton: true, |
624 | captions: videoCaptions.length !== 0, | 642 | captions: videoCaptions.length !== 0, |
625 | 643 | ||
626 | videoViewUrl: video.privacy.id !== VideoPrivacy.PRIVATE | ||
627 | ? this.videoService.getVideoViewUrl(video.uuid) | ||
628 | : null, | ||
629 | authorizationHeader: this.authService.getRequestHeaderValue(), | ||
630 | |||
631 | metricsUrl: environment.apiUrl + '/api/v1/metrics/playback', | ||
632 | |||
633 | embedUrl: video.embedUrl, | 644 | embedUrl: video.embedUrl, |
634 | embedTitle: video.name, | 645 | embedTitle: video.name, |
635 | instanceName: this.serverConfig.instance.name, | 646 | instanceName: this.serverConfig.instance.name, |
@@ -639,7 +650,17 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
639 | 650 | ||
640 | language: this.localeId, | 651 | language: this.localeId, |
641 | 652 | ||
642 | serverUrl: environment.apiUrl, | 653 | metricsUrl: environment.apiUrl + '/api/v1/metrics/playback', |
654 | |||
655 | videoViewUrl: video.privacy.id !== VideoPrivacy.PRIVATE | ||
656 | ? this.videoService.getVideoViewUrl(video.uuid) | ||
657 | : null, | ||
658 | authorizationHeader: () => this.authService.getRequestHeaderValue(), | ||
659 | |||
660 | serverUrl: environment.originServerUrl, | ||
661 | |||
662 | videoFileToken: () => videoFileToken, | ||
663 | requiresAuth: videoRequiresAuth(video), | ||
643 | 664 | ||
644 | videoCaptions: playerCaptions, | 665 | videoCaptions: playerCaptions, |
645 | 666 | ||
diff --git a/client/src/app/core/auth/auth-user.model.ts b/client/src/app/core/auth/auth-user.model.ts index cd9665e37..a12325421 100644 --- a/client/src/app/core/auth/auth-user.model.ts +++ b/client/src/app/core/auth/auth-user.model.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Observable, of } from 'rxjs' | 1 | import { Observable, of } from 'rxjs' |
2 | import { map } from 'rxjs/operators' | 2 | import { map } from 'rxjs/operators' |
3 | import { User } from '@app/core/users/user.model' | 3 | import { User } from '@app/core/users/user.model' |
4 | import { UserTokens } from '@root-helpers/users' | 4 | import { OAuthUserTokens } from '@root-helpers/users' |
5 | import { hasUserRight } from '@shared/core-utils/users' | 5 | import { hasUserRight } from '@shared/core-utils/users' |
6 | import { | 6 | import { |
7 | MyUser as ServerMyUserModel, | 7 | MyUser as ServerMyUserModel, |
@@ -13,33 +13,33 @@ import { | |||
13 | } from '@shared/models' | 13 | } from '@shared/models' |
14 | 14 | ||
15 | export class AuthUser extends User implements ServerMyUserModel { | 15 | export class AuthUser extends User implements ServerMyUserModel { |
16 | tokens: UserTokens | 16 | oauthTokens: OAuthUserTokens |
17 | specialPlaylists: MyUserSpecialPlaylist[] | 17 | specialPlaylists: MyUserSpecialPlaylist[] |
18 | 18 | ||
19 | canSeeVideosLink = true | 19 | canSeeVideosLink = true |
20 | 20 | ||
21 | constructor (userHash: Partial<ServerMyUserModel>, hashTokens: Partial<UserTokens>) { | 21 | constructor (userHash: Partial<ServerMyUserModel>, hashTokens: Partial<OAuthUserTokens>) { |
22 | super(userHash) | 22 | super(userHash) |
23 | 23 | ||
24 | this.tokens = new UserTokens(hashTokens) | 24 | this.oauthTokens = new OAuthUserTokens(hashTokens) |
25 | this.specialPlaylists = userHash.specialPlaylists | 25 | this.specialPlaylists = userHash.specialPlaylists |
26 | } | 26 | } |
27 | 27 | ||
28 | getAccessToken () { | 28 | getAccessToken () { |
29 | return this.tokens.accessToken | 29 | return this.oauthTokens.accessToken |
30 | } | 30 | } |
31 | 31 | ||
32 | getRefreshToken () { | 32 | getRefreshToken () { |
33 | return this.tokens.refreshToken | 33 | return this.oauthTokens.refreshToken |
34 | } | 34 | } |
35 | 35 | ||
36 | getTokenType () { | 36 | getTokenType () { |
37 | return this.tokens.tokenType | 37 | return this.oauthTokens.tokenType |
38 | } | 38 | } |
39 | 39 | ||
40 | refreshTokens (accessToken: string, refreshToken: string) { | 40 | refreshTokens (accessToken: string, refreshToken: string) { |
41 | this.tokens.accessToken = accessToken | 41 | this.oauthTokens.accessToken = accessToken |
42 | this.tokens.refreshToken = refreshToken | 42 | this.oauthTokens.refreshToken = refreshToken |
43 | } | 43 | } |
44 | 44 | ||
45 | hasRight (right: UserRight) { | 45 | hasRight (right: UserRight) { |
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts index 7f4fae4aa..4de28e51e 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts | |||
@@ -5,7 +5,7 @@ import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular | |||
5 | import { Injectable } from '@angular/core' | 5 | import { Injectable } from '@angular/core' |
6 | import { Router } from '@angular/router' | 6 | import { Router } from '@angular/router' |
7 | import { Notifier } from '@app/core/notification/notifier.service' | 7 | import { Notifier } from '@app/core/notification/notifier.service' |
8 | import { logger, objectToUrlEncoded, peertubeLocalStorage, UserTokens } from '@root-helpers/index' | 8 | import { logger, OAuthUserTokens, objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index' |
9 | import { HttpStatusCode, MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models' | 9 | import { HttpStatusCode, MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models' |
10 | import { environment } from '../../../environments/environment' | 10 | import { environment } from '../../../environments/environment' |
11 | import { RestExtractor } from '../rest/rest-extractor.service' | 11 | import { RestExtractor } from '../rest/rest-extractor.service' |
@@ -74,7 +74,7 @@ export class AuthService { | |||
74 | ] | 74 | ] |
75 | } | 75 | } |
76 | 76 | ||
77 | buildAuthUser (userInfo: Partial<User>, tokens: UserTokens) { | 77 | buildAuthUser (userInfo: Partial<User>, tokens: OAuthUserTokens) { |
78 | this.user = new AuthUser(userInfo, tokens) | 78 | this.user = new AuthUser(userInfo, tokens) |
79 | } | 79 | } |
80 | 80 | ||
diff --git a/client/src/app/core/users/user-local-storage.service.ts b/client/src/app/core/users/user-local-storage.service.ts index fff649eef..f1588bdd2 100644 --- a/client/src/app/core/users/user-local-storage.service.ts +++ b/client/src/app/core/users/user-local-storage.service.ts | |||
@@ -4,7 +4,7 @@ import { Injectable } from '@angular/core' | |||
4 | import { AuthService, AuthStatus } from '@app/core/auth' | 4 | import { AuthService, AuthStatus } from '@app/core/auth' |
5 | import { getBoolOrDefault } from '@root-helpers/local-storage-utils' | 5 | import { getBoolOrDefault } from '@root-helpers/local-storage-utils' |
6 | import { logger } from '@root-helpers/logger' | 6 | import { logger } from '@root-helpers/logger' |
7 | import { UserLocalStorageKeys, UserTokens } from '@root-helpers/users' | 7 | import { UserLocalStorageKeys, OAuthUserTokens } from '@root-helpers/users' |
8 | import { UserRole, UserUpdateMe } from '@shared/models' | 8 | import { UserRole, UserUpdateMe } from '@shared/models' |
9 | import { NSFWPolicyType } from '@shared/models/videos' | 9 | import { NSFWPolicyType } from '@shared/models/videos' |
10 | import { ServerService } from '../server' | 10 | import { ServerService } from '../server' |
@@ -24,7 +24,7 @@ export class UserLocalStorageService { | |||
24 | 24 | ||
25 | this.setLoggedInUser(user) | 25 | this.setLoggedInUser(user) |
26 | this.setUserInfo(user) | 26 | this.setUserInfo(user) |
27 | this.setTokens(user.tokens) | 27 | this.setTokens(user.oauthTokens) |
28 | } | 28 | } |
29 | }) | 29 | }) |
30 | 30 | ||
@@ -43,7 +43,7 @@ export class UserLocalStorageService { | |||
43 | next: () => { | 43 | next: () => { |
44 | const user = this.authService.getUser() | 44 | const user = this.authService.getUser() |
45 | 45 | ||
46 | this.setTokens(user.tokens) | 46 | this.setTokens(user.oauthTokens) |
47 | } | 47 | } |
48 | }) | 48 | }) |
49 | } | 49 | } |
@@ -174,14 +174,14 @@ export class UserLocalStorageService { | |||
174 | // --------------------------------------------------------------------------- | 174 | // --------------------------------------------------------------------------- |
175 | 175 | ||
176 | getTokens () { | 176 | getTokens () { |
177 | return UserTokens.getUserTokens(this.localStorageService) | 177 | return OAuthUserTokens.getUserTokens(this.localStorageService) |
178 | } | 178 | } |
179 | 179 | ||
180 | setTokens (tokens: UserTokens) { | 180 | setTokens (tokens: OAuthUserTokens) { |
181 | UserTokens.saveToLocalStorage(this.localStorageService, tokens) | 181 | OAuthUserTokens.saveToLocalStorage(this.localStorageService, tokens) |
182 | } | 182 | } |
183 | 183 | ||
184 | flushTokens () { | 184 | flushTokens () { |
185 | UserTokens.flushLocalStorage(this.localStorageService) | 185 | OAuthUserTokens.flushLocalStorage(this.localStorageService) |
186 | } | 186 | } |
187 | } | 187 | } |
diff --git a/client/src/app/helpers/utils/url.ts b/client/src/app/helpers/utils/url.ts index 08c27e3c1..9e7dc3e6f 100644 --- a/client/src/app/helpers/utils/url.ts +++ b/client/src/app/helpers/utils/url.ts | |||
@@ -54,8 +54,9 @@ function objectToFormData (obj: any, form?: FormData, namespace?: string) { | |||
54 | } | 54 | } |
55 | 55 | ||
56 | export { | 56 | export { |
57 | objectToFormData, | ||
58 | getAbsoluteAPIUrl, | 57 | getAbsoluteAPIUrl, |
59 | getAPIHost, | 58 | getAPIHost, |
60 | getAbsoluteEmbedUrl | 59 | getAbsoluteEmbedUrl, |
60 | |||
61 | objectToFormData | ||
61 | } | 62 | } |
diff --git a/client/src/app/shared/shared-main/shared-main.module.ts b/client/src/app/shared/shared-main/shared-main.module.ts index 04b223cc5..c1523bc50 100644 --- a/client/src/app/shared/shared-main/shared-main.module.ts +++ b/client/src/app/shared/shared-main/shared-main.module.ts | |||
@@ -44,7 +44,15 @@ import { | |||
44 | import { PluginPlaceholderComponent, PluginSelectorDirective } from './plugins' | 44 | import { PluginPlaceholderComponent, PluginSelectorDirective } from './plugins' |
45 | import { ActorRedirectGuard } from './router' | 45 | import { ActorRedirectGuard } from './router' |
46 | import { UserHistoryService, UserNotificationsComponent, UserNotificationService, UserQuotaComponent } from './users' | 46 | import { UserHistoryService, UserNotificationsComponent, UserNotificationService, UserQuotaComponent } from './users' |
47 | import { EmbedComponent, RedundancyService, VideoImportService, VideoOwnershipService, VideoResolver, VideoService } from './video' | 47 | import { |
48 | EmbedComponent, | ||
49 | RedundancyService, | ||
50 | VideoFileTokenService, | ||
51 | VideoImportService, | ||
52 | VideoOwnershipService, | ||
53 | VideoResolver, | ||
54 | VideoService | ||
55 | } from './video' | ||
48 | import { VideoCaptionService } from './video-caption' | 56 | import { VideoCaptionService } from './video-caption' |
49 | import { VideoChannelService } from './video-channel' | 57 | import { VideoChannelService } from './video-channel' |
50 | 58 | ||
@@ -185,6 +193,7 @@ import { VideoChannelService } from './video-channel' | |||
185 | VideoImportService, | 193 | VideoImportService, |
186 | VideoOwnershipService, | 194 | VideoOwnershipService, |
187 | VideoService, | 195 | VideoService, |
196 | VideoFileTokenService, | ||
188 | VideoResolver, | 197 | VideoResolver, |
189 | 198 | ||
190 | VideoCaptionService, | 199 | VideoCaptionService, |
diff --git a/client/src/app/shared/shared-main/video/index.ts b/client/src/app/shared/shared-main/video/index.ts index 361601456..a2e47883e 100644 --- a/client/src/app/shared/shared-main/video/index.ts +++ b/client/src/app/shared/shared-main/video/index.ts | |||
@@ -2,6 +2,7 @@ export * from './embed.component' | |||
2 | export * from './redundancy.service' | 2 | export * from './redundancy.service' |
3 | export * from './video-details.model' | 3 | export * from './video-details.model' |
4 | export * from './video-edit.model' | 4 | export * from './video-edit.model' |
5 | export * from './video-file-token.service' | ||
5 | export * from './video-import.service' | 6 | export * from './video-import.service' |
6 | export * from './video-ownership.service' | 7 | export * from './video-ownership.service' |
7 | export * from './video.model' | 8 | export * from './video.model' |
diff --git a/client/src/app/shared/shared-main/video/video-file-token.service.ts b/client/src/app/shared/shared-main/video/video-file-token.service.ts new file mode 100644 index 000000000..791607249 --- /dev/null +++ b/client/src/app/shared/shared-main/video/video-file-token.service.ts | |||
@@ -0,0 +1,33 @@ | |||
1 | import { catchError, map, of, tap } from 'rxjs' | ||
2 | import { HttpClient } from '@angular/common/http' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { RestExtractor } from '@app/core' | ||
5 | import { VideoToken } from '@shared/models' | ||
6 | import { VideoService } from './video.service' | ||
7 | |||
8 | @Injectable() | ||
9 | export class VideoFileTokenService { | ||
10 | |||
11 | private readonly store = new Map<string, { token: string, expires: Date }>() | ||
12 | |||
13 | constructor ( | ||
14 | private authHttp: HttpClient, | ||
15 | private restExtractor: RestExtractor | ||
16 | ) {} | ||
17 | |||
18 | getVideoFileToken (videoUUID: string) { | ||
19 | const existing = this.store.get(videoUUID) | ||
20 | if (existing) return of(existing) | ||
21 | |||
22 | return this.createVideoFileToken(videoUUID) | ||
23 | .pipe(tap(result => this.store.set(videoUUID, { token: result.token, expires: new Date(result.expires) }))) | ||
24 | } | ||
25 | |||
26 | private createVideoFileToken (videoUUID: string) { | ||
27 | return this.authHttp.post<VideoToken>(`${VideoService.BASE_VIDEO_URL}/${videoUUID}/token`, {}) | ||
28 | .pipe( | ||
29 | map(({ files }) => files), | ||
30 | catchError(err => this.restExtractor.handleError(err)) | ||
31 | ) | ||
32 | } | ||
33 | } | ||
diff --git a/client/src/app/shared/shared-video-miniature/video-download.component.html b/client/src/app/shared/shared-video-miniature/video-download.component.html index 1c7458b4b..1f622933d 100644 --- a/client/src/app/shared/shared-video-miniature/video-download.component.html +++ b/client/src/app/shared/shared-video-miniature/video-download.component.html | |||
@@ -48,10 +48,7 @@ | |||
48 | 48 | ||
49 | <ng-template ngbNavContent> | 49 | <ng-template ngbNavContent> |
50 | <div class="nav-content"> | 50 | <div class="nav-content"> |
51 | <my-input-text | 51 | <my-input-text [show]="true" [readonly]="true" [withCopy]="true" [withToggle]="false" [value]="getLink()"></my-input-text> |
52 | *ngIf="!isConfidentialVideo()" | ||
53 | [show]="true" [readonly]="true" [withCopy]="true" [withToggle]="false" [value]="getLink()" | ||
54 | ></my-input-text> | ||
55 | </div> | 52 | </div> |
56 | </ng-template> | 53 | </ng-template> |
57 | </ng-container> | 54 | </ng-container> |
diff --git a/client/src/app/shared/shared-video-miniature/video-download.component.ts b/client/src/app/shared/shared-video-miniature/video-download.component.ts index 47482caaa..667cb107f 100644 --- a/client/src/app/shared/shared-video-miniature/video-download.component.ts +++ b/client/src/app/shared/shared-video-miniature/video-download.component.ts | |||
@@ -2,11 +2,12 @@ import { mapValues, pick } from 'lodash-es' | |||
2 | import { firstValueFrom } from 'rxjs' | 2 | import { firstValueFrom } from 'rxjs' |
3 | import { tap } from 'rxjs/operators' | 3 | import { tap } from 'rxjs/operators' |
4 | import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core' | 4 | import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core' |
5 | import { AuthService, HooksService, Notifier } from '@app/core' | 5 | import { HooksService } from '@app/core' |
6 | import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' | 6 | import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' |
7 | import { logger } from '@root-helpers/logger' | 7 | import { logger } from '@root-helpers/logger' |
8 | import { videoRequiresAuth } from '@root-helpers/video' | ||
8 | import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models' | 9 | import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models' |
9 | import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoService } from '../shared-main' | 10 | import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoFileTokenService, VideoService } from '../shared-main' |
10 | 11 | ||
11 | type DownloadType = 'video' | 'subtitles' | 12 | type DownloadType = 'video' | 'subtitles' |
12 | type FileMetadata = { [key: string]: { label: string, value: string }} | 13 | type FileMetadata = { [key: string]: { label: string, value: string }} |
@@ -32,6 +33,8 @@ export class VideoDownloadComponent { | |||
32 | 33 | ||
33 | type: DownloadType = 'video' | 34 | type: DownloadType = 'video' |
34 | 35 | ||
36 | videoFileToken: string | ||
37 | |||
35 | private activeModal: NgbModalRef | 38 | private activeModal: NgbModalRef |
36 | 39 | ||
37 | private bytesPipe: BytesPipe | 40 | private bytesPipe: BytesPipe |
@@ -42,10 +45,9 @@ export class VideoDownloadComponent { | |||
42 | 45 | ||
43 | constructor ( | 46 | constructor ( |
44 | @Inject(LOCALE_ID) private localeId: string, | 47 | @Inject(LOCALE_ID) private localeId: string, |
45 | private notifier: Notifier, | ||
46 | private modalService: NgbModal, | 48 | private modalService: NgbModal, |
47 | private videoService: VideoService, | 49 | private videoService: VideoService, |
48 | private auth: AuthService, | 50 | private videoFileTokenService: VideoFileTokenService, |
49 | private hooks: HooksService | 51 | private hooks: HooksService |
50 | ) { | 52 | ) { |
51 | this.bytesPipe = new BytesPipe() | 53 | this.bytesPipe = new BytesPipe() |
@@ -71,6 +73,8 @@ export class VideoDownloadComponent { | |||
71 | } | 73 | } |
72 | 74 | ||
73 | show (video: VideoDetails, videoCaptions?: VideoCaption[]) { | 75 | show (video: VideoDetails, videoCaptions?: VideoCaption[]) { |
76 | this.videoFileToken = undefined | ||
77 | |||
74 | this.video = video | 78 | this.video = video |
75 | this.videoCaptions = videoCaptions | 79 | this.videoCaptions = videoCaptions |
76 | 80 | ||
@@ -84,6 +88,11 @@ export class VideoDownloadComponent { | |||
84 | this.subtitleLanguageId = this.videoCaptions[0].language.id | 88 | this.subtitleLanguageId = this.videoCaptions[0].language.id |
85 | } | 89 | } |
86 | 90 | ||
91 | if (videoRequiresAuth(this.video)) { | ||
92 | this.videoFileTokenService.getVideoFileToken(this.video.uuid) | ||
93 | .subscribe(({ token }) => this.videoFileToken = token) | ||
94 | } | ||
95 | |||
87 | this.activeModal.shown.subscribe(() => { | 96 | this.activeModal.shown.subscribe(() => { |
88 | this.hooks.runAction('action:modal.video-download.shown', 'common') | 97 | this.hooks.runAction('action:modal.video-download.shown', 'common') |
89 | }) | 98 | }) |
@@ -155,7 +164,7 @@ export class VideoDownloadComponent { | |||
155 | if (!file) return '' | 164 | if (!file) return '' |
156 | 165 | ||
157 | const suffix = this.isConfidentialVideo() | 166 | const suffix = this.isConfidentialVideo() |
158 | ? '?access_token=' + this.auth.getAccessToken() | 167 | ? '?videoFileToken=' + this.videoFileToken |
159 | : '' | 168 | : '' |
160 | 169 | ||
161 | switch (this.downloadType) { | 170 | switch (this.downloadType) { |