diff options
-rw-r--r-- | package.json | 1 | ||||
-rwxr-xr-x | scripts/prune-storage.ts | 96 |
2 files changed, 97 insertions, 0 deletions
diff --git a/package.json b/package.json index 5d8cbd23d..c4ee202e1 100644 --- a/package.json +++ b/package.json | |||
@@ -40,6 +40,7 @@ | |||
40 | "help": "scripty", | 40 | "help": "scripty", |
41 | "generate-api-doc": "scripty", | 41 | "generate-api-doc": "scripty", |
42 | "parse-log": "node ./dist/scripts/parse-log.js", | 42 | "parse-log": "node ./dist/scripts/parse-log.js", |
43 | "prune-storage": "node ./dist/scripts/prune-storage.js", | ||
43 | "postinstall": "cd client && yarn install --pure-lockfile", | 44 | "postinstall": "cd client && yarn install --pure-lockfile", |
44 | "tsc": "tsc", | 45 | "tsc": "tsc", |
45 | "spectacle-docs": "node_modules/spectacle-docs/bin/spectacle.js", | 46 | "spectacle-docs": "node_modules/spectacle-docs/bin/spectacle.js", |
diff --git a/scripts/prune-storage.ts b/scripts/prune-storage.ts new file mode 100755 index 000000000..1973725c8 --- /dev/null +++ b/scripts/prune-storage.ts | |||
@@ -0,0 +1,96 @@ | |||
1 | import * as prompt from 'prompt' | ||
2 | import { createReadStream } from 'fs' | ||
3 | import { join } from 'path' | ||
4 | import { createInterface } from 'readline' | ||
5 | import { readdirPromise, unlinkPromise } from '../server/helpers/core-utils' | ||
6 | import { CONFIG } from '../server/initializers/constants' | ||
7 | import { VideoModel } from '../server/models/video/video' | ||
8 | import { initDatabaseModels } from '../server/initializers' | ||
9 | |||
10 | run() | ||
11 | .then(() => process.exit(0)) | ||
12 | .catch(err => { | ||
13 | console.error(err) | ||
14 | process.exit(-1) | ||
15 | }) | ||
16 | |||
17 | async function run () { | ||
18 | await initDatabaseModels(true) | ||
19 | |||
20 | const storageToPrune = [ | ||
21 | CONFIG.STORAGE.VIDEOS_DIR, | ||
22 | CONFIG.STORAGE.PREVIEWS_DIR, | ||
23 | CONFIG.STORAGE.THUMBNAILS_DIR, | ||
24 | CONFIG.STORAGE.TORRENTS_DIR | ||
25 | ] | ||
26 | |||
27 | let toDelete: string[] = [] | ||
28 | for (const directory of storageToPrune) { | ||
29 | toDelete = toDelete.concat(await pruneDirectory(directory)) | ||
30 | } | ||
31 | |||
32 | if (toDelete.length === 0) { | ||
33 | console.log('No files to delete.') | ||
34 | return | ||
35 | } | ||
36 | |||
37 | console.log('Will delete %d files:\n\n%s\n\n', toDelete.length, toDelete.join('\n')) | ||
38 | |||
39 | const res = await askConfirmation() | ||
40 | if (res === true) { | ||
41 | console.log('Processing delete...\n') | ||
42 | |||
43 | for (const path of toDelete) { | ||
44 | await unlinkPromise(path) | ||
45 | } | ||
46 | |||
47 | console.log('Done!') | ||
48 | } else { | ||
49 | console.log('Exiting without deleting files.') | ||
50 | } | ||
51 | } | ||
52 | |||
53 | async function pruneDirectory (directory: string) { | ||
54 | const files = await readdirPromise(directory) | ||
55 | |||
56 | const toDelete: string[] = [] | ||
57 | for (const file of files) { | ||
58 | const uuid = getUUIDFromFilename(file) | ||
59 | let video: VideoModel | ||
60 | |||
61 | if (uuid) video = await VideoModel.loadByUUID(uuid) | ||
62 | |||
63 | if (!uuid || !video) toDelete.push(join(directory, file)) | ||
64 | } | ||
65 | |||
66 | return toDelete | ||
67 | } | ||
68 | |||
69 | function getUUIDFromFilename (filename: string) { | ||
70 | const regex = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/ | ||
71 | const result = filename.match(regex) | ||
72 | |||
73 | if (!result || Array.isArray(result) === false) return null | ||
74 | |||
75 | return result[0] | ||
76 | } | ||
77 | |||
78 | async function askConfirmation () { | ||
79 | return new Promise((res, rej) => { | ||
80 | prompt.start() | ||
81 | const schema = { | ||
82 | properties: { | ||
83 | confirm: { | ||
84 | type: 'string', | ||
85 | description: 'Are you sure you want to delete these files? Please check carefully', | ||
86 | default: 'n', | ||
87 | required: true | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | prompt.get(schema, function (err, result) { | ||
92 | if (err) return rej(err) | ||
93 | return res(result.confirm && result.confirm.match(/y/) !== null) | ||
94 | }) | ||
95 | }) | ||
96 | } | ||