diff options
author | Wicklow <123956049+wickloww@users.noreply.github.com> | 2023-06-29 07:48:55 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-29 09:48:55 +0200 |
commit | 40346ead2b0b7afa475aef057d3673b6c7574b7a (patch) | |
tree | 24ffdc23c3a9d987334842e0d400b5bd44500cf7 /client/src/app/+videos | |
parent | ae22c59f14d0d553f60b281948b6c232c2aca178 (diff) | |
download | PeerTube-40346ead2b0b7afa475aef057d3673b6c7574b7a.tar.gz PeerTube-40346ead2b0b7afa475aef057d3673b6c7574b7a.tar.zst PeerTube-40346ead2b0b7afa475aef057d3673b6c7574b7a.zip |
Feature/password protected videos (#5836)
* Add server endpoints
* Refactoring test suites
* Update server and add openapi documentation
* fix compliation and tests
* upload/import password protected video on client
* add server error code
* Add video password to update resolver
* add custom message when sharing pw protected video
* improve confirm component
* Add new alert in component
* Add ability to watch protected video on client
* Cannot have password protected replay privacy
* Add migration
* Add tests
* update after review
* Update check params tests
* Add live videos test
* Add more filter test
* Update static file privacy test
* Update object storage tests
* Add test on feeds
* Add missing word
* Fix tests
* Fix tests on live videos
* add embed support on password protected videos
* fix style
* Correcting data leaks
* Unable to add password protected privacy on replay
* Updated code based on review comments
* fix validator and command
* Updated code based on review comments
Diffstat (limited to 'client/src/app/+videos')
16 files changed, 148 insertions, 40 deletions
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.html b/client/src/app/+videos/+video-edit/shared/video-edit.component.html index b607dabe9..97b713874 100644 --- a/client/src/app/+videos/+video-edit/shared/video-edit.component.html +++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.html | |||
@@ -120,7 +120,12 @@ | |||
120 | </div> | 120 | </div> |
121 | </div> | 121 | </div> |
122 | 122 | ||
123 | <div *ngIf="schedulePublicationEnabled" class="form-group"> | 123 | <div *ngIf="passwordProtectionSelected" class="form-group"> |
124 | <label i18n for="videoPassword">Password</label> | ||
125 | <my-input-text formControlName="videoPassword" inputId="videoPassword" [withCopy]="true" [formError]="formErrors['videoPassword']"></my-input-text> | ||
126 | </div> | ||
127 | |||
128 | <div *ngIf="schedulePublicationSelected" class="form-group"> | ||
124 | <label i18n for="schedulePublicationAt">Schedule publication ({{ calendarTimezone }})</label> | 129 | <label i18n for="schedulePublicationAt">Schedule publication ({{ calendarTimezone }})</label> |
125 | <p-calendar | 130 | <p-calendar |
126 | id="schedulePublicationAt" formControlName="schedulePublicationAt" [dateFormat]="calendarDateFormat" | 131 | id="schedulePublicationAt" formControlName="schedulePublicationAt" [dateFormat]="calendarDateFormat" |
@@ -287,7 +292,7 @@ | |||
287 | <div class="form-group mx-4" *ngIf="isSaveReplayEnabled()"> | 292 | <div class="form-group mx-4" *ngIf="isSaveReplayEnabled()"> |
288 | <label i18n for="replayPrivacy">Privacy of the new replay</label> | 293 | <label i18n for="replayPrivacy">Privacy of the new replay</label> |
289 | <my-select-options | 294 | <my-select-options |
290 | labelForId="replayPrivacy" [items]="videoPrivacies" [clearable]="false" formControlName="replayPrivacy" | 295 | labelForId="replayPrivacy" [items]="replayPrivacies" [clearable]="false" formControlName="replayPrivacy" |
291 | ></my-select-options> | 296 | ></my-select-options> |
292 | </div> | 297 | </div> |
293 | 298 | ||
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts index 8ed54ce6b..5e5df8db7 100644 --- a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts +++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts | |||
@@ -14,6 +14,7 @@ import { | |||
14 | VIDEO_LICENCE_VALIDATOR, | 14 | VIDEO_LICENCE_VALIDATOR, |
15 | VIDEO_NAME_VALIDATOR, | 15 | VIDEO_NAME_VALIDATOR, |
16 | VIDEO_ORIGINALLY_PUBLISHED_AT_VALIDATOR, | 16 | VIDEO_ORIGINALLY_PUBLISHED_AT_VALIDATOR, |
17 | VIDEO_PASSWORD_VALIDATOR, | ||
17 | VIDEO_PRIVACY_VALIDATOR, | 18 | VIDEO_PRIVACY_VALIDATOR, |
18 | VIDEO_SCHEDULE_PUBLICATION_AT_VALIDATOR, | 19 | VIDEO_SCHEDULE_PUBLICATION_AT_VALIDATOR, |
19 | VIDEO_SUPPORT_VALIDATOR, | 20 | VIDEO_SUPPORT_VALIDATOR, |
@@ -79,7 +80,8 @@ export class VideoEditComponent implements OnInit, OnDestroy { | |||
79 | // So that it can be accessed in the template | 80 | // So that it can be accessed in the template |
80 | readonly SPECIAL_SCHEDULED_PRIVACY = VideoEdit.SPECIAL_SCHEDULED_PRIVACY | 81 | readonly SPECIAL_SCHEDULED_PRIVACY = VideoEdit.SPECIAL_SCHEDULED_PRIVACY |
81 | 82 | ||
82 | videoPrivacies: VideoConstant<VideoPrivacy>[] = [] | 83 | videoPrivacies: VideoConstant<VideoPrivacy | typeof VideoEdit.SPECIAL_SCHEDULED_PRIVACY > [] = [] |
84 | replayPrivacies: VideoConstant<VideoPrivacy> [] = [] | ||
83 | videoCategories: VideoConstant<number>[] = [] | 85 | videoCategories: VideoConstant<number>[] = [] |
84 | videoLicences: VideoConstant<number>[] = [] | 86 | videoLicences: VideoConstant<number>[] = [] |
85 | videoLanguages: VideoLanguages[] = [] | 87 | videoLanguages: VideoLanguages[] = [] |
@@ -103,7 +105,8 @@ export class VideoEditComponent implements OnInit, OnDestroy { | |||
103 | 105 | ||
104 | pluginDataFormGroup: FormGroup | 106 | pluginDataFormGroup: FormGroup |
105 | 107 | ||
106 | schedulePublicationEnabled = false | 108 | schedulePublicationSelected = false |
109 | passwordProtectionSelected = false | ||
107 | 110 | ||
108 | calendarLocale: any = {} | 111 | calendarLocale: any = {} |
109 | minScheduledDate = new Date() | 112 | minScheduledDate = new Date() |
@@ -148,6 +151,7 @@ export class VideoEditComponent implements OnInit, OnDestroy { | |||
148 | const obj: { [ id: string ]: BuildFormValidator } = { | 151 | const obj: { [ id: string ]: BuildFormValidator } = { |
149 | name: VIDEO_NAME_VALIDATOR, | 152 | name: VIDEO_NAME_VALIDATOR, |
150 | privacy: VIDEO_PRIVACY_VALIDATOR, | 153 | privacy: VIDEO_PRIVACY_VALIDATOR, |
154 | videoPassword: VIDEO_PASSWORD_VALIDATOR, | ||
151 | channelId: VIDEO_CHANNEL_VALIDATOR, | 155 | channelId: VIDEO_CHANNEL_VALIDATOR, |
152 | nsfw: null, | 156 | nsfw: null, |
153 | commentsEnabled: null, | 157 | commentsEnabled: null, |
@@ -222,7 +226,9 @@ export class VideoEditComponent implements OnInit, OnDestroy { | |||
222 | 226 | ||
223 | this.serverService.getVideoPrivacies() | 227 | this.serverService.getVideoPrivacies() |
224 | .subscribe(privacies => { | 228 | .subscribe(privacies => { |
225 | this.videoPrivacies = this.videoService.explainedPrivacyLabels(privacies).videoPrivacies | 229 | const videoPrivacies = this.videoService.explainedPrivacyLabels(privacies).videoPrivacies |
230 | this.videoPrivacies = videoPrivacies | ||
231 | this.replayPrivacies = videoPrivacies.filter((privacy) => privacy.id !== VideoPrivacy.PASSWORD_PROTECTED) | ||
226 | 232 | ||
227 | // Can't schedule publication if private privacy is not available (could be deleted by a plugin) | 233 | // Can't schedule publication if private privacy is not available (could be deleted by a plugin) |
228 | const hasPrivatePrivacy = this.videoPrivacies.some(p => p.id === VideoPrivacy.PRIVATE) | 234 | const hasPrivatePrivacy = this.videoPrivacies.some(p => p.id === VideoPrivacy.PRIVATE) |
@@ -410,13 +416,13 @@ export class VideoEditComponent implements OnInit, OnDestroy { | |||
410 | .subscribe( | 416 | .subscribe( |
411 | newPrivacyId => { | 417 | newPrivacyId => { |
412 | 418 | ||
413 | this.schedulePublicationEnabled = newPrivacyId === this.SPECIAL_SCHEDULED_PRIVACY | 419 | this.schedulePublicationSelected = newPrivacyId === this.SPECIAL_SCHEDULED_PRIVACY |
414 | 420 | ||
415 | // Value changed | 421 | // Value changed |
416 | const scheduleControl = this.form.get('schedulePublicationAt') | 422 | const scheduleControl = this.form.get('schedulePublicationAt') |
417 | const waitTranscodingControl = this.form.get('waitTranscoding') | 423 | const waitTranscodingControl = this.form.get('waitTranscoding') |
418 | 424 | ||
419 | if (this.schedulePublicationEnabled) { | 425 | if (this.schedulePublicationSelected) { |
420 | scheduleControl.setValidators([ Validators.required ]) | 426 | scheduleControl.setValidators([ Validators.required ]) |
421 | 427 | ||
422 | waitTranscodingControl.disable() | 428 | waitTranscodingControl.disable() |
@@ -437,6 +443,16 @@ export class VideoEditComponent implements OnInit, OnDestroy { | |||
437 | 443 | ||
438 | this.firstPatchDone = true | 444 | this.firstPatchDone = true |
439 | 445 | ||
446 | this.passwordProtectionSelected = newPrivacyId === VideoPrivacy.PASSWORD_PROTECTED | ||
447 | const videoPasswordControl = this.form.get('videoPassword') | ||
448 | |||
449 | if (this.passwordProtectionSelected) { | ||
450 | videoPasswordControl.setValidators([ Validators.required ]) | ||
451 | } else { | ||
452 | videoPasswordControl.clearValidators() | ||
453 | } | ||
454 | videoPasswordControl.updateValueAndValidity() | ||
455 | |||
440 | } | 456 | } |
441 | ) | 457 | ) |
442 | } | 458 | } |
diff --git a/client/src/app/+videos/+video-edit/video-update.component.ts b/client/src/app/+videos/+video-edit/video-update.component.ts index ad71162b8..629d95c08 100644 --- a/client/src/app/+videos/+video-edit/video-update.component.ts +++ b/client/src/app/+videos/+video-edit/video-update.component.ts | |||
@@ -49,10 +49,10 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { | |||
49 | this.buildForm({}) | 49 | this.buildForm({}) |
50 | 50 | ||
51 | const { videoData } = this.route.snapshot.data | 51 | const { videoData } = this.route.snapshot.data |
52 | const { video, videoChannels, videoCaptions, videoSource, liveVideo } = videoData | 52 | const { video, videoChannels, videoCaptions, videoSource, liveVideo, videoPassword } = videoData |
53 | 53 | ||
54 | this.videoDetails = video | 54 | this.videoDetails = video |
55 | this.videoEdit = new VideoEdit(this.videoDetails) | 55 | this.videoEdit = new VideoEdit(this.videoDetails, videoPassword) |
56 | 56 | ||
57 | this.userVideoChannels = videoChannels | 57 | this.userVideoChannels = videoChannels |
58 | this.videoCaptions = videoCaptions | 58 | this.videoCaptions = videoCaptions |
diff --git a/client/src/app/+videos/+video-edit/video-update.resolver.ts b/client/src/app/+videos/+video-edit/video-update.resolver.ts index 6612d22de..2c99b36a8 100644 --- a/client/src/app/+videos/+video-edit/video-update.resolver.ts +++ b/client/src/app/+videos/+video-edit/video-update.resolver.ts | |||
@@ -4,8 +4,9 @@ import { Injectable } from '@angular/core' | |||
4 | import { ActivatedRouteSnapshot } from '@angular/router' | 4 | import { ActivatedRouteSnapshot } from '@angular/router' |
5 | import { AuthService } from '@app/core' | 5 | import { AuthService } from '@app/core' |
6 | import { listUserChannelsForSelect } from '@app/helpers' | 6 | import { listUserChannelsForSelect } from '@app/helpers' |
7 | import { VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main' | 7 | import { VideoCaptionService, VideoDetails, VideoService, VideoPasswordService } from '@app/shared/shared-main' |
8 | import { LiveVideoService } from '@app/shared/shared-video-live' | 8 | import { LiveVideoService } from '@app/shared/shared-video-live' |
9 | import { VideoPrivacy } from '@shared/models/videos' | ||
9 | 10 | ||
10 | @Injectable() | 11 | @Injectable() |
11 | export class VideoUpdateResolver { | 12 | export class VideoUpdateResolver { |
@@ -13,7 +14,8 @@ export class VideoUpdateResolver { | |||
13 | private videoService: VideoService, | 14 | private videoService: VideoService, |
14 | private liveVideoService: LiveVideoService, | 15 | private liveVideoService: LiveVideoService, |
15 | private authService: AuthService, | 16 | private authService: AuthService, |
16 | private videoCaptionService: VideoCaptionService | 17 | private videoCaptionService: VideoCaptionService, |
18 | private videoPasswordService: VideoPasswordService | ||
17 | ) { | 19 | ) { |
18 | } | 20 | } |
19 | 21 | ||
@@ -21,11 +23,11 @@ export class VideoUpdateResolver { | |||
21 | const uuid: string = route.params['uuid'] | 23 | const uuid: string = route.params['uuid'] |
22 | 24 | ||
23 | return this.videoService.getVideo({ videoId: uuid }) | 25 | return this.videoService.getVideo({ videoId: uuid }) |
24 | .pipe( | 26 | .pipe( |
25 | switchMap(video => forkJoin(this.buildVideoObservables(video))), | 27 | switchMap(video => forkJoin(this.buildVideoObservables(video))), |
26 | map(([ video, videoSource, videoChannels, videoCaptions, liveVideo ]) => | 28 | map(([ video, videoSource, videoChannels, videoCaptions, liveVideo, videoPassword ]) => |
27 | ({ video, videoChannels, videoCaptions, videoSource, liveVideo })) | 29 | ({ video, videoChannels, videoCaptions, videoSource, liveVideo, videoPassword })) |
28 | ) | 30 | ) |
29 | } | 31 | } |
30 | 32 | ||
31 | private buildVideoObservables (video: VideoDetails) { | 33 | private buildVideoObservables (video: VideoDetails) { |
@@ -46,6 +48,10 @@ export class VideoUpdateResolver { | |||
46 | 48 | ||
47 | video.isLive | 49 | video.isLive |
48 | ? this.liveVideoService.getVideoLive(video.id) | 50 | ? this.liveVideoService.getVideoLive(video.id) |
51 | : of(undefined), | ||
52 | |||
53 | video.privacy.id === VideoPrivacy.PASSWORD_PROTECTED | ||
54 | ? this.videoPasswordService.getVideoPasswords({ videoUUID: video.uuid }) | ||
49 | : of(undefined) | 55 | : of(undefined) |
50 | ] | 56 | ] |
51 | } | 57 | } |
diff --git a/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.html b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.html index cf32e371a..140a391e9 100644 --- a/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.html +++ b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.html | |||
@@ -1,7 +1,7 @@ | |||
1 | <div class="video-actions-rates"> | 1 | <div class="video-actions-rates"> |
2 | <div class="video-actions justify-content-end"> | 2 | <div class="video-actions justify-content-end"> |
3 | <my-video-rate | 3 | <my-video-rate |
4 | [video]="video" [isUserLoggedIn]="isUserLoggedIn" | 4 | [video]="video" [videoPassword]="videoPassword" [isUserLoggedIn]="isUserLoggedIn" |
5 | (rateUpdated)="onRateUpdated($event)" (userRatingLoaded)="onRateUpdated($event)" | 5 | (rateUpdated)="onRateUpdated($event)" (userRatingLoaded)="onRateUpdated($event)" |
6 | ></my-video-rate> | 6 | ></my-video-rate> |
7 | 7 | ||
@@ -20,7 +20,7 @@ | |||
20 | 20 | ||
21 | <div | 21 | <div |
22 | class="action-dropdown" ngbDropdown placement="top" role="button" autoClose="outside" | 22 | class="action-dropdown" ngbDropdown placement="top" role="button" autoClose="outside" |
23 | *ngIf="isUserLoggedIn" (openChange)="addContent.openChange($event)" | 23 | *ngIf="isVideoAddableToPlaylist()" (openChange)="addContent.openChange($event)" |
24 | [ngbTooltip]="tooltipSaveToPlaylist" | 24 | [ngbTooltip]="tooltipSaveToPlaylist" |
25 | placement="bottom auto" | 25 | placement="bottom auto" |
26 | > | 26 | > |
@@ -43,7 +43,7 @@ | |||
43 | <span class="icon-text d-none d-sm-inline" i18n>DOWNLOAD</span> | 43 | <span class="icon-text d-none d-sm-inline" i18n>DOWNLOAD</span> |
44 | </button> | 44 | </button> |
45 | 45 | ||
46 | <my-video-download #videoDownloadModal></my-video-download> | 46 | <my-video-download #videoDownloadModal [videoPassword]="videoPassword"></my-video-download> |
47 | </ng-container> | 47 | </ng-container> |
48 | 48 | ||
49 | <ng-container *ngIf="isUserLoggedIn"> | 49 | <ng-container *ngIf="isUserLoggedIn"> |
diff --git a/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.ts b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.ts index 51718827d..e6c0d4de1 100644 --- a/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.ts +++ b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.ts | |||
@@ -5,7 +5,7 @@ import { VideoShareComponent } from '@app/shared/shared-share-modal' | |||
5 | import { SupportModalComponent } from '@app/shared/shared-support-modal' | 5 | import { SupportModalComponent } from '@app/shared/shared-support-modal' |
6 | import { VideoActionsDisplayType, VideoDownloadComponent } from '@app/shared/shared-video-miniature' | 6 | import { VideoActionsDisplayType, VideoDownloadComponent } from '@app/shared/shared-video-miniature' |
7 | import { VideoPlaylist } from '@app/shared/shared-video-playlist' | 7 | import { VideoPlaylist } from '@app/shared/shared-video-playlist' |
8 | import { UserVideoRateType, VideoCaption } from '@shared/models/videos' | 8 | import { UserVideoRateType, VideoCaption, VideoPrivacy } from '@shared/models/videos' |
9 | 9 | ||
10 | @Component({ | 10 | @Component({ |
11 | selector: 'my-action-buttons', | 11 | selector: 'my-action-buttons', |
@@ -18,10 +18,12 @@ export class ActionButtonsComponent implements OnInit, OnChanges { | |||
18 | @ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent | 18 | @ViewChild('videoDownloadModal') videoDownloadModal: VideoDownloadComponent |
19 | 19 | ||
20 | @Input() video: VideoDetails | 20 | @Input() video: VideoDetails |
21 | @Input() videoPassword: string | ||
21 | @Input() videoCaptions: VideoCaption[] | 22 | @Input() videoCaptions: VideoCaption[] |
22 | @Input() playlist: VideoPlaylist | 23 | @Input() playlist: VideoPlaylist |
23 | 24 | ||
24 | @Input() isUserLoggedIn: boolean | 25 | @Input() isUserLoggedIn: boolean |
26 | @Input() isUserOwner: boolean | ||
25 | 27 | ||
26 | @Input() currentTime: number | 28 | @Input() currentTime: number |
27 | @Input() currentPlaylistPosition: number | 29 | @Input() currentPlaylistPosition: number |
@@ -92,4 +94,14 @@ export class ActionButtonsComponent implements OnInit, OnChanges { | |||
92 | private setVideoLikesBarTooltipText () { | 94 | private setVideoLikesBarTooltipText () { |
93 | this.likesBarTooltipText = `${this.video.likes} likes / ${this.video.dislikes} dislikes` | 95 | this.likesBarTooltipText = `${this.video.likes} likes / ${this.video.dislikes} dislikes` |
94 | } | 96 | } |
97 | |||
98 | isVideoAddableToPlaylist () { | ||
99 | const isPasswordProtected = this.video.privacy.id === VideoPrivacy.PASSWORD_PROTECTED | ||
100 | |||
101 | if (!this.isUserLoggedIn) return false | ||
102 | |||
103 | if (isPasswordProtected) return this.isUserOwner | ||
104 | |||
105 | return true | ||
106 | } | ||
95 | } | 107 | } |
diff --git a/client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.ts b/client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.ts index d0c138834..11966ce34 100644 --- a/client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.ts +++ b/client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.ts | |||
@@ -12,6 +12,7 @@ import { UserVideoRateType } from '@shared/models' | |||
12 | }) | 12 | }) |
13 | export class VideoRateComponent implements OnInit, OnChanges, OnDestroy { | 13 | export class VideoRateComponent implements OnInit, OnChanges, OnDestroy { |
14 | @Input() video: VideoDetails | 14 | @Input() video: VideoDetails |
15 | @Input() videoPassword: string | ||
15 | @Input() isUserLoggedIn: boolean | 16 | @Input() isUserLoggedIn: boolean |
16 | 17 | ||
17 | @Output() userRatingLoaded = new EventEmitter<UserVideoRateType>() | 18 | @Output() userRatingLoaded = new EventEmitter<UserVideoRateType>() |
@@ -103,13 +104,13 @@ export class VideoRateComponent implements OnInit, OnChanges, OnDestroy { | |||
103 | } | 104 | } |
104 | 105 | ||
105 | private setRating (nextRating: UserVideoRateType) { | 106 | private setRating (nextRating: UserVideoRateType) { |
106 | const ratingMethods: { [id in UserVideoRateType]: (id: string) => Observable<any> } = { | 107 | const ratingMethods: { [id in UserVideoRateType]: (id: string, videoPassword: string) => Observable<any> } = { |
107 | like: this.videoService.setVideoLike, | 108 | like: this.videoService.setVideoLike, |
108 | dislike: this.videoService.setVideoDislike, | 109 | dislike: this.videoService.setVideoDislike, |
109 | none: this.videoService.unsetVideoLike | 110 | none: this.videoService.unsetVideoLike |
110 | } | 111 | } |
111 | 112 | ||
112 | ratingMethods[nextRating].call(this.videoService, this.video.uuid) | 113 | ratingMethods[nextRating].call(this.videoService, this.video.uuid, this.videoPassword) |
113 | .subscribe({ | 114 | .subscribe({ |
114 | next: () => { | 115 | next: () => { |
115 | // Update the video like attribute | 116 | // Update the video like attribute |
diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts b/client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts index 033097084..1d9e10d0a 100644 --- a/client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts +++ b/client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts | |||
@@ -29,6 +29,7 @@ import { VideoCommentCreate } from '@shared/models' | |||
29 | export class VideoCommentAddComponent extends FormReactive implements OnChanges, OnInit { | 29 | export class VideoCommentAddComponent extends FormReactive implements OnChanges, OnInit { |
30 | @Input() user: User | 30 | @Input() user: User |
31 | @Input() video: Video | 31 | @Input() video: Video |
32 | @Input() videoPassword: string | ||
32 | @Input() parentComment?: VideoComment | 33 | @Input() parentComment?: VideoComment |
33 | @Input() parentComments?: VideoComment[] | 34 | @Input() parentComments?: VideoComment[] |
34 | @Input() focusOnInit = false | 35 | @Input() focusOnInit = false |
@@ -176,12 +177,17 @@ export class VideoCommentAddComponent extends FormReactive implements OnChanges, | |||
176 | 177 | ||
177 | private addCommentReply (commentCreate: VideoCommentCreate) { | 178 | private addCommentReply (commentCreate: VideoCommentCreate) { |
178 | return this.videoCommentService | 179 | return this.videoCommentService |
179 | .addCommentReply(this.video.uuid, this.parentComment.id, commentCreate) | 180 | .addCommentReply({ |
181 | videoId: this.video.uuid, | ||
182 | inReplyToCommentId: this.parentComment.id, | ||
183 | comment: commentCreate, | ||
184 | videoPassword: this.videoPassword | ||
185 | }) | ||
180 | } | 186 | } |
181 | 187 | ||
182 | private addCommentThread (commentCreate: VideoCommentCreate) { | 188 | private addCommentThread (commentCreate: VideoCommentCreate) { |
183 | return this.videoCommentService | 189 | return this.videoCommentService |
184 | .addCommentThread(this.video.uuid, commentCreate) | 190 | .addCommentThread(this.video.uuid, commentCreate, this.videoPassword) |
185 | } | 191 | } |
186 | 192 | ||
187 | private initTextValue () { | 193 | private initTextValue () { |
diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.html b/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.html index 91bd8309c..80ea22a20 100644 --- a/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.html +++ b/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.html | |||
@@ -62,6 +62,7 @@ | |||
62 | *ngIf="!comment.isDeleted && inReplyToCommentId === comment.id" | 62 | *ngIf="!comment.isDeleted && inReplyToCommentId === comment.id" |
63 | [user]="user" | 63 | [user]="user" |
64 | [video]="video" | 64 | [video]="video" |
65 | [videoPassword]="videoPassword" | ||
65 | [parentComment]="comment" | 66 | [parentComment]="comment" |
66 | [parentComments]="newParentComments" | 67 | [parentComments]="newParentComments" |
67 | [focusOnInit]="true" | 68 | [focusOnInit]="true" |
@@ -75,6 +76,7 @@ | |||
75 | <my-video-comment | 76 | <my-video-comment |
76 | [comment]="commentChild.comment" | 77 | [comment]="commentChild.comment" |
77 | [video]="video" | 78 | [video]="video" |
79 | [videoPassword]="videoPassword" | ||
78 | [inReplyToCommentId]="inReplyToCommentId" | 80 | [inReplyToCommentId]="inReplyToCommentId" |
79 | [commentTree]="commentChild" | 81 | [commentTree]="commentChild" |
80 | [parentComments]="newParentComments" | 82 | [parentComments]="newParentComments" |
diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts b/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts index 191ec4a28..4c85df657 100644 --- a/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts +++ b/client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts | |||
@@ -16,6 +16,7 @@ export class VideoCommentComponent implements OnInit, OnChanges { | |||
16 | @ViewChild('commentReportModal') commentReportModal: CommentReportComponent | 16 | @ViewChild('commentReportModal') commentReportModal: CommentReportComponent |
17 | 17 | ||
18 | @Input() video: Video | 18 | @Input() video: Video |
19 | @Input() videoPassword: string | ||
19 | @Input() comment: VideoComment | 20 | @Input() comment: VideoComment |
20 | @Input() parentComments: VideoComment[] = [] | 21 | @Input() parentComments: VideoComment[] = [] |
21 | @Input() commentTree: VideoCommentThreadTree | 22 | @Input() commentTree: VideoCommentThreadTree |
diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.html b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.html index a003a10eb..0932d2b7f 100644 --- a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.html +++ b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.html | |||
@@ -20,6 +20,7 @@ | |||
20 | <ng-template [ngIf]="video.commentsEnabled === true"> | 20 | <ng-template [ngIf]="video.commentsEnabled === true"> |
21 | <my-video-comment-add | 21 | <my-video-comment-add |
22 | [video]="video" | 22 | [video]="video" |
23 | [videoPassword]="videoPassword" | ||
23 | [user]="user" | 24 | [user]="user" |
24 | (commentCreated)="onCommentThreadCreated($event)" | 25 | (commentCreated)="onCommentThreadCreated($event)" |
25 | [textValue]="commentThreadRedraftValue" | 26 | [textValue]="commentThreadRedraftValue" |
@@ -34,6 +35,7 @@ | |||
34 | *ngIf="highlightedThread" | 35 | *ngIf="highlightedThread" |
35 | [comment]="highlightedThread" | 36 | [comment]="highlightedThread" |
36 | [video]="video" | 37 | [video]="video" |
38 | [videoPassword]="videoPassword" | ||
37 | [inReplyToCommentId]="inReplyToCommentId" | 39 | [inReplyToCommentId]="inReplyToCommentId" |
38 | [commentTree]="threadComments[highlightedThread.id]" | 40 | [commentTree]="threadComments[highlightedThread.id]" |
39 | [highlightedComment]="true" | 41 | [highlightedComment]="true" |
@@ -53,6 +55,7 @@ | |||
53 | *ngIf="!highlightedThread || comment.id !== highlightedThread.id" | 55 | *ngIf="!highlightedThread || comment.id !== highlightedThread.id" |
54 | [comment]="comment" | 56 | [comment]="comment" |
55 | [video]="video" | 57 | [video]="video" |
58 | [videoPassword]="videoPassword" | ||
56 | [inReplyToCommentId]="inReplyToCommentId" | 59 | [inReplyToCommentId]="inReplyToCommentId" |
57 | [commentTree]="threadComments[comment.id]" | 60 | [commentTree]="threadComments[comment.id]" |
58 | [firstInThread]="i + 1 !== comments.length" | 61 | [firstInThread]="i + 1 !== comments.length" |
diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts index 96bdb28c9..848936f91 100644 --- a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts +++ b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts | |||
@@ -15,6 +15,7 @@ import { PeerTubeProblemDocument, ServerErrorCode } from '@shared/models' | |||
15 | export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy { | 15 | export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy { |
16 | @ViewChild('commentHighlightBlock') commentHighlightBlock: ElementRef | 16 | @ViewChild('commentHighlightBlock') commentHighlightBlock: ElementRef |
17 | @Input() video: VideoDetails | 17 | @Input() video: VideoDetails |
18 | @Input() videoPassword: string | ||
18 | @Input() user: User | 19 | @Input() user: User |
19 | 20 | ||
20 | @Output() timestampClicked = new EventEmitter<number>() | 21 | @Output() timestampClicked = new EventEmitter<number>() |
@@ -80,7 +81,8 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy { | |||
80 | 81 | ||
81 | const params = { | 82 | const params = { |
82 | videoId: this.video.uuid, | 83 | videoId: this.video.uuid, |
83 | threadId: commentId | 84 | threadId: commentId, |
85 | videoPassword: this.videoPassword | ||
84 | } | 86 | } |
85 | 87 | ||
86 | const obs = this.hooks.wrapObsFun( | 88 | const obs = this.hooks.wrapObsFun( |
@@ -119,6 +121,7 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy { | |||
119 | loadMoreThreads () { | 121 | loadMoreThreads () { |
120 | const params = { | 122 | const params = { |
121 | videoId: this.video.uuid, | 123 | videoId: this.video.uuid, |
124 | videoPassword: this.videoPassword, | ||
122 | componentPagination: this.componentPagination, | 125 | componentPagination: this.componentPagination, |
123 | sort: this.sort | 126 | sort: this.sort |
124 | } | 127 | } |
diff --git a/client/src/app/+videos/+video-watch/shared/information/video-alert.component.html b/client/src/app/+videos/+video-watch/shared/information/video-alert.component.html index 79b83811d..45e222743 100644 --- a/client/src/app/+videos/+video-watch/shared/information/video-alert.component.html +++ b/client/src/app/+videos/+video-watch/shared/information/video-alert.component.html | |||
@@ -42,3 +42,7 @@ | |||
42 | <div class="blocked-label" i18n>This video is blocked.</div> | 42 | <div class="blocked-label" i18n>This video is blocked.</div> |
43 | {{ video.blacklistedReason }} | 43 | {{ video.blacklistedReason }} |
44 | </div> | 44 | </div> |
45 | |||
46 | <div i18n class="alert alert-warning" *ngIf="video?.canAccessPasswordProtectedVideoWithoutPassword(user)"> | ||
47 | This video is password protected. | ||
48 | </div> | ||
diff --git a/client/src/app/+videos/+video-watch/shared/information/video-alert.component.ts b/client/src/app/+videos/+video-watch/shared/information/video-alert.component.ts index ba79fabc8..8781ead7e 100644 --- a/client/src/app/+videos/+video-watch/shared/information/video-alert.component.ts +++ b/client/src/app/+videos/+video-watch/shared/information/video-alert.component.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { Component, Input } from '@angular/core' | 1 | import { Component, Input } from '@angular/core' |
2 | import { AuthUser } from '@app/core' | ||
2 | import { VideoDetails } from '@app/shared/shared-main' | 3 | import { VideoDetails } from '@app/shared/shared-main' |
3 | import { VideoState } from '@shared/models' | 4 | import { VideoPrivacy, VideoState } from '@shared/models' |
4 | 5 | ||
5 | @Component({ | 6 | @Component({ |
6 | selector: 'my-video-alert', | 7 | selector: 'my-video-alert', |
@@ -8,6 +9,7 @@ import { VideoState } from '@shared/models' | |||
8 | styleUrls: [ './video-alert.component.scss' ] | 9 | styleUrls: [ './video-alert.component.scss' ] |
9 | }) | 10 | }) |
10 | export class VideoAlertComponent { | 11 | export class VideoAlertComponent { |
12 | @Input() user: AuthUser | ||
11 | @Input() video: VideoDetails | 13 | @Input() video: VideoDetails |
12 | @Input() noPlaylistVideoFound: boolean | 14 | @Input() noPlaylistVideoFound: boolean |
13 | 15 | ||
@@ -46,4 +48,8 @@ export class VideoAlertComponent { | |||
46 | isLiveEnded () { | 48 | isLiveEnded () { |
47 | return this.video?.state.id === VideoState.LIVE_ENDED | 49 | return this.video?.state.id === VideoState.LIVE_ENDED |
48 | } | 50 | } |
51 | |||
52 | isVideoPasswordProtected () { | ||
53 | return this.video?.privacy.id === VideoPrivacy.PASSWORD_PROTECTED | ||
54 | } | ||
49 | } | 55 | } |
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.html b/client/src/app/+videos/+video-watch/video-watch.component.html index 461891779..80fd6e40f 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.html +++ b/client/src/app/+videos/+video-watch/video-watch.component.html | |||
@@ -19,7 +19,7 @@ | |||
19 | <my-plugin-placeholder pluginId="player-next"></my-plugin-placeholder> | 19 | <my-plugin-placeholder pluginId="player-next"></my-plugin-placeholder> |
20 | </div> | 20 | </div> |
21 | 21 | ||
22 | <my-video-alert [video]="video" [noPlaylistVideoFound]="noPlaylistVideoFound"></my-video-alert> | 22 | <my-video-alert [video]="video" [user]="user" [noPlaylistVideoFound]="noPlaylistVideoFound"></my-video-alert> |
23 | 23 | ||
24 | <!-- Video information --> | 24 | <!-- Video information --> |
25 | <div *ngIf="video" class="margin-content video-bottom"> | 25 | <div *ngIf="video" class="margin-content video-bottom"> |
@@ -51,8 +51,8 @@ | |||
51 | </div> | 51 | </div> |
52 | 52 | ||
53 | <my-action-buttons | 53 | <my-action-buttons |
54 | [video]="video" [isUserLoggedIn]="isUserLoggedIn()" [videoCaptions]="videoCaptions" [playlist]="playlist" | 54 | [video]="video" [videoPassword]="videoPassword" [isUserLoggedIn]="isUserLoggedIn()" [isUserOwner]="isUserOwner()" [videoCaptions]="videoCaptions" |
55 | [currentTime]="getCurrentTime()" [currentPlaylistPosition]="getCurrentPlaylistPosition()" | 55 | [playlist]="playlist" [currentTime]="getCurrentTime()" [currentPlaylistPosition]="getCurrentPlaylistPosition()" |
56 | ></my-action-buttons> | 56 | ></my-action-buttons> |
57 | </div> | 57 | </div> |
58 | </div> | 58 | </div> |
@@ -92,6 +92,7 @@ | |||
92 | <my-video-comments | 92 | <my-video-comments |
93 | class="border-top" | 93 | class="border-top" |
94 | [video]="video" | 94 | [video]="video" |
95 | [videoPassword]="videoPassword" | ||
95 | [user]="user" | 96 | [user]="user" |
96 | (timestampClicked)="handleTimestampClicked($event)" | 97 | (timestampClicked)="handleTimestampClicked($event)" |
97 | ></my-video-comments> | 98 | ></my-video-comments> |
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 19ad97d42..aba3ee086 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.ts +++ b/client/src/app/+videos/+video-watch/video-watch.component.ts | |||
@@ -25,7 +25,7 @@ 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, videoRequiresAuth } from '@root-helpers/video' | 28 | import { isP2PEnabled, videoRequiresUserAuth, videoRequiresFileToken } 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, |
@@ -68,6 +68,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
68 | video: VideoDetails = null | 68 | video: VideoDetails = null |
69 | videoCaptions: VideoCaption[] = [] | 69 | videoCaptions: VideoCaption[] = [] |
70 | liveVideo: LiveVideo | 70 | liveVideo: LiveVideo |
71 | videoPassword: string | ||
71 | 72 | ||
72 | playlistPosition: number | 73 | playlistPosition: number |
73 | playlist: VideoPlaylist = null | 74 | playlist: VideoPlaylist = null |
@@ -191,6 +192,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
191 | return this.authService.isLoggedIn() | 192 | return this.authService.isLoggedIn() |
192 | } | 193 | } |
193 | 194 | ||
195 | isUserOwner () { | ||
196 | return this.video.isLocal === true && this.video.account.name === this.user?.username | ||
197 | } | ||
198 | |||
194 | isVideoBlur (video: Video) { | 199 | isVideoBlur (video: Video) { |
195 | return video.isVideoNSFWForUser(this.user, this.serverConfig) | 200 | return video.isVideoNSFWForUser(this.user, this.serverConfig) |
196 | } | 201 | } |
@@ -243,8 +248,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
243 | private loadVideo (options: { | 248 | private loadVideo (options: { |
244 | videoId: string | 249 | videoId: string |
245 | forceAutoplay: boolean | 250 | forceAutoplay: boolean |
251 | videoPassword?: string | ||
246 | }) { | 252 | }) { |
247 | const { videoId, forceAutoplay } = options | 253 | const { videoId, forceAutoplay, videoPassword } = options |
248 | 254 | ||
249 | if (this.isSameElement(this.video, videoId)) return | 255 | if (this.isSameElement(this.video, videoId)) return |
250 | 256 | ||
@@ -254,7 +260,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
254 | 260 | ||
255 | const videoObs = this.hooks.wrapObsFun( | 261 | const videoObs = this.hooks.wrapObsFun( |
256 | this.videoService.getVideo.bind(this.videoService), | 262 | this.videoService.getVideo.bind(this.videoService), |
257 | { videoId }, | 263 | { videoId, videoPassword }, |
258 | 'video-watch', | 264 | 'video-watch', |
259 | 'filter:api.video-watch.video.get.params', | 265 | 'filter:api.video-watch.video.get.params', |
260 | 'filter:api.video-watch.video.get.result' | 266 | 'filter:api.video-watch.video.get.result' |
@@ -269,16 +275,16 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
269 | }), | 275 | }), |
270 | 276 | ||
271 | switchMap(({ video, live }) => { | 277 | switchMap(({ video, live }) => { |
272 | if (!videoRequiresAuth(video)) return of({ video, live, videoFileToken: undefined }) | 278 | if (!videoRequiresFileToken(video)) return of({ video, live, videoFileToken: undefined }) |
273 | 279 | ||
274 | return this.videoFileTokenService.getVideoFileToken(video.uuid) | 280 | return this.videoFileTokenService.getVideoFileToken({ videoUUID: video.uuid, videoPassword }) |
275 | .pipe(map(({ token }) => ({ video, live, videoFileToken: token }))) | 281 | .pipe(map(({ token }) => ({ video, live, videoFileToken: token }))) |
276 | }) | 282 | }) |
277 | ) | 283 | ) |
278 | 284 | ||
279 | forkJoin([ | 285 | forkJoin([ |
280 | videoAndLiveObs, | 286 | videoAndLiveObs, |
281 | this.videoCaptionService.listCaptions(videoId), | 287 | this.videoCaptionService.listCaptions(videoId, videoPassword), |
282 | this.userService.getAnonymousOrLoggedUser() | 288 | this.userService.getAnonymousOrLoggedUser() |
283 | ]).subscribe({ | 289 | ]).subscribe({ |
284 | next: ([ { video, live, videoFileToken }, captionsResult, loggedInOrAnonymousUser ]) => { | 290 | next: ([ { video, live, videoFileToken }, captionsResult, loggedInOrAnonymousUser ]) => { |
@@ -304,13 +310,25 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
304 | live, | 310 | live, |
305 | videoCaptions: captionsResult.data, | 311 | videoCaptions: captionsResult.data, |
306 | videoFileToken, | 312 | videoFileToken, |
313 | videoPassword, | ||
307 | loggedInOrAnonymousUser, | 314 | loggedInOrAnonymousUser, |
308 | urlOptions, | 315 | urlOptions, |
309 | forceAutoplay | 316 | forceAutoplay |
310 | }).catch(err => this.handleGlobalError(err)) | 317 | }).catch(err => { |
318 | this.handleGlobalError(err) | ||
319 | }) | ||
311 | }, | 320 | }, |
321 | error: async err => { | ||
322 | if (err.body.code === ServerErrorCode.VIDEO_REQUIRES_PASSWORD || err.body.code === ServerErrorCode.INCORRECT_VIDEO_PASSWORD) { | ||
323 | const { confirmed, password } = await this.handleVideoPasswordError(err) | ||
324 | |||
325 | if (confirmed === false) return this.location.back() | ||
312 | 326 | ||
313 | error: err => this.handleRequestError(err) | 327 | this.loadVideo({ ...options, videoPassword: password }) |
328 | } else { | ||
329 | this.handleRequestError(err) | ||
330 | } | ||
331 | } | ||
314 | }) | 332 | }) |
315 | } | 333 | } |
316 | 334 | ||
@@ -375,17 +393,35 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
375 | this.notifier.error(errorMessage) | 393 | this.notifier.error(errorMessage) |
376 | } | 394 | } |
377 | 395 | ||
396 | private handleVideoPasswordError (err: any) { | ||
397 | let isIncorrectPassword: boolean | ||
398 | |||
399 | if (err.body.code === ServerErrorCode.VIDEO_REQUIRES_PASSWORD) { | ||
400 | isIncorrectPassword = false | ||
401 | } else if (err.body.code === ServerErrorCode.INCORRECT_VIDEO_PASSWORD) { | ||
402 | this.videoPassword = undefined | ||
403 | isIncorrectPassword = true | ||
404 | } | ||
405 | |||
406 | return this.confirmService.confirmWithPassword({ | ||
407 | message: $localize`You need a password to watch this video`, | ||
408 | title: $localize`This video is password protected`, | ||
409 | errorMessage: isIncorrectPassword ? $localize`Incorrect password, please enter a correct password` : '' | ||
410 | }) | ||
411 | } | ||
412 | |||
378 | private async onVideoFetched (options: { | 413 | private async onVideoFetched (options: { |
379 | video: VideoDetails | 414 | video: VideoDetails |
380 | live: LiveVideo | 415 | live: LiveVideo |
381 | videoCaptions: VideoCaption[] | 416 | videoCaptions: VideoCaption[] |
382 | videoFileToken: string | 417 | videoFileToken: string |
418 | videoPassword: string | ||
383 | 419 | ||
384 | urlOptions: URLOptions | 420 | urlOptions: URLOptions |
385 | loggedInOrAnonymousUser: User | 421 | loggedInOrAnonymousUser: User |
386 | forceAutoplay: boolean | 422 | forceAutoplay: boolean |
387 | }) { | 423 | }) { |
388 | const { video, live, videoCaptions, urlOptions, videoFileToken, loggedInOrAnonymousUser, forceAutoplay } = options | 424 | const { video, live, videoCaptions, urlOptions, videoFileToken, videoPassword, loggedInOrAnonymousUser, forceAutoplay } = options |
389 | 425 | ||
390 | this.subscribeToLiveEventsIfNeeded(this.video, video) | 426 | this.subscribeToLiveEventsIfNeeded(this.video, video) |
391 | 427 | ||
@@ -393,6 +429,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
393 | this.videoCaptions = videoCaptions | 429 | this.videoCaptions = videoCaptions |
394 | this.liveVideo = live | 430 | this.liveVideo = live |
395 | this.videoFileToken = videoFileToken | 431 | this.videoFileToken = videoFileToken |
432 | this.videoPassword = videoPassword | ||
396 | 433 | ||
397 | // Re init attributes | 434 | // Re init attributes |
398 | this.playerPlaceholderImgSrc = undefined | 435 | this.playerPlaceholderImgSrc = undefined |
@@ -450,6 +487,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
450 | videoCaptions: this.videoCaptions, | 487 | videoCaptions: this.videoCaptions, |
451 | liveVideo: this.liveVideo, | 488 | liveVideo: this.liveVideo, |
452 | videoFileToken: this.videoFileToken, | 489 | videoFileToken: this.videoFileToken, |
490 | videoPassword: this.videoPassword, | ||
453 | urlOptions, | 491 | urlOptions, |
454 | loggedInOrAnonymousUser, | 492 | loggedInOrAnonymousUser, |
455 | forceAutoplay, | 493 | forceAutoplay, |
@@ -600,6 +638,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
600 | videoCaptions: VideoCaption[] | 638 | videoCaptions: VideoCaption[] |
601 | 639 | ||
602 | videoFileToken: string | 640 | videoFileToken: string |
641 | videoPassword: string | ||
603 | 642 | ||
604 | urlOptions: CustomizationOptions & { playerMode: PlayerMode } | 643 | urlOptions: CustomizationOptions & { playerMode: PlayerMode } |
605 | 644 | ||
@@ -607,7 +646,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
607 | forceAutoplay: boolean | 646 | forceAutoplay: boolean |
608 | user?: AuthUser // Keep for plugins | 647 | user?: AuthUser // Keep for plugins |
609 | }) { | 648 | }) { |
610 | const { video, liveVideo, videoCaptions, videoFileToken, urlOptions, loggedInOrAnonymousUser, forceAutoplay } = params | 649 | const { video, liveVideo, videoCaptions, videoFileToken, videoPassword, urlOptions, loggedInOrAnonymousUser, forceAutoplay } = params |
611 | 650 | ||
612 | const getStartTime = () => { | 651 | const getStartTime = () => { |
613 | const byUrl = urlOptions.startTime !== undefined | 652 | const byUrl = urlOptions.startTime !== undefined |
@@ -689,7 +728,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
689 | serverUrl: environment.originServerUrl || window.location.origin, | 728 | serverUrl: environment.originServerUrl || window.location.origin, |
690 | 729 | ||
691 | videoFileToken: () => videoFileToken, | 730 | videoFileToken: () => videoFileToken, |
692 | requiresAuth: videoRequiresAuth(video), | 731 | requiresUserAuth: videoRequiresUserAuth(video, videoPassword), |
732 | requiresPassword: video.privacy.id === VideoPrivacy.PASSWORD_PROTECTED && | ||
733 | !video.canAccessPasswordProtectedVideoWithoutPassword(this.user), | ||
734 | videoPassword: () => videoPassword, | ||
693 | 735 | ||
694 | videoCaptions: playerCaptions, | 736 | videoCaptions: playerCaptions, |
695 | 737 | ||