diff options
author | Chocobozzz <me@florianbigard.com> | 2019-03-05 10:58:44 +0100 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2019-03-18 11:17:59 +0100 |
commit | df0b219d36bf6852cdf2a7ad09ed4a41c6bccefa (patch) | |
tree | c4984e854f5dc18e5c27afd73b843bd52c143034 /server/models/video/video-playlist.ts | |
parent | 07b1a18aa678d260009a93e36606c5c5f585723d (diff) | |
download | PeerTube-df0b219d36bf6852cdf2a7ad09ed4a41c6bccefa.tar.gz PeerTube-df0b219d36bf6852cdf2a7ad09ed4a41c6bccefa.tar.zst PeerTube-df0b219d36bf6852cdf2a7ad09ed4a41c6bccefa.zip |
Add playlist rest tests
Diffstat (limited to 'server/models/video/video-playlist.ts')
-rw-r--r-- | server/models/video/video-playlist.ts | 92 |
1 files changed, 76 insertions, 16 deletions
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts index 397887ebf..ce49f77ec 100644 --- a/server/models/video/video-playlist.ts +++ b/server/models/video/video-playlist.ts | |||
@@ -24,7 +24,14 @@ import { | |||
24 | isVideoPlaylistPrivacyValid | 24 | isVideoPlaylistPrivacyValid |
25 | } from '../../helpers/custom-validators/video-playlists' | 25 | } from '../../helpers/custom-validators/video-playlists' |
26 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 26 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
27 | import { CONFIG, CONSTRAINTS_FIELDS, STATIC_PATHS, THUMBNAILS_SIZE, VIDEO_PLAYLIST_PRIVACIES } from '../../initializers' | 27 | import { |
28 | CONFIG, | ||
29 | CONSTRAINTS_FIELDS, | ||
30 | STATIC_PATHS, | ||
31 | THUMBNAILS_SIZE, | ||
32 | VIDEO_PLAYLIST_PRIVACIES, | ||
33 | VIDEO_PLAYLIST_TYPES | ||
34 | } from '../../initializers' | ||
28 | import { VideoPlaylist } from '../../../shared/models/videos/playlist/video-playlist.model' | 35 | import { VideoPlaylist } from '../../../shared/models/videos/playlist/video-playlist.model' |
29 | import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account' | 36 | import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account' |
30 | import { ScopeNames as VideoChannelScopeNames, VideoChannelModel } from './video-channel' | 37 | import { ScopeNames as VideoChannelScopeNames, VideoChannelModel } from './video-channel' |
@@ -34,22 +41,25 @@ import { PlaylistObject } from '../../../shared/models/activitypub/objects/playl | |||
34 | import { activityPubCollectionPagination } from '../../helpers/activitypub' | 41 | import { activityPubCollectionPagination } from '../../helpers/activitypub' |
35 | import { remove } from 'fs-extra' | 42 | import { remove } from 'fs-extra' |
36 | import { logger } from '../../helpers/logger' | 43 | import { logger } from '../../helpers/logger' |
44 | import { VideoPlaylistType } from '../../../shared/models/videos/playlist/video-playlist-type.model' | ||
37 | 45 | ||
38 | enum ScopeNames { | 46 | enum ScopeNames { |
39 | AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', | 47 | AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', |
40 | WITH_VIDEOS_LENGTH = 'WITH_VIDEOS_LENGTH', | 48 | WITH_VIDEOS_LENGTH = 'WITH_VIDEOS_LENGTH', |
41 | WITH_ACCOUNT_AND_CHANNEL = 'WITH_ACCOUNT_AND_CHANNEL' | 49 | WITH_ACCOUNT_AND_CHANNEL_SUMMARY = 'WITH_ACCOUNT_AND_CHANNEL_SUMMARY', |
50 | WITH_ACCOUNT = 'WITH_ACCOUNT' | ||
42 | } | 51 | } |
43 | 52 | ||
44 | type AvailableForListOptions = { | 53 | type AvailableForListOptions = { |
45 | followerActorId: number | 54 | followerActorId: number |
46 | accountId?: number, | 55 | type?: VideoPlaylistType |
56 | accountId?: number | ||
47 | videoChannelId?: number | 57 | videoChannelId?: number |
48 | privateAndUnlisted?: boolean | 58 | privateAndUnlisted?: boolean |
49 | } | 59 | } |
50 | 60 | ||
51 | @Scopes({ | 61 | @Scopes({ |
52 | [ScopeNames.WITH_VIDEOS_LENGTH]: { | 62 | [ ScopeNames.WITH_VIDEOS_LENGTH ]: { |
53 | attributes: { | 63 | attributes: { |
54 | include: [ | 64 | include: [ |
55 | [ | 65 | [ |
@@ -59,7 +69,15 @@ type AvailableForListOptions = { | |||
59 | ] | 69 | ] |
60 | } | 70 | } |
61 | }, | 71 | }, |
62 | [ScopeNames.WITH_ACCOUNT_AND_CHANNEL]: { | 72 | [ ScopeNames.WITH_ACCOUNT ]: { |
73 | include: [ | ||
74 | { | ||
75 | model: () => AccountModel, | ||
76 | required: true | ||
77 | } | ||
78 | ] | ||
79 | }, | ||
80 | [ ScopeNames.WITH_ACCOUNT_AND_CHANNEL_SUMMARY ]: { | ||
63 | include: [ | 81 | include: [ |
64 | { | 82 | { |
65 | model: () => AccountModel.scope(AccountScopeNames.SUMMARY), | 83 | model: () => AccountModel.scope(AccountScopeNames.SUMMARY), |
@@ -71,7 +89,7 @@ type AvailableForListOptions = { | |||
71 | } | 89 | } |
72 | ] | 90 | ] |
73 | }, | 91 | }, |
74 | [ScopeNames.AVAILABLE_FOR_LIST]: (options: AvailableForListOptions) => { | 92 | [ ScopeNames.AVAILABLE_FOR_LIST ]: (options: AvailableForListOptions) => { |
75 | // Only list local playlists OR playlists that are on an instance followed by actorId | 93 | // Only list local playlists OR playlists that are on an instance followed by actorId |
76 | const inQueryInstanceFollow = buildServerIdsFollowedBy(options.followerActorId) | 94 | const inQueryInstanceFollow = buildServerIdsFollowedBy(options.followerActorId) |
77 | const actorWhere = { | 95 | const actorWhere = { |
@@ -107,6 +125,12 @@ type AvailableForListOptions = { | |||
107 | }) | 125 | }) |
108 | } | 126 | } |
109 | 127 | ||
128 | if (options.type) { | ||
129 | whereAnd.push({ | ||
130 | type: options.type | ||
131 | }) | ||
132 | } | ||
133 | |||
110 | const where = { | 134 | const where = { |
111 | [Sequelize.Op.and]: whereAnd | 135 | [Sequelize.Op.and]: whereAnd |
112 | } | 136 | } |
@@ -179,6 +203,11 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
179 | @Column(DataType.UUID) | 203 | @Column(DataType.UUID) |
180 | uuid: string | 204 | uuid: string |
181 | 205 | ||
206 | @AllowNull(false) | ||
207 | @Default(VideoPlaylistType.REGULAR) | ||
208 | @Column | ||
209 | type: VideoPlaylistType | ||
210 | |||
182 | @ForeignKey(() => AccountModel) | 211 | @ForeignKey(() => AccountModel) |
183 | @Column | 212 | @Column |
184 | ownerAccountId: number | 213 | ownerAccountId: number |
@@ -208,13 +237,10 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
208 | name: 'videoPlaylistId', | 237 | name: 'videoPlaylistId', |
209 | allowNull: false | 238 | allowNull: false |
210 | }, | 239 | }, |
211 | onDelete: 'cascade' | 240 | onDelete: 'CASCADE' |
212 | }) | 241 | }) |
213 | VideoPlaylistElements: VideoPlaylistElementModel[] | 242 | VideoPlaylistElements: VideoPlaylistElementModel[] |
214 | 243 | ||
215 | // Calculated field | ||
216 | videosLength?: number | ||
217 | |||
218 | @BeforeDestroy | 244 | @BeforeDestroy |
219 | static async removeFiles (instance: VideoPlaylistModel) { | 245 | static async removeFiles (instance: VideoPlaylistModel) { |
220 | logger.info('Removing files of video playlist %s.', instance.url) | 246 | logger.info('Removing files of video playlist %s.', instance.url) |
@@ -227,6 +253,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
227 | start: number, | 253 | start: number, |
228 | count: number, | 254 | count: number, |
229 | sort: string, | 255 | sort: string, |
256 | type?: VideoPlaylistType, | ||
230 | accountId?: number, | 257 | accountId?: number, |
231 | videoChannelId?: number, | 258 | videoChannelId?: number, |
232 | privateAndUnlisted?: boolean | 259 | privateAndUnlisted?: boolean |
@@ -242,6 +269,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
242 | method: [ | 269 | method: [ |
243 | ScopeNames.AVAILABLE_FOR_LIST, | 270 | ScopeNames.AVAILABLE_FOR_LIST, |
244 | { | 271 | { |
272 | type: options.type, | ||
245 | followerActorId: options.followerActorId, | 273 | followerActorId: options.followerActorId, |
246 | accountId: options.accountId, | 274 | accountId: options.accountId, |
247 | videoChannelId: options.videoChannelId, | 275 | videoChannelId: options.videoChannelId, |
@@ -289,7 +317,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
289 | .then(e => !!e) | 317 | .then(e => !!e) |
290 | } | 318 | } |
291 | 319 | ||
292 | static load (id: number | string, transaction: Sequelize.Transaction) { | 320 | static loadWithAccountAndChannel (id: number | string, transaction: Sequelize.Transaction) { |
293 | const where = buildWhereIdOrUUID(id) | 321 | const where = buildWhereIdOrUUID(id) |
294 | 322 | ||
295 | const query = { | 323 | const query = { |
@@ -298,14 +326,39 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
298 | } | 326 | } |
299 | 327 | ||
300 | return VideoPlaylistModel | 328 | return VideoPlaylistModel |
301 | .scope([ ScopeNames.WITH_ACCOUNT_AND_CHANNEL, ScopeNames.WITH_VIDEOS_LENGTH ]) | 329 | .scope([ ScopeNames.WITH_ACCOUNT_AND_CHANNEL_SUMMARY, ScopeNames.WITH_VIDEOS_LENGTH ]) |
302 | .findOne(query) | 330 | .findOne(query) |
303 | } | 331 | } |
304 | 332 | ||
333 | static loadByUrlAndPopulateAccount (url: string) { | ||
334 | const query = { | ||
335 | where: { | ||
336 | url | ||
337 | } | ||
338 | } | ||
339 | |||
340 | return VideoPlaylistModel.scope(ScopeNames.WITH_ACCOUNT).findOne(query) | ||
341 | } | ||
342 | |||
305 | static getPrivacyLabel (privacy: VideoPlaylistPrivacy) { | 343 | static getPrivacyLabel (privacy: VideoPlaylistPrivacy) { |
306 | return VIDEO_PLAYLIST_PRIVACIES[privacy] || 'Unknown' | 344 | return VIDEO_PLAYLIST_PRIVACIES[privacy] || 'Unknown' |
307 | } | 345 | } |
308 | 346 | ||
347 | static getTypeLabel (type: VideoPlaylistType) { | ||
348 | return VIDEO_PLAYLIST_TYPES[type] || 'Unknown' | ||
349 | } | ||
350 | |||
351 | static resetPlaylistsOfChannel (videoChannelId: number, transaction: Sequelize.Transaction) { | ||
352 | const query = { | ||
353 | where: { | ||
354 | videoChannelId | ||
355 | }, | ||
356 | transaction | ||
357 | } | ||
358 | |||
359 | return VideoPlaylistModel.update({ privacy: VideoPlaylistPrivacy.PRIVATE, videoChannelId: null }, query) | ||
360 | } | ||
361 | |||
309 | getThumbnailName () { | 362 | getThumbnailName () { |
310 | const extension = '.jpg' | 363 | const extension = '.jpg' |
311 | 364 | ||
@@ -345,7 +398,12 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
345 | 398 | ||
346 | thumbnailPath: this.getThumbnailStaticPath(), | 399 | thumbnailPath: this.getThumbnailStaticPath(), |
347 | 400 | ||
348 | videosLength: this.videosLength, | 401 | type: { |
402 | id: this.type, | ||
403 | label: VideoPlaylistModel.getTypeLabel(this.type) | ||
404 | }, | ||
405 | |||
406 | videosLength: this.get('videosLength'), | ||
349 | 407 | ||
350 | createdAt: this.createdAt, | 408 | createdAt: this.createdAt, |
351 | updatedAt: this.updatedAt, | 409 | updatedAt: this.updatedAt, |
@@ -355,18 +413,20 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
355 | } | 413 | } |
356 | } | 414 | } |
357 | 415 | ||
358 | toActivityPubObject (): Promise<PlaylistObject> { | 416 | toActivityPubObject (page: number, t: Sequelize.Transaction): Promise<PlaylistObject> { |
359 | const handler = (start: number, count: number) => { | 417 | const handler = (start: number, count: number) => { |
360 | return VideoPlaylistElementModel.listUrlsOfForAP(this.id, start, count) | 418 | return VideoPlaylistElementModel.listUrlsOfForAP(this.id, start, count, t) |
361 | } | 419 | } |
362 | 420 | ||
363 | return activityPubCollectionPagination(this.url, handler, null) | 421 | return activityPubCollectionPagination(this.url, handler, page) |
364 | .then(o => { | 422 | .then(o => { |
365 | return Object.assign(o, { | 423 | return Object.assign(o, { |
366 | type: 'Playlist' as 'Playlist', | 424 | type: 'Playlist' as 'Playlist', |
367 | name: this.name, | 425 | name: this.name, |
368 | content: this.description, | 426 | content: this.description, |
369 | uuid: this.uuid, | 427 | uuid: this.uuid, |
428 | published: this.createdAt.toISOString(), | ||
429 | updated: this.updatedAt.toISOString(), | ||
370 | attributedTo: this.VideoChannel ? [ this.VideoChannel.Actor.url ] : [], | 430 | attributedTo: this.VideoChannel ? [ this.VideoChannel.Actor.url ] : [], |
371 | icon: { | 431 | icon: { |
372 | type: 'Image' as 'Image', | 432 | type: 'Image' as 'Image', |