diff options
author | Chocobozzz <me@florianbigard.com> | 2019-04-17 10:07:00 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2019-04-24 16:25:52 +0200 |
commit | e8bafea35bc930cb8ac5b2d521a188642a1adffe (patch) | |
tree | 7537f957ed7307b464e3c90b71b813d992acaade /server/models/video/video-playlist.ts | |
parent | 94565d52bb2883e09f16d1363170ac9c0dccb7a1 (diff) | |
download | PeerTube-e8bafea35bc930cb8ac5b2d521a188642a1adffe.tar.gz PeerTube-e8bafea35bc930cb8ac5b2d521a188642a1adffe.tar.zst PeerTube-e8bafea35bc930cb8ac5b2d521a188642a1adffe.zip |
Create a dedicated table to track video thumbnails
Diffstat (limited to 'server/models/video/video-playlist.ts')
-rw-r--r-- | server/models/video/video-playlist.ts | 85 |
1 files changed, 56 insertions, 29 deletions
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts index 0725b752a..073609c24 100644 --- a/server/models/video/video-playlist.ts +++ b/server/models/video/video-playlist.ts | |||
@@ -1,6 +1,5 @@ | |||
1 | import { | 1 | import { |
2 | AllowNull, | 2 | AllowNull, |
3 | BeforeDestroy, | ||
4 | BelongsTo, | 3 | BelongsTo, |
5 | Column, | 4 | Column, |
6 | CreatedAt, | 5 | CreatedAt, |
@@ -8,6 +7,7 @@ import { | |||
8 | Default, | 7 | Default, |
9 | ForeignKey, | 8 | ForeignKey, |
10 | HasMany, | 9 | HasMany, |
10 | HasOne, | ||
11 | Is, | 11 | Is, |
12 | IsUUID, | 12 | IsUUID, |
13 | Model, | 13 | Model, |
@@ -40,16 +40,16 @@ import { join } from 'path' | |||
40 | import { VideoPlaylistElementModel } from './video-playlist-element' | 40 | import { VideoPlaylistElementModel } from './video-playlist-element' |
41 | import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' | 41 | import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' |
42 | import { activityPubCollectionPagination } from '../../helpers/activitypub' | 42 | import { activityPubCollectionPagination } from '../../helpers/activitypub' |
43 | import { remove } from 'fs-extra' | ||
44 | import { logger } from '../../helpers/logger' | ||
45 | import { VideoPlaylistType } from '../../../shared/models/videos/playlist/video-playlist-type.model' | 43 | import { VideoPlaylistType } from '../../../shared/models/videos/playlist/video-playlist-type.model' |
46 | import { CONFIG } from '../../initializers/config' | 44 | import { ThumbnailModel } from './thumbnail' |
45 | import { ActivityIconObject } from '../../../shared/models/activitypub/objects' | ||
47 | 46 | ||
48 | enum ScopeNames { | 47 | enum ScopeNames { |
49 | AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', | 48 | AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', |
50 | WITH_VIDEOS_LENGTH = 'WITH_VIDEOS_LENGTH', | 49 | WITH_VIDEOS_LENGTH = 'WITH_VIDEOS_LENGTH', |
51 | WITH_ACCOUNT_AND_CHANNEL_SUMMARY = 'WITH_ACCOUNT_AND_CHANNEL_SUMMARY', | 50 | WITH_ACCOUNT_AND_CHANNEL_SUMMARY = 'WITH_ACCOUNT_AND_CHANNEL_SUMMARY', |
52 | WITH_ACCOUNT = 'WITH_ACCOUNT', | 51 | WITH_ACCOUNT = 'WITH_ACCOUNT', |
52 | WITH_THUMBNAIL = 'WITH_THUMBNAIL', | ||
53 | WITH_ACCOUNT_AND_CHANNEL = 'WITH_ACCOUNT_AND_CHANNEL' | 53 | WITH_ACCOUNT_AND_CHANNEL = 'WITH_ACCOUNT_AND_CHANNEL' |
54 | } | 54 | } |
55 | 55 | ||
@@ -62,6 +62,14 @@ type AvailableForListOptions = { | |||
62 | } | 62 | } |
63 | 63 | ||
64 | @Scopes({ | 64 | @Scopes({ |
65 | [ ScopeNames.WITH_THUMBNAIL ]: { | ||
66 | include: [ | ||
67 | { | ||
68 | model: () => ThumbnailModel, | ||
69 | required: false | ||
70 | } | ||
71 | ] | ||
72 | }, | ||
65 | [ ScopeNames.WITH_VIDEOS_LENGTH ]: { | 73 | [ ScopeNames.WITH_VIDEOS_LENGTH ]: { |
66 | attributes: { | 74 | attributes: { |
67 | include: [ | 75 | include: [ |
@@ -256,12 +264,15 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
256 | }) | 264 | }) |
257 | VideoPlaylistElements: VideoPlaylistElementModel[] | 265 | VideoPlaylistElements: VideoPlaylistElementModel[] |
258 | 266 | ||
259 | @BeforeDestroy | 267 | @HasOne(() => ThumbnailModel, { |
260 | static async removeFiles (instance: VideoPlaylistModel) { | 268 | foreignKey: { |
261 | logger.info('Removing files of video playlist %s.', instance.url) | 269 | name: 'videoPlaylistId', |
262 | 270 | allowNull: true | |
263 | return instance.removeThumbnail() | 271 | }, |
264 | } | 272 | onDelete: 'CASCADE', |
273 | hooks: true | ||
274 | }) | ||
275 | Thumbnail: ThumbnailModel | ||
265 | 276 | ||
266 | static listForApi (options: { | 277 | static listForApi (options: { |
267 | followerActorId: number | 278 | followerActorId: number |
@@ -292,7 +303,8 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
292 | } as AvailableForListOptions | 303 | } as AvailableForListOptions |
293 | ] | 304 | ] |
294 | } as any, // FIXME: typings | 305 | } as any, // FIXME: typings |
295 | ScopeNames.WITH_VIDEOS_LENGTH | 306 | ScopeNames.WITH_VIDEOS_LENGTH, |
307 | ScopeNames.WITH_THUMBNAIL | ||
296 | ] | 308 | ] |
297 | 309 | ||
298 | return VideoPlaylistModel | 310 | return VideoPlaylistModel |
@@ -365,7 +377,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
365 | } | 377 | } |
366 | 378 | ||
367 | return VideoPlaylistModel | 379 | return VideoPlaylistModel |
368 | .scope([ ScopeNames.WITH_ACCOUNT_AND_CHANNEL_SUMMARY, ScopeNames.WITH_VIDEOS_LENGTH ]) | 380 | .scope([ ScopeNames.WITH_ACCOUNT_AND_CHANNEL_SUMMARY, ScopeNames.WITH_VIDEOS_LENGTH, ScopeNames.WITH_THUMBNAIL ]) |
369 | .findOne(query) | 381 | .findOne(query) |
370 | } | 382 | } |
371 | 383 | ||
@@ -378,7 +390,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
378 | } | 390 | } |
379 | 391 | ||
380 | return VideoPlaylistModel | 392 | return VideoPlaylistModel |
381 | .scope([ ScopeNames.WITH_ACCOUNT_AND_CHANNEL, ScopeNames.WITH_VIDEOS_LENGTH ]) | 393 | .scope([ ScopeNames.WITH_ACCOUNT_AND_CHANNEL, ScopeNames.WITH_VIDEOS_LENGTH, ScopeNames.WITH_THUMBNAIL ]) |
382 | .findOne(query) | 394 | .findOne(query) |
383 | } | 395 | } |
384 | 396 | ||
@@ -389,7 +401,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
389 | } | 401 | } |
390 | } | 402 | } |
391 | 403 | ||
392 | return VideoPlaylistModel.scope(ScopeNames.WITH_ACCOUNT).findOne(query) | 404 | return VideoPlaylistModel.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_THUMBNAIL ]).findOne(query) |
393 | } | 405 | } |
394 | 406 | ||
395 | static getPrivacyLabel (privacy: VideoPlaylistPrivacy) { | 407 | static getPrivacyLabel (privacy: VideoPlaylistPrivacy) { |
@@ -411,24 +423,34 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
411 | return VideoPlaylistModel.update({ privacy: VideoPlaylistPrivacy.PRIVATE, videoChannelId: null }, query) | 423 | return VideoPlaylistModel.update({ privacy: VideoPlaylistPrivacy.PRIVATE, videoChannelId: null }, query) |
412 | } | 424 | } |
413 | 425 | ||
414 | getThumbnailName () { | 426 | setThumbnail (thumbnail: ThumbnailModel) { |
427 | this.Thumbnail = thumbnail | ||
428 | } | ||
429 | |||
430 | getThumbnail () { | ||
431 | return this.Thumbnail | ||
432 | } | ||
433 | |||
434 | hasThumbnail () { | ||
435 | return !!this.Thumbnail | ||
436 | } | ||
437 | |||
438 | generateThumbnailName () { | ||
415 | const extension = '.jpg' | 439 | const extension = '.jpg' |
416 | 440 | ||
417 | return 'playlist-' + this.uuid + extension | 441 | return 'playlist-' + this.uuid + extension |
418 | } | 442 | } |
419 | 443 | ||
420 | getThumbnailUrl () { | 444 | getThumbnailUrl () { |
421 | return WEBSERVER.URL + STATIC_PATHS.THUMBNAILS + this.getThumbnailName() | 445 | if (!this.hasThumbnail()) return null |
446 | |||
447 | return WEBSERVER.URL + STATIC_PATHS.THUMBNAILS + this.getThumbnail().filename | ||
422 | } | 448 | } |
423 | 449 | ||
424 | getThumbnailStaticPath () { | 450 | getThumbnailStaticPath () { |
425 | return join(STATIC_PATHS.THUMBNAILS, this.getThumbnailName()) | 451 | if (!this.hasThumbnail()) return null |
426 | } | ||
427 | 452 | ||
428 | removeThumbnail () { | 453 | return join(STATIC_PATHS.THUMBNAILS, this.getThumbnail().filename) |
429 | const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName()) | ||
430 | return remove(thumbnailPath) | ||
431 | .catch(err => logger.warn('Cannot delete thumbnail %s.', thumbnailPath, { err })) | ||
432 | } | 454 | } |
433 | 455 | ||
434 | setAsRefreshed () { | 456 | setAsRefreshed () { |
@@ -482,6 +504,17 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
482 | return VideoPlaylistElementModel.listUrlsOfForAP(this.id, start, count, t) | 504 | return VideoPlaylistElementModel.listUrlsOfForAP(this.id, start, count, t) |
483 | } | 505 | } |
484 | 506 | ||
507 | let icon: ActivityIconObject | ||
508 | if (this.hasThumbnail()) { | ||
509 | icon = { | ||
510 | type: 'Image' as 'Image', | ||
511 | url: this.getThumbnailUrl(), | ||
512 | mediaType: 'image/jpeg' as 'image/jpeg', | ||
513 | width: THUMBNAILS_SIZE.width, | ||
514 | height: THUMBNAILS_SIZE.height | ||
515 | } | ||
516 | } | ||
517 | |||
485 | return activityPubCollectionPagination(this.url, handler, page) | 518 | return activityPubCollectionPagination(this.url, handler, page) |
486 | .then(o => { | 519 | .then(o => { |
487 | return Object.assign(o, { | 520 | return Object.assign(o, { |
@@ -492,13 +525,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
492 | published: this.createdAt.toISOString(), | 525 | published: this.createdAt.toISOString(), |
493 | updated: this.updatedAt.toISOString(), | 526 | updated: this.updatedAt.toISOString(), |
494 | attributedTo: this.VideoChannel ? [ this.VideoChannel.Actor.url ] : [], | 527 | attributedTo: this.VideoChannel ? [ this.VideoChannel.Actor.url ] : [], |
495 | icon: { | 528 | icon |
496 | type: 'Image' as 'Image', | ||
497 | url: this.getThumbnailUrl(), | ||
498 | mediaType: 'image/jpeg' as 'image/jpeg', | ||
499 | width: THUMBNAILS_SIZE.width, | ||
500 | height: THUMBNAILS_SIZE.height | ||
501 | } | ||
502 | }) | 529 | }) |
503 | }) | 530 | }) |
504 | } | 531 | } |