1 // eslint-disable @typescript-eslint/no-unnecessary-type-assertion
3 import { registerTSPaths } from '../helpers/register-ts-paths'
6 import * as program 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'
16 import bytes = require('bytes')
20 .usage('[command] [options]')
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'))
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'))
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) => addRedundancyCLI(options))
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) => removeRedundancyCLI(options))
56 if (!process.argv.slice(2).length) {
60 program.parse(process.argv)
62 // ----------------------------------------------------------------------------
64 async function listRedundanciesCLI (target: VideoRedundanciesTarget) {
65 const { url, username, password } = await getServerCredentials(program)
66 const accessToken = await getAdminTokenOrDie(url, username, password)
68 const redundancies = await listVideoRedundanciesData(url, accessToken, target)
70 const table = new CliTable3({
71 head: [ 'video id', 'video name', 'video url', 'files', 'playlists', 'by instances', 'total size' ]
74 for (const redundancy of redundancies) {
75 const webtorrentFiles = redundancy.redundancies.files
76 const streamingPlaylists = redundancy.redundancies.streamingPlaylists
79 if (target === 'remote-videos') {
80 const tmp = webtorrentFiles.concat(streamingPlaylists)
81 .reduce((a, b) => a + b.size, 0)
83 totalSize = bytes(tmp)
86 const instances = uniq(
87 webtorrentFiles.concat(streamingPlaylists)
89 .map(u => new URL(u).host)
93 redundancy.id.toString(),
96 webtorrentFiles.length,
97 streamingPlaylists.length,
103 console.log(table.toString())
107 async function addRedundancyCLI (options: { videoId: number }) {
108 const { url, username, password } = await getServerCredentials(program)
109 const accessToken = await getAdminTokenOrDie(url, username, password)
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')
118 await addVideoRedundancy({
121 videoId: options['video']
124 console.log('Video will be duplicated by your instance!')
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.')
140 async function removeRedundancyCLI (options: { videoId: number }) {
141 const { url, username, password } = await getServerCredentials(program)
142 const accessToken = await getAdminTokenOrDie(url, username, password)
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')
150 const videoId = parseInt(options['video'] + '', 10)
152 let redundancies = await listVideoRedundanciesData(url, accessToken, 'my-videos')
153 let videoRedundancy = redundancies.find(r => videoId === r.id)
155 if (!videoRedundancy) {
156 redundancies = await listVideoRedundanciesData(url, accessToken, 'remote-videos')
157 videoRedundancy = redundancies.find(r => videoId === r.id)
160 if (!videoRedundancy) {
161 console.error('Video redundancy not found.')
166 const ids = videoRedundancy.redundancies.files
167 .concat(videoRedundancy.redundancies.streamingPlaylists)
170 for (const id of ids) {
171 await removeVideoRedundancy({
178 console.log('Video redundancy removed!')
187 async function listVideoRedundanciesData (url: string, accessToken: string, target: VideoRedundanciesTarget) {
188 const res = await listVideoRedundancies({
197 return res.body.data as VideoRedundancy[]