aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/+videos
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2020-10-26 16:44:23 +0100
committerChocobozzz <chocobozzz@cpy.re>2020-11-09 15:33:04 +0100
commitb5b687550d8ef8beafdf706e45d6556fb5f4c876 (patch)
tree232412d463c78af1f7ab5797db5aecf1096d08da /client/src/app/+videos
parentef680f68351ec10ab73a1131570a6d14ce14c195 (diff)
downloadPeerTube-b5b687550d8ef8beafdf706e45d6556fb5f4c876.tar.gz
PeerTube-b5b687550d8ef8beafdf706e45d6556fb5f4c876.tar.zst
PeerTube-b5b687550d8ef8beafdf706e45d6556fb5f4c876.zip
Add ability to save live replay
Diffstat (limited to 'client/src/app/+videos')
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.html14
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.ts7
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.html5
-rw-r--r--client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts39
-rw-r--r--client/src/app/+videos/+video-edit/video-add.component.html2
-rw-r--r--client/src/app/+videos/+video-edit/video-update.component.ts30
-rw-r--r--client/src/app/+videos/+video-edit/video-update.resolver.ts2
-rw-r--r--client/src/app/+videos/+video-watch/video-duration-formatter.pipe.ts23
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.html2
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.module.ts2
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
2import { forkJoin } from 'rxjs'
2import { Component, EventEmitter, OnInit, Output } from '@angular/core' 3import { Component, EventEmitter, OnInit, Output } from '@angular/core'
3import { Router } from '@angular/router' 4import { Router } from '@angular/router'
4import { AuthService, CanComponentDeactivate, Notifier, ServerService } from '@app/core' 5import { AuthService, CanComponentDeactivate, Notifier, ServerService } from '@app/core'
@@ -6,7 +7,7 @@ import { scrollToTop } from '@app/helpers'
6import { FormValidatorService } from '@app/shared/shared-forms' 7import { FormValidatorService } from '@app/shared/shared-forms'
7import { LiveVideoService, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main' 8import { LiveVideoService, VideoCaptionService, VideoEdit, VideoService } from '@app/shared/shared-main'
8import { LoadingBarService } from '@ngx-loading-bar/core' 9import { LoadingBarService } from '@ngx-loading-bar/core'
9import { LiveVideo, VideoCreate, VideoPrivacy } from '@shared/models' 10import { LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoPrivacy } from '@shared/models'
10import { VideoSend } from './video-send' 11import { 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'
3import { ActivatedRoute, Router } from '@angular/router' 3import { ActivatedRoute, Router } from '@angular/router'
4import { Notifier } from '@app/core' 4import { Notifier } from '@app/core'
5import { FormReactive, FormValidatorService, SelectChannelItem } from '@app/shared/shared-forms' 5import { FormReactive, FormValidatorService, SelectChannelItem } from '@app/shared/shared-forms'
6import { VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main' 6import { LiveVideoService, VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main'
7import { LoadingBarService } from '@ngx-loading-bar/core' 7import { LoadingBarService } from '@ngx-loading-bar/core'
8import { LiveVideo, VideoPrivacy } from '@shared/models' 8import { LiveVideo, LiveVideoUpdate, VideoPrivacy } from '@shared/models'
9import { hydrateFormFromVideo } from './shared/video-edit-utils' 9import { hydrateFormFromVideo } from './shared/video-edit-utils'
10import { 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 @@
1import { Pipe, PipeTransform } from '@angular/core'
2
3@Pipe({
4 name: 'myVideoDurationFormatter'
5})
6export 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'
15import { VideoSupportComponent } from './modal/video-support.component' 15import { VideoSupportComponent } from './modal/video-support.component'
16import { RecommendationsModule } from './recommendations/recommendations.module' 16import { RecommendationsModule } from './recommendations/recommendations.module'
17import { TimestampRouteTransformerDirective } from './timestamp-route-transformer.directive' 17import { TimestampRouteTransformerDirective } from './timestamp-route-transformer.directive'
18import { VideoDurationPipe } from './video-duration-formatter.pipe'
19import { VideoWatchPlaylistComponent } from './video-watch-playlist.component' 18import { VideoWatchPlaylistComponent } from './video-watch-playlist.component'
20import { VideoWatchRoutingModule } from './video-watch-routing.module' 19import { VideoWatchRoutingModule } from './video-watch-routing.module'
21import { VideoWatchComponent } from './video-watch.component' 20import { 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