diff options
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | scripts/regenerate-thumbnails.ts | 54 | ||||
-rw-r--r-- | server/tests/cli/index.ts | 1 | ||||
-rw-r--r-- | server/tests/cli/regenerate-thumbnails.ts | 110 | ||||
-rw-r--r-- | support/doc/tools.md | 10 |
5 files changed, 176 insertions, 0 deletions
diff --git a/package.json b/package.json index 345106ce1..72b95e842 100644 --- a/package.json +++ b/package.json | |||
@@ -53,6 +53,7 @@ | |||
53 | "start:server": "node dist/server --no-client", | 53 | "start:server": "node dist/server --no-client", |
54 | "update-host": "node ./dist/scripts/update-host.js", | 54 | "update-host": "node ./dist/scripts/update-host.js", |
55 | "create-transcoding-job": "node ./dist/scripts/create-transcoding-job.js", | 55 | "create-transcoding-job": "node ./dist/scripts/create-transcoding-job.js", |
56 | "regenerate-thumbnails": "node ./dist/scripts/regenerate-thumbnails.js", | ||
56 | "create-import-video-file-job": "node ./dist/scripts/create-import-video-file-job.js", | 57 | "create-import-video-file-job": "node ./dist/scripts/create-import-video-file-job.js", |
57 | "print-transcode-command": "node ./dist/scripts/print-transcode-command.js", | 58 | "print-transcode-command": "node ./dist/scripts/print-transcode-command.js", |
58 | "test": "scripty", | 59 | "test": "scripty", |
diff --git a/scripts/regenerate-thumbnails.ts b/scripts/regenerate-thumbnails.ts new file mode 100644 index 000000000..b0071efe0 --- /dev/null +++ b/scripts/regenerate-thumbnails.ts | |||
@@ -0,0 +1,54 @@ | |||
1 | import { registerTSPaths } from '../server/helpers/register-ts-paths' | ||
2 | registerTSPaths() | ||
3 | |||
4 | import * as Bluebird from 'bluebird' | ||
5 | import * as program from 'commander' | ||
6 | import { pathExists } from 'fs-extra' | ||
7 | import { processImage } from '@server/helpers/image-utils' | ||
8 | import { THUMBNAILS_SIZE } from '@server/initializers/constants' | ||
9 | import { VideoModel } from '@server/models/video/video' | ||
10 | import { MVideo } from '@server/types/models' | ||
11 | import { initDatabaseModels } from '@server/initializers/database' | ||
12 | |||
13 | program | ||
14 | .description('Regenerate local thumbnails using preview files') | ||
15 | .parse(process.argv) | ||
16 | |||
17 | run() | ||
18 | .then(() => process.exit(0)) | ||
19 | .catch(err => console.error(err)) | ||
20 | |||
21 | async function run () { | ||
22 | await initDatabaseModels(true) | ||
23 | |||
24 | const videos = await VideoModel.listLocal() | ||
25 | |||
26 | await Bluebird.map(videos, v => { | ||
27 | return processVideo(v) | ||
28 | .catch(err => console.error('Cannot process video %s.', v.url, err)) | ||
29 | }, { concurrency: 20 }) | ||
30 | } | ||
31 | |||
32 | async function processVideo (videoArg: MVideo) { | ||
33 | const video = await VideoModel.loadWithFiles(videoArg.id) | ||
34 | |||
35 | const thumbnail = video.getMiniature() | ||
36 | const preview = video.getPreview() | ||
37 | |||
38 | const thumbnailPath = thumbnail.getPath() | ||
39 | const previewPath = preview.getPath() | ||
40 | |||
41 | if (!await pathExists(thumbnailPath)) { | ||
42 | throw new Error(`Thumbnail ${thumbnailPath} does not exist on disk`) | ||
43 | } | ||
44 | |||
45 | if (!await pathExists(previewPath)) { | ||
46 | throw new Error(`Preview ${previewPath} does not exist on disk`) | ||
47 | } | ||
48 | |||
49 | const size = { | ||
50 | width: THUMBNAILS_SIZE.width, | ||
51 | height: THUMBNAILS_SIZE.height | ||
52 | } | ||
53 | await processImage(previewPath, thumbnailPath, size, true) | ||
54 | } | ||
diff --git a/server/tests/cli/index.ts b/server/tests/cli/index.ts index 242589010..7e6eebd17 100644 --- a/server/tests/cli/index.ts +++ b/server/tests/cli/index.ts | |||
@@ -6,5 +6,6 @@ import './peertube' | |||
6 | import './plugins' | 6 | import './plugins' |
7 | import './print-transcode-command' | 7 | import './print-transcode-command' |
8 | import './prune-storage' | 8 | import './prune-storage' |
9 | import './regenerate-thumbnails' | ||
9 | import './reset-password' | 10 | import './reset-password' |
10 | import './update-host' | 11 | import './update-host' |
diff --git a/server/tests/cli/regenerate-thumbnails.ts b/server/tests/cli/regenerate-thumbnails.ts new file mode 100644 index 000000000..56005518a --- /dev/null +++ b/server/tests/cli/regenerate-thumbnails.ts | |||
@@ -0,0 +1,110 @@ | |||
1 | import 'mocha' | ||
2 | import { expect } from 'chai' | ||
3 | import { writeFile } from 'fs-extra' | ||
4 | import { basename, join } from 'path' | ||
5 | import { Video } from '@shared/models' | ||
6 | import { | ||
7 | buildServerDirectory, | ||
8 | cleanupTests, | ||
9 | doubleFollow, | ||
10 | execCLI, | ||
11 | flushAndRunMultipleServers, | ||
12 | getEnvCli, | ||
13 | getVideo, | ||
14 | makeRawRequest, | ||
15 | ServerInfo, | ||
16 | setAccessTokensToServers, | ||
17 | uploadVideoAndGetId, | ||
18 | waitJobs | ||
19 | } from '../../../shared/extra-utils' | ||
20 | import { HttpStatusCode } from '@shared/core-utils' | ||
21 | |||
22 | describe('Test regenerate thumbnails script', function () { | ||
23 | let servers: ServerInfo[] | ||
24 | |||
25 | let video1: Video | ||
26 | let video2: Video | ||
27 | let remoteVideo: Video | ||
28 | |||
29 | let thumbnail1Path: string | ||
30 | let thumbnailRemotePath: string | ||
31 | |||
32 | before(async function () { | ||
33 | this.timeout(60000) | ||
34 | |||
35 | servers = await flushAndRunMultipleServers(2) | ||
36 | await setAccessTokensToServers(servers) | ||
37 | |||
38 | await doubleFollow(servers[0], servers[1]) | ||
39 | |||
40 | { | ||
41 | const videoUUID1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 1' })).uuid | ||
42 | video1 = await (getVideo(servers[0].url, videoUUID1).then(res => res.body)) | ||
43 | |||
44 | thumbnail1Path = join(buildServerDirectory(servers[0], 'thumbnails'), basename(video1.thumbnailPath)) | ||
45 | |||
46 | const videoUUID2 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 2' })).uuid | ||
47 | video2 = await (getVideo(servers[0].url, videoUUID2).then(res => res.body)) | ||
48 | } | ||
49 | |||
50 | { | ||
51 | const videoUUID = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 3' })).uuid | ||
52 | await waitJobs(servers) | ||
53 | |||
54 | remoteVideo = await (getVideo(servers[0].url, videoUUID).then(res => res.body)) | ||
55 | |||
56 | thumbnailRemotePath = join(buildServerDirectory(servers[0], 'thumbnails'), basename(remoteVideo.thumbnailPath)) | ||
57 | } | ||
58 | |||
59 | await writeFile(thumbnail1Path, '') | ||
60 | await writeFile(thumbnailRemotePath, '') | ||
61 | }) | ||
62 | |||
63 | it('Should have empty thumbnails', async function () { | ||
64 | { | ||
65 | const res = await makeRawRequest(join(servers[0].url, video1.thumbnailPath), HttpStatusCode.OK_200) | ||
66 | expect(res.body).to.have.lengthOf(0) | ||
67 | } | ||
68 | |||
69 | { | ||
70 | const res = await makeRawRequest(join(servers[0].url, video2.thumbnailPath), HttpStatusCode.OK_200) | ||
71 | expect(res.body).to.not.have.lengthOf(0) | ||
72 | } | ||
73 | |||
74 | { | ||
75 | const res = await makeRawRequest(join(servers[0].url, remoteVideo.thumbnailPath), HttpStatusCode.OK_200) | ||
76 | expect(res.body).to.have.lengthOf(0) | ||
77 | } | ||
78 | }) | ||
79 | |||
80 | it('Should regenerate thumbnails from the CLI', async function () { | ||
81 | this.timeout(15000) | ||
82 | |||
83 | const env = getEnvCli(servers[0]) | ||
84 | await execCLI(`${env} npm run regenerate-thumbnails`) | ||
85 | }) | ||
86 | |||
87 | it('Should have regenerated thumbbnails', async function () { | ||
88 | { | ||
89 | const res1 = await makeRawRequest(join(servers[0].url, video1.thumbnailPath), HttpStatusCode.OK_200) | ||
90 | expect(res1.body).to.not.have.lengthOf(0) | ||
91 | |||
92 | const res2 = await makeRawRequest(join(servers[0].url, video1.previewPath), HttpStatusCode.OK_200) | ||
93 | expect(res2.body).to.not.have.lengthOf(0) | ||
94 | } | ||
95 | |||
96 | { | ||
97 | const res = await makeRawRequest(join(servers[0].url, video2.thumbnailPath), HttpStatusCode.OK_200) | ||
98 | expect(res.body).to.not.have.lengthOf(0) | ||
99 | } | ||
100 | |||
101 | { | ||
102 | const res = await makeRawRequest(join(servers[0].url, remoteVideo.thumbnailPath), HttpStatusCode.OK_200) | ||
103 | expect(res.body).to.have.lengthOf(0) | ||
104 | } | ||
105 | }) | ||
106 | |||
107 | after(async function () { | ||
108 | await cleanupTests(servers) | ||
109 | }) | ||
110 | }) | ||
diff --git a/support/doc/tools.md b/support/doc/tools.md index da32506bd..175c22cd8 100644 --- a/support/doc/tools.md +++ b/support/doc/tools.md | |||
@@ -15,6 +15,7 @@ | |||
15 | - [peertube-redundancy.js](#peertube-redundancyjs) | 15 | - [peertube-redundancy.js](#peertube-redundancyjs) |
16 | - [Server tools](#server-tools) | 16 | - [Server tools](#server-tools) |
17 | - [parse-log](#parse-log) | 17 | - [parse-log](#parse-log) |
18 | - [regenerate-thumbnails.js](#regenerate-thumbnailsjs) | ||
18 | - [create-transcoding-job.js](#create-transcoding-jobjs) | 19 | - [create-transcoding-job.js](#create-transcoding-jobjs) |
19 | - [create-import-video-file-job.js](#create-import-video-file-jobjs) | 20 | - [create-import-video-file-job.js](#create-import-video-file-jobjs) |
20 | - [prune-storage.js](#prune-storagejs) | 21 | - [prune-storage.js](#prune-storagejs) |
@@ -251,6 +252,15 @@ $ cd /var/www/peertube/peertube-latest | |||
251 | $ sudo -u peertube NODE_CONFIG_DIR=/var/www/peertube/config NODE_ENV=production npm run parse-log -- --level debug --not-tags http sql | 252 | $ sudo -u peertube NODE_CONFIG_DIR=/var/www/peertube/config NODE_ENV=production npm run parse-log -- --level debug --not-tags http sql |
252 | ``` | 253 | ``` |
253 | 254 | ||
255 | ### regenerate-thumbnails.js | ||
256 | |||
257 | Regenerating local video thumbnails could be useful because new PeerTube releases may increase thumbnail sizes: | ||
258 | |||
259 | ``` | ||
260 | $ cd /var/www/peertube/peertube-latest | ||
261 | $ sudo -u peertube NODE_CONFIG_DIR=/var/www/peertube/config NODE_ENV=production npm run regenerate-thumbnails | ||
262 | ``` | ||
263 | |||
254 | ### create-transcoding-job.js | 264 | ### create-transcoding-job.js |
255 | 265 | ||
256 | You can use this script to force transcoding of an existing video. PeerTube needs to be running. | 266 | You can use this script to force transcoding of an existing video. PeerTube needs to be running. |