From b5b687550d8ef8beafdf706e45d6556fb5f4c876 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 26 Oct 2020 16:44:23 +0100 Subject: Add ability to save live replay --- .../+video-edit/shared/video-edit.component.html | 14 +++++++- .../+video-edit/shared/video-edit.component.ts | 7 +++- .../video-go-live.component.html | 5 +++ .../video-go-live.component.ts | 39 ++++++++++++++-------- .../+videos/+video-edit/video-add.component.html | 2 +- .../+videos/+video-edit/video-update.component.ts | 30 ++++++++++++++--- .../+videos/+video-edit/video-update.resolver.ts | 2 +- .../+video-watch/video-duration-formatter.pipe.ts | 23 ------------- .../+video-watch/video-watch.component.html | 2 +- .../app/+videos/+video-watch/video-watch.module.ts | 2 -- .../shared-main/angular/duration-formatter.pipe.ts | 32 ++++++++++++++++++ client/src/app/shared/shared-main/angular/index.ts | 1 + .../app/shared/shared-main/shared-main.module.ts | 14 ++++++-- .../shared/shared-main/video/live-video.service.ts | 10 ++++-- 14 files changed, 130 insertions(+), 53 deletions(-) delete mode 100644 client/src/app/+videos/+video-watch/video-duration-formatter.pipe.ts create mode 100644 client/src/app/shared/shared-main/angular/duration-formatter.pipe.ts (limited to 'client') 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 @@ - + Captions @@ -211,6 +211,18 @@ + +
+ + + Automatically publish a replay when your live ends + + + + ⚠️ If you enable this option, your live will be terminated if you exceed your video quota + + +
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 { support: VIDEO_SUPPORT_VALIDATOR, schedulePublicationAt: VIDEO_SCHEDULE_PUBLICATION_AT_VALIDATOR, originallyPublishedAt: VIDEO_ORIGINALLY_PUBLISHED_AT_VALIDATOR, - liveStreamKey: null + liveStreamKey: null, + saveReplay: null } this.formValidatorService.updateForm( @@ -239,6 +240,10 @@ export class VideoEditComponent implements OnInit, OnDestroy { this.videoCaptionAddModal.show() } + isSaveReplayEnabled () { + return this.serverConfig.live.allowReplay + } + private sortVideoCaptions () { this.videoCaptions.sort((v1, v2) => { 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 @@ {{ error }} +
+ Max live duration is {{ getMaxLiveDuration() | myDurationFormatter }}. + If your live reaches this limit, it will be automatically terminated. +
+
{ - this.notifier.success($localize`Live published.`) + forkJoin([ + this.updateVideoAndCaptions(video), - this.router.navigate([ '/videos/watch', video.uuid ]) - }, + this.liveVideoService.updateLive(this.videoId, liveVideoUpdate) + ]).subscribe( + () => { + this.notifier.success($localize`Live published.`) + + this.router.navigate(['/videos/watch', video.uuid]) + }, - err => { - this.error = err.message - scrollToTop() - console.error(err) - } - ) + err => { + this.error = err.message + scrollToTop() + console.error(err) + } + ) + } + getMaxLiveDuration () { + return this.serverConfig.live.maxDuration / 1000 } 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 @@ Instead, create a dedicated account to upload your videos. - +
Import {{ videoName }} 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' import { ActivatedRoute, Router } from '@angular/router' import { Notifier } from '@app/core' import { FormReactive, FormValidatorService, SelectChannelItem } from '@app/shared/shared-forms' -import { VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main' +import { LiveVideoService, VideoCaptionEdit, VideoCaptionService, VideoDetails, VideoEdit, VideoService } from '@app/shared/shared-main' import { LoadingBarService } from '@ngx-loading-bar/core' -import { LiveVideo, VideoPrivacy } from '@shared/models' +import { LiveVideo, LiveVideoUpdate, VideoPrivacy } from '@shared/models' import { hydrateFormFromVideo } from './shared/video-edit-utils' +import { of } from 'rxjs' @Component({ selector: 'my-videos-update', @@ -32,7 +33,8 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { private notifier: Notifier, private videoService: VideoService, private loadingBar: LoadingBarService, - private videoCaptionService: VideoCaptionService + private videoCaptionService: VideoCaptionService, + private liveVideoService: LiveVideoService ) { super() } @@ -56,7 +58,15 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { } // FIXME: Angular does not detect the change inside this subscription, so use the patched setTimeout - setTimeout(() => hydrateFormFromVideo(this.form, this.video, true)) + setTimeout(() => { + hydrateFormFromVideo(this.form, this.video, true) + + if (this.liveVideo) { + this.form.patchValue({ + saveReplay: this.liveVideo.saveReplay + }) + } + }) }, err => { @@ -102,6 +112,10 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { this.video.patch(this.form.value) + const liveVideoUpdate: LiveVideoUpdate = { + saveReplay: this.form.value.saveReplay + } + this.loadingBar.useRef().start() this.isUpdatingVideo = true @@ -109,7 +123,13 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { this.videoService.updateVideo(this.video) .pipe( // Then update captions - switchMap(() => this.videoCaptionService.updateCaptions(this.video.id, this.videoCaptions)) + switchMap(() => this.videoCaptionService.updateCaptions(this.video.id, this.videoCaptions)), + + switchMap(() => { + if (!this.liveVideo) return of(undefined) + + return this.liveVideoService.updateLive(this.video.id, liveVideoUpdate) + }) ) .subscribe( () => { 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 { return this.videoService.getVideo({ videoId: uuid }) .pipe( switchMap(video => forkJoin(this.buildVideoObservables(video))), - map(([ video, videoChannels, videoCaptions, videoLive ]) => ({ video, videoChannels, videoCaptions, videoLive })) + map(([ video, videoChannels, videoCaptions, liveVideo ]) => ({ video, videoChannels, videoCaptions, liveVideo })) ) } 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 @@ -import { Pipe, PipeTransform } from '@angular/core' - -@Pipe({ - name: 'myVideoDurationFormatter' -}) -export class VideoDurationPipe implements PipeTransform { - - transform (value: number): string { - const hours = Math.floor(value / 3600) - const minutes = Math.floor((value % 3600) / 60) - const seconds = value % 60 - - if (hours > 0) { - return $localize`${hours} h ${minutes} min ${seconds} sec` - } - - if (minutes > 0) { - return $localize`${minutes} min ${seconds} sec` - } - - return $localize`${seconds} sec` - } -} 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 @@
Duration - {{ video.duration | myVideoDurationFormatter }} + {{ video.duration | myDurationFormatter }}
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' import { VideoSupportComponent } from './modal/video-support.component' import { RecommendationsModule } from './recommendations/recommendations.module' import { TimestampRouteTransformerDirective } from './timestamp-route-transformer.directive' -import { VideoDurationPipe } from './video-duration-formatter.pipe' import { VideoWatchPlaylistComponent } from './video-watch-playlist.component' import { VideoWatchRoutingModule } from './video-watch-routing.module' import { VideoWatchComponent } from './video-watch.component' @@ -46,7 +45,6 @@ import { VideoWatchComponent } from './video-watch.component' VideoCommentComponent, TimestampRouteTransformerDirective, - VideoDurationPipe, TimestampRouteTransformerDirective ], diff --git a/client/src/app/shared/shared-main/angular/duration-formatter.pipe.ts b/client/src/app/shared/shared-main/angular/duration-formatter.pipe.ts new file mode 100644 index 000000000..29ff864ec --- /dev/null +++ b/client/src/app/shared/shared-main/angular/duration-formatter.pipe.ts @@ -0,0 +1,32 @@ +import { Pipe, PipeTransform } from '@angular/core' + +@Pipe({ + name: 'myDurationFormatter' +}) +export class DurationFormatterPipe implements PipeTransform { + + transform (value: number): string { + const hours = Math.floor(value / 3600) + const minutes = Math.floor((value % 3600) / 60) + const seconds = value % 60 + + if (hours > 0) { + let result = $localize`${hours}h` + + if (minutes !== 0) result += ' ' + $localize`${minutes}min` + if (seconds !== 0) result += ' ' + $localize`${seconds}sec` + + return result + } + + if (minutes > 0) { + let result = $localize`${minutes}min` + + if (seconds !== 0) result += ' ' + `${seconds}sec` + + return result + } + + return $localize`${seconds} sec` + } +} diff --git a/client/src/app/shared/shared-main/angular/index.ts b/client/src/app/shared/shared-main/angular/index.ts index 9ba815136..29f8b3650 100644 --- a/client/src/app/shared/shared-main/angular/index.ts +++ b/client/src/app/shared/shared-main/angular/index.ts @@ -1,4 +1,5 @@ export * from './bytes.pipe' +export * from './duration-formatter.pipe' export * from './from-now.pipe' export * from './infinite-scroller.directive' export * from './number-formatter.pipe' 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 0580872f4..3816cab19 100644 --- a/client/src/app/shared/shared-main/shared-main.module.ts +++ b/client/src/app/shared/shared-main/shared-main.module.ts @@ -15,7 +15,14 @@ import { } from '@ng-bootstrap/ng-bootstrap' import { SharedGlobalIconModule } from '../shared-icons' import { AccountService, ActorAvatarInfoComponent, AvatarComponent } from './account' -import { FromNowPipe, InfiniteScrollerDirective, NumberFormatterPipe, PeerTubeTemplateDirective, BytesPipe } from './angular' +import { + BytesPipe, + DurationFormatterPipe, + FromNowPipe, + InfiniteScrollerDirective, + NumberFormatterPipe, + PeerTubeTemplateDirective +} from './angular' import { AUTH_INTERCEPTOR_PROVIDER } from './auth' import { ActionDropdownComponent, ButtonComponent, DeleteButtonComponent, EditButtonComponent } from './buttons' import { DateToggleComponent } from './date' @@ -23,7 +30,7 @@ import { FeedComponent } from './feeds' import { LoaderComponent, SmallLoaderComponent } from './loaders' import { HelpComponent, ListOverflowComponent, TopMenuDropdownComponent } from './misc' import { UserHistoryService, UserNotificationsComponent, UserNotificationService, UserQuotaComponent } from './users' -import { RedundancyService, VideoImportService, VideoOwnershipService, VideoService, LiveVideoService } from './video' +import { LiveVideoService, RedundancyService, VideoImportService, VideoOwnershipService, VideoService } from './video' import { VideoCaptionService } from './video-caption' import { VideoChannelService } from './video-channel' @@ -56,6 +63,8 @@ import { VideoChannelService } from './video-channel' FromNowPipe, NumberFormatterPipe, BytesPipe, + DurationFormatterPipe, + InfiniteScrollerDirective, PeerTubeTemplateDirective, @@ -103,6 +112,7 @@ import { VideoChannelService } from './video-channel' FromNowPipe, BytesPipe, NumberFormatterPipe, + DurationFormatterPipe, InfiniteScrollerDirective, PeerTubeTemplateDirective, diff --git a/client/src/app/shared/shared-main/video/live-video.service.ts b/client/src/app/shared/shared-main/video/live-video.service.ts index 2cd1c66a5..093d65e83 100644 --- a/client/src/app/shared/shared-main/video/live-video.service.ts +++ b/client/src/app/shared/shared-main/video/live-video.service.ts @@ -2,7 +2,7 @@ import { catchError } from 'rxjs/operators' import { HttpClient } from '@angular/common/http' import { Injectable } from '@angular/core' import { RestExtractor } from '@app/core' -import { VideoCreate, LiveVideo } from '@shared/models' +import { LiveVideo, LiveVideoCreate, LiveVideoUpdate } from '@shared/models' import { environment } from '../../../../environments/environment' @Injectable() @@ -14,7 +14,7 @@ export class LiveVideoService { private restExtractor: RestExtractor ) {} - goLive (video: VideoCreate) { + goLive (video: LiveVideoCreate) { return this.authHttp .post<{ video: { id: number, uuid: string } }>(LiveVideoService.BASE_VIDEO_LIVE_URL, video) .pipe(catchError(err => this.restExtractor.handleError(err))) @@ -25,4 +25,10 @@ export class LiveVideoService { .get(LiveVideoService.BASE_VIDEO_LIVE_URL + videoId) .pipe(catchError(err => this.restExtractor.handleError(err))) } + + updateLive (videoId: number | string, liveUpdate: LiveVideoUpdate) { + return this.authHttp + .put(LiveVideoService.BASE_VIDEO_LIVE_URL + videoId, liveUpdate) + .pipe(catchError(err => this.restExtractor.handleError(err))) + } } -- cgit v1.2.3