]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tools/peertube-redundancy.ts
Introduce blocklist command
[github/Chocobozzz/PeerTube.git] / server / tools / peertube-redundancy.ts
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 }