diff options
author | Chocobozzz <me@florianbigard.com> | 2018-05-29 11:11:52 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-05-29 11:11:52 +0200 |
commit | a9729e21d1cc5046c1a788448e347b62e0c45c53 (patch) | |
tree | c9efbd64153d9778a7b720b55e79d8cac3000aff /scripts | |
parent | 8c2c915e0ffce745cda5746ecc0e7f7040b3f893 (diff) | |
download | PeerTube-a9729e21d1cc5046c1a788448e347b62e0c45c53.tar.gz PeerTube-a9729e21d1cc5046c1a788448e347b62e0c45c53.tar.zst PeerTube-a9729e21d1cc5046c1a788448e347b62e0c45c53.zip |
Add script that prunes storage files
Diffstat (limited to 'scripts')
-rwxr-xr-x | scripts/prune-storage.ts | 96 |
1 files changed, 96 insertions, 0 deletions
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 | } | ||