diff options
author | Chocobozzz <me@florianbigard.com> | 2019-02-11 11:52:34 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2019-02-11 11:52:34 +0100 |
commit | 88108880bbdba473cfe36ecbebc1c3c4f972e102 (patch) | |
tree | b242efb3b4f0d7e49d88f2d1f2063b5b3b0489c0 /server/tools | |
parent | 53a94c7cfa8368da4cd248d65df8346905938f0c (diff) | |
parent | 9b712a2017e4ab3cf12cd6bd58278905520159d0 (diff) | |
download | PeerTube-88108880bbdba473cfe36ecbebc1c3c4f972e102.tar.gz PeerTube-88108880bbdba473cfe36ecbebc1c3c4f972e102.tar.zst PeerTube-88108880bbdba473cfe36ecbebc1c3c4f972e102.zip |
Merge branch 'develop' into pr/1217
Diffstat (limited to 'server/tools')
-rw-r--r-- | server/tools/README.md | 82 | ||||
-rw-r--r-- | server/tools/cli.ts | 2 | ||||
-rw-r--r-- | server/tools/peertube-auth.ts | 113 | ||||
-rw-r--r-- | server/tools/peertube-get-access-token.ts | 2 | ||||
-rw-r--r-- | server/tools/peertube-import-videos.ts | 34 | ||||
-rw-r--r-- | server/tools/peertube-repl.ts | 76 | ||||
-rw-r--r-- | server/tools/peertube-upload.ts | 4 | ||||
-rwxr-xr-x | server/tools/peertube.ts | 3 |
8 files changed, 244 insertions, 72 deletions
diff --git a/server/tools/README.md b/server/tools/README.md new file mode 100644 index 000000000..6b94d74e5 --- /dev/null +++ b/server/tools/README.md | |||
@@ -0,0 +1,82 @@ | |||
1 | peertube(8) -- companion CLI for PeerTube | ||
2 | ========================================= | ||
3 | |||
4 | SYNOPSIS | ||
5 | -------- | ||
6 | |||
7 | ``` | ||
8 | peertube [command] [options] | ||
9 | ``` | ||
10 | |||
11 | DESCRIPTION | ||
12 | ----------- | ||
13 | |||
14 | `peertube` wraps various utilities around PeerTube that are used either on a running local, running remote, or cold local instance. | ||
15 | |||
16 | COMMANDS | ||
17 | -------- | ||
18 | |||
19 | Unless otherwise specified, every command can be queried for its own help or manual by passing its name to the `help` command, or by using the `--help` option. | ||
20 | |||
21 | `auth [action]`: stores credentials for your accounts on remote instances so that you don't need to pass them at every command | ||
22 | |||
23 | `upload|up`: upload a video to a remote instance | ||
24 | |||
25 | $ peertube upload \ | ||
26 | -u "PEERTUBE_URL" \ | ||
27 | -U "PEERTUBE_USER" \ | ||
28 | --password "PEERTUBE_PASSWORD" | ||
29 | |||
30 | `import-videos|import`: import a video from a streaming platform to a remote instance | ||
31 | |||
32 | $ peertube import \ | ||
33 | -u "PEERTUBE_URL" \ | ||
34 | -U "PEERTUBE_USER" \ | ||
35 | --password "PEERTUBE_PASSWORD" \ | ||
36 | -t "TARGET_URL" | ||
37 | |||
38 | The target URL can be directly the video file, or any of the supported sites of youtube-dl. The video is downloaded locally and then uploaded. Already downloaded videos will not be uploaded twice, so you can run and re-run the script in case of crash, disconnection… | ||
39 | |||
40 | `watch|w`: watch a video in the terminal ✩°。⋆ | ||
41 | |||
42 | -g, --gui <player> player type (default: ascii) | ||
43 | -i, --invert invert colors (ascii player only) | ||
44 | -r, --resolution <res> video resolution (default: 720) | ||
45 | |||
46 | It provides support for different players: | ||
47 | |||
48 | - ascii (default ; plays in ascii art in your terminal!) | ||
49 | - mpv | ||
50 | - mplayer | ||
51 | - vlc | ||
52 | - stdout | ||
53 | - xbmc | ||
54 | - airplay | ||
55 | - chromecast | ||
56 | |||
57 | `repl`: interact with the application libraries and objects even when PeerTube is not running | ||
58 | |||
59 | Type .help to see the repl-only functions, or to see the available PeerTube core functions: | ||
60 | |||
61 | repl> lodash.keys(context) | ||
62 | |||
63 | `help [cmd]`: display help for [cmd] | ||
64 | |||
65 | EXAMPLES | ||
66 | -------- | ||
67 | |||
68 | $ peertube auth add -u "PEERTUBE_URL" -U "PEERTUBE_USER" --password "PEERTUBE_PASSWORD" | ||
69 | $ peertube up <videoFile> | ||
70 | $ peertube watch https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10 | ||
71 | |||
72 | SEE ALSO | ||
73 | -------- | ||
74 | |||
75 | [PeerTube Tools Documentation](https://github.com/Chocobozzz/PeerTube/blob/develop/support/doc/tools.md) | ||
76 | |||
77 | [PeerTube Admin Documentation](https://docs.joinpeertube.org/lang/en/docs/) | ||
78 | |||
79 | REPORTING BUGS | ||
80 | -------------- | ||
81 | |||
82 | See [PeerTube repository](https://github.com/Chocobozzz/PeerTube). | ||
diff --git a/server/tools/cli.ts b/server/tools/cli.ts index 53b20964e..108c44218 100644 --- a/server/tools/cli.ts +++ b/server/tools/cli.ts | |||
@@ -23,7 +23,7 @@ async function getSettings () { | |||
23 | if (err) { | 23 | if (err) { |
24 | return rej(err) | 24 | return rej(err) |
25 | } | 25 | } |
26 | return res(data || settings) | 26 | return res(Object.keys(data).length === 0 ? settings : data) |
27 | }) | 27 | }) |
28 | }) | 28 | }) |
29 | } | 29 | } |
diff --git a/server/tools/peertube-auth.ts b/server/tools/peertube-auth.ts index 33438811e..a962944a4 100644 --- a/server/tools/peertube-auth.ts +++ b/server/tools/peertube-auth.ts | |||
@@ -5,34 +5,25 @@ import { getSettings, writeSettings, netrc } from './cli' | |||
5 | import { isHostValid } from '../helpers/custom-validators/servers' | 5 | import { isHostValid } from '../helpers/custom-validators/servers' |
6 | import { isUserUsernameValid } from '../helpers/custom-validators/users' | 6 | import { isUserUsernameValid } from '../helpers/custom-validators/users' |
7 | 7 | ||
8 | function delInstance (url: string) { | 8 | async function delInstance (url: string) { |
9 | return new Promise((res, rej): void => { | 9 | const settings = await getSettings() |
10 | getSettings() | 10 | |
11 | .then(async (settings) => { | 11 | settings.remotes.splice(settings.remotes.indexOf(url)) |
12 | settings.remotes.splice(settings.remotes.indexOf(url)) | 12 | await writeSettings(settings) |
13 | await writeSettings(settings) | 13 | |
14 | delete netrc.machines[url] | 14 | delete netrc.machines[url] |
15 | netrc.save() | 15 | await netrc.save() |
16 | res() | ||
17 | }) | ||
18 | .catch(err => rej(err)) | ||
19 | }) | ||
20 | } | 16 | } |
21 | 17 | ||
22 | async function setInstance (url: string, username: string, password: string) { | 18 | async function setInstance (url: string, username: string, password: string) { |
23 | return new Promise((res, rej): void => { | 19 | const settings = await getSettings() |
24 | getSettings() | 20 | if (settings.remotes.indexOf(url) === -1) { |
25 | .then(async settings => { | 21 | settings.remotes.push(url) |
26 | if (settings.remotes.indexOf(url) === -1) { | 22 | } |
27 | settings.remotes.push(url) | 23 | await writeSettings(settings) |
28 | } | 24 | |
29 | await writeSettings(settings) | 25 | netrc.machines[url] = { login: username, password } |
30 | netrc.machines[url] = { login: username, password } | 26 | await netrc.save() |
31 | netrc.save() | ||
32 | res() | ||
33 | }) | ||
34 | .catch(err => rej(err)) | ||
35 | }) | ||
36 | } | 27 | } |
37 | 28 | ||
38 | function isURLaPeerTubeInstance (url: string) { | 29 | function isURLaPeerTubeInstance (url: string) { |
@@ -71,56 +62,60 @@ program | |||
71 | required: true | 62 | required: true |
72 | } | 63 | } |
73 | } | 64 | } |
74 | }, (_, result) => { | 65 | }, async (_, result) => { |
75 | setInstance(result.url, result.username, result.password) | 66 | await setInstance(result.url, result.username, result.password) |
67 | |||
68 | process.exit(0) | ||
76 | }) | 69 | }) |
77 | }) | 70 | }) |
78 | 71 | ||
79 | program | 72 | program |
80 | .command('del <url>') | 73 | .command('del <url>') |
81 | .description('unregisters a remote instance') | 74 | .description('unregisters a remote instance') |
82 | .action((url) => { | 75 | .action(async url => { |
83 | delInstance(url) | 76 | await delInstance(url) |
77 | |||
78 | process.exit(0) | ||
84 | }) | 79 | }) |
85 | 80 | ||
86 | program | 81 | program |
87 | .command('list') | 82 | .command('list') |
88 | .description('lists registered remote instances') | 83 | .description('lists registered remote instances') |
89 | .action(() => { | 84 | .action(async () => { |
90 | getSettings() | 85 | const settings = await getSettings() |
91 | .then(settings => { | 86 | const table = new Table({ |
92 | const table = new Table({ | 87 | head: ['instance', 'login'], |
93 | head: ['instance', 'login'], | 88 | colWidths: [30, 30] |
94 | colWidths: [30, 30] | 89 | }) |
95 | }) | 90 | netrc.loadSync() |
96 | netrc.loadSync() | 91 | settings.remotes.forEach(element => { |
97 | settings.remotes.forEach(element => { | 92 | table.push([ |
98 | table.push([ | 93 | element, |
99 | element, | 94 | netrc.machines[element].login |
100 | netrc.machines[element].login | 95 | ]) |
101 | ]) | 96 | }) |
102 | }) | 97 | |
103 | 98 | console.log(table.toString()) | |
104 | console.log(table.toString()) | 99 | |
105 | }) | 100 | process.exit(0) |
106 | }) | 101 | }) |
107 | 102 | ||
108 | program | 103 | program |
109 | .command('set-default <url>') | 104 | .command('set-default <url>') |
110 | .description('set an existing entry as default') | 105 | .description('set an existing entry as default') |
111 | .action((url) => { | 106 | .action(async url => { |
112 | getSettings() | 107 | const settings = await getSettings() |
113 | .then(settings => { | 108 | const instanceExists = settings.remotes.indexOf(url) !== -1 |
114 | const instanceExists = settings.remotes.indexOf(url) !== -1 | 109 | |
115 | 110 | if (instanceExists) { | |
116 | if (instanceExists) { | 111 | settings.default = settings.remotes.indexOf(url) |
117 | settings.default = settings.remotes.indexOf(url) | 112 | await writeSettings(settings) |
118 | writeSettings(settings) | 113 | |
119 | } else { | 114 | process.exit(0) |
120 | console.log('<url> is not a registered instance.') | 115 | } else { |
121 | process.exit(-1) | 116 | console.log('<url> is not a registered instance.') |
122 | } | 117 | process.exit(-1) |
123 | }) | 118 | } |
124 | }) | 119 | }) |
125 | 120 | ||
126 | program.on('--help', function () { | 121 | program.on('--help', function () { |
diff --git a/server/tools/peertube-get-access-token.ts b/server/tools/peertube-get-access-token.ts index eb2571a03..a68665f5b 100644 --- a/server/tools/peertube-get-access-token.ts +++ b/server/tools/peertube-get-access-token.ts | |||
@@ -6,7 +6,7 @@ import { | |||
6 | Server, | 6 | Server, |
7 | Client, | 7 | Client, |
8 | User | 8 | User |
9 | } from '../tests/utils/index' | 9 | } from '../../shared/utils' |
10 | 10 | ||
11 | program | 11 | program |
12 | .option('-u, --url <url>', 'Server url') | 12 | .option('-u, --url <url>', 'Server url') |
diff --git a/server/tools/peertube-import-videos.ts b/server/tools/peertube-import-videos.ts index 15f517cab..151c5a989 100644 --- a/server/tools/peertube-import-videos.ts +++ b/server/tools/peertube-import-videos.ts | |||
@@ -6,10 +6,11 @@ import { join } from 'path' | |||
6 | import { VideoPrivacy } from '../../shared/models/videos' | 6 | import { VideoPrivacy } from '../../shared/models/videos' |
7 | import { doRequestAndSaveToFile } from '../helpers/requests' | 7 | import { doRequestAndSaveToFile } from '../helpers/requests' |
8 | import { CONSTRAINTS_FIELDS } from '../initializers' | 8 | import { CONSTRAINTS_FIELDS } from '../initializers' |
9 | import { getClient, getVideoCategories, login, searchVideoWithSort, uploadVideo } from '../tests/utils' | 9 | import { getClient, getVideoCategories, login, searchVideoWithSort, uploadVideo } from '../../shared/utils/index' |
10 | import { truncate } from 'lodash' | 10 | import { truncate } from 'lodash' |
11 | import * as prompt from 'prompt' | 11 | import * as prompt from 'prompt' |
12 | import { remove } from 'fs-extra' | 12 | import { remove } from 'fs-extra' |
13 | import { sha256 } from '../helpers/core-utils' | ||
13 | import { safeGetYoutubeDL } from '../helpers/youtube-dl' | 14 | import { safeGetYoutubeDL } from '../helpers/youtube-dl' |
14 | import { getSettings, netrc } from './cli' | 15 | import { getSettings, netrc } from './cli' |
15 | 16 | ||
@@ -57,6 +58,7 @@ getSettings() | |||
57 | settings.remotes[settings.default] : | 58 | settings.remotes[settings.default] : |
58 | settings.remotes[0] | 59 | settings.remotes[0] |
59 | } | 60 | } |
61 | |||
60 | if (!program['username']) program['username'] = netrc.machines[program['url']].login | 62 | if (!program['username']) program['username'] = netrc.machines[program['url']].login |
61 | if (!program['password']) program['password'] = netrc.machines[program['url']].password | 63 | if (!program['password']) program['password'] = netrc.machines[program['url']].password |
62 | } | 64 | } |
@@ -68,12 +70,19 @@ getSettings() | |||
68 | process.exit(-1) | 70 | process.exit(-1) |
69 | } | 71 | } |
70 | 72 | ||
73 | removeEndSlashes(program['url']) | ||
74 | removeEndSlashes(program['targetUrl']) | ||
75 | |||
71 | const user = { | 76 | const user = { |
72 | username: program['username'], | 77 | username: program['username'], |
73 | password: program['password'] | 78 | password: program['password'] |
74 | } | 79 | } |
75 | 80 | ||
76 | run(user, program['url']).catch(err => console.error(err)) | 81 | run(user, program['url']) |
82 | .catch(err => { | ||
83 | console.error(err) | ||
84 | process.exit(-1) | ||
85 | }) | ||
77 | }) | 86 | }) |
78 | 87 | ||
79 | async function promptPassword () { | 88 | async function promptPassword () { |
@@ -107,8 +116,12 @@ async function run (user, url: string) { | |||
107 | secret: res.body.client_secret | 116 | secret: res.body.client_secret |
108 | } | 117 | } |
109 | 118 | ||
110 | const res2 = await login(url, client, user) | 119 | try { |
111 | accessToken = res2.body.access_token | 120 | const res = await login(program[ 'url' ], client, user) |
121 | accessToken = res.body.access_token | ||
122 | } catch (err) { | ||
123 | throw new Error('Cannot authenticate. Please check your username/password.') | ||
124 | } | ||
112 | 125 | ||
113 | const youtubeDL = await safeGetYoutubeDL() | 126 | const youtubeDL = await safeGetYoutubeDL() |
114 | 127 | ||
@@ -133,8 +146,7 @@ async function run (user, url: string) { | |||
133 | await processVideo(info, program['language'], processOptions.cwd, url, user) | 146 | await processVideo(info, program['language'], processOptions.cwd, url, user) |
134 | } | 147 | } |
135 | 148 | ||
136 | // https://www.youtube.com/watch?v=2Upx39TBc1s | 149 | console.log('Video/s for user %s imported: %s', program['username'], program['targetUrl']) |
137 | console.log('I\'m finished!') | ||
138 | process.exit(0) | 150 | process.exit(0) |
139 | }) | 151 | }) |
140 | } | 152 | } |
@@ -155,7 +167,7 @@ function processVideo (info: any, languageCode: string, cwd: string, url: string | |||
155 | return res() | 167 | return res() |
156 | } | 168 | } |
157 | 169 | ||
158 | const path = join(cwd, new Date().getTime() + '.mp4') | 170 | const path = join(cwd, sha256(videoInfo.url) + '.mp4') |
159 | 171 | ||
160 | console.log('Downloading video "%s"...', videoInfo.title) | 172 | console.log('Downloading video "%s"...', videoInfo.title) |
161 | 173 | ||
@@ -192,7 +204,7 @@ async function uploadVideoOnPeerTube (videoInfo: any, videoPath: string, cwd: st | |||
192 | 204 | ||
193 | let thumbnailfile | 205 | let thumbnailfile |
194 | if (videoInfo.thumbnail) { | 206 | if (videoInfo.thumbnail) { |
195 | thumbnailfile = join(cwd, 'thumbnail.jpg') | 207 | thumbnailfile = join(cwd, sha256(videoInfo.thumbnail) + '.jpg') |
196 | 208 | ||
197 | await doRequestAndSaveToFile({ | 209 | await doRequestAndSaveToFile({ |
198 | method: 'GET', | 210 | method: 'GET', |
@@ -322,3 +334,9 @@ function isNSFW (info: any) { | |||
322 | 334 | ||
323 | return false | 335 | return false |
324 | } | 336 | } |
337 | |||
338 | function removeEndSlashes (url: string) { | ||
339 | while (url.endsWith('/')) { | ||
340 | url.slice(0, -1) | ||
341 | } | ||
342 | } | ||
diff --git a/server/tools/peertube-repl.ts b/server/tools/peertube-repl.ts new file mode 100644 index 000000000..04d8b95a3 --- /dev/null +++ b/server/tools/peertube-repl.ts | |||
@@ -0,0 +1,76 @@ | |||
1 | import * as repl from 'repl' | ||
2 | import * as path from 'path' | ||
3 | import * as _ from 'lodash' | ||
4 | import * as uuidv1 from 'uuid/v1' | ||
5 | import * as uuidv3 from 'uuid/v3' | ||
6 | import * as uuidv4 from 'uuid/v4' | ||
7 | import * as uuidv5 from 'uuid/v5' | ||
8 | import * as Sequelize from 'sequelize' | ||
9 | import * as YoutubeDL from 'youtube-dl' | ||
10 | |||
11 | import { initDatabaseModels, sequelizeTypescript } from '../initializers' | ||
12 | import * as cli from '../tools/cli' | ||
13 | import { logger } from '../helpers/logger' | ||
14 | import * as constants from '../initializers/constants' | ||
15 | import * as modelsUtils from '../models/utils' | ||
16 | import * as coreUtils from '../helpers/core-utils' | ||
17 | import * as ffmpegUtils from '../helpers/ffmpeg-utils' | ||
18 | import * as peertubeCryptoUtils from '../helpers/peertube-crypto' | ||
19 | import * as signupUtils from '../helpers/signup' | ||
20 | import * as utils from '../helpers/utils' | ||
21 | import * as YoutubeDLUtils from '../helpers/youtube-dl' | ||
22 | |||
23 | const start = async () => { | ||
24 | await initDatabaseModels(true) | ||
25 | |||
26 | const versionCommitHash = await utils.getServerCommit() | ||
27 | |||
28 | const initContext = (replServer) => { | ||
29 | return (context) => { | ||
30 | const properties = { | ||
31 | context, repl: replServer, env: process.env, | ||
32 | lodash: _, path, | ||
33 | uuidv1, uuidv3, uuidv4, uuidv5, | ||
34 | cli, logger, constants, | ||
35 | Sequelize, sequelizeTypescript, modelsUtils, | ||
36 | models: sequelizeTypescript.models, transaction: sequelizeTypescript.transaction, | ||
37 | query: sequelizeTypescript.query, queryInterface: sequelizeTypescript.getQueryInterface(), | ||
38 | YoutubeDL, | ||
39 | coreUtils, ffmpegUtils, peertubeCryptoUtils, signupUtils, utils, YoutubeDLUtils | ||
40 | } | ||
41 | |||
42 | for (let prop in properties) { | ||
43 | Object.defineProperty(context, prop, { | ||
44 | configurable: false, | ||
45 | enumerable: true, | ||
46 | value: properties[prop] | ||
47 | }) | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | |||
52 | const replServer = repl.start({ | ||
53 | prompt: `PeerTube [${cli.version}] (${versionCommitHash})> ` | ||
54 | }) | ||
55 | |||
56 | initContext(replServer)(replServer.context) | ||
57 | replServer.on('reset', initContext(replServer)) | ||
58 | replServer.on('exit', () => process.exit()) | ||
59 | |||
60 | const resetCommand = { | ||
61 | help: 'Reset REPL', | ||
62 | action () { | ||
63 | this.write('.clear\n') | ||
64 | this.displayPrompt() | ||
65 | } | ||
66 | } | ||
67 | replServer.defineCommand('reset', resetCommand) | ||
68 | replServer.defineCommand('r', resetCommand) | ||
69 | |||
70 | } | ||
71 | |||
72 | start().then((data) => { | ||
73 | // do nothing | ||
74 | }).catch((err) => { | ||
75 | console.error(err) | ||
76 | }) | ||
diff --git a/server/tools/peertube-upload.ts b/server/tools/peertube-upload.ts index b17bc4288..ebc62c965 100644 --- a/server/tools/peertube-upload.ts +++ b/server/tools/peertube-upload.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | import * as program from 'commander' | 1 | import * as program from 'commander' |
2 | import { access, constants } from 'fs-extra' | 2 | import { access, constants } from 'fs-extra' |
3 | import { isAbsolute } from 'path' | 3 | import { isAbsolute } from 'path' |
4 | import { getClient, login } from '../tests/utils' | 4 | import { getClient, login } from '../../shared/utils' |
5 | import { uploadVideo } from '../tests/utils/index' | 5 | import { uploadVideo } from '../../shared/utils/' |
6 | import { VideoPrivacy } from '../../shared/models/videos' | 6 | import { VideoPrivacy } from '../../shared/models/videos' |
7 | import { netrc, getSettings } from './cli' | 7 | import { netrc, getSettings } from './cli' |
8 | 8 | ||
diff --git a/server/tools/peertube.ts b/server/tools/peertube.ts index ad76bafb4..5d3ab2815 100755 --- a/server/tools/peertube.ts +++ b/server/tools/peertube.ts | |||
@@ -17,6 +17,7 @@ program | |||
17 | .command('import-videos', 'import a video from a streaming platform').alias('import') | 17 | .command('import-videos', 'import a video from a streaming platform').alias('import') |
18 | .command('get-access-token', 'get a peertube access token', { noHelp: true }).alias('token') | 18 | .command('get-access-token', 'get a peertube access token', { noHelp: true }).alias('token') |
19 | .command('watch', 'watch a video in the terminal ✩°。⋆').alias('w') | 19 | .command('watch', 'watch a video in the terminal ✩°。⋆').alias('w') |
20 | .command('repl', 'initiate a REPL to access internals') | ||
20 | 21 | ||
21 | /* Not Yet Implemented */ | 22 | /* Not Yet Implemented */ |
22 | program | 23 | program |
@@ -57,7 +58,7 @@ if (!process.argv.slice(2).length) { | |||
57 | ,"\\/ | 58 | ,"\\/ |
58 | _,.__/"\\/_ (the CLI for red chocobos) | 59 | _,.__/"\\/_ (the CLI for red chocobos) |
59 | / \\) "./, ". | 60 | / \\) "./, ". |
60 | --/---"---" "-) )---- by Chocobozzz et al.`) | 61 | --/---"---" "-) )---- by Chocobozzz et al.\n`) |
61 | } | 62 | } |
62 | 63 | ||
63 | getSettings() | 64 | getSettings() |