diff options
author | kontrollanten <6680299+kontrollanten@users.noreply.github.com> | 2022-10-24 14:48:03 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-24 14:48:03 +0200 |
commit | 38a3ccc7f8ad0ea94362b58c732af7c387ab46be (patch) | |
tree | ccbd200c4b95d0fcfa56b7d5fc3c9490887187b1 | |
parent | 01a3c07a7913891d4830797403b3865d53f0af61 (diff) | |
download | PeerTube-38a3ccc7f8ad0ea94362b58c732af7c387ab46be.tar.gz PeerTube-38a3ccc7f8ad0ea94362b58c732af7c387ab46be.tar.zst PeerTube-38a3ccc7f8ad0ea94362b58c732af7c387ab46be.zip |
feat: show contained playlists under My videos (#5125)
* feat: show contained playlists under My videos
closes #4769
* refactor(server): remove unused types
* fixes after code review
* fix(client/video-miniature): add to playlist
* fix(server/user/me): shortUUID response
* Revert "fix(client/video-miniature): add to playlist"
This reverts commit f1a0412391c7e2370b87df2594c9fe3f39a40ddc.
* fix(client/PlaylistService): caching
* Revert "fix(server/user/me): shortUUID response"
This reverts commit e3f1ee4e335739b895bced938540c003df24af73.
* Fix fetching playlists
Co-authored-by: Chocobozzz <me@florianbigard.com>
14 files changed, 79 insertions, 28 deletions
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.html b/client/src/app/+my-library/my-videos/my-videos.component.html index 146dcf41e..995f6b75b 100644 --- a/client/src/app/+my-library/my-videos/my-videos.component.html +++ b/client/src/app/+my-library/my-videos/my-videos.component.html | |||
@@ -34,6 +34,7 @@ | |||
34 | </div> | 34 | </div> |
35 | 35 | ||
36 | <my-videos-selection | 36 | <my-videos-selection |
37 | [videosContainedInPlaylists]="videosContainedInPlaylists" | ||
37 | [pagination]="pagination" | 38 | [pagination]="pagination" |
38 | [(selection)]="selection" | 39 | [(selection)]="selection" |
39 | [(videosModel)]="videos" | 40 | [(videosModel)]="videos" |
diff --git a/client/src/app/+my-library/my-videos/my-videos.component.ts b/client/src/app/+my-library/my-videos/my-videos.component.ts index 3ff0ee248..f5262da86 100644 --- a/client/src/app/+my-library/my-videos/my-videos.component.ts +++ b/client/src/app/+my-library/my-videos/my-videos.component.ts | |||
@@ -1,10 +1,11 @@ | |||
1 | import { uniqBy } from 'lodash' | ||
1 | import { concat, Observable } from 'rxjs' | 2 | import { concat, Observable } from 'rxjs' |
2 | import { tap, toArray } from 'rxjs/operators' | 3 | import { tap, toArray } from 'rxjs/operators' |
3 | import { Component, OnInit, ViewChild } from '@angular/core' | 4 | import { Component, OnInit, ViewChild } from '@angular/core' |
4 | import { ActivatedRoute, Router } from '@angular/router' | 5 | import { ActivatedRoute, Router } from '@angular/router' |
5 | import { AuthService, ComponentPagination, ConfirmService, Notifier, ScreenService, ServerService, User } from '@app/core' | 6 | import { AuthService, ComponentPagination, ConfirmService, Notifier, ScreenService, ServerService, User } from '@app/core' |
6 | import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' | 7 | import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' |
7 | import { prepareIcu, immutableAssign } from '@app/helpers' | 8 | import { immutableAssign, prepareIcu } from '@app/helpers' |
8 | import { AdvancedInputFilter } from '@app/shared/shared-forms' | 9 | import { AdvancedInputFilter } from '@app/shared/shared-forms' |
9 | import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' | 10 | import { DropdownAction, Video, VideoService } from '@app/shared/shared-main' |
10 | import { LiveStreamInformationComponent } from '@app/shared/shared-video-live' | 11 | import { LiveStreamInformationComponent } from '@app/shared/shared-video-live' |
@@ -14,7 +15,8 @@ import { | |||
14 | VideoActionsDisplayType, | 15 | VideoActionsDisplayType, |
15 | VideosSelectionComponent | 16 | VideosSelectionComponent |
16 | } from '@app/shared/shared-video-miniature' | 17 | } from '@app/shared/shared-video-miniature' |
17 | import { VideoChannel, VideoSortField } from '@shared/models' | 18 | import { VideoPlaylistService } from '@app/shared/shared-video-playlist' |
19 | import { VideoChannel, VideoExistInPlaylist, VideosExistInPlaylists, VideoSortField } from '@shared/models' | ||
18 | import { VideoChangeOwnershipComponent } from './modals/video-change-ownership.component' | 20 | import { VideoChangeOwnershipComponent } from './modals/video-change-ownership.component' |
19 | 21 | ||
20 | @Component({ | 22 | @Component({ |
@@ -26,6 +28,7 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook { | |||
26 | @ViewChild('videoChangeOwnershipModal', { static: true }) videoChangeOwnershipModal: VideoChangeOwnershipComponent | 28 | @ViewChild('videoChangeOwnershipModal', { static: true }) videoChangeOwnershipModal: VideoChangeOwnershipComponent |
27 | @ViewChild('liveStreamInformationModal', { static: true }) liveStreamInformationModal: LiveStreamInformationComponent | 29 | @ViewChild('liveStreamInformationModal', { static: true }) liveStreamInformationModal: LiveStreamInformationComponent |
28 | 30 | ||
31 | videosContainedInPlaylists: VideosExistInPlaylists = {} | ||
29 | titlePage: string | 32 | titlePage: string |
30 | selection: SelectionType = {} | 33 | selection: SelectionType = {} |
31 | pagination: ComponentPagination = { | 34 | pagination: ComponentPagination = { |
@@ -83,7 +86,8 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook { | |||
83 | protected notifier: Notifier, | 86 | protected notifier: Notifier, |
84 | protected screenService: ScreenService, | 87 | protected screenService: ScreenService, |
85 | private confirmService: ConfirmService, | 88 | private confirmService: ConfirmService, |
86 | private videoService: VideoService | 89 | private videoService: VideoService, |
90 | private playlistService: VideoPlaylistService | ||
87 | ) { | 91 | ) { |
88 | this.titlePage = $localize`My videos` | 92 | this.titlePage = $localize`My videos` |
89 | } | 93 | } |
@@ -156,10 +160,20 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook { | |||
156 | sort: this.sort, | 160 | sort: this.sort, |
157 | userChannels: this.userChannels, | 161 | userChannels: this.userChannels, |
158 | search: this.search | 162 | search: this.search |
159 | }) | 163 | }).pipe( |
160 | .pipe( | 164 | tap(res => this.pagination.totalItems = res.total), |
161 | tap(res => this.pagination.totalItems = res.total) | 165 | tap(({ data }) => this.fetchVideosContainedInPlaylists(data)) |
162 | ) | 166 | ) |
167 | } | ||
168 | |||
169 | private fetchVideosContainedInPlaylists (videos: Video[]) { | ||
170 | this.playlistService.doVideosExistInPlaylist(videos.map(v => v.id)) | ||
171 | .subscribe(result => { | ||
172 | this.videosContainedInPlaylists = Object.keys(result).reduce((acc, videoId) => ({ | ||
173 | ...acc, | ||
174 | [videoId]: uniqBy(result[videoId], (p: VideoExistInPlaylist) => p.playlistId) | ||
175 | }), this.videosContainedInPlaylists) | ||
176 | }) | ||
163 | } | 177 | } |
164 | 178 | ||
165 | async deleteSelectedVideos () { | 179 | async deleteSelectedVideos () { |
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.html b/client/src/app/shared/shared-video-miniature/video-miniature.component.html index e8d2ca1c4..6fdf24b2d 100644 --- a/client/src/app/shared/shared-video-miniature/video-miniature.component.html +++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.html | |||
@@ -52,6 +52,12 @@ | |||
52 | <ng-container *ngIf="displayOptions.privacyText && displayOptions.state && getStateLabel(video)"> - </ng-container> | 52 | <ng-container *ngIf="displayOptions.privacyText && displayOptions.state && getStateLabel(video)"> - </ng-container> |
53 | <ng-container *ngIf="displayOptions.state">{{ getStateLabel(video) }}</ng-container> | 53 | <ng-container *ngIf="displayOptions.state">{{ getStateLabel(video) }}</ng-container> |
54 | </div> | 54 | </div> |
55 | |||
56 | <div *ngIf="containedInPlaylists" class="video-contained-in-playlists"> | ||
57 | <a *ngFor="let playlist of containedInPlaylists" class="chip rectangular bg-secondary text-light" [routerLink]="['/w/p/', playlist.playlistShortUUID]"> | ||
58 | {{ playlist.playlistDisplayName }} | ||
59 | </a> | ||
60 | </div> | ||
55 | </div> | 61 | </div> |
56 | </div> | 62 | </div> |
57 | 63 | ||
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.scss b/client/src/app/shared/shared-video-miniature/video-miniature.component.scss index a397efdca..ba2adfc5a 100644 --- a/client/src/app/shared/shared-video-miniature/video-miniature.component.scss +++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.scss | |||
@@ -4,6 +4,10 @@ | |||
4 | 4 | ||
5 | $more-button-width: 40px; | 5 | $more-button-width: 40px; |
6 | 6 | ||
7 | .chip { | ||
8 | @include chip; | ||
9 | } | ||
10 | |||
7 | .video-miniature { | 11 | .video-miniature { |
8 | font-size: 14px; | 12 | font-size: 14px; |
9 | } | 13 | } |
diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts index 20596d6d0..85c63c173 100644 --- a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts +++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts | |||
@@ -11,7 +11,7 @@ import { | |||
11 | Output | 11 | Output |
12 | } from '@angular/core' | 12 | } from '@angular/core' |
13 | import { AuthService, ScreenService, ServerService, User } from '@app/core' | 13 | import { AuthService, ScreenService, ServerService, User } from '@app/core' |
14 | import { HTMLServerConfig, VideoPlaylistType, VideoPrivacy, VideoState } from '@shared/models' | 14 | import { HTMLServerConfig, VideoExistInPlaylist, VideoPlaylistType, VideoPrivacy, VideoState } from '@shared/models' |
15 | import { LinkType } from '../../../types/link.type' | 15 | import { LinkType } from '../../../types/link.type' |
16 | import { ActorAvatarSize } from '../shared-actor-image/actor-avatar.component' | 16 | import { ActorAvatarSize } from '../shared-actor-image/actor-avatar.component' |
17 | import { Video } from '../shared-main' | 17 | import { Video } from '../shared-main' |
@@ -40,6 +40,7 @@ export type MiniatureDisplayOptions = { | |||
40 | export class VideoMiniatureComponent implements OnInit { | 40 | export class VideoMiniatureComponent implements OnInit { |
41 | @Input() user: User | 41 | @Input() user: User |
42 | @Input() video: Video | 42 | @Input() video: Video |
43 | @Input() containedInPlaylists: VideoExistInPlaylist[] | ||
43 | 44 | ||
44 | @Input() displayOptions: MiniatureDisplayOptions = { | 45 | @Input() displayOptions: MiniatureDisplayOptions = { |
45 | date: true, | 46 | date: true, |
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.html b/client/src/app/shared/shared-video-miniature/videos-selection.component.html index 6ea2661e4..6c6db4b96 100644 --- a/client/src/app/shared/shared-video-miniature/videos-selection.component.html +++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.html | |||
@@ -12,6 +12,7 @@ | |||
12 | </div> | 12 | </div> |
13 | 13 | ||
14 | <my-video-miniature | 14 | <my-video-miniature |
15 | [containedInPlaylists]="videosContainedInPlaylists ? videosContainedInPlaylists[video.id] : undefined" | ||
15 | [video]="video" [displayAsRow]="true" [displayOptions]="miniatureDisplayOptions" | 16 | [video]="video" [displayAsRow]="true" [displayOptions]="miniatureDisplayOptions" |
16 | [displayVideoActions]="false" [user]="user" | 17 | [displayVideoActions]="false" [user]="user" |
17 | ></my-video-miniature> | 18 | ></my-video-miniature> |
diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts index fa3c79bbb..460a0080e 100644 --- a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts +++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts | |||
@@ -2,7 +2,7 @@ import { Observable, Subject } from 'rxjs' | |||
2 | import { AfterContentInit, Component, ContentChildren, EventEmitter, Input, Output, QueryList, TemplateRef } from '@angular/core' | 2 | import { AfterContentInit, Component, ContentChildren, EventEmitter, Input, Output, QueryList, TemplateRef } from '@angular/core' |
3 | import { ComponentPagination, Notifier, User } from '@app/core' | 3 | import { ComponentPagination, Notifier, User } from '@app/core' |
4 | import { logger } from '@root-helpers/logger' | 4 | import { logger } from '@root-helpers/logger' |
5 | import { ResultList, VideoSortField } from '@shared/models' | 5 | import { ResultList, VideosExistInPlaylists, VideoSortField } from '@shared/models' |
6 | import { PeerTubeTemplateDirective, Video } from '../shared-main' | 6 | import { PeerTubeTemplateDirective, Video } from '../shared-main' |
7 | import { MiniatureDisplayOptions } from './video-miniature.component' | 7 | import { MiniatureDisplayOptions } from './video-miniature.component' |
8 | 8 | ||
@@ -14,6 +14,7 @@ export type SelectionType = { [ id: number ]: boolean } | |||
14 | styleUrls: [ './videos-selection.component.scss' ] | 14 | styleUrls: [ './videos-selection.component.scss' ] |
15 | }) | 15 | }) |
16 | export class VideosSelectionComponent implements AfterContentInit { | 16 | export class VideosSelectionComponent implements AfterContentInit { |
17 | @Input() videosContainedInPlaylists: VideosExistInPlaylists | ||
17 | @Input() user: User | 18 | @Input() user: User |
18 | @Input() pagination: ComponentPagination | 19 | @Input() pagination: ComponentPagination |
19 | 20 | ||
diff --git a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts index f81de7c6b..2fc39fc75 100644 --- a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts +++ b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts | |||
@@ -6,8 +6,8 @@ import { AuthService, DisableForReuseHook, Notifier } from '@app/core' | |||
6 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' | 6 | import { FormReactive, FormReactiveService } from '@app/shared/shared-forms' |
7 | import { secondsToTime } from '@shared/core-utils' | 7 | import { secondsToTime } from '@shared/core-utils' |
8 | import { | 8 | import { |
9 | CachedVideoExistInPlaylist, | ||
9 | Video, | 10 | Video, |
10 | VideoExistInPlaylist, | ||
11 | VideoPlaylistCreate, | 11 | VideoPlaylistCreate, |
12 | VideoPlaylistElementCreate, | 12 | VideoPlaylistElementCreate, |
13 | VideoPlaylistElementUpdate, | 13 | VideoPlaylistElementUpdate, |
@@ -330,7 +330,7 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
330 | } | 330 | } |
331 | } | 331 | } |
332 | 332 | ||
333 | private rebuildPlaylists (existResult: VideoExistInPlaylist[]) { | 333 | private rebuildPlaylists (existResult: CachedVideoExistInPlaylist[]) { |
334 | debugLogger('Got existing results for %d.', this.video.id, existResult) | 334 | debugLogger('Got existing results for %d.', this.video.id, existResult) |
335 | 335 | ||
336 | const oldPlaylists = this.videoPlaylists | 336 | const oldPlaylists = this.videoPlaylists |
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist.service.ts b/client/src/app/shared/shared-video-playlist/video-playlist.service.ts index d71f8f72e..330a51f91 100644 --- a/client/src/app/shared/shared-video-playlist/video-playlist.service.ts +++ b/client/src/app/shared/shared-video-playlist/video-playlist.service.ts | |||
@@ -8,6 +8,8 @@ import { buildBulkObservable, objectToFormData } from '@app/helpers' | |||
8 | import { Account, AccountService, VideoChannel, VideoChannelService } from '@app/shared/shared-main' | 8 | import { Account, AccountService, VideoChannel, VideoChannelService } from '@app/shared/shared-main' |
9 | import { NGX_LOADING_BAR_IGNORED } from '@ngx-loading-bar/http-client' | 9 | import { NGX_LOADING_BAR_IGNORED } from '@ngx-loading-bar/http-client' |
10 | import { | 10 | import { |
11 | CachedVideoExistInPlaylist, | ||
12 | CachedVideosExistInPlaylists, | ||
11 | ResultList, | 13 | ResultList, |
12 | VideoExistInPlaylist, | 14 | VideoExistInPlaylist, |
13 | VideoPlaylist as VideoPlaylistServerModel, | 15 | VideoPlaylist as VideoPlaylistServerModel, |
@@ -34,11 +36,11 @@ export class VideoPlaylistService { | |||
34 | 36 | ||
35 | // Use a replay subject because we "next" a value before subscribing | 37 | // Use a replay subject because we "next" a value before subscribing |
36 | private videoExistsInPlaylistNotifier = new ReplaySubject<number>(1) | 38 | private videoExistsInPlaylistNotifier = new ReplaySubject<number>(1) |
37 | private videoExistsInPlaylistCacheSubject = new Subject<VideosExistInPlaylists>() | 39 | private videoExistsInPlaylistCacheSubject = new Subject<CachedVideosExistInPlaylists>() |
38 | private readonly videoExistsInPlaylistObservable: Observable<VideosExistInPlaylists> | 40 | private readonly videoExistsInPlaylistObservable: Observable<CachedVideosExistInPlaylists> |
39 | 41 | ||
40 | private videoExistsObservableCache: { [ id: number ]: Observable<VideoExistInPlaylist[]> } = {} | 42 | private videoExistsObservableCache: { [ id: number ]: Observable<CachedVideoExistInPlaylist[]> } = {} |
41 | private videoExistsCache: { [ id: number ]: VideoExistInPlaylist[] } = {} | 43 | private videoExistsCache: { [ id: number ]: CachedVideoExistInPlaylist[] } = {} |
42 | 44 | ||
43 | private myAccountPlaylistCache: ResultList<CachedPlaylist> = undefined | 45 | private myAccountPlaylistCache: ResultList<CachedPlaylist> = undefined |
44 | private myAccountPlaylistCacheRunning: Observable<ResultList<CachedPlaylist>> | 46 | private myAccountPlaylistCacheRunning: Observable<ResultList<CachedPlaylist>> |
@@ -346,7 +348,7 @@ export class VideoPlaylistService { | |||
346 | ) | 348 | ) |
347 | } | 349 | } |
348 | 350 | ||
349 | private doVideosExistInPlaylist (videoIds: number[]): Observable<VideosExistInPlaylists> { | 351 | doVideosExistInPlaylist (videoIds: number[]): Observable<VideosExistInPlaylists> { |
350 | const url = VideoPlaylistService.MY_VIDEO_PLAYLIST_URL + 'videos-exist' | 352 | const url = VideoPlaylistService.MY_VIDEO_PLAYLIST_URL + 'videos-exist' |
351 | 353 | ||
352 | let params = new HttpParams() | 354 | let params = new HttpParams() |
diff --git a/server/controllers/api/users/my-video-playlists.ts b/server/controllers/api/users/my-video-playlists.ts index f55ea2ec4..715717610 100644 --- a/server/controllers/api/users/my-video-playlists.ts +++ b/server/controllers/api/users/my-video-playlists.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | import { uuidToShort } from '@shared/extra-utils' | ||
1 | import express from 'express' | 2 | import express from 'express' |
2 | import { VideosExistInPlaylists } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model' | 3 | import { VideosExistInPlaylists } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model' |
3 | import { asyncMiddleware, authenticate } from '../../../middlewares' | 4 | import { asyncMiddleware, authenticate } from '../../../middlewares' |
@@ -24,7 +25,7 @@ async function doVideosInPlaylistExist (req: express.Request, res: express.Respo | |||
24 | const videoIds = req.query.videoIds.map(i => parseInt(i + '', 10)) | 25 | const videoIds = req.query.videoIds.map(i => parseInt(i + '', 10)) |
25 | const user = res.locals.oauth.token.User | 26 | const user = res.locals.oauth.token.User |
26 | 27 | ||
27 | const results = await VideoPlaylistModel.listPlaylistIdsOf(user.Account.id, videoIds) | 28 | const results = await VideoPlaylistModel.listPlaylistSummariesOf(user.Account.id, videoIds) |
28 | 29 | ||
29 | const existObject: VideosExistInPlaylists = {} | 30 | const existObject: VideosExistInPlaylists = {} |
30 | 31 | ||
@@ -37,6 +38,8 @@ async function doVideosInPlaylistExist (req: express.Request, res: express.Respo | |||
37 | existObject[element.videoId].push({ | 38 | existObject[element.videoId].push({ |
38 | playlistElementId: element.id, | 39 | playlistElementId: element.id, |
39 | playlistId: result.id, | 40 | playlistId: result.id, |
41 | playlistDisplayName: result.name, | ||
42 | playlistShortUUID: uuidToShort(result.uuid), | ||
40 | startTimestamp: element.startTimestamp, | 43 | startTimestamp: element.startTimestamp, |
41 | stopTimestamp: element.stopTimestamp | 44 | stopTimestamp: element.stopTimestamp |
42 | }) | 45 | }) |
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts index 81ce3dc9e..8bbe54c49 100644 --- a/server/models/video/video-playlist.ts +++ b/server/models/video/video-playlist.ts | |||
@@ -49,7 +49,7 @@ import { | |||
49 | MVideoPlaylistFormattable, | 49 | MVideoPlaylistFormattable, |
50 | MVideoPlaylistFull, | 50 | MVideoPlaylistFull, |
51 | MVideoPlaylistFullSummary, | 51 | MVideoPlaylistFullSummary, |
52 | MVideoPlaylistIdWithElements | 52 | MVideoPlaylistSummaryWithElements |
53 | } from '../../types/models/video/video-playlist' | 53 | } from '../../types/models/video/video-playlist' |
54 | import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions } from '../account/account' | 54 | import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions } from '../account/account' |
55 | import { ActorModel } from '../actor/actor' | 55 | import { ActorModel } from '../actor/actor' |
@@ -470,9 +470,9 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli | |||
470 | })) | 470 | })) |
471 | } | 471 | } |
472 | 472 | ||
473 | static listPlaylistIdsOf (accountId: number, videoIds: number[]): Promise<MVideoPlaylistIdWithElements[]> { | 473 | static listPlaylistSummariesOf (accountId: number, videoIds: number[]): Promise<MVideoPlaylistSummaryWithElements[]> { |
474 | const query = { | 474 | const query = { |
475 | attributes: [ 'id' ], | 475 | attributes: [ 'id', 'name', 'uuid' ], |
476 | where: { | 476 | where: { |
477 | ownerAccountId: accountId | 477 | ownerAccountId: accountId |
478 | }, | 478 | }, |
diff --git a/server/tests/api/videos/video-playlists.ts b/server/tests/api/videos/video-playlists.ts index 9d223de48..a3de73ba5 100644 --- a/server/tests/api/videos/video-playlists.ts +++ b/server/tests/api/videos/video-playlists.ts | |||
@@ -23,6 +23,7 @@ import { | |||
23 | setDefaultVideoChannel, | 23 | setDefaultVideoChannel, |
24 | waitJobs | 24 | waitJobs |
25 | } from '@shared/server-commands' | 25 | } from '@shared/server-commands' |
26 | import { uuidToShort } from '@shared/extra-utils' | ||
26 | 27 | ||
27 | async function checkPlaylistElementType ( | 28 | async function checkPlaylistElementType ( |
28 | servers: PeerTubeServer[], | 29 | servers: PeerTubeServer[], |
@@ -56,6 +57,7 @@ describe('Test video playlists', function () { | |||
56 | let playlistServer2UUID2: string | 57 | let playlistServer2UUID2: string |
57 | 58 | ||
58 | let playlistServer1Id: number | 59 | let playlistServer1Id: number |
60 | let playlistServer1DisplayName: string | ||
59 | let playlistServer1UUID: string | 61 | let playlistServer1UUID: string |
60 | let playlistServer1UUID2: string | 62 | let playlistServer1UUID2: string |
61 | 63 | ||
@@ -489,15 +491,17 @@ describe('Test video playlists', function () { | |||
489 | return commands[0].addElement({ playlistId: playlistServer1Id, attributes }) | 491 | return commands[0].addElement({ playlistId: playlistServer1Id, attributes }) |
490 | } | 492 | } |
491 | 493 | ||
494 | const playlistDisplayName = 'playlist 4' | ||
492 | const playlist = await commands[0].create({ | 495 | const playlist = await commands[0].create({ |
493 | attributes: { | 496 | attributes: { |
494 | displayName: 'playlist 4', | 497 | displayName: playlistDisplayName, |
495 | privacy: VideoPlaylistPrivacy.PUBLIC, | 498 | privacy: VideoPlaylistPrivacy.PUBLIC, |
496 | videoChannelId: servers[0].store.channel.id | 499 | videoChannelId: servers[0].store.channel.id |
497 | } | 500 | } |
498 | }) | 501 | }) |
499 | 502 | ||
500 | playlistServer1Id = playlist.id | 503 | playlistServer1Id = playlist.id |
504 | playlistServer1DisplayName = playlistDisplayName | ||
501 | playlistServer1UUID = playlist.uuid | 505 | playlistServer1UUID = playlist.uuid |
502 | 506 | ||
503 | await addVideo({ videoId: servers[0].store.videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 }) | 507 | await addVideo({ videoId: servers[0].store.videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 }) |
@@ -908,6 +912,8 @@ describe('Test video playlists', function () { | |||
908 | const elem = obj[servers[0].store.videos[0].id] | 912 | const elem = obj[servers[0].store.videos[0].id] |
909 | expect(elem).to.have.lengthOf(1) | 913 | expect(elem).to.have.lengthOf(1) |
910 | expect(elem[0].playlistElementId).to.exist | 914 | expect(elem[0].playlistElementId).to.exist |
915 | expect(elem[0].playlistDisplayName).to.equal(playlistServer1DisplayName) | ||
916 | expect(elem[0].playlistShortUUID).to.equal(uuidToShort(playlistServer1UUID)) | ||
911 | expect(elem[0].playlistId).to.equal(playlistServer1Id) | 917 | expect(elem[0].playlistId).to.equal(playlistServer1Id) |
912 | expect(elem[0].startTimestamp).to.equal(15) | 918 | expect(elem[0].startTimestamp).to.equal(15) |
913 | expect(elem[0].stopTimestamp).to.equal(28) | 919 | expect(elem[0].stopTimestamp).to.equal(28) |
@@ -917,6 +923,8 @@ describe('Test video playlists', function () { | |||
917 | const elem = obj[servers[0].store.videos[3].id] | 923 | const elem = obj[servers[0].store.videos[3].id] |
918 | expect(elem).to.have.lengthOf(1) | 924 | expect(elem).to.have.lengthOf(1) |
919 | expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4) | 925 | expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4) |
926 | expect(elem[0].playlistDisplayName).to.equal(playlistServer1DisplayName) | ||
927 | expect(elem[0].playlistShortUUID).to.equal(uuidToShort(playlistServer1UUID)) | ||
920 | expect(elem[0].playlistId).to.equal(playlistServer1Id) | 928 | expect(elem[0].playlistId).to.equal(playlistServer1Id) |
921 | expect(elem[0].startTimestamp).to.equal(1) | 929 | expect(elem[0].startTimestamp).to.equal(1) |
922 | expect(elem[0].stopTimestamp).to.equal(35) | 930 | expect(elem[0].stopTimestamp).to.equal(35) |
@@ -926,6 +934,8 @@ describe('Test video playlists', function () { | |||
926 | const elem = obj[servers[0].store.videos[4].id] | 934 | const elem = obj[servers[0].store.videos[4].id] |
927 | expect(elem).to.have.lengthOf(1) | 935 | expect(elem).to.have.lengthOf(1) |
928 | expect(elem[0].playlistId).to.equal(playlistServer1Id) | 936 | expect(elem[0].playlistId).to.equal(playlistServer1Id) |
937 | expect(elem[0].playlistDisplayName).to.equal(playlistServer1DisplayName) | ||
938 | expect(elem[0].playlistShortUUID).to.equal(uuidToShort(playlistServer1UUID)) | ||
929 | expect(elem[0].startTimestamp).to.equal(45) | 939 | expect(elem[0].startTimestamp).to.equal(45) |
930 | expect(elem[0].stopTimestamp).to.equal(null) | 940 | expect(elem[0].stopTimestamp).to.equal(null) |
931 | } | 941 | } |
diff --git a/server/types/models/video/video-playlist.ts b/server/types/models/video/video-playlist.ts index 33fe5416a..40f0dfc14 100644 --- a/server/types/models/video/video-playlist.ts +++ b/server/types/models/video/video-playlist.ts | |||
@@ -14,6 +14,10 @@ export type MVideoPlaylist = Omit<VideoPlaylistModel, 'OwnerAccount' | 'VideoCha | |||
14 | // ############################################################################ | 14 | // ############################################################################ |
15 | 15 | ||
16 | export type MVideoPlaylistId = Pick<MVideoPlaylist, 'id'> | 16 | export type MVideoPlaylistId = Pick<MVideoPlaylist, 'id'> |
17 | export type MVideoPlaylistSummary = | ||
18 | Pick<MVideoPlaylist, 'id'> & | ||
19 | Pick<MVideoPlaylist, 'name'> & | ||
20 | Pick<MVideoPlaylist, 'uuid'> | ||
17 | export type MVideoPlaylistPrivacy = Pick<MVideoPlaylist, 'privacy'> | 21 | export type MVideoPlaylistPrivacy = Pick<MVideoPlaylist, 'privacy'> |
18 | export type MVideoPlaylistUUID = Pick<MVideoPlaylist, 'uuid'> | 22 | export type MVideoPlaylistUUID = Pick<MVideoPlaylist, 'uuid'> |
19 | export type MVideoPlaylistVideosLength = MVideoPlaylist & { videosLength?: number } | 23 | export type MVideoPlaylistVideosLength = MVideoPlaylist & { videosLength?: number } |
@@ -22,12 +26,8 @@ export type MVideoPlaylistVideosLength = MVideoPlaylist & { videosLength?: numbe | |||
22 | 26 | ||
23 | // With elements | 27 | // With elements |
24 | 28 | ||
25 | export type MVideoPlaylistWithElements = | 29 | export type MVideoPlaylistSummaryWithElements = |
26 | MVideoPlaylist & | 30 | MVideoPlaylistSummary & |
27 | Use<'VideoPlaylistElements', MVideoPlaylistElementLight[]> | ||
28 | |||
29 | export type MVideoPlaylistIdWithElements = | ||
30 | MVideoPlaylistId & | ||
31 | Use<'VideoPlaylistElements', MVideoPlaylistElementLight[]> | 31 | Use<'VideoPlaylistElements', MVideoPlaylistElementLight[]> |
32 | 32 | ||
33 | // ############################################################################ | 33 | // ############################################################################ |
diff --git a/shared/models/videos/playlist/video-exist-in-playlist.model.ts b/shared/models/videos/playlist/video-exist-in-playlist.model.ts index fc979c8c0..bc803a99c 100644 --- a/shared/models/videos/playlist/video-exist-in-playlist.model.ts +++ b/shared/models/videos/playlist/video-exist-in-playlist.model.ts | |||
@@ -1,10 +1,18 @@ | |||
1 | export type VideosExistInPlaylists = { | 1 | export type VideosExistInPlaylists = { |
2 | [videoId: number ]: VideoExistInPlaylist[] | 2 | [videoId: number ]: VideoExistInPlaylist[] |
3 | } | 3 | } |
4 | export type CachedVideosExistInPlaylists = { | ||
5 | [videoId: number ]: CachedVideoExistInPlaylist[] | ||
6 | } | ||
4 | 7 | ||
5 | export type VideoExistInPlaylist = { | 8 | export type CachedVideoExistInPlaylist = { |
6 | playlistElementId: number | 9 | playlistElementId: number |
7 | playlistId: number | 10 | playlistId: number |
8 | startTimestamp?: number | 11 | startTimestamp?: number |
9 | stopTimestamp?: number | 12 | stopTimestamp?: number |
10 | } | 13 | } |
14 | |||
15 | export type VideoExistInPlaylist = CachedVideoExistInPlaylist & { | ||
16 | playlistDisplayName: string | ||
17 | playlistShortUUID: string | ||
18 | } | ||