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