diff options
Diffstat (limited to 'server/tools/peertube-redundancy.ts')
-rw-r--r-- | server/tools/peertube-redundancy.ts | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/server/tools/peertube-redundancy.ts b/server/tools/peertube-redundancy.ts new file mode 100644 index 000000000..a71f48104 --- /dev/null +++ b/server/tools/peertube-redundancy.ts | |||
@@ -0,0 +1,194 @@ | |||
1 | import { registerTSPaths } from '../helpers/register-ts-paths' | ||
2 | registerTSPaths() | ||
3 | |||
4 | import * as program from 'commander' | ||
5 | import { getAdminTokenOrDie, getServerCredentials } from './cli' | ||
6 | import { VideoRedundanciesTarget, VideoRedundancy } from '@shared/models' | ||
7 | import { addVideoRedundancy, listVideoRedundancies, removeVideoRedundancy } from '@shared/extra-utils/server/redundancy' | ||
8 | import validator from 'validator' | ||
9 | import bytes = require('bytes') | ||
10 | import * as CliTable3 from 'cli-table3' | ||
11 | import { parse } from 'url' | ||
12 | import { uniq } from 'lodash' | ||
13 | |||
14 | program | ||
15 | .name('plugins') | ||
16 | .usage('[command] [options]') | ||
17 | |||
18 | program | ||
19 | .command('list-remote-redundancies') | ||
20 | .description('List remote redundancies on your videos') | ||
21 | .option('-u, --url <url>', 'Server url') | ||
22 | .option('-U, --username <username>', 'Username') | ||
23 | .option('-p, --password <token>', 'Password') | ||
24 | .action(() => listRedundanciesCLI('my-videos')) | ||
25 | |||
26 | program | ||
27 | .command('list-my-redundancies') | ||
28 | .description('List your redundancies of remote videos') | ||
29 | .option('-u, --url <url>', 'Server url') | ||
30 | .option('-U, --username <username>', 'Username') | ||
31 | .option('-p, --password <token>', 'Password') | ||
32 | .action(() => listRedundanciesCLI('remote-videos')) | ||
33 | |||
34 | program | ||
35 | .command('add') | ||
36 | .description('Duplicate a video in your redundancy system') | ||
37 | .option('-u, --url <url>', 'Server url') | ||
38 | .option('-U, --username <username>', 'Username') | ||
39 | .option('-p, --password <token>', 'Password') | ||
40 | .option('-v, --video <videoId>', 'Video id to duplicate') | ||
41 | .action((options) => addRedundancyCLI(options)) | ||
42 | |||
43 | program | ||
44 | .command('remove') | ||
45 | .description('Remove a video from your redundancies') | ||
46 | .option('-u, --url <url>', 'Server url') | ||
47 | .option('-U, --username <username>', 'Username') | ||
48 | .option('-p, --password <token>', 'Password') | ||
49 | .option('-v, --video <videoId>', 'Video id to remove from redundancies') | ||
50 | .action((options) => removeRedundancyCLI(options)) | ||
51 | |||
52 | if (!process.argv.slice(2).length) { | ||
53 | program.outputHelp() | ||
54 | } | ||
55 | |||
56 | program.parse(process.argv) | ||
57 | |||
58 | // ---------------------------------------------------------------------------- | ||
59 | |||
60 | async function listRedundanciesCLI (target: VideoRedundanciesTarget) { | ||
61 | const { url, username, password } = await getServerCredentials(program) | ||
62 | const accessToken = await getAdminTokenOrDie(url, username, password) | ||
63 | |||
64 | const redundancies = await listVideoRedundanciesData(url, accessToken, target) | ||
65 | |||
66 | const table = new CliTable3({ | ||
67 | head: [ 'video id', 'video name', 'video url', 'files', 'playlists', 'by instances', 'total size' ] | ||
68 | }) as CliTable3.HorizontalTable | ||
69 | |||
70 | for (const redundancy of redundancies) { | ||
71 | const webtorrentFiles = redundancy.redundancies.files | ||
72 | const streamingPlaylists = redundancy.redundancies.streamingPlaylists | ||
73 | |||
74 | let totalSize = '' | ||
75 | if (target === 'remote-videos') { | ||
76 | const tmp = webtorrentFiles.concat(streamingPlaylists) | ||
77 | .reduce((a, b) => a + b.size, 0) | ||
78 | |||
79 | totalSize = bytes(tmp) | ||
80 | } | ||
81 | |||
82 | const instances = uniq( | ||
83 | webtorrentFiles.concat(streamingPlaylists) | ||
84 | .map(r => r.fileUrl) | ||
85 | .map(u => parse(u).host) | ||
86 | ) | ||
87 | |||
88 | table.push([ | ||
89 | redundancy.id.toString(), | ||
90 | redundancy.name, | ||
91 | redundancy.url, | ||
92 | webtorrentFiles.length, | ||
93 | streamingPlaylists.length, | ||
94 | instances.join('\n'), | ||
95 | totalSize | ||
96 | ]) | ||
97 | } | ||
98 | |||
99 | console.log(table.toString()) | ||
100 | process.exit(0) | ||
101 | } | ||
102 | |||
103 | async function addRedundancyCLI (options: { videoId: number }) { | ||
104 | const { url, username, password } = await getServerCredentials(program) | ||
105 | const accessToken = await getAdminTokenOrDie(url, username, password) | ||
106 | |||
107 | if (!options[ 'video' ] || validator.isInt('' + options[ 'video' ]) === false) { | ||
108 | console.error('You need to specify the video id to duplicate and it should be a number.\n') | ||
109 | program.outputHelp() | ||
110 | process.exit(-1) | ||
111 | } | ||
112 | |||
113 | try { | ||
114 | await addVideoRedundancy({ | ||
115 | url, | ||
116 | accessToken, | ||
117 | videoId: options[ 'video' ] | ||
118 | }) | ||
119 | |||
120 | console.log('Video will be duplicated by your instance!') | ||
121 | |||
122 | process.exit(0) | ||
123 | } catch (err) { | ||
124 | if (err.message.includes(409)) { | ||
125 | console.error('This video is already duplicated by your instance.') | ||
126 | } else if (err.message.includes(404)) { | ||
127 | console.error('This video id does not exist.') | ||
128 | } else { | ||
129 | console.error(err) | ||
130 | } | ||
131 | |||
132 | process.exit(-1) | ||
133 | } | ||
134 | } | ||
135 | |||
136 | async function removeRedundancyCLI (options: { videoId: number }) { | ||
137 | const { url, username, password } = await getServerCredentials(program) | ||
138 | const accessToken = await getAdminTokenOrDie(url, username, password) | ||
139 | |||
140 | if (!options[ 'video' ] || validator.isInt('' + options[ 'video' ]) === false) { | ||
141 | console.error('You need to specify the video id to remove from your redundancies.\n') | ||
142 | program.outputHelp() | ||
143 | process.exit(-1) | ||
144 | } | ||
145 | |||
146 | const videoId = parseInt(options[ 'video' ] + '', 10) | ||
147 | |||
148 | let redundancies = await listVideoRedundanciesData(url, accessToken, 'my-videos') | ||
149 | let videoRedundancy = redundancies.find(r => videoId === r.id) | ||
150 | |||
151 | if (!videoRedundancy) { | ||
152 | redundancies = await listVideoRedundanciesData(url, accessToken, 'remote-videos') | ||
153 | videoRedundancy = redundancies.find(r => videoId === r.id) | ||
154 | } | ||
155 | |||
156 | if (!videoRedundancy) { | ||
157 | console.error('Video redundancy not found.') | ||
158 | process.exit(-1) | ||
159 | } | ||
160 | |||
161 | try { | ||
162 | const ids = videoRedundancy.redundancies.files | ||
163 | .concat(videoRedundancy.redundancies.streamingPlaylists) | ||
164 | .map(r => r.id) | ||
165 | |||
166 | for (const id of ids) { | ||
167 | await removeVideoRedundancy({ | ||
168 | url, | ||
169 | accessToken, | ||
170 | redundancyId: id | ||
171 | }) | ||
172 | } | ||
173 | |||
174 | console.log('Video redundancy removed!') | ||
175 | |||
176 | process.exit(0) | ||
177 | } catch (err) { | ||
178 | console.error(err) | ||
179 | process.exit(-1) | ||
180 | } | ||
181 | } | ||
182 | |||
183 | async function listVideoRedundanciesData (url: string, accessToken: string, target: VideoRedundanciesTarget) { | ||
184 | const res = await listVideoRedundancies({ | ||
185 | url, | ||
186 | accessToken, | ||
187 | start: 0, | ||
188 | count: 100, | ||
189 | sort: 'name', | ||
190 | target | ||
191 | }) | ||
192 | |||
193 | return res.body.data as VideoRedundancy[] | ||
194 | } | ||