aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-07-09 15:03:44 +0200
committerChocobozzz <me@florianbigard.com>2021-07-20 15:27:18 +0200
commit078f17e6d90376050f43ce639e88e11869b49ee7 (patch)
tree1aeef2c43591de2f72b84b85672de8e83815e8fa
parent12edc1495a36b2199f1bf1ba37f50c7b694be382 (diff)
downloadPeerTube-078f17e6d90376050f43ce639e88e11869b49ee7.tar.gz
PeerTube-078f17e6d90376050f43ce639e88e11869b49ee7.tar.zst
PeerTube-078f17e6d90376050f43ce639e88e11869b49ee7.zip
Fix CLI tools
-rw-r--r--server/tests/cli/peertube.ts9
-rw-r--r--server/tests/cli/update-host.ts3
-rw-r--r--server/tools/cli.ts32
-rw-r--r--server/tools/peertube-get-access-token.ts19
-rw-r--r--server/tools/peertube-import-videos.ts89
-rw-r--r--server/tools/peertube-plugins.ts49
-rw-r--r--server/tools/peertube-redundancy.ts61
-rw-r--r--server/tools/peertube-upload.ts9
-rw-r--r--server/tools/test-live.ts (renamed from server/tools/test.ts)47
-rw-r--r--shared/extra-utils/server/servers.ts89
-rw-r--r--shared/extra-utils/users/login.ts4
11 files changed, 187 insertions, 224 deletions
diff --git a/server/tests/cli/peertube.ts b/server/tests/cli/peertube.ts
index ef7ab5bd5..5ab07edb0 100644
--- a/server/tests/cli/peertube.ts
+++ b/server/tests/cli/peertube.ts
@@ -12,6 +12,7 @@ import {
12 doubleFollow, 12 doubleFollow,
13 flushAndRunServer, 13 flushAndRunServer,
14 getLocalIdByUUID, 14 getLocalIdByUUID,
15 getMyUserInformation,
15 getVideo, 16 getVideo,
16 getVideosList, 17 getVideosList,
17 ImportsCommand, 18 ImportsCommand,
@@ -52,6 +53,14 @@ describe('Test CLI wrapper', function () {
52 53
53 describe('Authentication and instance selection', function () { 54 describe('Authentication and instance selection', function () {
54 55
56 it('Should get an access token', async function () {
57 const stdout = await cliCommand.execWithEnv(`${cmd} token --url ${server.url} --username user_1 --password super_password`)
58 const token = stdout.trim()
59
60 const res = await getMyUserInformation(server.url, token)
61 expect(res.body.username).to.equal('user_1')
62 })
63
55 it('Should display no selected instance', async function () { 64 it('Should display no selected instance', async function () {
56 this.timeout(60000) 65 this.timeout(60000)
57 66
diff --git a/server/tests/cli/update-host.ts b/server/tests/cli/update-host.ts
index 61a6c403a..d3a1520cf 100644
--- a/server/tests/cli/update-host.ts
+++ b/server/tests/cli/update-host.ts
@@ -1,6 +1,7 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import 'mocha' 3import 'mocha'
4import { expect } from 'chai'
4import { 5import {
5 cleanupTests, 6 cleanupTests,
6 createUser, 7 createUser,
@@ -18,8 +19,6 @@ import {
18} from '@shared/extra-utils' 19} from '@shared/extra-utils'
19import { VideoDetails } from '@shared/models' 20import { VideoDetails } from '@shared/models'
20 21
21const expect = chai.expect
22
23describe('Test update host scripts', function () { 22describe('Test update host scripts', function () {
24 let server: ServerInfo 23 let server: ServerInfo
25 24
diff --git a/server/tools/cli.ts b/server/tools/cli.ts
index 7b94306cd..0528859a4 100644
--- a/server/tools/cli.ts
+++ b/server/tools/cli.ts
@@ -1,14 +1,14 @@
1import { Command } from 'commander'
1import { Netrc } from 'netrc-parser' 2import { Netrc } from 'netrc-parser'
2import { getAppNumber, isTestInstance } from '../helpers/core-utils'
3import { join } from 'path' 3import { join } from 'path'
4import { root } from '../../shared/extra-utils/miscs/miscs'
5import { getVideoChannel } from '../../shared/extra-utils/videos/video-channels'
6import { VideoChannel, VideoPrivacy } from '../../shared/models/videos'
7import { createLogger, format, transports } from 'winston' 4import { createLogger, format, transports } from 'winston'
5import { assignCommands, ServerInfo } from '@shared/extra-utils'
6import { getAccessToken } from '@shared/extra-utils/users/login'
8import { getMyUserInformation } from '@shared/extra-utils/users/users' 7import { getMyUserInformation } from '@shared/extra-utils/users/users'
9import { User, UserRole } from '@shared/models' 8import { User, UserRole } from '@shared/models'
10import { getAccessToken } from '@shared/extra-utils/users/login' 9import { root } from '../../shared/extra-utils/miscs/miscs'
11import { Command } from 'commander' 10import { VideoPrivacy } from '../../shared/models/videos'
11import { getAppNumber, isTestInstance } from '../helpers/core-utils'
12 12
13let configName = 'PeerTube/CLI' 13let configName = 'PeerTube/CLI'
14if (isTestInstance()) configName += `-${getAppNumber()}` 14if (isTestInstance()) configName += `-${getAppNumber()}`
@@ -30,6 +30,10 @@ async function getAdminTokenOrDie (url: string, username: string, password: stri
30 return accessToken 30 return accessToken
31} 31}
32 32
33async function getAccessTokenOrDie (url: string, username: string, password: string) {
34 return getAccessToken(url, username, password)
35}
36
33interface Settings { 37interface Settings {
34 remotes: any[] 38 remotes: any[]
35 default: number 39 default: number
@@ -128,7 +132,7 @@ function buildCommonVideoOptions (command: Command) {
128 .option('-v, --verbose <verbose>', 'Verbosity, from 0/\'error\' to 4/\'debug\'', 'info') 132 .option('-v, --verbose <verbose>', 'Verbosity, from 0/\'error\' to 4/\'debug\'', 'info')
129} 133}
130 134
131async function buildVideoAttributesFromCommander (url: string, command: Command, defaultAttributes: any = {}) { 135async function buildVideoAttributesFromCommander (server: ServerInfo, command: Command, defaultAttributes: any = {}) {
132 const options = command.opts() 136 const options = command.opts()
133 137
134 const defaultBooleanAttributes = { 138 const defaultBooleanAttributes = {
@@ -164,8 +168,7 @@ async function buildVideoAttributesFromCommander (url: string, command: Command,
164 Object.assign(videoAttributes, booleanAttributes) 168 Object.assign(videoAttributes, booleanAttributes)
165 169
166 if (options.channelName) { 170 if (options.channelName) {
167 const res = await getVideoChannel(url, options.channelName) 171 const videoChannel = await server.channelsCommand.get({ channelName: options.channelName })
168 const videoChannel: VideoChannel = res.body
169 172
170 Object.assign(videoAttributes, { channelId: videoChannel.id }) 173 Object.assign(videoAttributes, { channelId: videoChannel.id })
171 174
@@ -184,6 +187,13 @@ function getServerCredentials (program: Command) {
184 }) 187 })
185} 188}
186 189
190function buildServer (url: string, accessToken?: string): ServerInfo {
191 const server = { url, accessToken, internalServerNumber: undefined }
192 assignCommands(server)
193
194 return server
195}
196
187function getLogger (logLevel = 'info') { 197function getLogger (logLevel = 'info') {
188 const logLevels = { 198 const logLevels = {
189 0: 0, 199 0: 0,
@@ -230,5 +240,7 @@ export {
230 buildCommonVideoOptions, 240 buildCommonVideoOptions,
231 buildVideoAttributesFromCommander, 241 buildVideoAttributesFromCommander,
232 242
233 getAdminTokenOrDie 243 getAdminTokenOrDie,
244 getAccessTokenOrDie,
245 buildServer
234} 246}
diff --git a/server/tools/peertube-get-access-token.ts b/server/tools/peertube-get-access-token.ts
index 9488eba0e..5868d0548 100644
--- a/server/tools/peertube-get-access-token.ts
+++ b/server/tools/peertube-get-access-token.ts
@@ -2,7 +2,7 @@ import { registerTSPaths } from '../helpers/register-ts-paths'
2registerTSPaths() 2registerTSPaths()
3 3
4import { program } from 'commander' 4import { program } from 'commander'
5import { getClient, Server, serverLogin } from '../../shared/extra-utils' 5import { getAccessToken } from '../../shared/extra-utils'
6 6
7program 7program
8 .option('-u, --url <url>', 'Server url') 8 .option('-u, --url <url>', 'Server url')
@@ -24,22 +24,7 @@ if (
24 process.exit(-1) 24 process.exit(-1)
25} 25}
26 26
27getClient(options.url) 27getAccessToken(options.url, options.username, options.password)
28 .then(res => {
29 const server = {
30 url: options.url,
31 user: {
32 username: options.username,
33 password: options.password
34 },
35 client: {
36 id: res.body.client_id,
37 secret: res.body.client_secret
38 }
39 } as Server
40
41 return serverLogin(server)
42 })
43 .then(accessToken => { 28 .then(accessToken => {
44 console.log(accessToken) 29 console.log(accessToken)
45 process.exit(0) 30 process.exit(0)
diff --git a/server/tools/peertube-import-videos.ts b/server/tools/peertube-import-videos.ts
index 101a95b2a..a546a8dbe 100644
--- a/server/tools/peertube-import-videos.ts
+++ b/server/tools/peertube-import-videos.ts
@@ -8,17 +8,19 @@ import { truncate } from 'lodash'
8import { join } from 'path' 8import { join } from 'path'
9import * as prompt from 'prompt' 9import * as prompt from 'prompt'
10import { promisify } from 'util' 10import { promisify } from 'util'
11import { advancedVideosSearch, getClient, getVideoCategories, login, uploadVideo } from '../../shared/extra-utils/index' 11import { YoutubeDL } from '@server/helpers/youtube-dl'
12import { getVideoCategories, uploadVideo } from '../../shared/extra-utils/index'
12import { sha256 } from '../helpers/core-utils' 13import { sha256 } from '../helpers/core-utils'
13import { doRequestAndSaveToFile } from '../helpers/requests' 14import { doRequestAndSaveToFile } from '../helpers/requests'
14import { CONSTRAINTS_FIELDS } from '../initializers/constants' 15import { CONSTRAINTS_FIELDS } from '../initializers/constants'
15import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getLogger, getServerCredentials } from './cli' 16import {
16import { YoutubeDL } from '@server/helpers/youtube-dl' 17 buildCommonVideoOptions,
17 18 buildServer,
18type UserInfo = { 19 buildVideoAttributesFromCommander,
19 username: string 20 getAccessTokenOrDie,
20 password: string 21 getLogger,
21} 22 getServerCredentials
23} from './cli'
22 24
23const processOptions = { 25const processOptions = {
24 maxBuffer: Infinity 26 maxBuffer: Infinity
@@ -62,17 +64,13 @@ getServerCredentials(command)
62 url = normalizeTargetUrl(url) 64 url = normalizeTargetUrl(url)
63 options.targetUrl = normalizeTargetUrl(options.targetUrl) 65 options.targetUrl = normalizeTargetUrl(options.targetUrl)
64 66
65 const user = { username, password } 67 run(url, username, password)
66
67 run(url, user)
68 .catch(err => exitError(err)) 68 .catch(err => exitError(err))
69 }) 69 })
70 .catch(err => console.error(err)) 70 .catch(err => console.error(err))
71 71
72async function run (url: string, user: UserInfo) { 72async function run (url: string, username: string, password: string) {
73 if (!user.password) { 73 if (!password) password = await promptPassword()
74 user.password = await promptPassword()
75 }
76 74
77 const youtubeDLBinary = await YoutubeDL.safeGetYoutubeDL() 75 const youtubeDLBinary = await YoutubeDL.safeGetYoutubeDL()
78 76
@@ -111,7 +109,8 @@ async function run (url: string, user: UserInfo) {
111 await processVideo({ 109 await processVideo({
112 cwd: options.tmpdir, 110 cwd: options.tmpdir,
113 url, 111 url,
114 user, 112 username,
113 password,
115 youtubeInfo: info 114 youtubeInfo: info
116 }) 115 })
117 } catch (err) { 116 } catch (err) {
@@ -119,17 +118,18 @@ async function run (url: string, user: UserInfo) {
119 } 118 }
120 } 119 }
121 120
122 log.info('Video/s for user %s imported: %s', user.username, options.targetUrl) 121 log.info('Video/s for user %s imported: %s', username, options.targetUrl)
123 process.exit(0) 122 process.exit(0)
124} 123}
125 124
126async function processVideo (parameters: { 125async function processVideo (parameters: {
127 cwd: string 126 cwd: string
128 url: string 127 url: string
129 user: { username: string, password: string } 128 username: string
129 password: string
130 youtubeInfo: any 130 youtubeInfo: any
131}) { 131}) {
132 const { youtubeInfo, cwd, url, user } = parameters 132 const { youtubeInfo, cwd, url, username, password } = parameters
133 const youtubeDL = new YoutubeDL('', []) 133 const youtubeDL = new YoutubeDL('', [])
134 134
135 log.debug('Fetching object.', youtubeInfo) 135 log.debug('Fetching object.', youtubeInfo)
@@ -138,22 +138,29 @@ async function processVideo (parameters: {
138 log.debug('Fetched object.', videoInfo) 138 log.debug('Fetched object.', videoInfo)
139 139
140 const originallyPublishedAt = youtubeDL.buildOriginallyPublishedAt(videoInfo) 140 const originallyPublishedAt = youtubeDL.buildOriginallyPublishedAt(videoInfo)
141
141 if (options.since && originallyPublishedAt && originallyPublishedAt.getTime() < options.since.getTime()) { 142 if (options.since && originallyPublishedAt && originallyPublishedAt.getTime() < options.since.getTime()) {
142 log.info('Video "%s" has been published before "%s", don\'t upload it.\n', 143 log.info('Video "%s" has been published before "%s", don\'t upload it.\n', videoInfo.title, formatDate(options.since))
143 videoInfo.title, formatDate(options.since))
144 return 144 return
145 } 145 }
146
146 if (options.until && originallyPublishedAt && originallyPublishedAt.getTime() > options.until.getTime()) { 147 if (options.until && originallyPublishedAt && originallyPublishedAt.getTime() > options.until.getTime()) {
147 log.info('Video "%s" has been published after "%s", don\'t upload it.\n', 148 log.info('Video "%s" has been published after "%s", don\'t upload it.\n', videoInfo.title, formatDate(options.until))
148 videoInfo.title, formatDate(options.until))
149 return 149 return
150 } 150 }
151 151
152 const result = await advancedVideosSearch(url, { search: videoInfo.title, sort: '-match', searchTarget: 'local' }) 152 const server = buildServer(url)
153 const { data } = await server.searchCommand.advancedVideoSearch({
154 search: {
155 search: videoInfo.title,
156 sort: '-match',
157 searchTarget: 'local'
158 }
159 })
153 160
154 log.info('############################################################\n') 161 log.info('############################################################\n')
155 162
156 if (result.body.data.find(v => v.name === videoInfo.title)) { 163 if (data.find(v => v.name === videoInfo.title)) {
157 log.info('Video "%s" already exists, don\'t reupload it.\n', videoInfo.title) 164 log.info('Video "%s" already exists, don\'t reupload it.\n', videoInfo.title)
158 return 165 return
159 } 166 }
@@ -172,7 +179,8 @@ async function processVideo (parameters: {
172 youtubeDL, 179 youtubeDL,
173 cwd, 180 cwd,
174 url, 181 url,
175 user, 182 username,
183 password,
176 videoInfo: normalizeObject(videoInfo), 184 videoInfo: normalizeObject(videoInfo),
177 videoPath: path 185 videoPath: path
178 }) 186 })
@@ -187,9 +195,10 @@ async function uploadVideoOnPeerTube (parameters: {
187 videoPath: string 195 videoPath: string
188 cwd: string 196 cwd: string
189 url: string 197 url: string
190 user: { username: string, password: string } 198 username: string
199 password: string
191}) { 200}) {
192 const { youtubeDL, videoInfo, videoPath, cwd, url, user } = parameters 201 const { youtubeDL, videoInfo, videoPath, cwd, url, username, password } = parameters
193 202
194 const category = await getCategory(videoInfo.categories, url) 203 const category = await getCategory(videoInfo.categories, url)
195 const licence = getLicence(videoInfo.license) 204 const licence = getLicence(videoInfo.license)
@@ -223,7 +232,10 @@ async function uploadVideoOnPeerTube (parameters: {
223 tags 232 tags
224 } 233 }
225 234
226 const videoAttributes = await buildVideoAttributesFromCommander(url, program, defaultAttributes) 235 let accessToken = await getAccessTokenOrDie(url, username, password)
236 const server = buildServer(url, accessToken)
237
238 const videoAttributes = await buildVideoAttributesFromCommander(server, program, defaultAttributes)
227 239
228 Object.assign(videoAttributes, { 240 Object.assign(videoAttributes, {
229 originallyPublishedAt: originallyPublishedAt ? originallyPublishedAt.toISOString() : null, 241 originallyPublishedAt: originallyPublishedAt ? originallyPublishedAt.toISOString() : null,
@@ -234,15 +246,13 @@ async function uploadVideoOnPeerTube (parameters: {
234 246
235 log.info('\nUploading on PeerTube video "%s".', videoAttributes.name) 247 log.info('\nUploading on PeerTube video "%s".', videoAttributes.name)
236 248
237 let accessToken = await getAccessTokenOrDie(url, user)
238
239 try { 249 try {
240 await uploadVideo(url, accessToken, videoAttributes) 250 await uploadVideo(url, accessToken, videoAttributes)
241 } catch (err) { 251 } catch (err) {
242 if (err.message.indexOf('401') !== -1) { 252 if (err.message.indexOf('401') !== -1) {
243 log.info('Got 401 Unauthorized, token may have expired, renewing token and retry.') 253 log.info('Got 401 Unauthorized, token may have expired, renewing token and retry.')
244 254
245 accessToken = await getAccessTokenOrDie(url, user) 255 accessToken = await getAccessTokenOrDie(url, username, password)
246 256
247 await uploadVideo(url, accessToken, videoAttributes) 257 await uploadVideo(url, accessToken, videoAttributes)
248 } else { 258 } else {
@@ -362,21 +372,6 @@ async function promptPassword () {
362 }) 372 })
363} 373}
364 374
365async function getAccessTokenOrDie (url: string, user: UserInfo) {
366 const resClient = await getClient(url)
367 const client = {
368 id: resClient.body.client_id,
369 secret: resClient.body.client_secret
370 }
371
372 try {
373 const res = await login(url, client, user)
374 return res.body.access_token
375 } catch (err) {
376 exitError('Cannot authenticate. Please check your username/password.')
377 }
378}
379
380function parseDate (dateAsStr: string): Date { 375function parseDate (dateAsStr: string): Date {
381 if (!/\d{4}-\d{2}-\d{2}/.test(dateAsStr)) { 376 if (!/\d{4}-\d{2}-\d{2}/.test(dateAsStr)) {
382 exitError(`Invalid date passed: ${dateAsStr}. Expected format: YYYY-MM-DD. See help for usage.`) 377 exitError(`Invalid date passed: ${dateAsStr}. Expected format: YYYY-MM-DD. See help for usage.`)
diff --git a/server/tools/peertube-plugins.ts b/server/tools/peertube-plugins.ts
index 54ea1264d..63541bf2c 100644
--- a/server/tools/peertube-plugins.ts
+++ b/server/tools/peertube-plugins.ts
@@ -4,9 +4,8 @@ import { registerTSPaths } from '../helpers/register-ts-paths'
4registerTSPaths() 4registerTSPaths()
5 5
6import { program, Command, OptionValues } from 'commander' 6import { program, Command, OptionValues } from 'commander'
7import { installPlugin, listPlugins, uninstallPlugin, updatePlugin } from '../../shared/extra-utils/server/plugins' 7import { buildServer, getAdminTokenOrDie, getServerCredentials } from './cli'
8import { getAdminTokenOrDie, getServerCredentials } from './cli' 8import { PluginType } from '../../shared/models'
9import { PeerTubePlugin, PluginType } from '../../shared/models'
10import { isAbsolute } from 'path' 9import { isAbsolute } from 'path'
11import * as CliTable3 from 'cli-table3' 10import * as CliTable3 from 'cli-table3'
12 11
@@ -63,28 +62,21 @@ program.parse(process.argv)
63 62
64async function pluginsListCLI (command: Command, options: OptionValues) { 63async function pluginsListCLI (command: Command, options: OptionValues) {
65 const { url, username, password } = await getServerCredentials(command) 64 const { url, username, password } = await getServerCredentials(command)
66 const accessToken = await getAdminTokenOrDie(url, username, password) 65 const token = await getAdminTokenOrDie(url, username, password)
66 const server = buildServer(url, token)
67 67
68 let pluginType: PluginType 68 let pluginType: PluginType
69 if (options.onlyThemes) pluginType = PluginType.THEME 69 if (options.onlyThemes) pluginType = PluginType.THEME
70 if (options.onlyPlugins) pluginType = PluginType.PLUGIN 70 if (options.onlyPlugins) pluginType = PluginType.PLUGIN
71 71
72 const res = await listPlugins({ 72 const { data } = await server.pluginsCommand.list({ start: 0, count: 100, sort: 'name', pluginType })
73 url,
74 accessToken,
75 start: 0,
76 count: 100,
77 sort: 'name',
78 pluginType
79 })
80 const plugins: PeerTubePlugin[] = res.body.data
81 73
82 const table = new CliTable3({ 74 const table = new CliTable3({
83 head: [ 'name', 'version', 'homepage' ], 75 head: [ 'name', 'version', 'homepage' ],
84 colWidths: [ 50, 10, 50 ] 76 colWidths: [ 50, 10, 50 ]
85 }) as any 77 }) as any
86 78
87 for (const plugin of plugins) { 79 for (const plugin of data) {
88 const npmName = plugin.type === PluginType.PLUGIN 80 const npmName = plugin.type === PluginType.PLUGIN
89 ? 'peertube-plugin-' + plugin.name 81 ? 'peertube-plugin-' + plugin.name
90 : 'peertube-theme-' + plugin.name 82 : 'peertube-theme-' + plugin.name
@@ -113,15 +105,11 @@ async function installPluginCLI (command: Command, options: OptionValues) {
113 } 105 }
114 106
115 const { url, username, password } = await getServerCredentials(command) 107 const { url, username, password } = await getServerCredentials(command)
116 const accessToken = await getAdminTokenOrDie(url, username, password) 108 const token = await getAdminTokenOrDie(url, username, password)
109 const server = buildServer(url, token)
117 110
118 try { 111 try {
119 await installPlugin({ 112 await server.pluginsCommand.install({ npmName: options.npmName, path: options.path })
120 url,
121 accessToken,
122 npmName: options.npmName,
123 path: options.path
124 })
125 } catch (err) { 113 } catch (err) {
126 console.error('Cannot install plugin.', err) 114 console.error('Cannot install plugin.', err)
127 process.exit(-1) 115 process.exit(-1)
@@ -144,15 +132,11 @@ async function updatePluginCLI (command: Command, options: OptionValues) {
144 } 132 }
145 133
146 const { url, username, password } = await getServerCredentials(command) 134 const { url, username, password } = await getServerCredentials(command)
147 const accessToken = await getAdminTokenOrDie(url, username, password) 135 const token = await getAdminTokenOrDie(url, username, password)
136 const server = buildServer(url, token)
148 137
149 try { 138 try {
150 await updatePlugin({ 139 await server.pluginsCommand.update({ npmName: options.npmName, path: options.path })
151 url,
152 accessToken,
153 npmName: options.npmName,
154 path: options.path
155 })
156 } catch (err) { 140 } catch (err) {
157 console.error('Cannot update plugin.', err) 141 console.error('Cannot update plugin.', err)
158 process.exit(-1) 142 process.exit(-1)
@@ -170,14 +154,11 @@ async function uninstallPluginCLI (command: Command, options: OptionValues) {
170 } 154 }
171 155
172 const { url, username, password } = await getServerCredentials(command) 156 const { url, username, password } = await getServerCredentials(command)
173 const accessToken = await getAdminTokenOrDie(url, username, password) 157 const token = await getAdminTokenOrDie(url, username, password)
158 const server = buildServer(url, token)
174 159
175 try { 160 try {
176 await uninstallPlugin({ 161 await server.pluginsCommand.uninstall({ npmName: options.npmName })
177 url,
178 accessToken,
179 npmName: options.npmName
180 })
181 } catch (err) { 162 } catch (err) {
182 console.error('Cannot uninstall plugin.', err) 163 console.error('Cannot uninstall plugin.', err)
183 process.exit(-1) 164 process.exit(-1)
diff --git a/server/tools/peertube-redundancy.ts b/server/tools/peertube-redundancy.ts
index 4810deee0..76d633f9e 100644
--- a/server/tools/peertube-redundancy.ts
+++ b/server/tools/peertube-redundancy.ts
@@ -1,17 +1,14 @@
1// eslint-disable @typescript-eslint/no-unnecessary-type-assertion
2
3import { registerTSPaths } from '../helpers/register-ts-paths' 1import { registerTSPaths } from '../helpers/register-ts-paths'
4registerTSPaths() 2registerTSPaths()
5 3
6import { program, Command } from 'commander'
7import { getAdminTokenOrDie, getServerCredentials } from './cli'
8import { VideoRedundanciesTarget, VideoRedundancy } from '@shared/models'
9import { addVideoRedundancy, listVideoRedundancies, removeVideoRedundancy } from '@shared/extra-utils/server/redundancy'
10import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
11import validator from 'validator'
12import * as CliTable3 from 'cli-table3' 4import * as CliTable3 from 'cli-table3'
13import { URL } from 'url' 5import { Command, program } from 'commander'
14import { uniq } from 'lodash' 6import { uniq } from 'lodash'
7import { URL } from 'url'
8import validator from 'validator'
9import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
10import { VideoRedundanciesTarget } from '@shared/models'
11import { buildServer, getAdminTokenOrDie, getServerCredentials } from './cli'
15 12
16import bytes = require('bytes') 13import bytes = require('bytes')
17 14
@@ -63,15 +60,16 @@ program.parse(process.argv)
63 60
64async function listRedundanciesCLI (target: VideoRedundanciesTarget) { 61async function listRedundanciesCLI (target: VideoRedundanciesTarget) {
65 const { url, username, password } = await getServerCredentials(program) 62 const { url, username, password } = await getServerCredentials(program)
66 const accessToken = await getAdminTokenOrDie(url, username, password) 63 const token = await getAdminTokenOrDie(url, username, password)
64 const server = buildServer(url, token)
67 65
68 const redundancies = await listVideoRedundanciesData(url, accessToken, target) 66 const { data } = await server.redundancyCommand.listVideos({ start: 0, count: 100, sort: 'name', target })
69 67
70 const table = new CliTable3({ 68 const table = new CliTable3({
71 head: [ 'video id', 'video name', 'video url', 'files', 'playlists', 'by instances', 'total size' ] 69 head: [ 'video id', 'video name', 'video url', 'files', 'playlists', 'by instances', 'total size' ]
72 }) as any 70 }) as any
73 71
74 for (const redundancy of redundancies) { 72 for (const redundancy of data) {
75 const webtorrentFiles = redundancy.redundancies.files 73 const webtorrentFiles = redundancy.redundancies.files
76 const streamingPlaylists = redundancy.redundancies.streamingPlaylists 74 const streamingPlaylists = redundancy.redundancies.streamingPlaylists
77 75
@@ -106,7 +104,8 @@ async function listRedundanciesCLI (target: VideoRedundanciesTarget) {
106 104
107async function addRedundancyCLI (options: { video: number }, command: Command) { 105async function addRedundancyCLI (options: { video: number }, command: Command) {
108 const { url, username, password } = await getServerCredentials(command) 106 const { url, username, password } = await getServerCredentials(command)
109 const accessToken = await getAdminTokenOrDie(url, username, password) 107 const token = await getAdminTokenOrDie(url, username, password)
108 const server = buildServer(url, token)
110 109
111 if (!options.video || validator.isInt('' + options.video) === false) { 110 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') 111 console.error('You need to specify the video id to duplicate and it should be a number.\n')
@@ -115,11 +114,7 @@ async function addRedundancyCLI (options: { video: number }, command: Command) {
115 } 114 }
116 115
117 try { 116 try {
118 await addVideoRedundancy({ 117 await server.redundancyCommand.addVideo({ videoId: options.video })
119 url,
120 accessToken,
121 videoId: options.video
122 })
123 118
124 console.log('Video will be duplicated by your instance!') 119 console.log('Video will be duplicated by your instance!')
125 120
@@ -139,7 +134,8 @@ async function addRedundancyCLI (options: { video: number }, command: Command) {
139 134
140async function removeRedundancyCLI (options: { video: number }, command: Command) { 135async function removeRedundancyCLI (options: { video: number }, command: Command) {
141 const { url, username, password } = await getServerCredentials(command) 136 const { url, username, password } = await getServerCredentials(command)
142 const accessToken = await getAdminTokenOrDie(url, username, password) 137 const token = await getAdminTokenOrDie(url, username, password)
138 const server = buildServer(url, token)
143 139
144 if (!options.video || validator.isInt('' + options.video) === false) { 140 if (!options.video || validator.isInt('' + options.video) === false) {
145 console.error('You need to specify the video id to remove from your redundancies.\n') 141 console.error('You need to specify the video id to remove from your redundancies.\n')
@@ -149,12 +145,12 @@ async function removeRedundancyCLI (options: { video: number }, command: Command
149 145
150 const videoId = parseInt(options.video + '', 10) 146 const videoId = parseInt(options.video + '', 10)
151 147
152 let redundancies = await listVideoRedundanciesData(url, accessToken, 'my-videos') 148 const myVideoRedundancies = await server.redundancyCommand.listVideos({ target: 'my-videos' })
153 let videoRedundancy = redundancies.find(r => videoId === r.id) 149 let videoRedundancy = myVideoRedundancies.data.find(r => videoId === r.id)
154 150
155 if (!videoRedundancy) { 151 if (!videoRedundancy) {
156 redundancies = await listVideoRedundanciesData(url, accessToken, 'remote-videos') 152 const remoteVideoRedundancies = await server.redundancyCommand.listVideos({ target: 'remote-videos' })
157 videoRedundancy = redundancies.find(r => videoId === r.id) 153 videoRedundancy = remoteVideoRedundancies.data.find(r => videoId === r.id)
158 } 154 }
159 155
160 if (!videoRedundancy) { 156 if (!videoRedundancy) {
@@ -168,11 +164,7 @@ async function removeRedundancyCLI (options: { video: number }, command: Command
168 .map(r => r.id) 164 .map(r => r.id)
169 165
170 for (const id of ids) { 166 for (const id of ids) {
171 await removeVideoRedundancy({ 167 await server.redundancyCommand.removeVideo({ redundancyId: id })
172 url,
173 accessToken,
174 redundancyId: id
175 })
176 } 168 }
177 169
178 console.log('Video redundancy removed!') 170 console.log('Video redundancy removed!')
@@ -183,16 +175,3 @@ async function removeRedundancyCLI (options: { video: number }, command: Command
183 process.exit(-1) 175 process.exit(-1)
184 } 176 }
185} 177}
186
187async 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}
diff --git a/server/tools/peertube-upload.ts b/server/tools/peertube-upload.ts
index 02edbd809..d1c5348d1 100644
--- a/server/tools/peertube-upload.ts
+++ b/server/tools/peertube-upload.ts
@@ -6,7 +6,7 @@ import { access, constants } from 'fs-extra'
6import { isAbsolute } from 'path' 6import { isAbsolute } from 'path'
7import { getAccessToken } from '../../shared/extra-utils' 7import { getAccessToken } from '../../shared/extra-utils'
8import { uploadVideo } from '../../shared/extra-utils/' 8import { uploadVideo } from '../../shared/extra-utils/'
9import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getServerCredentials } from './cli' 9import { buildCommonVideoOptions, buildServer, buildVideoAttributesFromCommander, getServerCredentials } from './cli'
10 10
11let command = program 11let command = program
12 .name('upload') 12 .name('upload')
@@ -46,13 +46,14 @@ getServerCredentials(command)
46 .catch(err => console.error(err)) 46 .catch(err => console.error(err))
47 47
48async function run (url: string, username: string, password: string) { 48async function run (url: string, username: string, password: string) {
49 const accessToken = await getAccessToken(url, username, password) 49 const token = await getAccessToken(url, username, password)
50 const server = buildServer(url, token)
50 51
51 await access(options.file, constants.F_OK) 52 await access(options.file, constants.F_OK)
52 53
53 console.log('Uploading %s video...', options.videoName) 54 console.log('Uploading %s video...', options.videoName)
54 55
55 const videoAttributes = await buildVideoAttributesFromCommander(url, program) 56 const videoAttributes = await buildVideoAttributesFromCommander(server, program)
56 57
57 Object.assign(videoAttributes, { 58 Object.assign(videoAttributes, {
58 fixture: options.file, 59 fixture: options.file,
@@ -61,7 +62,7 @@ async function run (url: string, username: string, password: string) {
61 }) 62 })
62 63
63 try { 64 try {
64 await uploadVideo(url, accessToken, videoAttributes) 65 await uploadVideo(url, token, videoAttributes)
65 console.log(`Video ${options.videoName} uploaded.`) 66 console.log(`Video ${options.videoName} uploaded.`)
66 process.exit(0) 67 process.exit(0)
67 } catch (err) { 68 } catch (err) {
diff --git a/server/tools/test.ts b/server/tools/test-live.ts
index fbdbae0b0..76fd69435 100644
--- a/server/tools/test.ts
+++ b/server/tools/test-live.ts
@@ -1,26 +1,23 @@
1import { registerTSPaths } from '../helpers/register-ts-paths'
2registerTSPaths()
3
4import { LiveVideo, LiveVideoCreate, VideoPrivacy } from '@shared/models'
5import { program } from 'commander' 1import { program } from 'commander'
2import { LiveVideoCreate, VideoPrivacy } from '@shared/models'
6import { 3import {
7 createLive,
8 flushAndRunServer, 4 flushAndRunServer,
9 getLive,
10 killallServers, 5 killallServers,
11 sendRTMPStream, 6 sendRTMPStream,
12 ServerInfo, 7 ServerInfo,
13 setAccessTokensToServers, 8 setAccessTokensToServers,
14 setDefaultVideoChannel, 9 setDefaultVideoChannel
15 updateCustomSubConfig
16} from '../../shared/extra-utils' 10} from '../../shared/extra-utils'
11import { registerTSPaths } from '../helpers/register-ts-paths'
12
13registerTSPaths()
17 14
18type CommandType = 'live-mux' | 'live-transcoding' 15type CommandType = 'live-mux' | 'live-transcoding'
19 16
20registerTSPaths() 17registerTSPaths()
21 18
22const command = program 19const command = program
23 .name('test') 20 .name('test-live')
24 .option('-t, --type <type>', 'live-muxing|live-transcoding') 21 .option('-t, --type <type>', 'live-muxing|live-transcoding')
25 .parse(process.argv) 22 .parse(process.argv)
26 23
@@ -63,11 +60,9 @@ async function run () {
63 60
64 console.log('Creating live.') 61 console.log('Creating live.')
65 62
66 const res = await createLive(server.url, server.accessToken, attributes) 63 const { uuid: liveVideoUUID } = await server.liveCommand.create({ fields: attributes })
67 const liveVideoUUID = res.body.video.uuid
68 64
69 const resLive = await getLive(server.url, server.accessToken, liveVideoUUID) 65 const live = await server.liveCommand.get({ videoId: liveVideoUUID })
70 const live: LiveVideo = resLive.body
71 66
72 console.log('Sending RTMP stream.') 67 console.log('Sending RTMP stream.')
73 68
@@ -87,18 +82,20 @@ async function run () {
87// ---------------------------------------------------------------------------- 82// ----------------------------------------------------------------------------
88 83
89async function buildConfig (server: ServerInfo, commandType: CommandType) { 84async function buildConfig (server: ServerInfo, commandType: CommandType) {
90 await updateCustomSubConfig(server.url, server.accessToken, { 85 await server.configCommand.updateCustomSubConfig({
91 instance: { 86 newConfig: {
92 customizations: { 87 instance: {
93 javascript: '', 88 customizations: {
94 css: '' 89 javascript: '',
95 } 90 css: ''
96 }, 91 }
97 live: { 92 },
98 enabled: true, 93 live: {
99 allowReplay: true, 94 enabled: true,
100 transcoding: { 95 allowReplay: true,
101 enabled: commandType === 'live-transcoding' 96 transcoding: {
97 enabled: commandType === 'live-transcoding'
98 }
102 } 99 }
103 } 100 }
104 }) 101 })
diff --git a/shared/extra-utils/server/servers.ts b/shared/extra-utils/server/servers.ts
index 8e80a9842..41b48a8ee 100644
--- a/shared/extra-utils/server/servers.ts
+++ b/shared/extra-utils/server/servers.ts
@@ -41,25 +41,25 @@ import { RedundancyCommand } from './redundancy-command'
41import { StatsCommand } from './stats-command' 41import { StatsCommand } from './stats-command'
42 42
43interface ServerInfo { 43interface ServerInfo {
44 app: ChildProcess 44 app?: ChildProcess
45 45
46 url: string 46 url: string
47 host: string 47 host?: string
48 hostname: string 48 hostname?: string
49 port: number 49 port?: number
50 50
51 rtmpPort: number 51 rtmpPort?: number
52 52
53 parallel: boolean 53 parallel?: boolean
54 internalServerNumber: number 54 internalServerNumber: number
55 serverNumber: number 55 serverNumber?: number
56 56
57 client: { 57 client?: {
58 id: string 58 id?: string
59 secret: string 59 secret?: string
60 } 60 }
61 61
62 user: { 62 user?: {
63 username: string 63 username: string
64 password: string 64 password: string
65 email?: string 65 email?: string
@@ -328,43 +328,47 @@ async function runServer (server: ServerInfo, configOverrideArg?: any, args = []
328 } catch { /* empty */ } 328 } catch { /* empty */ }
329 }) 329 })
330 330
331 server.bulkCommand = new BulkCommand(server) 331 assignCommands(server)
332 server.cliCommand = new CLICommand(server)
333 server.customPageCommand = new CustomPagesCommand(server)
334 server.feedCommand = new FeedCommand(server)
335 server.logsCommand = new LogsCommand(server)
336 server.abusesCommand = new AbusesCommand(server)
337 server.overviewsCommand = new OverviewsCommand(server)
338 server.searchCommand = new SearchCommand(server)
339 server.contactFormCommand = new ContactFormCommand(server)
340 server.debugCommand = new DebugCommand(server)
341 server.followsCommand = new FollowsCommand(server)
342 server.jobsCommand = new JobsCommand(server)
343 server.pluginsCommand = new PluginsCommand(server)
344 server.redundancyCommand = new RedundancyCommand(server)
345 server.statsCommand = new StatsCommand(server)
346 server.configCommand = new ConfigCommand(server)
347 server.socketIOCommand = new SocketIOCommand(server)
348 server.accountsCommand = new AccountsCommand(server)
349 server.blocklistCommand = new BlocklistCommand(server)
350 server.subscriptionsCommand = new SubscriptionsCommand(server)
351 server.liveCommand = new LiveCommand(server)
352 server.servicesCommand = new ServicesCommand(server)
353 server.blacklistCommand = new BlacklistCommand(server)
354 server.captionsCommand = new CaptionsCommand(server)
355 server.changeOwnershipCommand = new ChangeOwnershipCommand(server)
356 server.playlistsCommand = new PlaylistsCommand(server)
357 server.historyCommand = new HistoryCommand(server)
358 server.importsCommand = new ImportsCommand(server)
359 server.streamingPlaylistsCommand = new StreamingPlaylistsCommand(server)
360 server.channelsCommand = new ChannelsCommand(server)
361 server.commentsCommand = new CommentsCommand(server)
362 332
363 res(server) 333 res(server)
364 }) 334 })
365 }) 335 })
366} 336}
367 337
338function assignCommands (server: ServerInfo) {
339 server.bulkCommand = new BulkCommand(server)
340 server.cliCommand = new CLICommand(server)
341 server.customPageCommand = new CustomPagesCommand(server)
342 server.feedCommand = new FeedCommand(server)
343 server.logsCommand = new LogsCommand(server)
344 server.abusesCommand = new AbusesCommand(server)
345 server.overviewsCommand = new OverviewsCommand(server)
346 server.searchCommand = new SearchCommand(server)
347 server.contactFormCommand = new ContactFormCommand(server)
348 server.debugCommand = new DebugCommand(server)
349 server.followsCommand = new FollowsCommand(server)
350 server.jobsCommand = new JobsCommand(server)
351 server.pluginsCommand = new PluginsCommand(server)
352 server.redundancyCommand = new RedundancyCommand(server)
353 server.statsCommand = new StatsCommand(server)
354 server.configCommand = new ConfigCommand(server)
355 server.socketIOCommand = new SocketIOCommand(server)
356 server.accountsCommand = new AccountsCommand(server)
357 server.blocklistCommand = new BlocklistCommand(server)
358 server.subscriptionsCommand = new SubscriptionsCommand(server)
359 server.liveCommand = new LiveCommand(server)
360 server.servicesCommand = new ServicesCommand(server)
361 server.blacklistCommand = new BlacklistCommand(server)
362 server.captionsCommand = new CaptionsCommand(server)
363 server.changeOwnershipCommand = new ChangeOwnershipCommand(server)
364 server.playlistsCommand = new PlaylistsCommand(server)
365 server.historyCommand = new HistoryCommand(server)
366 server.importsCommand = new ImportsCommand(server)
367 server.streamingPlaylistsCommand = new StreamingPlaylistsCommand(server)
368 server.channelsCommand = new ChannelsCommand(server)
369 server.commentsCommand = new CommentsCommand(server)
370}
371
368async function reRunServer (server: ServerInfo, configOverride?: any) { 372async function reRunServer (server: ServerInfo, configOverride?: any) {
369 const newServer = await runServer(server, configOverride) 373 const newServer = await runServer(server, configOverride)
370 server.app = newServer.app 374 server.app = newServer.app
@@ -475,5 +479,6 @@ export {
475 flushAndRunServer, 479 flushAndRunServer,
476 killallServers, 480 killallServers,
477 reRunServer, 481 reRunServer,
482 assignCommands,
478 waitUntilLog 483 waitUntilLog
479} 484}
diff --git a/shared/extra-utils/users/login.ts b/shared/extra-utils/users/login.ts
index 39e1a2747..c14367542 100644
--- a/shared/extra-utils/users/login.ts
+++ b/shared/extra-utils/users/login.ts
@@ -4,9 +4,9 @@ import { ServerInfo } from '../server/servers'
4import { getClient } from '../server/clients' 4import { getClient } from '../server/clients'
5import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' 5import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
6 6
7type Client = { id: string, secret: string } 7type Client = { id?: string, secret?: string }
8type User = { username: string, password: string } 8type User = { username: string, password: string }
9type Server = { url: string, client: Client, user: User } 9type Server = { url?: string, client?: Client, user?: User }
10 10
11function login (url: string, client: Client, user: User, expectedStatus = HttpStatusCode.OK_200) { 11function login (url: string, client: Client, user: User, expectedStatus = HttpStatusCode.OK_200) {
12 const path = '/api/v1/users/token' 12 const path = '/api/v1/users/token'