aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--package.json1
-rwxr-xr-xscripts/prune-storage.ts96
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 @@
1import * as prompt from 'prompt'
2import { createReadStream } from 'fs'
3import { join } from 'path'
4import { createInterface } from 'readline'
5import { readdirPromise, unlinkPromise } from '../server/helpers/core-utils'
6import { CONFIG } from '../server/initializers/constants'
7import { VideoModel } from '../server/models/video/video'
8import { initDatabaseModels } from '../server/initializers'
9
10run()
11 .then(() => process.exit(0))
12 .catch(err => {
13 console.error(err)
14 process.exit(-1)
15 })
16
17async 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
53async 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
69function 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
78async 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}