diff options
author | Chocobozzz <me@florianbigard.com> | 2020-10-26 16:44:23 +0100 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2020-11-09 15:33:04 +0100 |
commit | b5b687550d8ef8beafdf706e45d6556fb5f4c876 (patch) | |
tree | 232412d463c78af1f7ab5797db5aecf1096d08da /client/src/app/+videos | |
parent | ef680f68351ec10ab73a1131570a6d14ce14c195 (diff) | |
download | PeerTube-b5b687550d8ef8beafdf706e45d6556fb5f4c876.tar.gz PeerTube-b5b687550d8ef8beafdf706e45d6556fb5f4c876.tar.zst PeerTube-b5b687550d8ef8beafdf706e45d6556fb5f4c876.zip |
Add ability to save live replay
Diffstat (limited to 'client/src/app/+videos')
10 files changed, 77 insertions, 49 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 0802e906d..d9e09c453 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 | |||
@@ -142,7 +142,7 @@ | |||
142 | </ng-template> | 142 | </ng-template> |
143 | </ng-container> | 143 | </ng-container> |
144 | 144 | ||
145 | <ng-container ngbNavItem> | 145 | <ng-container ngbNavItem *ngIf="!liveVideo"> |
146 | <a ngbNavLink i18n>Captions</a> | 146 | <a ngbNavLink i18n>Captions</a> |
147 | 147 | ||
148 | <ng-template ngbNavContent> | 148 | <ng-template ngbNavContent> |
@@ -211,6 +211,18 @@ | |||
211 | <label for="liveVideoStreamKey" i18n>Live stream key</label> | 211 | <label for="liveVideoStreamKey" i18n>Live stream key</label> |
212 | <my-input-readonly-copy id="liveVideoStreamKey" [value]="liveVideo.streamKey"></my-input-readonly-copy> | 212 | <my-input-readonly-copy id="liveVideoStreamKey" [value]="liveVideo.streamKey"></my-input-readonly-copy> |
213 | </div> | 213 | </div> |
214 | |||
215 | <div class="form-group" *ngIf="isSaveReplayEnabled()"> | ||
216 | <my-peertube-checkbox inputName="liveVideoSaveReplay" formControlName="saveReplay"> | ||
217 | <ng-template ptTemplate="label"> | ||
218 | <ng-container i18n>Automatically publish a replay when your live ends</ng-container> | ||
219 | </ng-template> | ||
220 | |||
221 | <ng-container ngProjectAs="description"> | ||
222 | <span i18n>⚠️ If you enable this option, your live will be terminated if you exceed your video quota</span> | ||
223 | </ng-container> | ||
224 | </my-peertube-checkbox> | ||
225 | </div> | ||
214 | </div> | 226 | </div> |
215 | </div> | 227 | </div> |
216 | </ng-template> | 228 | </ng-template> |
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 304bf7ed0..26d871e59 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 | |||
@@ -127,7 +127,8 @@ export class VideoEditComponent implements OnInit, OnDestroy { | |||
127 | support: VIDEO_SUPPORT_VALIDATOR, | 127 | support: VIDEO_SUPPORT_VALIDATOR, |
128 | schedulePublicationAt: VIDEO_SCHEDULE_PUBLICATION_AT_VALIDATOR, | 128 | schedulePublicationAt: VIDEO_SCHEDULE_PUBLICATION_AT_VALIDATOR, |
129 | originallyPublishedAt: VIDEO_ORIGINALLY_PUBLISHED_AT_VALIDATOR, | 129 | originallyPublishedAt: VIDEO_ORIGINALLY_PUBLISHED_AT_VALIDATOR, |
130 | liveStreamKey: null | 130 | liveStreamKey: null, |
131 | saveReplay: null | ||
131 | } | 132 | } |
132 | 133 | ||
133 | this.formValidatorService.updateForm( | 134 | this.formValidatorService.updateForm( |
@@ -239,6 +240,10 @@ export class VideoEditComponent implements OnInit, OnDestroy { | |||
239 | this.videoCaptionAddModal.show() | 240 | this.videoCaptionAddModal.show() |
240 | } | 241 | } |
241 | 242 | ||
243 | isSaveReplayEnabled () { | ||
244 | return this.serverConfig.live.allowReplay | ||
245 | } | ||
246 | |||
242 | private sortVideoCaptions () { | 247 | private sortVideoCaptions () { |
243 | this.videoCaptions.sort((v1, v2) => { | 248 | this.videoCaptions.sort((v1, v2) => { |
244 | if (v1.language.label < v2.language.label) return -1 | 249 | if (v1.language.label < v2.language.label) return -1 |
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.html b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.html index 8fae4044a..5657827a9 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.html +++ b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.html | |||
@@ -27,6 +27,11 @@ | |||
27 | {{ error }} | 27 | {{ error }} |
28 | </div> | 28 | </div> |
29 | 29 | ||
30 | <div class="alert alert-info" i18n *ngIf="isInUpdateForm && getMaxLiveDuration()"> | ||
31 | Max live duration is {{ getMaxLiveDuration() | myDurationFormatter }}. | ||
32 | If your live reaches this limit, it will be automatically terminated. | ||
33 | </div> | ||
34 | |||
30 | <!-- Hidden because we want to load the component --> | 35 | <!-- Hidden because we want to load the component --> |
31 | <form [hidden]="!isInUpdateForm" novalidate [formGroup]="form"> | 36 | <form [hidden]="!isInUpdateForm" novalidate [formGroup]="form"> |
32 | <my-video-edit | 37 | <my-video-edit |
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts index 0a9efc693..9868c37d2 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | 1 | ||
2 | import { forkJoin } from 'rxjs' | ||
2 | import { Component, EventEmitter, OnInit, Output } from '@angular/core' | 3 | import { Component, EventEmitter, OnInit, Output } from '@angular/core' |
3 | import { Router } from '@angular/router' | 4 | import { Router } from '@angular/router' |
4 | import { AuthService, CanComponentDeactivate, Notifier, ServerService } from '@app/core' | 5 | import { AuthService, CanComponentDeactivate, Notifier, ServerService } from '@app/core' |
@@ -6,7 +7,7 @@ import { scrollToTop } from '@app/helpers' | |||
6 | import { FormValidatorService } from '@app/shared/shared-forms' | 7 | import { FormValidatorService } from '@app/shared/shared-forms' |
7 | import { LiveVideoService, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' | 8 | import { LiveVideoService, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' |
8 | import { LoadingBarService } from '@ngx-loading-bar/core' | 9 | import { LoadingBarService } from '@ngx-loading-bar/core' |
9 | import { LiveVideo, VideoCreate, VideoPrivacy } from '@shared/models' | 10 | import { LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoPrivacy } from '@shared/models' |
10 | import { VideoSend } from './video-send' | 11 | import { VideoSend } from './video-send' |
11 | 12 | ||
12 | @Component({ | 13 | @Component({ |
@@ -53,7 +54,7 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, CanCompon | |||
53 | } | 54 | } |
54 | 55 | ||
55 | goLive () { | 56 | goLive () { |
56 | const video: VideoCreate = { | 57 | const video: LiveVideoCreate = { |
57 | name: 'Live', | 58 | name: 'Live', |
58 | privacy: VideoPrivacy.PRIVATE, | 59 | privacy: VideoPrivacy.PRIVATE, |
59 | nsfw: this.serverConfig.instance.isNSFW, | 60 | nsfw: this.serverConfig.instance.isNSFW, |
@@ -95,22 +96,32 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, CanCompon | |||
95 | video.id = this.videoId | 96 | video.id = this.videoId |
96 | video.uuid = this.videoUUID | 97 | video.uuid = this.videoUUID |
97 | 98 | ||
99 | const liveVideoUpdate: LiveVideoUpdate = { | ||
100 | saveReplay: this.form.value.saveReplay | ||
101 | } | ||
102 | |||
98 | // Update the video | 103 | // Update the video |
99 | this.updateVideoAndCaptions(video) | 104 | forkJoin([ |
100 | .subscribe( | 105 | this.updateVideoAndCaptions(video), |
101 | () => { | ||
102 | this.notifier.success($localize`Live published.`) | ||
103 | 106 | ||
104 | this.router.navigate([ '/videos/watch', video.uuid ]) | 107 | this.liveVideoService.updateLive(this.videoId, liveVideoUpdate) |
105 | }, | 108 | ]).subscribe( |
109 | () => { | ||
110 | this.notifier.success($localize`Live published.`) | ||
111 | |||
112 | this.router.navigate(['/videos/watch', video.uuid]) | ||
113 | }, | ||
106 | 114 | ||
107 | err => { | 115 | err => { |
108 | this.error = err.message | 116 | this.error = err.message |
109 | scrollToTop() | 117 | scrollToTop() |
110 | console.error(err) | 118 | console.error(err) |
111 | } | 119 | } |
112 | ) | 120 | ) |
121 | } | ||
113 | 122 | ||
123 | getMaxLiveDuration () { | ||
124 | return this.serverConfig.live.maxDuration / 1000 | ||
114 | } | 125 | } |
115 | 126 | ||
116 | private fetchVideoLive () { | 127 | private fetchVideoLive () { |
diff --git a/client/src/app/+videos/+video-edit/video-add.component.html b/client/src/app/+videos/+video-edit/video-add.component.html index bf2cc9c83..dc8c2f21d 100644 --- a/client/src/app/+videos/+video-edit/video-add.component.html +++ b/client/src/app/+videos/+video-edit/video-add.component.html | |||
@@ -13,7 +13,7 @@ | |||
13 | Instead, <a routerLink="/admin/users">create a dedicated account</a> to upload your videos. | 13 | Instead, <a routerLink="/admin/users">create a dedicated account</a> to upload your videos. |
14 | </div> | 14 | </div> |
15 | 15 | ||
16 | <my-user-quota *ngIf="!isInSecondStep()" [user]="user" [userInformationLoaded]="userInformationLoaded"></my-user-quota> | 16 | <my-user-quota *ngIf="!isInSecondStep() || secondStepType === 'go-live'" [user]="user" [userInformationLoaded]="userInformationLoaded"></my-user-quota> |
17 | 17 | ||
18 | <div class="title-page title-page-single" *ngIf="isInSecondStep()"> | 18 | <div class="title-page title-page-single" *ngIf="isInSecondStep()"> |
19 | <ng-container *ngIf="secondStepType === 'import-url' || secondStepType === 'import-torrent'" i18n>Import {{ videoName }}</ng-container> | 19 | <ng-container *ngIf="secondStepType === 'import-url' || secondStepType === 'import-torrent'" i18n>Import {{ videoName }}</ng-container> |
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 ec1305a33..7126ad05b 100644 --- a/client/src/app/+videos/+video-edit/video-update.component.ts +++ b/client/src/app/+videos/+video-edit/video-update.component.ts | |||
@@ -3,10 +3,11 @@ import { Component, HostListener, OnInit } from '@angular/core' | |||
3 | import { ActivatedRoute, Router } from '@angular/router' | 3 | import { ActivatedRoute, Router } from '@angular/router' |
4 | import { Notifier } from '@app/core' | 4 | import { Notifier } from '@app/core' |
5 | import { FormReactive, FormValidatorService, SelectChannelItem } from '@app/shared/shared-forms' | 5 | import { FormReactive, FormValidatorService, SelectChannelItem } from '@app/shared/shared-forms' |
6 | import { VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main' | 6 | import { LiveVideoService, VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main' |
7 | import { LoadingBarService } from '@ngx-loading-bar/core' | 7 | import { LoadingBarService } from '@ngx-loading-bar/core' |
8 | import { LiveVideo, VideoPrivacy } from '@shared/models' | 8 | import { LiveVideo, LiveVideoUpdate, VideoPrivacy } from '@shared/models' |
9 | import { hydrateFormFromVideo } from './shared/video-edit-utils' | 9 | import { hydrateFormFromVideo } from './shared/video-edit-utils' |
10 | import { of } from 'rxjs' | ||
10 | 11 | ||
11 | @Component({ | 12 | @Component({ |
12 | selector: 'my-videos-update', | 13 | selector: 'my-videos-update', |
@@ -32,7 +33,8 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { | |||
32 | private notifier: Notifier, | 33 | private notifier: Notifier, |
33 | private videoService: VideoService, | 34 | private videoService: VideoService, |
34 | private loadingBar: LoadingBarService, | 35 | private loadingBar: LoadingBarService, |
35 | private videoCaptionService: VideoCaptionService | 36 | private videoCaptionService: VideoCaptionService, |
37 | private liveVideoService: LiveVideoService | ||
36 | ) { | 38 | ) { |
37 | super() | 39 | super() |
38 | } | 40 | } |
@@ -56,7 +58,15 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { | |||
56 | } | 58 | } |
57 | 59 | ||
58 | // FIXME: Angular does not detect the change inside this subscription, so use the patched setTimeout | 60 | // FIXME: Angular does not detect the change inside this subscription, so use the patched setTimeout |
59 | setTimeout(() => hydrateFormFromVideo(this.form, this.video, true)) | 61 | setTimeout(() => { |
62 | hydrateFormFromVideo(this.form, this.video, true) | ||
63 | |||
64 | if (this.liveVideo) { | ||
65 | this.form.patchValue({ | ||
66 | saveReplay: this.liveVideo.saveReplay | ||
67 | }) | ||
68 | } | ||
69 | }) | ||
60 | }, | 70 | }, |
61 | 71 | ||
62 | err => { | 72 | err => { |
@@ -102,6 +112,10 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { | |||
102 | 112 | ||
103 | this.video.patch(this.form.value) | 113 | this.video.patch(this.form.value) |
104 | 114 | ||
115 | const liveVideoUpdate: LiveVideoUpdate = { | ||
116 | saveReplay: this.form.value.saveReplay | ||
117 | } | ||
118 | |||
105 | this.loadingBar.useRef().start() | 119 | this.loadingBar.useRef().start() |
106 | this.isUpdatingVideo = true | 120 | this.isUpdatingVideo = true |
107 | 121 | ||
@@ -109,7 +123,13 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { | |||
109 | this.videoService.updateVideo(this.video) | 123 | this.videoService.updateVideo(this.video) |
110 | .pipe( | 124 | .pipe( |
111 | // Then update captions | 125 | // Then update captions |
112 | switchMap(() => this.videoCaptionService.updateCaptions(this.video.id, this.videoCaptions)) | 126 | switchMap(() => this.videoCaptionService.updateCaptions(this.video.id, this.videoCaptions)), |
127 | |||
128 | switchMap(() => { | ||
129 | if (!this.liveVideo) return of(undefined) | ||
130 | |||
131 | return this.liveVideoService.updateLive(this.video.id, liveVideoUpdate) | ||
132 | }) | ||
113 | ) | 133 | ) |
114 | .subscribe( | 134 | .subscribe( |
115 | () => { | 135 | () => { |
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 b7ec22dd5..5388a64b0 100644 --- a/client/src/app/+videos/+video-edit/video-update.resolver.ts +++ b/client/src/app/+videos/+video-edit/video-update.resolver.ts | |||
@@ -20,7 +20,7 @@ export class VideoUpdateResolver implements Resolve<any> { | |||
20 | return this.videoService.getVideo({ videoId: uuid }) | 20 | return this.videoService.getVideo({ videoId: uuid }) |
21 | .pipe( | 21 | .pipe( |
22 | switchMap(video => forkJoin(this.buildVideoObservables(video))), | 22 | switchMap(video => forkJoin(this.buildVideoObservables(video))), |
23 | map(([ video, videoChannels, videoCaptions, videoLive ]) => ({ video, videoChannels, videoCaptions, videoLive })) | 23 | map(([ video, videoChannels, videoCaptions, liveVideo ]) => ({ video, videoChannels, videoCaptions, liveVideo })) |
24 | ) | 24 | ) |
25 | } | 25 | } |
26 | 26 | ||
diff --git a/client/src/app/+videos/+video-watch/video-duration-formatter.pipe.ts b/client/src/app/+videos/+video-watch/video-duration-formatter.pipe.ts deleted file mode 100644 index 19b34f984..000000000 --- a/client/src/app/+videos/+video-watch/video-duration-formatter.pipe.ts +++ /dev/null | |||
@@ -1,23 +0,0 @@ | |||
1 | import { Pipe, PipeTransform } from '@angular/core' | ||
2 | |||
3 | @Pipe({ | ||
4 | name: 'myVideoDurationFormatter' | ||
5 | }) | ||
6 | export class VideoDurationPipe implements PipeTransform { | ||
7 | |||
8 | transform (value: number): string { | ||
9 | const hours = Math.floor(value / 3600) | ||
10 | const minutes = Math.floor((value % 3600) / 60) | ||
11 | const seconds = value % 60 | ||
12 | |||
13 | if (hours > 0) { | ||
14 | return $localize`${hours} h ${minutes} min ${seconds} sec` | ||
15 | } | ||
16 | |||
17 | if (minutes > 0) { | ||
18 | return $localize`${minutes} min ${seconds} sec` | ||
19 | } | ||
20 | |||
21 | return $localize`${seconds} sec` | ||
22 | } | ||
23 | } | ||
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 13242a2bc..bc1c302de 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.html +++ b/client/src/app/+videos/+video-watch/video-watch.component.html | |||
@@ -270,7 +270,7 @@ | |||
270 | 270 | ||
271 | <div class="video-attribute"> | 271 | <div class="video-attribute"> |
272 | <span i18n class="video-attribute-label">Duration</span> | 272 | <span i18n class="video-attribute-label">Duration</span> |
273 | <span class="video-attribute-value">{{ video.duration | myVideoDurationFormatter }}</span> | 273 | <span class="video-attribute-value">{{ video.duration | myDurationFormatter }}</span> |
274 | </div> | 274 | </div> |
275 | </div> | 275 | </div> |
276 | 276 | ||
diff --git a/client/src/app/+videos/+video-watch/video-watch.module.ts b/client/src/app/+videos/+video-watch/video-watch.module.ts index 612bbccc4..21aa33b84 100644 --- a/client/src/app/+videos/+video-watch/video-watch.module.ts +++ b/client/src/app/+videos/+video-watch/video-watch.module.ts | |||
@@ -15,7 +15,6 @@ import { VideoCommentsComponent } from './comment/video-comments.component' | |||
15 | import { VideoSupportComponent } from './modal/video-support.component' | 15 | import { VideoSupportComponent } from './modal/video-support.component' |
16 | import { RecommendationsModule } from './recommendations/recommendations.module' | 16 | import { RecommendationsModule } from './recommendations/recommendations.module' |
17 | import { TimestampRouteTransformerDirective } from './timestamp-route-transformer.directive' | 17 | import { TimestampRouteTransformerDirective } from './timestamp-route-transformer.directive' |
18 | import { VideoDurationPipe } from './video-duration-formatter.pipe' | ||
19 | import { VideoWatchPlaylistComponent } from './video-watch-playlist.component' | 18 | import { VideoWatchPlaylistComponent } from './video-watch-playlist.component' |
20 | import { VideoWatchRoutingModule } from './video-watch-routing.module' | 19 | import { VideoWatchRoutingModule } from './video-watch-routing.module' |
21 | import { VideoWatchComponent } from './video-watch.component' | 20 | import { VideoWatchComponent } from './video-watch.component' |
@@ -46,7 +45,6 @@ import { VideoWatchComponent } from './video-watch.component' | |||
46 | VideoCommentComponent, | 45 | VideoCommentComponent, |
47 | 46 | ||
48 | TimestampRouteTransformerDirective, | 47 | TimestampRouteTransformerDirective, |
49 | VideoDurationPipe, | ||
50 | TimestampRouteTransformerDirective | 48 | TimestampRouteTransformerDirective |
51 | ], | 49 | ], |
52 | 50 | ||