aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2023-06-01 14:51:16 +0200
committerChocobozzz <me@florianbigard.com>2023-06-29 10:16:55 +0200
commitd8f39b126d9fe4bec1c12fb213548cc6edc87867 (patch)
tree7f0f1cb23165cf4dd789b2d78b1fef7ee116f647 /client/src/app
parent1fb7d094229acdc190c3f7551b43ac5445814dee (diff)
downloadPeerTube-d8f39b126d9fe4bec1c12fb213548cc6edc87867.tar.gz
PeerTube-d8f39b126d9fe4bec1c12fb213548cc6edc87867.tar.zst
PeerTube-d8f39b126d9fe4bec1c12fb213548cc6edc87867.zip
Add storyboard support
Diffstat (limited to 'client/src/app')
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.html14
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.ts2
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts12
-rw-r--r--client/src/app/+videos/+video-watch/video-watch.component.ts45
-rw-r--r--client/src/app/shared/form-validators/custom-config-validators.ts17
-rw-r--r--client/src/app/shared/shared-main/video/video.service.ts24
6 files changed, 92 insertions, 22 deletions
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.html b/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.html
index bbf946df0..9701e7f85 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.html
+++ b/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.html
@@ -52,6 +52,20 @@
52 52
53 <div *ngIf="formErrors.cache.torrents.size" class="form-error">{{ formErrors.cache.torrents.size }}</div> 53 <div *ngIf="formErrors.cache.torrents.size" class="form-error">{{ formErrors.cache.torrents.size }}</div>
54 </div> 54 </div>
55
56 <div class="form-group" formGroupName="torrents">
57 <label i18n for="cacheTorrentsSize">Number of video storyboard images to keep in cache</label>
58
59 <div class="number-with-unit">
60 <input
61 type="number" min="0" id="cacheStoryboardsSize" class="form-control"
62 formControlName="size" [ngClass]="{ 'input-error': formErrors['cache.storyboards.size'] }"
63 >
64 <span i18n>{getCacheSize('storyboards'), plural, =1 {cached storyboard} other {cached storyboards}}</span>
65 </div>
66
67 <div *ngIf="formErrors.cache.storyboards.size" class="form-error">{{ formErrors.cache.storyboards.size }}</div>
68 </div>
55 </ng-container> 69 </ng-container>
56 70
57 </div> 71 </div>
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.ts
index 79a98f288..06c5e6221 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.ts
+++ b/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.ts
@@ -10,7 +10,7 @@ export class EditAdvancedConfigurationComponent {
10 @Input() form: FormGroup 10 @Input() form: FormGroup
11 @Input() formErrors: any 11 @Input() formErrors: any
12 12
13 getCacheSize (type: 'captions' | 'previews' | 'torrents') { 13 getCacheSize (type: 'captions' | 'previews' | 'torrents' | 'storyboards') {
14 return this.form.value['cache'][type]['size'] 14 return this.form.value['cache'][type]['size']
15 } 15 }
16} 16}
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 2c3b7560d..9219d608b 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
@@ -9,8 +9,7 @@ import { Notifier } from '@app/core'
9import { ServerService } from '@app/core/server/server.service' 9import { ServerService } from '@app/core/server/server.service'
10import { 10import {
11 ADMIN_EMAIL_VALIDATOR, 11 ADMIN_EMAIL_VALIDATOR,
12 CACHE_CAPTIONS_SIZE_VALIDATOR, 12 CACHE_SIZE_VALIDATOR,
13 CACHE_PREVIEWS_SIZE_VALIDATOR,
14 CONCURRENCY_VALIDATOR, 13 CONCURRENCY_VALIDATOR,
15 INDEX_URL_VALIDATOR, 14 INDEX_URL_VALIDATOR,
16 INSTANCE_NAME_VALIDATOR, 15 INSTANCE_NAME_VALIDATOR,
@@ -120,13 +119,16 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
120 }, 119 },
121 cache: { 120 cache: {
122 previews: { 121 previews: {
123 size: CACHE_PREVIEWS_SIZE_VALIDATOR 122 size: CACHE_SIZE_VALIDATOR
124 }, 123 },
125 captions: { 124 captions: {
126 size: CACHE_CAPTIONS_SIZE_VALIDATOR 125 size: CACHE_SIZE_VALIDATOR
127 }, 126 },
128 torrents: { 127 torrents: {
129 size: CACHE_CAPTIONS_SIZE_VALIDATOR 128 size: CACHE_SIZE_VALIDATOR
129 },
130 storyboards: {
131 size: CACHE_SIZE_VALIDATOR
130 } 132 }
131 }, 133 },
132 signup: { 134 signup: {
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 aba3ee086..43744789d 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.component.ts
@@ -33,6 +33,7 @@ import {
33 LiveVideo, 33 LiveVideo,
34 PeerTubeProblemDocument, 34 PeerTubeProblemDocument,
35 ServerErrorCode, 35 ServerErrorCode,
36 Storyboard,
36 VideoCaption, 37 VideoCaption,
37 VideoPrivacy, 38 VideoPrivacy,
38 VideoState 39 VideoState
@@ -69,6 +70,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
69 videoCaptions: VideoCaption[] = [] 70 videoCaptions: VideoCaption[] = []
70 liveVideo: LiveVideo 71 liveVideo: LiveVideo
71 videoPassword: string 72 videoPassword: string
73 storyboards: Storyboard[] = []
72 74
73 playlistPosition: number 75 playlistPosition: number
74 playlist: VideoPlaylist = null 76 playlist: VideoPlaylist = null
@@ -285,9 +287,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
285 forkJoin([ 287 forkJoin([
286 videoAndLiveObs, 288 videoAndLiveObs,
287 this.videoCaptionService.listCaptions(videoId, videoPassword), 289 this.videoCaptionService.listCaptions(videoId, videoPassword),
290 this.videoService.getStoryboards(videoId),
288 this.userService.getAnonymousOrLoggedUser() 291 this.userService.getAnonymousOrLoggedUser()
289 ]).subscribe({ 292 ]).subscribe({
290 next: ([ { video, live, videoFileToken }, captionsResult, loggedInOrAnonymousUser ]) => { 293 next: ([ { video, live, videoFileToken }, captionsResult, storyboards, loggedInOrAnonymousUser ]) => {
291 const queryParams = this.route.snapshot.queryParams 294 const queryParams = this.route.snapshot.queryParams
292 295
293 const urlOptions = { 296 const urlOptions = {
@@ -309,6 +312,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
309 video, 312 video,
310 live, 313 live,
311 videoCaptions: captionsResult.data, 314 videoCaptions: captionsResult.data,
315 storyboards,
312 videoFileToken, 316 videoFileToken,
313 videoPassword, 317 videoPassword,
314 loggedInOrAnonymousUser, 318 loggedInOrAnonymousUser,
@@ -414,6 +418,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
414 video: VideoDetails 418 video: VideoDetails
415 live: LiveVideo 419 live: LiveVideo
416 videoCaptions: VideoCaption[] 420 videoCaptions: VideoCaption[]
421 storyboards: Storyboard[]
417 videoFileToken: string 422 videoFileToken: string
418 videoPassword: string 423 videoPassword: string
419 424
@@ -421,7 +426,17 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
421 loggedInOrAnonymousUser: User 426 loggedInOrAnonymousUser: User
422 forceAutoplay: boolean 427 forceAutoplay: boolean
423 }) { 428 }) {
424 const { video, live, videoCaptions, urlOptions, videoFileToken, videoPassword, loggedInOrAnonymousUser, forceAutoplay } = options 429 const {
430 video,
431 live,
432 videoCaptions,
433 storyboards,
434 urlOptions,
435 videoFileToken,
436 videoPassword,
437 loggedInOrAnonymousUser,
438 forceAutoplay
439 } = options
425 440
426 this.subscribeToLiveEventsIfNeeded(this.video, video) 441 this.subscribeToLiveEventsIfNeeded(this.video, video)
427 442
@@ -430,6 +445,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
430 this.liveVideo = live 445 this.liveVideo = live
431 this.videoFileToken = videoFileToken 446 this.videoFileToken = videoFileToken
432 this.videoPassword = videoPassword 447 this.videoPassword = videoPassword
448 this.storyboards = storyboards
433 449
434 // Re init attributes 450 // Re init attributes
435 this.playerPlaceholderImgSrc = undefined 451 this.playerPlaceholderImgSrc = undefined
@@ -485,6 +501,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
485 const params = { 501 const params = {
486 video: this.video, 502 video: this.video,
487 videoCaptions: this.videoCaptions, 503 videoCaptions: this.videoCaptions,
504 storyboards: this.storyboards,
488 liveVideo: this.liveVideo, 505 liveVideo: this.liveVideo,
489 videoFileToken: this.videoFileToken, 506 videoFileToken: this.videoFileToken,
490 videoPassword: this.videoPassword, 507 videoPassword: this.videoPassword,
@@ -636,6 +653,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
636 video: VideoDetails 653 video: VideoDetails
637 liveVideo: LiveVideo 654 liveVideo: LiveVideo
638 videoCaptions: VideoCaption[] 655 videoCaptions: VideoCaption[]
656 storyboards: Storyboard[]
639 657
640 videoFileToken: string 658 videoFileToken: string
641 videoPassword: string 659 videoPassword: string
@@ -646,7 +664,17 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
646 forceAutoplay: boolean 664 forceAutoplay: boolean
647 user?: AuthUser // Keep for plugins 665 user?: AuthUser // Keep for plugins
648 }) { 666 }) {
649 const { video, liveVideo, videoCaptions, videoFileToken, videoPassword, urlOptions, loggedInOrAnonymousUser, forceAutoplay } = params 667 const {
668 video,
669 liveVideo,
670 videoCaptions,
671 storyboards,
672 videoFileToken,
673 videoPassword,
674 urlOptions,
675 loggedInOrAnonymousUser,
676 forceAutoplay
677 } = params
650 678
651 const getStartTime = () => { 679 const getStartTime = () => {
652 const byUrl = urlOptions.startTime !== undefined 680 const byUrl = urlOptions.startTime !== undefined
@@ -673,6 +701,15 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
673 src: environment.apiUrl + c.captionPath 701 src: environment.apiUrl + c.captionPath
674 })) 702 }))
675 703
704 const storyboard = storyboards.length !== 0
705 ? {
706 url: environment.apiUrl + storyboards[0].storyboardPath,
707 height: storyboards[0].spriteHeight,
708 width: storyboards[0].spriteWidth,
709 interval: storyboards[0].spriteDuration
710 }
711 : undefined
712
676 const liveOptions = video.isLive 713 const liveOptions = video.isLive
677 ? { latencyMode: liveVideo.latencyMode } 714 ? { latencyMode: liveVideo.latencyMode }
678 : undefined 715 : undefined
@@ -734,6 +771,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
734 videoPassword: () => videoPassword, 771 videoPassword: () => videoPassword,
735 772
736 videoCaptions: playerCaptions, 773 videoCaptions: playerCaptions,
774 storyboard,
737 775
738 videoShortUUID: video.shortUUID, 776 videoShortUUID: video.shortUUID,
739 videoUUID: video.uuid, 777 videoUUID: video.uuid,
@@ -767,6 +805,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
767 else mode = 'webtorrent' 805 else mode = 'webtorrent'
768 } 806 }
769 807
808 // FIXME: remove, we don't support these old web browsers anymore
770 // p2p-media-loader needs TextEncoder, fallback on WebTorrent if not available 809 // p2p-media-loader needs TextEncoder, fallback on WebTorrent if not available
771 if (typeof TextEncoder === 'undefined') { 810 if (typeof TextEncoder === 'undefined') {
772 mode = 'webtorrent' 811 mode = 'webtorrent'
diff --git a/client/src/app/shared/form-validators/custom-config-validators.ts b/client/src/app/shared/form-validators/custom-config-validators.ts
index ff0813f7d..3672e5610 100644
--- a/client/src/app/shared/form-validators/custom-config-validators.ts
+++ b/client/src/app/shared/form-validators/custom-config-validators.ts
@@ -22,21 +22,12 @@ export const SERVICES_TWITTER_USERNAME_VALIDATOR: BuildFormValidator = {
22 } 22 }
23} 23}
24 24
25export const CACHE_PREVIEWS_SIZE_VALIDATOR: BuildFormValidator = { 25export const CACHE_SIZE_VALIDATOR: BuildFormValidator = {
26 VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ], 26 VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ],
27 MESSAGES: { 27 MESSAGES: {
28 required: $localize`Previews cache size is required.`, 28 required: $localize`Cache size is required.`,
29 min: $localize`Previews cache size must be greater than 1.`, 29 min: $localize`Cache size must be greater than 1.`,
30 pattern: $localize`Previews cache size must be a number.` 30 pattern: $localize`Cache size must be a number.`
31 }
32}
33
34export const CACHE_CAPTIONS_SIZE_VALIDATOR: BuildFormValidator = {
35 VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ],
36 MESSAGES: {
37 required: $localize`Captions cache size is required.`,
38 min: $localize`Captions cache size must be greater than 1.`,
39 pattern: $localize`Captions cache size must be a number.`
40 } 31 }
41} 32}
42 33
diff --git a/client/src/app/shared/shared-main/video/video.service.ts b/client/src/app/shared/shared-main/video/video.service.ts
index d67a2e192..c2e3d7511 100644
--- a/client/src/app/shared/shared-main/video/video.service.ts
+++ b/client/src/app/shared/shared-main/video/video.service.ts
@@ -11,6 +11,7 @@ import {
11 FeedFormat, 11 FeedFormat,
12 NSFWPolicyType, 12 NSFWPolicyType,
13 ResultList, 13 ResultList,
14 Storyboard,
14 UserVideoRate, 15 UserVideoRate,
15 UserVideoRateType, 16 UserVideoRateType,
16 UserVideoRateUpdate, 17 UserVideoRateUpdate,
@@ -344,6 +345,25 @@ export class VideoService {
344 ) 345 )
345 } 346 }
346 347
348 // ---------------------------------------------------------------------------
349
350 getStoryboards (videoId: string | number) {
351 return this.authHttp
352 .get<{ storyboards: Storyboard[] }>(VideoService.BASE_VIDEO_URL + '/' + videoId + '/storyboards')
353 .pipe(
354 map(({ storyboards }) => storyboards),
355 catchError(err => {
356 if (err.status === 404) {
357 return of([])
358 }
359
360 this.restExtractor.handleError(err)
361 })
362 )
363 }
364
365 // ---------------------------------------------------------------------------
366
347 getSource (videoId: number) { 367 getSource (videoId: number) {
348 return this.authHttp 368 return this.authHttp
349 .get<{ source: VideoSource }>(VideoService.BASE_VIDEO_URL + '/' + videoId + '/source') 369 .get<{ source: VideoSource }>(VideoService.BASE_VIDEO_URL + '/' + videoId + '/source')
@@ -358,6 +378,8 @@ export class VideoService {
358 ) 378 )
359 } 379 }
360 380
381 // ---------------------------------------------------------------------------
382
361 setVideoLike (id: string, videoPassword: string) { 383 setVideoLike (id: string, videoPassword: string) {
362 return this.setVideoRate(id, 'like', videoPassword) 384 return this.setVideoRate(id, 'like', videoPassword)
363 } 385 }
@@ -370,6 +392,8 @@ export class VideoService {
370 return this.setVideoRate(id, 'none', videoPassword) 392 return this.setVideoRate(id, 'none', videoPassword)
371 } 393 }
372 394
395 // ---------------------------------------------------------------------------
396
373 getUserVideoRating (id: string) { 397 getUserVideoRating (id: string) {
374 const url = UserService.BASE_USERS_URL + 'me/videos/' + id + '/rating' 398 const url = UserService.BASE_USERS_URL + 'me/videos/' + id + '/rating'
375 399