diff options
author | Chocobozzz <me@florianbigard.com> | 2023-06-01 14:51:16 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2023-06-29 10:16:55 +0200 |
commit | d8f39b126d9fe4bec1c12fb213548cc6edc87867 (patch) | |
tree | 7f0f1cb23165cf4dd789b2d78b1fef7ee116f647 /client/src/app | |
parent | 1fb7d094229acdc190c3f7551b43ac5445814dee (diff) | |
download | PeerTube-d8f39b126d9fe4bec1c12fb213548cc6edc87867.tar.gz PeerTube-d8f39b126d9fe4bec1c12fb213548cc6edc87867.tar.zst PeerTube-d8f39b126d9fe4bec1c12fb213548cc6edc87867.zip |
Add storyboard support
Diffstat (limited to 'client/src/app')
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' | |||
9 | import { ServerService } from '@app/core/server/server.service' | 9 | import { ServerService } from '@app/core/server/server.service' |
10 | import { | 10 | import { |
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 | ||
25 | export const CACHE_PREVIEWS_SIZE_VALIDATOR: BuildFormValidator = { | 25 | export 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 | |||
34 | export 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 | ||