aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app')
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts3
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-live-configuration.component.html12
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.html11
-rw-r--r--client/src/app/+videos/+video-edit/shared/video-edit.component.ts25
-rw-r--r--client/src/app/+videos/+video-edit/video-update.component.ts4
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.ts34
6 files changed, 81 insertions, 8 deletions
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
index e3b6f8305..94f1021bf 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
@@ -189,6 +189,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
189 maxInstanceLives: MAX_INSTANCE_LIVES_VALIDATOR, 189 maxInstanceLives: MAX_INSTANCE_LIVES_VALIDATOR,
190 maxUserLives: MAX_USER_LIVES_VALIDATOR, 190 maxUserLives: MAX_USER_LIVES_VALIDATOR,
191 allowReplay: null, 191 allowReplay: null,
192 latencySetting: {
193 enabled: null
194 },
192 195
193 transcoding: { 196 transcoding: {
194 enabled: null, 197 enabled: null,
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-live-configuration.component.html b/client/src/app/+admin/config/edit-custom-config/edit-live-configuration.component.html
index 10d5278c1..8d6a4ce19 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-live-configuration.component.html
+++ b/client/src/app/+admin/config/edit-custom-config/edit-live-configuration.component.html
@@ -36,6 +36,18 @@
36 </my-peertube-checkbox> 36 </my-peertube-checkbox>
37 </div> 37 </div>
38 38
39 <div class="form-group" formGroupName="latencySetting" [ngClass]="getDisabledLiveClass()">
40 <my-peertube-checkbox
41 inputName="liveLatencySettingEnabled" formControlName="enabled"
42 i18n-labelText labelText="Allow your users to change live latency"
43 >
44 <ng-container ngProjectAs="description" i18n>
45 Small latency disables P2P and high latency can increase P2P ratio
46 </ng-container>
47
48 </my-peertube-checkbox>
49 </div>
50
39 <div class="form-group" [ngClass]="getDisabledLiveClass()"> 51 <div class="form-group" [ngClass]="getDisabledLiveClass()">
40 <label i18n for="liveMaxInstanceLives"> 52 <label i18n for="liveMaxInstanceLives">
41 Max simultaneous lives created on your instance <span class="text-muted">(-1 for "unlimited")</span> 53 Max simultaneous lives created on your instance <span class="text-muted">(-1 for "unlimited")</span>
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 2281f8631..515daf15f 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
@@ -289,6 +289,17 @@
289 </ng-container> 289 </ng-container>
290 </my-peertube-checkbox> 290 </my-peertube-checkbox>
291 </div> 291 </div>
292
293 <div class="form-group" *ngIf="isLatencyModeEnabled()">
294 <label i18n for="latencyMode">Latency mode</label>
295 <my-select-options
296 labelForId="latencyMode" [items]="latencyModes" formControlName="latencyMode" [clearable]="true"
297 ></my-select-options>
298
299 <div *ngIf="formErrors.latencyMode" class="form-error">
300 {{ formErrors.latencyMode }}
301 </div>
302 </div>
292 </div> 303 </div>
293 </div> 304 </div>
294 </ng-template> 305 </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 2801fc519..a2399eafb 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
@@ -1,6 +1,6 @@
1import { forkJoin } from 'rxjs' 1import { forkJoin } from 'rxjs'
2import { map } from 'rxjs/operators' 2import { map } from 'rxjs/operators'
3import { SelectChannelItem } from 'src/types/select-options-item.model' 3import { SelectChannelItem, SelectOptionsItem } from 'src/types/select-options-item.model'
4import { ChangeDetectorRef, Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output, ViewChild } from '@angular/core' 4import { ChangeDetectorRef, Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'
5import { AbstractControl, FormArray, FormControl, FormGroup, Validators } from '@angular/forms' 5import { AbstractControl, FormArray, FormControl, FormGroup, Validators } from '@angular/forms'
6import { HooksService, PluginService, ServerService } from '@app/core' 6import { HooksService, PluginService, ServerService } from '@app/core'
@@ -26,6 +26,7 @@ import { PluginInfo } from '@root-helpers/plugins-manager'
26import { 26import {
27 HTMLServerConfig, 27 HTMLServerConfig,
28 LiveVideo, 28 LiveVideo,
29 LiveVideoLatencyMode,
29 RegisterClientFormFieldOptions, 30 RegisterClientFormFieldOptions,
30 RegisterClientVideoFieldOptions, 31 RegisterClientVideoFieldOptions,
31 VideoConstant, 32 VideoConstant,
@@ -78,6 +79,23 @@ export class VideoEditComponent implements OnInit, OnDestroy {
78 videoCategories: VideoConstant<number>[] = [] 79 videoCategories: VideoConstant<number>[] = []
79 videoLicences: VideoConstant<number>[] = [] 80 videoLicences: VideoConstant<number>[] = []
80 videoLanguages: VideoLanguages[] = [] 81 videoLanguages: VideoLanguages[] = []
82 latencyModes: SelectOptionsItem[] = [
83 {
84 id: LiveVideoLatencyMode.SMALL_LATENCY,
85 label: $localize`Small latency`,
86 description: $localize`Reduce latency to ~15s disabling P2P`
87 },
88 {
89 id: LiveVideoLatencyMode.DEFAULT,
90 label: $localize`Default`,
91 description: $localize`Average latency of 30s`
92 },
93 {
94 id: LiveVideoLatencyMode.HIGH_LATENCY,
95 label: $localize`High latency`,
96 description: $localize`Average latency of 60s increasing P2P ratio`
97 }
98 ]
81 99
82 pluginDataFormGroup: FormGroup 100 pluginDataFormGroup: FormGroup
83 101
@@ -141,6 +159,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
141 originallyPublishedAt: VIDEO_ORIGINALLY_PUBLISHED_AT_VALIDATOR, 159 originallyPublishedAt: VIDEO_ORIGINALLY_PUBLISHED_AT_VALIDATOR,
142 liveStreamKey: null, 160 liveStreamKey: null,
143 permanentLive: null, 161 permanentLive: null,
162 latencyMode: null,
144 saveReplay: null 163 saveReplay: null
145 } 164 }
146 165
@@ -273,6 +292,10 @@ export class VideoEditComponent implements OnInit, OnDestroy {
273 return this.form.value['permanentLive'] === true 292 return this.form.value['permanentLive'] === true
274 } 293 }
275 294
295 isLatencyModeEnabled () {
296 return this.serverConfig.live.latencySetting.enabled
297 }
298
276 isPluginFieldHidden (pluginField: PluginField) { 299 isPluginFieldHidden (pluginField: PluginField) {
277 if (typeof pluginField.commonOptions.hidden !== 'function') return false 300 if (typeof pluginField.commonOptions.hidden !== 'function') return false
278 301
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 d9e8344fc..9c4998f2e 100644
--- a/client/src/app/+videos/+video-edit/video-update.component.ts
+++ b/client/src/app/+videos/+video-edit/video-update.component.ts
@@ -64,6 +64,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
64 if (this.liveVideo) { 64 if (this.liveVideo) {
65 this.form.patchValue({ 65 this.form.patchValue({
66 saveReplay: this.liveVideo.saveReplay, 66 saveReplay: this.liveVideo.saveReplay,
67 latencyMode: this.liveVideo.latencyMode,
67 permanentLive: this.liveVideo.permanentLive 68 permanentLive: this.liveVideo.permanentLive
68 }) 69 })
69 } 70 }
@@ -127,7 +128,8 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
127 128
128 const liveVideoUpdate: LiveVideoUpdate = { 129 const liveVideoUpdate: LiveVideoUpdate = {
129 saveReplay: !!this.form.value.saveReplay, 130 saveReplay: !!this.form.value.saveReplay,
130 permanentLive: !!this.form.value.permanentLive 131 permanentLive: !!this.form.value.permanentLive,
132 latencyMode: this.form.value.latencyMode
131 } 133 }
132 134
133 // Don't update live attributes if they did not change 135 // Don't update live attributes if they did not change
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 1f45c4d26..067d3bc84 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.component.ts
@@ -1,5 +1,5 @@
1import { Hotkey, HotkeysService } from 'angular2-hotkeys' 1import { Hotkey, HotkeysService } from 'angular2-hotkeys'
2import { forkJoin, Subscription } from 'rxjs' 2import { forkJoin, map, Observable, of, Subscription, switchMap } from 'rxjs'
3import { isP2PEnabled } from 'src/assets/player/utils' 3import { isP2PEnabled } from 'src/assets/player/utils'
4import { PlatformLocation } from '@angular/common' 4import { PlatformLocation } from '@angular/common'
5import { Component, ElementRef, Inject, LOCALE_ID, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core' 5import { Component, ElementRef, Inject, LOCALE_ID, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core'
@@ -22,11 +22,13 @@ import { HooksService } from '@app/core/plugins/hooks.service'
22import { isXPercentInViewport, scrollToTop } from '@app/helpers' 22import { isXPercentInViewport, scrollToTop } from '@app/helpers'
23import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main' 23import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main'
24import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription' 24import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
25import { LiveVideoService } from '@app/shared/shared-video-live'
25import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist' 26import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
26import { timeToInt } from '@shared/core-utils' 27import { timeToInt } from '@shared/core-utils'
27import { 28import {
28 HTMLServerConfig, 29 HTMLServerConfig,
29 HttpStatusCode, 30 HttpStatusCode,
31 LiveVideo,
30 PeerTubeProblemDocument, 32 PeerTubeProblemDocument,
31 ServerErrorCode, 33 ServerErrorCode,
32 VideoCaption, 34 VideoCaption,
@@ -63,6 +65,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
63 65
64 video: VideoDetails = null 66 video: VideoDetails = null
65 videoCaptions: VideoCaption[] = [] 67 videoCaptions: VideoCaption[] = []
68 liveVideo: LiveVideo
66 69
67 playlistPosition: number 70 playlistPosition: number
68 playlist: VideoPlaylist = null 71 playlist: VideoPlaylist = null
@@ -89,6 +92,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
89 private router: Router, 92 private router: Router,
90 private videoService: VideoService, 93 private videoService: VideoService,
91 private playlistService: VideoPlaylistService, 94 private playlistService: VideoPlaylistService,
95 private liveVideoService: LiveVideoService,
92 private confirmService: ConfirmService, 96 private confirmService: ConfirmService,
93 private metaService: MetaService, 97 private metaService: MetaService,
94 private authService: AuthService, 98 private authService: AuthService,
@@ -239,12 +243,21 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
239 'filter:api.video-watch.video.get.result' 243 'filter:api.video-watch.video.get.result'
240 ) 244 )
241 245
246 const videoAndLiveObs: Observable<{ video: VideoDetails, live?: LiveVideo }> = videoObs.pipe(
247 switchMap(video => {
248 if (!video.isLive) return of({ video })
249
250 return this.liveVideoService.getVideoLive(video.uuid)
251 .pipe(map(live => ({ live, video })))
252 })
253 )
254
242 forkJoin([ 255 forkJoin([
243 videoObs, 256 videoAndLiveObs,
244 this.videoCaptionService.listCaptions(videoId), 257 this.videoCaptionService.listCaptions(videoId),
245 this.userService.getAnonymousOrLoggedUser() 258 this.userService.getAnonymousOrLoggedUser()
246 ]).subscribe({ 259 ]).subscribe({
247 next: ([ video, captionsResult, loggedInOrAnonymousUser ]) => { 260 next: ([ { video, live }, captionsResult, loggedInOrAnonymousUser ]) => {
248 const queryParams = this.route.snapshot.queryParams 261 const queryParams = this.route.snapshot.queryParams
249 262
250 const urlOptions = { 263 const urlOptions = {
@@ -261,7 +274,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
261 peertubeLink: false 274 peertubeLink: false
262 } 275 }
263 276
264 this.onVideoFetched({ video, videoCaptions: captionsResult.data, loggedInOrAnonymousUser, urlOptions }) 277 this.onVideoFetched({ video, live, videoCaptions: captionsResult.data, loggedInOrAnonymousUser, urlOptions })
265 .catch(err => this.handleGlobalError(err)) 278 .catch(err => this.handleGlobalError(err))
266 }, 279 },
267 280
@@ -330,16 +343,18 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
330 343
331 private async onVideoFetched (options: { 344 private async onVideoFetched (options: {
332 video: VideoDetails 345 video: VideoDetails
346 live: LiveVideo
333 videoCaptions: VideoCaption[] 347 videoCaptions: VideoCaption[]
334 urlOptions: URLOptions 348 urlOptions: URLOptions
335 loggedInOrAnonymousUser: User 349 loggedInOrAnonymousUser: User
336 }) { 350 }) {
337 const { video, videoCaptions, urlOptions, loggedInOrAnonymousUser } = options 351 const { video, live, videoCaptions, urlOptions, loggedInOrAnonymousUser } = options
338 352
339 this.subscribeToLiveEventsIfNeeded(this.video, video) 353 this.subscribeToLiveEventsIfNeeded(this.video, video)
340 354
341 this.video = video 355 this.video = video
342 this.videoCaptions = videoCaptions 356 this.videoCaptions = videoCaptions
357 this.liveVideo = live
343 358
344 // Re init attributes 359 // Re init attributes
345 this.playerPlaceholderImgSrc = undefined 360 this.playerPlaceholderImgSrc = undefined
@@ -387,6 +402,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
387 const params = { 402 const params = {
388 video: this.video, 403 video: this.video,
389 videoCaptions: this.videoCaptions, 404 videoCaptions: this.videoCaptions,
405 liveVideo: this.liveVideo,
390 urlOptions, 406 urlOptions,
391 loggedInOrAnonymousUser, 407 loggedInOrAnonymousUser,
392 user: this.user 408 user: this.user
@@ -532,12 +548,13 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
532 548
533 private buildPlayerManagerOptions (params: { 549 private buildPlayerManagerOptions (params: {
534 video: VideoDetails 550 video: VideoDetails
551 liveVideo: LiveVideo
535 videoCaptions: VideoCaption[] 552 videoCaptions: VideoCaption[]
536 urlOptions: CustomizationOptions & { playerMode: PlayerMode } 553 urlOptions: CustomizationOptions & { playerMode: PlayerMode }
537 loggedInOrAnonymousUser: User 554 loggedInOrAnonymousUser: User
538 user?: AuthUser 555 user?: AuthUser
539 }) { 556 }) {
540 const { video, videoCaptions, urlOptions, loggedInOrAnonymousUser, user } = params 557 const { video, liveVideo, videoCaptions, urlOptions, loggedInOrAnonymousUser, user } = params
541 558
542 const getStartTime = () => { 559 const getStartTime = () => {
543 const byUrl = urlOptions.startTime !== undefined 560 const byUrl = urlOptions.startTime !== undefined
@@ -562,6 +579,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
562 src: environment.apiUrl + c.captionPath 579 src: environment.apiUrl + c.captionPath
563 })) 580 }))
564 581
582 const liveOptions = video.isLive
583 ? { latencyMode: liveVideo.latencyMode }
584 : undefined
585
565 const options: PeertubePlayerManagerOptions = { 586 const options: PeertubePlayerManagerOptions = {
566 common: { 587 common: {
567 autoplay: this.isAutoplay(), 588 autoplay: this.isAutoplay(),
@@ -597,6 +618,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
597 embedTitle: video.name, 618 embedTitle: video.name,
598 619
599 isLive: video.isLive, 620 isLive: video.isLive,
621 liveOptions,
600 622
601 language: this.localeId, 623 language: this.localeId,
602 624