aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--scripts/regenerate-thumbnails.ts25
-rw-r--r--server/lib/activitypub/videos.ts3
-rw-r--r--server/lib/thumbnail.ts5
-rw-r--r--server/models/account/actor-image.ts5
-rw-r--r--server/models/video/video.ts9
-rw-r--r--server/tests/cli/regenerate-thumbnails.ts32
6 files changed, 52 insertions, 27 deletions
diff --git a/scripts/regenerate-thumbnails.ts b/scripts/regenerate-thumbnails.ts
index 04c6e2b74..0213b8a22 100644
--- a/scripts/regenerate-thumbnails.ts
+++ b/scripts/regenerate-thumbnails.ts
@@ -3,12 +3,13 @@ registerTSPaths()
3 3
4import * as Bluebird from 'bluebird' 4import * as Bluebird from 'bluebird'
5import * as program from 'commander' 5import * as program from 'commander'
6import { pathExists } from 'fs-extra' 6import { pathExists, remove } from 'fs-extra'
7import { processImage } from '@server/helpers/image-utils' 7import { processImage } from '@server/helpers/image-utils'
8import { THUMBNAILS_SIZE } from '@server/initializers/constants' 8import { THUMBNAILS_SIZE } from '@server/initializers/constants'
9import { VideoModel } from '@server/models/video/video' 9import { VideoModel } from '@server/models/video/video'
10import { MVideo } from '@server/types/models' 10import { MVideo } from '@server/types/models'
11import { initDatabaseModels } from '@server/initializers/database' 11import { initDatabaseModels } from '@server/initializers/database'
12import { ActorImageModel } from '@server/models/account/actor-image'
12 13
13program 14program
14 .description('Regenerate local thumbnails using preview files') 15 .description('Regenerate local thumbnails using preview files')
@@ -37,13 +38,8 @@ async function processVideo (videoArg: MVideo) {
37 const thumbnail = video.getMiniature() 38 const thumbnail = video.getMiniature()
38 const preview = video.getPreview() 39 const preview = video.getPreview()
39 40
40 const thumbnailPath = thumbnail.getPath()
41 const previewPath = preview.getPath() 41 const previewPath = preview.getPath()
42 42
43 if (!await pathExists(thumbnailPath)) {
44 throw new Error(`Thumbnail ${thumbnailPath} does not exist on disk`)
45 }
46
47 if (!await pathExists(previewPath)) { 43 if (!await pathExists(previewPath)) {
48 throw new Error(`Preview ${previewPath} does not exist on disk`) 44 throw new Error(`Preview ${previewPath} does not exist on disk`)
49 } 45 }
@@ -52,5 +48,22 @@ async function processVideo (videoArg: MVideo) {
52 width: THUMBNAILS_SIZE.width, 48 width: THUMBNAILS_SIZE.width,
53 height: THUMBNAILS_SIZE.height 49 height: THUMBNAILS_SIZE.height
54 } 50 }
51
52 const oldPath = thumbnail.getPath()
53
54 // Update thumbnail
55 thumbnail.filename = ActorImageModel.generateFilename()
56 thumbnail.width = size.width
57 thumbnail.height = size.height
58
59 const thumbnailPath = thumbnail.getPath()
55 await processImage(previewPath, thumbnailPath, size, true) 60 await processImage(previewPath, thumbnailPath, size, true)
61
62 // Save new attributes
63 await thumbnail.save()
64
65 // Remove old thumbnail
66 await remove(oldPath)
67
68 // Don't federate, remote instances will refresh the thumbnails after a while
56} 69}
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts
index d484edd36..492b97b9e 100644
--- a/server/lib/activitypub/videos.ts
+++ b/server/lib/activitypub/videos.ts
@@ -3,6 +3,7 @@ import { maxBy, minBy } from 'lodash'
3import * as magnetUtil from 'magnet-uri' 3import * as magnetUtil from 'magnet-uri'
4import { basename, join } from 'path' 4import { basename, join } from 'path'
5import { Transaction } from 'sequelize/types' 5import { Transaction } from 'sequelize/types'
6import { ActorImageModel } from '@server/models/account/actor-image'
6import { TrackerModel } from '@server/models/server/tracker' 7import { TrackerModel } from '@server/models/server/tracker'
7import { VideoLiveModel } from '@server/models/video/video-live' 8import { VideoLiveModel } from '@server/models/video/video-live'
8import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 9import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
@@ -899,7 +900,7 @@ function getPreviewFromIcons (videoObject: VideoObject) {
899function getPreviewUrl (previewIcon: ActivityIconObject, video: MVideoWithHost) { 900function getPreviewUrl (previewIcon: ActivityIconObject, video: MVideoWithHost) {
900 return previewIcon 901 return previewIcon
901 ? previewIcon.url 902 ? previewIcon.url
902 : buildRemoteVideoBaseUrl(video, join(LAZY_STATIC_PATHS.PREVIEWS, video.generatePreviewName())) 903 : buildRemoteVideoBaseUrl(video, join(LAZY_STATIC_PATHS.PREVIEWS, ActorImageModel.generateFilename()))
903} 904}
904 905
905function getTrackerUrls (object: VideoObject, video: MVideoWithHost) { 906function getTrackerUrls (object: VideoObject, video: MVideoWithHost) {
diff --git a/server/lib/thumbnail.ts b/server/lib/thumbnail.ts
index 106f5fdaa..e1176ac08 100644
--- a/server/lib/thumbnail.ts
+++ b/server/lib/thumbnail.ts
@@ -1,4 +1,5 @@
1import { join } from 'path' 1import { join } from 'path'
2import { ActorImageModel } from '@server/models/account/actor-image'
2import { ThumbnailType } from '../../shared/models/videos/thumbnail.type' 3import { ThumbnailType } from '../../shared/models/videos/thumbnail.type'
3import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils' 4import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils'
4import { processImage } from '../helpers/image-utils' 5import { processImage } from '../helpers/image-utils'
@@ -200,7 +201,7 @@ function buildMetadataFromVideo (video: MVideoThumbnail, type: ThumbnailType, si
200 : undefined 201 : undefined
201 202
202 if (type === ThumbnailType.MINIATURE) { 203 if (type === ThumbnailType.MINIATURE) {
203 const filename = video.generateThumbnailName() 204 const filename = ActorImageModel.generateFilename()
204 const basePath = CONFIG.STORAGE.THUMBNAILS_DIR 205 const basePath = CONFIG.STORAGE.THUMBNAILS_DIR
205 206
206 return { 207 return {
@@ -214,7 +215,7 @@ function buildMetadataFromVideo (video: MVideoThumbnail, type: ThumbnailType, si
214 } 215 }
215 216
216 if (type === ThumbnailType.PREVIEW) { 217 if (type === ThumbnailType.PREVIEW) {
217 const filename = video.generatePreviewName() 218 const filename = ActorImageModel.generateFilename()
218 const basePath = CONFIG.STORAGE.PREVIEWS_DIR 219 const basePath = CONFIG.STORAGE.PREVIEWS_DIR
219 220
220 return { 221 return {
diff --git a/server/models/account/actor-image.ts b/server/models/account/actor-image.ts
index b779e3cf6..f7438991a 100644
--- a/server/models/account/actor-image.ts
+++ b/server/models/account/actor-image.ts
@@ -1,6 +1,7 @@
1import { remove } from 'fs-extra' 1import { remove } from 'fs-extra'
2import { join } from 'path' 2import { join } from 'path'
3import { AfterDestroy, AllowNull, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 3import { AfterDestroy, AllowNull, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
4import { v4 as uuidv4 } from 'uuid'
4import { MActorImageFormattable } from '@server/types/models' 5import { MActorImageFormattable } from '@server/types/models'
5import { ActorImageType } from '@shared/models' 6import { ActorImageType } from '@shared/models'
6import { ActorImage } from '../../../shared/models/actors/actor-image.model' 7import { ActorImage } from '../../../shared/models/actors/actor-image.model'
@@ -53,6 +54,10 @@ export class ActorImageModel extends Model {
53 .catch(err => logger.error('Cannot remove actor image file %s.', instance.filename, err)) 54 .catch(err => logger.error('Cannot remove actor image file %s.', instance.filename, err))
54 } 55 }
55 56
57 static generateFilename () {
58 return uuidv4() + '.jpg'
59 }
60
56 static loadByName (filename: string) { 61 static loadByName (filename: string) {
57 const query = { 62 const query = {
58 where: { 63 where: {
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 086269921..b10c6e38e 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -24,7 +24,6 @@ import {
24 Table, 24 Table,
25 UpdatedAt 25 UpdatedAt
26} from 'sequelize-typescript' 26} from 'sequelize-typescript'
27import { v4 as uuidv4 } from 'uuid'
28import { buildNSFWFilter } from '@server/helpers/express-utils' 27import { buildNSFWFilter } from '@server/helpers/express-utils'
29import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video' 28import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
30import { LiveManager } from '@server/lib/live-manager' 29import { LiveManager } from '@server/lib/live-manager'
@@ -1871,20 +1870,12 @@ export class VideoModel extends Model {
1871 this.Thumbnails.push(savedThumbnail) 1870 this.Thumbnails.push(savedThumbnail)
1872 } 1871 }
1873 1872
1874 generateThumbnailName () {
1875 return uuidv4() + '.jpg'
1876 }
1877
1878 getMiniature () { 1873 getMiniature () {
1879 if (Array.isArray(this.Thumbnails) === false) return undefined 1874 if (Array.isArray(this.Thumbnails) === false) return undefined
1880 1875
1881 return this.Thumbnails.find(t => t.type === ThumbnailType.MINIATURE) 1876 return this.Thumbnails.find(t => t.type === ThumbnailType.MINIATURE)
1882 } 1877 }
1883 1878
1884 generatePreviewName () {
1885 return uuidv4() + '.jpg'
1886 }
1887
1888 hasPreview () { 1879 hasPreview () {
1889 return !!this.getPreview() 1880 return !!this.getPreview()
1890 } 1881 }
diff --git a/server/tests/cli/regenerate-thumbnails.ts b/server/tests/cli/regenerate-thumbnails.ts
index a9c8642a5..8acb9f263 100644
--- a/server/tests/cli/regenerate-thumbnails.ts
+++ b/server/tests/cli/regenerate-thumbnails.ts
@@ -2,7 +2,7 @@ import 'mocha'
2import { expect } from 'chai' 2import { expect } from 'chai'
3import { writeFile } from 'fs-extra' 3import { writeFile } from 'fs-extra'
4import { basename, join } from 'path' 4import { basename, join } from 'path'
5import { Video } from '@shared/models' 5import { Video, VideoDetails } from '@shared/models'
6import { 6import {
7 buildServerDirectory, 7 buildServerDirectory,
8 cleanupTests, 8 cleanupTests,
@@ -19,6 +19,17 @@ import {
19} from '../../../shared/extra-utils' 19} from '../../../shared/extra-utils'
20import { HttpStatusCode } from '@shared/core-utils' 20import { HttpStatusCode } from '@shared/core-utils'
21 21
22async function testThumbnail (server: ServerInfo, videoId: number | string) {
23 const res = await getVideo(server.url, videoId)
24 const video: VideoDetails = res.body
25
26 const res1 = await makeRawRequest(join(server.url, video.thumbnailPath), HttpStatusCode.OK_200)
27 expect(res1.body).to.not.have.lengthOf(0)
28
29 const res2 = await makeRawRequest(join(server.url, video.thumbnailPath), HttpStatusCode.OK_200)
30 expect(res2.body).to.not.have.lengthOf(0)
31}
32
22describe('Test regenerate thumbnails script', function () { 33describe('Test regenerate thumbnails script', function () {
23 let servers: ServerInfo[] 34 let servers: ServerInfo[]
24 35
@@ -84,18 +95,21 @@ describe('Test regenerate thumbnails script', function () {
84 await execCLI(`${env} npm run regenerate-thumbnails`) 95 await execCLI(`${env} npm run regenerate-thumbnails`)
85 }) 96 })
86 97
87 it('Should have regenerated local thumbnails', async function () { 98 it('Should have generated new thumbnail files', async function () {
88 { 99 await testThumbnail(servers[0], video1.uuid)
89 const res1 = await makeRawRequest(join(servers[0].url, video1.thumbnailPath), HttpStatusCode.OK_200) 100 await testThumbnail(servers[0], video2.uuid)
90 expect(res1.body).to.not.have.lengthOf(0)
91 101
92 const res2 = await makeRawRequest(join(servers[0].url, video1.previewPath), HttpStatusCode.OK_200) 102 const res = await makeRawRequest(join(servers[0].url, remoteVideo.thumbnailPath), HttpStatusCode.OK_200)
93 expect(res2.body).to.not.have.lengthOf(0) 103 expect(res.body).to.have.lengthOf(0)
104 })
105
106 it('Should have deleted old thumbnail files', async function () {
107 {
108 await makeRawRequest(join(servers[0].url, video1.thumbnailPath), HttpStatusCode.NOT_FOUND_404)
94 } 109 }
95 110
96 { 111 {
97 const res = await makeRawRequest(join(servers[0].url, video2.thumbnailPath), HttpStatusCode.OK_200) 112 await makeRawRequest(join(servers[0].url, video2.thumbnailPath), HttpStatusCode.NOT_FOUND_404)
98 expect(res.body).to.not.have.lengthOf(0)
99 } 113 }
100 114
101 { 115 {