]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/models/video/thumbnail.ts
Fix video job error when video has been deleted
[github/Chocobozzz/PeerTube.git] / server / models / video / thumbnail.ts
index 3cad6c668d0f3260d395b70c5d68a40359de2328..f33bd31799b302a904591cb2fdcfb7d4cbcae1f6 100644 (file)
@@ -3,6 +3,8 @@ import { join } from 'path'
 import {
   AfterDestroy,
   AllowNull,
+  BeforeCreate,
+  BeforeUpdate,
   BelongsTo,
   Column,
   CreatedAt,
@@ -13,8 +15,9 @@ import {
   Table,
   UpdatedAt
 } from 'sequelize-typescript'
-import { buildRemoteVideoBaseUrl } from '@server/helpers/activitypub'
-import { MThumbnailVideo, MVideoAccountLight } from '@server/types/models'
+import { afterCommitIfTransaction } from '@server/helpers/database-utils'
+import { MThumbnail, MThumbnailVideo, MVideo } from '@server/types/models'
+import { AttributesOnly } from '@shared/typescript-utils'
 import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
 import { logger } from '../../helpers/logger'
 import { CONFIG } from '../../initializers/config'
@@ -38,7 +41,7 @@ import { VideoPlaylistModel } from './video-playlist'
     }
   ]
 })
-export class ThumbnailModel extends Model {
+export class ThumbnailModel extends Model<Partial<AttributesOnly<ThumbnailModel>>> {
 
   @AllowNull(false)
   @Column
@@ -96,6 +99,9 @@ export class ThumbnailModel extends Model {
   @UpdatedAt
   updatedAt: Date
 
+  // If this thumbnail replaced existing one, track the old name
+  previousThumbnailFilename: string
+
   private static readonly types: { [ id in ThumbnailType ]: { label: string, directory: string, staticPath: string } } = {
     [ThumbnailType.MINIATURE]: {
       label: 'miniature',
@@ -109,16 +115,33 @@ export class ThumbnailModel extends Model {
     }
   }
 
+  @BeforeCreate
+  @BeforeUpdate
+  static removeOldFile (instance: ThumbnailModel, options) {
+    return afterCommitIfTransaction(options.transaction, () => instance.removePreviousFilenameIfNeeded())
+  }
+
   @AfterDestroy
   static removeFiles (instance: ThumbnailModel) {
     logger.info('Removing %s file %s.', ThumbnailModel.types[instance.type].label, instance.filename)
 
     // Don't block the transaction
     instance.removeThumbnail()
-            .catch(err => logger.error('Cannot remove thumbnail file %s.', instance.filename, err))
+            .catch(err => logger.error('Cannot remove thumbnail file %s.', instance.filename, { err }))
   }
 
-  static loadWithVideoByName (filename: string, thumbnailType: ThumbnailType): Promise<MThumbnailVideo> {
+  static loadByFilename (filename: string, thumbnailType: ThumbnailType): Promise<MThumbnail> {
+    const query = {
+      where: {
+        filename,
+        type: thumbnailType
+      }
+    }
+
+    return ThumbnailModel.findOne(query)
+  }
+
+  static loadWithVideoByFilename (filename: string, thumbnailType: ThumbnailType): Promise<MThumbnailVideo> {
     const query = {
       where: {
         filename,
@@ -135,22 +158,39 @@ export class ThumbnailModel extends Model {
     return ThumbnailModel.findOne(query)
   }
 
-  getFileUrl (video: MVideoAccountLight) {
+  static buildPath (type: ThumbnailType, filename: string) {
+    const directory = ThumbnailModel.types[type].directory
+
+    return join(directory, filename)
+  }
+
+  getFileUrl (video: MVideo) {
     const staticPath = ThumbnailModel.types[this.type].staticPath + this.filename
 
     if (video.isOwned()) return WEBSERVER.URL + staticPath
-    if (this.fileUrl) return this.fileUrl
 
-    // Fallback if we don't have a file URL
-    return buildRemoteVideoBaseUrl(video, staticPath)
+    return this.fileUrl
   }
 
   getPath () {
-    const directory = ThumbnailModel.types[this.type].directory
-    return join(directory, this.filename)
+    return ThumbnailModel.buildPath(this.type, this.filename)
+  }
+
+  getPreviousPath () {
+    return ThumbnailModel.buildPath(this.type, this.previousThumbnailFilename)
   }
 
   removeThumbnail () {
     return remove(this.getPath())
   }
+
+  removePreviousFilenameIfNeeded () {
+    if (!this.previousThumbnailFilename) return
+
+    const previousPath = this.getPreviousPath()
+    remove(previousPath)
+      .catch(err => logger.error('Cannot remove previous thumbnail file %s.', previousPath, { err }))
+
+    this.previousThumbnailFilename = undefined
+  }
 }