diff options
-rw-r--r-- | client/src/app/menu/menu.component.scss | 8 | ||||
-rw-r--r-- | client/src/sass/application.scss | 2 | ||||
-rw-r--r-- | client/src/sass/include/_variables.scss | 2 | ||||
-rw-r--r-- | package.json | 10 | ||||
-rw-r--r-- | server/helpers/youtube-dl.ts | 25 | ||||
-rw-r--r-- | server/tests/cli/index.ts | 1 | ||||
-rw-r--r-- | server/tests/cli/peertube.ts | 51 | ||||
-rw-r--r-- | server/tools/cli.ts | 63 | ||||
-rw-r--r-- | server/tools/peertube-auth.ts | 140 | ||||
-rw-r--r-- | server/tools/peertube-get-access-token.ts (renamed from server/tools/get-access-token.ts) | 5 | ||||
-rw-r--r-- | server/tools/peertube-import-videos.ts (renamed from server/tools/import-videos.ts) | 109 | ||||
-rw-r--r-- | server/tools/peertube-upload.ts (renamed from server/tools/upload.ts) | 74 | ||||
-rw-r--r-- | server/tools/peertube-watch.ts | 61 | ||||
-rwxr-xr-x | server/tools/peertube.ts | 81 | ||||
-rw-r--r-- | support/doc/tools.md | 75 | ||||
-rw-r--r-- | tsconfig.json | 1 |
16 files changed, 625 insertions, 83 deletions
diff --git a/client/src/app/menu/menu.component.scss b/client/src/app/menu/menu.component.scss index 0f98da0e2..3e072279f 100644 --- a/client/src/app/menu/menu.component.scss +++ b/client/src/app/menu/menu.component.scss | |||
@@ -10,14 +10,14 @@ | |||
10 | } | 10 | } |
11 | 11 | ||
12 | menu { | 12 | menu { |
13 | background-color: $menu-background; | 13 | background-color: var(--menuBackgroundColor); |
14 | margin: 0; | 14 | margin: 0; |
15 | padding: 0; | 15 | padding: 0; |
16 | height: 100%; | 16 | height: 100%; |
17 | white-space: nowrap; | 17 | white-space: nowrap; |
18 | text-overflow: ellipsis; | 18 | text-overflow: ellipsis; |
19 | overflow: hidden; | 19 | overflow: hidden; |
20 | color: $menu-color; | 20 | color: var(--menuForegroundColor); |
21 | display: flex; | 21 | display: flex; |
22 | flex-direction: column; | 22 | flex-direction: column; |
23 | width: 100%; | 23 | width: 100%; |
@@ -52,7 +52,7 @@ menu { | |||
52 | .logged-in-username { | 52 | .logged-in-username { |
53 | font-size: 16px; | 53 | font-size: 16px; |
54 | font-weight: $font-semibold; | 54 | font-weight: $font-semibold; |
55 | color: $menu-color; | 55 | color: var(--menuForegroundColor); |
56 | cursor: pointer; | 56 | cursor: pointer; |
57 | 57 | ||
58 | @include disable-default-a-behaviour; | 58 | @include disable-default-a-behaviour; |
@@ -124,7 +124,7 @@ menu { | |||
124 | display: flex; | 124 | display: flex; |
125 | align-items: center; | 125 | align-items: center; |
126 | padding-left: $menu-lateral-padding; | 126 | padding-left: $menu-lateral-padding; |
127 | color: $menu-color; | 127 | color: var(--menuForegroundColor); |
128 | cursor: pointer; | 128 | cursor: pointer; |
129 | height: 40px; | 129 | height: 40px; |
130 | font-size: 16px; | 130 | font-size: 16px; |
diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index c1135cd02..f21b91d2e 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss | |||
@@ -26,6 +26,8 @@ body { | |||
26 | --mainHoverColor: #{$orange-hoover-color}; | 26 | --mainHoverColor: #{$orange-hoover-color}; |
27 | --mainBackgroundColor: #{$bg-color}; | 27 | --mainBackgroundColor: #{$bg-color}; |
28 | --mainForegroundColor: #{$fg-color}; | 28 | --mainForegroundColor: #{$fg-color}; |
29 | --menuBackgroundColor: #{$menu-background}; | ||
30 | --menuForegroundColor: #{$menu-color}; | ||
29 | --submenuColor: #{$sub-menu-color}; | 31 | --submenuColor: #{$sub-menu-color}; |
30 | --inputColor: #{$input-background-color}; | 32 | --inputColor: #{$input-background-color}; |
31 | --inputPlaceholderColor: #{$input-placeholder-color}; | 33 | --inputPlaceholderColor: #{$input-placeholder-color}; |
diff --git a/client/src/sass/include/_variables.scss b/client/src/sass/include/_variables.scss index 3514bdb9b..fdf33b12a 100644 --- a/client/src/sass/include/_variables.scss +++ b/client/src/sass/include/_variables.scss | |||
@@ -58,6 +58,8 @@ $variables: ( | |||
58 | --mainHoverColor: var(--mainHoverColor), | 58 | --mainHoverColor: var(--mainHoverColor), |
59 | --mainBackgroundColor: var(--mainBackgroundColor), | 59 | --mainBackgroundColor: var(--mainBackgroundColor), |
60 | --mainForegroundColor: var(--mainForegroundColor), | 60 | --mainForegroundColor: var(--mainForegroundColor), |
61 | --menuBackgroundColor: var(--menuBackgroundColor), | ||
62 | --menuForegroundColor: var(--menuForegroundColor), | ||
61 | --submenuColor: var(--submenuColor), | 63 | --submenuColor: var(--submenuColor), |
62 | --inputColor: var(--inputColor), | 64 | --inputColor: var(--inputColor), |
63 | --inputPlaceholderColor: var(--inputPlaceholderColor) | 65 | --inputPlaceholderColor: var(--inputPlaceholderColor) |
diff --git a/package.json b/package.json index 53e07a72b..d5cf95b83 100644 --- a/package.json +++ b/package.json | |||
@@ -7,6 +7,9 @@ | |||
7 | "engines": { | 7 | "engines": { |
8 | "node": ">=8.x" | 8 | "node": ">=8.x" |
9 | }, | 9 | }, |
10 | "bin": { | ||
11 | "peertube": "dist/server/tools/peertube.js" | ||
12 | }, | ||
10 | "author": { | 13 | "author": { |
11 | "name": "Florian Bigard", | 14 | "name": "Florian Bigard", |
12 | "email": "florian.bigard@gmail.com", | 15 | "email": "florian.bigard@gmail.com", |
@@ -78,6 +81,7 @@ | |||
78 | "@types/bluebird": "3.5.21" | 81 | "@types/bluebird": "3.5.21" |
79 | }, | 82 | }, |
80 | "dependencies": { | 83 | "dependencies": { |
84 | "application-config": "^1.0.1", | ||
81 | "async": "^2.0.0", | 85 | "async": "^2.0.0", |
82 | "async-lock": "^1.1.2", | 86 | "async-lock": "^1.1.2", |
83 | "async-lru": "^1.1.1", | 87 | "async-lru": "^1.1.1", |
@@ -86,6 +90,7 @@ | |||
86 | "bluebird": "^3.5.0", | 90 | "bluebird": "^3.5.0", |
87 | "body-parser": "^1.12.4", | 91 | "body-parser": "^1.12.4", |
88 | "bull": "^3.4.2", | 92 | "bull": "^3.4.2", |
93 | "cli-table": "^0.3.1", | ||
89 | "bytes": "^3.0.0", | 94 | "bytes": "^3.0.0", |
90 | "commander": "^2.13.0", | 95 | "commander": "^2.13.0", |
91 | "concurrently": "^4.0.1", | 96 | "concurrently": "^4.0.1", |
@@ -114,6 +119,7 @@ | |||
114 | "memoizee": "^0.4.14", | 119 | "memoizee": "^0.4.14", |
115 | "morgan": "^1.5.3", | 120 | "morgan": "^1.5.3", |
116 | "multer": "^1.1.0", | 121 | "multer": "^1.1.0", |
122 | "netrc-parser": "^3.1.6", | ||
117 | "nodemailer": "^4.4.2", | 123 | "nodemailer": "^4.4.2", |
118 | "parse-torrent": "^6.0.0", | 124 | "parse-torrent": "^6.0.0", |
119 | "password-generator": "^2.0.2", | 125 | "password-generator": "^2.0.2", |
@@ -131,6 +137,7 @@ | |||
131 | "sequelize-typescript": "0.6.6", | 137 | "sequelize-typescript": "0.6.6", |
132 | "sharp": "^0.20.0", | 138 | "sharp": "^0.20.0", |
133 | "srt-to-vtt": "^1.1.2", | 139 | "srt-to-vtt": "^1.1.2", |
140 | "summon-install": "^0.4.3", | ||
134 | "useragent": "^2.3.0", | 141 | "useragent": "^2.3.0", |
135 | "uuid": "^3.1.0", | 142 | "uuid": "^3.1.0", |
136 | "validator": "^10.2.0", | 143 | "validator": "^10.2.0", |
@@ -198,5 +205,8 @@ | |||
198 | "scripty": { | 205 | "scripty": { |
199 | "silent": true | 206 | "silent": true |
200 | }, | 207 | }, |
208 | "summon": { | ||
209 | "silent": true | ||
210 | }, | ||
201 | "sasslintConfig": "client/.sass-lint.yml" | 211 | "sasslintConfig": "client/.sass-lint.yml" |
202 | } | 212 | } |
diff --git a/server/helpers/youtube-dl.ts b/server/helpers/youtube-dl.ts index db2bddf78..25e719cc3 100644 --- a/server/helpers/youtube-dl.ts +++ b/server/helpers/youtube-dl.ts | |||
@@ -18,9 +18,9 @@ export type YoutubeDLInfo = { | |||
18 | thumbnailUrl?: string | 18 | thumbnailUrl?: string |
19 | } | 19 | } |
20 | 20 | ||
21 | function getYoutubeDLInfo (url: string): Promise<YoutubeDLInfo> { | 21 | function getYoutubeDLInfo (url: string, opts?: string[]): Promise<YoutubeDLInfo> { |
22 | return new Promise<YoutubeDLInfo>(async (res, rej) => { | 22 | return new Promise<YoutubeDLInfo>(async (res, rej) => { |
23 | const options = [ '-j', '--flat-playlist' ] | 23 | const options = opts || [ '-j', '--flat-playlist' ] |
24 | 24 | ||
25 | const youtubeDL = await safeGetYoutubeDL() | 25 | const youtubeDL = await safeGetYoutubeDL() |
26 | youtubeDL.getInfo(url, options, (err, info) => { | 26 | youtubeDL.getInfo(url, options, (err, info) => { |
@@ -110,16 +110,6 @@ async function updateYoutubeDLBinary () { | |||
110 | }) | 110 | }) |
111 | } | 111 | } |
112 | 112 | ||
113 | // --------------------------------------------------------------------------- | ||
114 | |||
115 | export { | ||
116 | updateYoutubeDLBinary, | ||
117 | downloadYoutubeDLVideo, | ||
118 | getYoutubeDLInfo | ||
119 | } | ||
120 | |||
121 | // --------------------------------------------------------------------------- | ||
122 | |||
123 | async function safeGetYoutubeDL () { | 113 | async function safeGetYoutubeDL () { |
124 | let youtubeDL | 114 | let youtubeDL |
125 | 115 | ||
@@ -134,6 +124,17 @@ async function safeGetYoutubeDL () { | |||
134 | return youtubeDL | 124 | return youtubeDL |
135 | } | 125 | } |
136 | 126 | ||
127 | // --------------------------------------------------------------------------- | ||
128 | |||
129 | export { | ||
130 | updateYoutubeDLBinary, | ||
131 | downloadYoutubeDLVideo, | ||
132 | getYoutubeDLInfo, | ||
133 | safeGetYoutubeDL | ||
134 | } | ||
135 | |||
136 | // --------------------------------------------------------------------------- | ||
137 | |||
137 | function normalizeObject (obj: any) { | 138 | function normalizeObject (obj: any) { |
138 | const newObj: any = {} | 139 | const newObj: any = {} |
139 | 140 | ||
diff --git a/server/tests/cli/index.ts b/server/tests/cli/index.ts index f99eafe03..33e33a070 100644 --- a/server/tests/cli/index.ts +++ b/server/tests/cli/index.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | // Order of the tests we want to execute | 1 | // Order of the tests we want to execute |
2 | import './create-transcoding-job' | 2 | import './create-transcoding-job' |
3 | import './create-import-video-file-job' | 3 | import './create-import-video-file-job' |
4 | import './peertube' | ||
4 | import './reset-password' | 5 | import './reset-password' |
5 | import './update-host' | 6 | import './update-host' |
diff --git a/server/tests/cli/peertube.ts b/server/tests/cli/peertube.ts new file mode 100644 index 000000000..548fd1257 --- /dev/null +++ b/server/tests/cli/peertube.ts | |||
@@ -0,0 +1,51 @@ | |||
1 | import 'mocha' | ||
2 | import { | ||
3 | expect | ||
4 | } from 'chai' | ||
5 | import { | ||
6 | createUser, | ||
7 | execCLI, | ||
8 | flushTests, | ||
9 | getEnvCli, | ||
10 | killallServers, | ||
11 | runServer, | ||
12 | ServerInfo, | ||
13 | setAccessTokensToServers | ||
14 | } from '../utils' | ||
15 | |||
16 | describe('Test CLI wrapper', function () { | ||
17 | let server: ServerInfo | ||
18 | const cmd = 'node ./dist/server/tools/peertube.js' | ||
19 | |||
20 | before(async function () { | ||
21 | this.timeout(30000) | ||
22 | |||
23 | await flushTests() | ||
24 | server = await runServer(1) | ||
25 | await setAccessTokensToServers([ server ]) | ||
26 | |||
27 | await createUser(server.url, server.accessToken, 'user_1', 'super password') | ||
28 | }) | ||
29 | |||
30 | it('Should display no selected instance', async function () { | ||
31 | this.timeout(60000) | ||
32 | |||
33 | const env = getEnvCli(server) | ||
34 | const stdout = await execCLI(`${env} ${cmd} --help`) | ||
35 | |||
36 | expect(stdout).to.contain('selected') | ||
37 | }) | ||
38 | |||
39 | it('Should remember the authentifying material of the user', async function () { | ||
40 | this.timeout(60000) | ||
41 | |||
42 | const env = getEnvCli(server) | ||
43 | const stdout = await execCLI(`${env} ` + cmd + ` auth add --url ${server.url} -U user_1 -p "super password"`) | ||
44 | }) | ||
45 | |||
46 | after(async function () { | ||
47 | await execCLI(cmd + ` auth del ${server.url}`) | ||
48 | |||
49 | killallServers([ server ]) | ||
50 | }) | ||
51 | }) | ||
diff --git a/server/tools/cli.ts b/server/tools/cli.ts new file mode 100644 index 000000000..9a170d4da --- /dev/null +++ b/server/tools/cli.ts | |||
@@ -0,0 +1,63 @@ | |||
1 | const config = require('application-config')('PeerTube/CLI') | ||
2 | const netrc = require('netrc-parser').default | ||
3 | |||
4 | const version = () => { | ||
5 | const tag = require('child_process') | ||
6 | .execSync('[[ ! -d .git ]] || git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || true', { stdio: [0,1,2] }) | ||
7 | if (tag) return tag | ||
8 | |||
9 | const version = require('child_process') | ||
10 | .execSync('[[ ! -d .git ]] || git rev-parse --short HEAD').toString().trim() | ||
11 | if (version) return version | ||
12 | |||
13 | return require('../../../package.json').version | ||
14 | } | ||
15 | |||
16 | let settings = { | ||
17 | remotes: [], | ||
18 | default: 0 | ||
19 | } | ||
20 | |||
21 | interface Settings { | ||
22 | remotes: any[], | ||
23 | default: number | ||
24 | } | ||
25 | |||
26 | async function getSettings () { | ||
27 | return new Promise<Settings>((res, rej) => { | ||
28 | let settings = { | ||
29 | remotes: [], | ||
30 | default: 0 | ||
31 | } as Settings | ||
32 | config.read((err, data) => { | ||
33 | if (err) { | ||
34 | return rej(err) | ||
35 | } | ||
36 | return res(data || settings) | ||
37 | }) | ||
38 | }) | ||
39 | } | ||
40 | |||
41 | async function writeSettings (settings) { | ||
42 | return new Promise((res, rej) => { | ||
43 | config.write(settings, function (err) { | ||
44 | if (err) { | ||
45 | return rej(err) | ||
46 | } | ||
47 | return res() | ||
48 | }) | ||
49 | }) | ||
50 | } | ||
51 | |||
52 | netrc.loadSync() | ||
53 | |||
54 | // --------------------------------------------------------------------------- | ||
55 | |||
56 | export { | ||
57 | version, | ||
58 | config, | ||
59 | settings, | ||
60 | getSettings, | ||
61 | writeSettings, | ||
62 | netrc | ||
63 | } | ||
diff --git a/server/tools/peertube-auth.ts b/server/tools/peertube-auth.ts new file mode 100644 index 000000000..33438811e --- /dev/null +++ b/server/tools/peertube-auth.ts | |||
@@ -0,0 +1,140 @@ | |||
1 | import * as program from 'commander' | ||
2 | import * as prompt from 'prompt' | ||
3 | const Table = require('cli-table') | ||
4 | import { getSettings, writeSettings, netrc } from './cli' | ||
5 | import { isHostValid } from '../helpers/custom-validators/servers' | ||
6 | import { isUserUsernameValid } from '../helpers/custom-validators/users' | ||
7 | |||
8 | function delInstance (url: string) { | ||
9 | return new Promise((res, rej): void => { | ||
10 | getSettings() | ||
11 | .then(async (settings) => { | ||
12 | settings.remotes.splice(settings.remotes.indexOf(url)) | ||
13 | await writeSettings(settings) | ||
14 | delete netrc.machines[url] | ||
15 | netrc.save() | ||
16 | res() | ||
17 | }) | ||
18 | .catch(err => rej(err)) | ||
19 | }) | ||
20 | } | ||
21 | |||
22 | async function setInstance (url: string, username: string, password: string) { | ||
23 | return new Promise((res, rej): void => { | ||
24 | getSettings() | ||
25 | .then(async settings => { | ||
26 | if (settings.remotes.indexOf(url) === -1) { | ||
27 | settings.remotes.push(url) | ||
28 | } | ||
29 | await writeSettings(settings) | ||
30 | netrc.machines[url] = { login: username, password } | ||
31 | netrc.save() | ||
32 | res() | ||
33 | }) | ||
34 | .catch(err => rej(err)) | ||
35 | }) | ||
36 | } | ||
37 | |||
38 | function isURLaPeerTubeInstance (url: string) { | ||
39 | return isHostValid(url) || (url.includes('localhost')) | ||
40 | } | ||
41 | |||
42 | program | ||
43 | .name('auth') | ||
44 | .usage('[command] [options]') | ||
45 | |||
46 | program | ||
47 | .command('add') | ||
48 | .description('remember your accounts on remote instances for easier use') | ||
49 | .option('-u, --url <url>', 'Server url') | ||
50 | .option('-U, --username <username>', 'Username') | ||
51 | .option('-p, --password <token>', 'Password') | ||
52 | .option('--default', 'add the entry as the new default') | ||
53 | .action(options => { | ||
54 | prompt.override = options | ||
55 | prompt.start() | ||
56 | prompt.get({ | ||
57 | properties: { | ||
58 | url: { | ||
59 | description: 'instance url', | ||
60 | conform: (value) => isURLaPeerTubeInstance(value), | ||
61 | required: true | ||
62 | }, | ||
63 | username: { | ||
64 | conform: (value) => isUserUsernameValid(value), | ||
65 | message: 'Name must be only letters, spaces, or dashes', | ||
66 | required: true | ||
67 | }, | ||
68 | password: { | ||
69 | hidden: true, | ||
70 | replace: '*', | ||
71 | required: true | ||
72 | } | ||
73 | } | ||
74 | }, (_, result) => { | ||
75 | setInstance(result.url, result.username, result.password) | ||
76 | }) | ||
77 | }) | ||
78 | |||
79 | program | ||
80 | .command('del <url>') | ||
81 | .description('unregisters a remote instance') | ||
82 | .action((url) => { | ||
83 | delInstance(url) | ||
84 | }) | ||
85 | |||
86 | program | ||
87 | .command('list') | ||
88 | .description('lists registered remote instances') | ||
89 | .action(() => { | ||
90 | getSettings() | ||
91 | .then(settings => { | ||
92 | const table = new Table({ | ||
93 | head: ['instance', 'login'], | ||
94 | colWidths: [30, 30] | ||
95 | }) | ||
96 | netrc.loadSync() | ||
97 | settings.remotes.forEach(element => { | ||
98 | table.push([ | ||
99 | element, | ||
100 | netrc.machines[element].login | ||
101 | ]) | ||
102 | }) | ||
103 | |||
104 | console.log(table.toString()) | ||
105 | }) | ||
106 | }) | ||
107 | |||
108 | program | ||
109 | .command('set-default <url>') | ||
110 | .description('set an existing entry as default') | ||
111 | .action((url) => { | ||
112 | getSettings() | ||
113 | .then(settings => { | ||
114 | const instanceExists = settings.remotes.indexOf(url) !== -1 | ||
115 | |||
116 | if (instanceExists) { | ||
117 | settings.default = settings.remotes.indexOf(url) | ||
118 | writeSettings(settings) | ||
119 | } else { | ||
120 | console.log('<url> is not a registered instance.') | ||
121 | process.exit(-1) | ||
122 | } | ||
123 | }) | ||
124 | }) | ||
125 | |||
126 | program.on('--help', function () { | ||
127 | console.log(' Examples:') | ||
128 | console.log() | ||
129 | console.log(' $ peertube add -u peertube.cpy.re -U "PEERTUBE_USER" --password "PEERTUBE_PASSWORD"') | ||
130 | console.log(' $ peertube add -u peertube.cpy.re -U root') | ||
131 | console.log(' $ peertube list') | ||
132 | console.log(' $ peertube del peertube.cpy.re') | ||
133 | console.log() | ||
134 | }) | ||
135 | |||
136 | if (!process.argv.slice(2).length) { | ||
137 | program.outputHelp() | ||
138 | } | ||
139 | |||
140 | program.parse(process.argv) | ||
diff --git a/server/tools/get-access-token.ts b/server/tools/peertube-get-access-token.ts index d86c84c8d..eb2571a03 100644 --- a/server/tools/get-access-token.ts +++ b/server/tools/peertube-get-access-token.ts | |||
@@ -19,7 +19,10 @@ if ( | |||
19 | !program['username'] || | 19 | !program['username'] || |
20 | !program['password'] | 20 | !program['password'] |
21 | ) { | 21 | ) { |
22 | throw new Error('All arguments are required.') | 22 | if (!program['url']) console.error('--url field is required.') |
23 | if (!program['username']) console.error('--username field is required.') | ||
24 | if (!program['password']) console.error('--password field is required.') | ||
25 | process.exit(-1) | ||
23 | } | 26 | } |
24 | 27 | ||
25 | getClient(program.url) | 28 | getClient(program.url) |
diff --git a/server/tools/import-videos.ts b/server/tools/peertube-import-videos.ts index 3ff194c83..13090a028 100644 --- a/server/tools/import-videos.ts +++ b/server/tools/peertube-import-videos.ts | |||
@@ -3,7 +3,6 @@ require('tls').DEFAULT_ECDH_CURVE = 'auto' | |||
3 | 3 | ||
4 | import * as program from 'commander' | 4 | import * as program from 'commander' |
5 | import { join } from 'path' | 5 | import { join } from 'path' |
6 | import * as youtubeDL from 'youtube-dl' | ||
7 | import { VideoPrivacy } from '../../shared/models/videos' | 6 | import { VideoPrivacy } from '../../shared/models/videos' |
8 | import { doRequestAndSaveToFile } from '../helpers/requests' | 7 | import { doRequestAndSaveToFile } from '../helpers/requests' |
9 | import { CONSTRAINTS_FIELDS } from '../initializers' | 8 | import { CONSTRAINTS_FIELDS } from '../initializers' |
@@ -11,8 +10,19 @@ import { getClient, getVideoCategories, login, searchVideoWithSort, uploadVideo | |||
11 | import { truncate } from 'lodash' | 10 | import { truncate } from 'lodash' |
12 | import * as prompt from 'prompt' | 11 | import * as prompt from 'prompt' |
13 | import { remove } from 'fs-extra' | 12 | import { remove } from 'fs-extra' |
13 | import { safeGetYoutubeDL } from '../helpers/youtube-dl' | ||
14 | import { getSettings, netrc } from './cli' | ||
15 | |||
16 | let accessToken: string | ||
17 | let client: { id: string, secret: string } | ||
18 | |||
19 | const processOptions = { | ||
20 | cwd: __dirname, | ||
21 | maxBuffer: Infinity | ||
22 | } | ||
14 | 23 | ||
15 | program | 24 | program |
25 | .name('import-videos') | ||
16 | .option('-u, --url <url>', 'Server url') | 26 | .option('-u, --url <url>', 'Server url') |
17 | .option('-U, --username <username>', 'Username') | 27 | .option('-U, --username <username>', 'Username') |
18 | .option('-p, --password <token>', 'Password') | 28 | .option('-p, --password <token>', 'Password') |
@@ -21,29 +31,50 @@ program | |||
21 | .option('-v, --verbose', 'Verbose mode') | 31 | .option('-v, --verbose', 'Verbose mode') |
22 | .parse(process.argv) | 32 | .parse(process.argv) |
23 | 33 | ||
24 | if ( | 34 | getSettings() |
25 | !program['url'] || | 35 | .then(settings => { |
26 | !program['username'] || | 36 | if ( |
27 | !program['targetUrl'] | 37 | (!program['url'] || |
28 | ) { | 38 | !program['username'] || |
29 | console.error('All arguments are required.') | 39 | !program['password']) && |
30 | process.exit(-1) | 40 | (settings.remotes.length === 0) |
31 | } | 41 | ) { |
42 | if (!program['url']) console.error('--url field is required.') | ||
43 | if (!program['username']) console.error('--username field is required.') | ||
44 | if (!program['password']) console.error('--password field is required.') | ||
45 | if (!program['targetUrl']) console.error('--targetUrl field is required.') | ||
46 | process.exit(-1) | ||
47 | } | ||
32 | 48 | ||
33 | const user = { | 49 | if ( |
34 | username: program['username'], | 50 | (!program['url'] || |
35 | password: program['password'] | 51 | !program['username'] || |
36 | } | 52 | !program['password']) && |
53 | (settings.remotes.length > 0) | ||
54 | ) { | ||
55 | if (!program['url']) { | ||
56 | program['url'] = (settings.default !== -1) ? | ||
57 | settings.remotes[settings.default] : | ||
58 | settings.remotes[0] | ||
59 | } | ||
60 | if (!program['username']) program['username'] = netrc.machines[program['url']].login | ||
61 | if (!program['password']) program['password'] = netrc.machines[program['url']].password | ||
62 | } | ||
37 | 63 | ||
38 | run().catch(err => console.error(err)) | 64 | if ( |
65 | !program['targetUrl'] | ||
66 | ) { | ||
67 | if (!program['targetUrl']) console.error('--targetUrl field is required.') | ||
68 | process.exit(-1) | ||
69 | } | ||
39 | 70 | ||
40 | let accessToken: string | 71 | const user = { |
41 | let client: { id: string, secret: string } | 72 | username: program['username'], |
73 | password: program['password'] | ||
74 | } | ||
42 | 75 | ||
43 | const processOptions = { | 76 | run(user, program['url']).catch(err => console.error(err)) |
44 | cwd: __dirname, | 77 | }) |
45 | maxBuffer: Infinity | ||
46 | } | ||
47 | 78 | ||
48 | async function promptPassword () { | 79 | async function promptPassword () { |
49 | return new Promise((res, rej) => { | 80 | return new Promise((res, rej) => { |
@@ -65,20 +96,22 @@ async function promptPassword () { | |||
65 | }) | 96 | }) |
66 | } | 97 | } |
67 | 98 | ||
68 | async function run () { | 99 | async function run (user, url: string) { |
69 | if (!user.password) { | 100 | if (!user.password) { |
70 | user.password = await promptPassword() | 101 | user.password = await promptPassword() |
71 | } | 102 | } |
72 | 103 | ||
73 | const res = await getClient(program['url']) | 104 | const res = await getClient(url) |
74 | client = { | 105 | client = { |
75 | id: res.body.client_id, | 106 | id: res.body.client_id, |
76 | secret: res.body.client_secret | 107 | secret: res.body.client_secret |
77 | } | 108 | } |
78 | 109 | ||
79 | const res2 = await login(program['url'], client, user) | 110 | const res2 = await login(url, client, user) |
80 | accessToken = res2.body.access_token | 111 | accessToken = res2.body.access_token |
81 | 112 | ||
113 | const youtubeDL = await safeGetYoutubeDL() | ||
114 | |||
82 | const options = [ '-j', '--flat-playlist', '--playlist-reverse' ] | 115 | const options = [ '-j', '--flat-playlist', '--playlist-reverse' ] |
83 | youtubeDL.getInfo(program['targetUrl'], options, processOptions, async (err, info) => { | 116 | youtubeDL.getInfo(program['targetUrl'], options, processOptions, async (err, info) => { |
84 | if (err) { | 117 | if (err) { |
@@ -97,7 +130,7 @@ async function run () { | |||
97 | console.log('Will download and upload %d videos.\n', infoArray.length) | 130 | console.log('Will download and upload %d videos.\n', infoArray.length) |
98 | 131 | ||
99 | for (const info of infoArray) { | 132 | for (const info of infoArray) { |
100 | await processVideo(info, program['language']) | 133 | await processVideo(info, program['language'], processOptions.cwd, url, user) |
101 | } | 134 | } |
102 | 135 | ||
103 | // https://www.youtube.com/watch?v=2Upx39TBc1s | 136 | // https://www.youtube.com/watch?v=2Upx39TBc1s |
@@ -106,14 +139,14 @@ async function run () { | |||
106 | }) | 139 | }) |
107 | } | 140 | } |
108 | 141 | ||
109 | function processVideo (info: any, languageCode: string) { | 142 | function processVideo (info: any, languageCode: string, cwd: string, url: string, user) { |
110 | return new Promise(async res => { | 143 | return new Promise(async res => { |
111 | if (program['verbose']) console.log('Fetching object.', info) | 144 | if (program['verbose']) console.log('Fetching object.', info) |
112 | 145 | ||
113 | const videoInfo = await fetchObject(info) | 146 | const videoInfo = await fetchObject(info) |
114 | if (program['verbose']) console.log('Fetched object.', videoInfo) | 147 | if (program['verbose']) console.log('Fetched object.', videoInfo) |
115 | 148 | ||
116 | const result = await searchVideoWithSort(program['url'], videoInfo.title, '-match') | 149 | const result = await searchVideoWithSort(url, videoInfo.title, '-match') |
117 | 150 | ||
118 | console.log('############################################################\n') | 151 | console.log('############################################################\n') |
119 | 152 | ||
@@ -122,12 +155,13 @@ function processVideo (info: any, languageCode: string) { | |||
122 | return res() | 155 | return res() |
123 | } | 156 | } |
124 | 157 | ||
125 | const path = join(__dirname, new Date().getTime() + '.mp4') | 158 | const path = join(cwd, new Date().getTime() + '.mp4') |
126 | 159 | ||
127 | console.log('Downloading video "%s"...', videoInfo.title) | 160 | console.log('Downloading video "%s"...', videoInfo.title) |
128 | 161 | ||
129 | const options = [ '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best', '-o', path ] | 162 | const options = [ '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best', '-o', path ] |
130 | try { | 163 | try { |
164 | const youtubeDL = await safeGetYoutubeDL() | ||
131 | youtubeDL.exec(videoInfo.url, options, processOptions, async (err, output) => { | 165 | youtubeDL.exec(videoInfo.url, options, processOptions, async (err, output) => { |
132 | if (err) { | 166 | if (err) { |
133 | console.error(err) | 167 | console.error(err) |
@@ -135,7 +169,7 @@ function processVideo (info: any, languageCode: string) { | |||
135 | } | 169 | } |
136 | 170 | ||
137 | console.log(output.join('\n')) | 171 | console.log(output.join('\n')) |
138 | await uploadVideoOnPeerTube(normalizeObject(videoInfo), path, languageCode) | 172 | await uploadVideoOnPeerTube(normalizeObject(videoInfo), path, cwd, url, user, languageCode) |
139 | return res() | 173 | return res() |
140 | }) | 174 | }) |
141 | } catch (err) { | 175 | } catch (err) { |
@@ -145,8 +179,8 @@ function processVideo (info: any, languageCode: string) { | |||
145 | }) | 179 | }) |
146 | } | 180 | } |
147 | 181 | ||
148 | async function uploadVideoOnPeerTube (videoInfo: any, videoPath: string, language?: string) { | 182 | async function uploadVideoOnPeerTube (videoInfo: any, videoPath: string, cwd: string, url: string, user, language?: string) { |
149 | const category = await getCategory(videoInfo.categories) | 183 | const category = await getCategory(videoInfo.categories, url) |
150 | const licence = getLicence(videoInfo.license) | 184 | const licence = getLicence(videoInfo.license) |
151 | let tags = [] | 185 | let tags = [] |
152 | if (Array.isArray(videoInfo.tags)) { | 186 | if (Array.isArray(videoInfo.tags)) { |
@@ -158,7 +192,7 @@ async function uploadVideoOnPeerTube (videoInfo: any, videoPath: string, languag | |||
158 | 192 | ||
159 | let thumbnailfile | 193 | let thumbnailfile |
160 | if (videoInfo.thumbnail) { | 194 | if (videoInfo.thumbnail) { |
161 | thumbnailfile = join(__dirname, 'thumbnail.jpg') | 195 | thumbnailfile = join(cwd, 'thumbnail.jpg') |
162 | 196 | ||
163 | await doRequestAndSaveToFile({ | 197 | await doRequestAndSaveToFile({ |
164 | method: 'GET', | 198 | method: 'GET', |
@@ -189,15 +223,15 @@ async function uploadVideoOnPeerTube (videoInfo: any, videoPath: string, languag | |||
189 | 223 | ||
190 | console.log('\nUploading on PeerTube video "%s".', videoAttributes.name) | 224 | console.log('\nUploading on PeerTube video "%s".', videoAttributes.name) |
191 | try { | 225 | try { |
192 | await uploadVideo(program['url'], accessToken, videoAttributes) | 226 | await uploadVideo(url, accessToken, videoAttributes) |
193 | } catch (err) { | 227 | } catch (err) { |
194 | if (err.message.indexOf('401') !== -1) { | 228 | if (err.message.indexOf('401') !== -1) { |
195 | console.log('Got 401 Unauthorized, token may have expired, renewing token and retry.') | 229 | console.log('Got 401 Unauthorized, token may have expired, renewing token and retry.') |
196 | 230 | ||
197 | const res = await login(program['url'], client, user) | 231 | const res = await login(url, client, user) |
198 | accessToken = res.body.access_token | 232 | accessToken = res.body.access_token |
199 | 233 | ||
200 | await uploadVideo(program['url'], accessToken, videoAttributes) | 234 | await uploadVideo(url, accessToken, videoAttributes) |
201 | } else { | 235 | } else { |
202 | console.log(err.message) | 236 | console.log(err.message) |
203 | process.exit(1) | 237 | process.exit(1) |
@@ -210,14 +244,14 @@ async function uploadVideoOnPeerTube (videoInfo: any, videoPath: string, languag | |||
210 | console.log('Uploaded video "%s"!\n', videoAttributes.name) | 244 | console.log('Uploaded video "%s"!\n', videoAttributes.name) |
211 | } | 245 | } |
212 | 246 | ||
213 | async function getCategory (categories: string[]) { | 247 | async function getCategory (categories: string[], url: string) { |
214 | if (!categories) return undefined | 248 | if (!categories) return undefined |
215 | 249 | ||
216 | const categoryString = categories[0] | 250 | const categoryString = categories[0] |
217 | 251 | ||
218 | if (categoryString === 'News & Politics') return 11 | 252 | if (categoryString === 'News & Politics') return 11 |
219 | 253 | ||
220 | const res = await getVideoCategories(program['url']) | 254 | const res = await getVideoCategories(url) |
221 | const categoriesServer = res.body | 255 | const categoriesServer = res.body |
222 | 256 | ||
223 | for (const key of Object.keys(categoriesServer)) { | 257 | for (const key of Object.keys(categoriesServer)) { |
@@ -228,6 +262,8 @@ async function getCategory (categories: string[]) { | |||
228 | return undefined | 262 | return undefined |
229 | } | 263 | } |
230 | 264 | ||
265 | /* ---------------------------------------------------------- */ | ||
266 | |||
231 | function getLicence (licence: string) { | 267 | function getLicence (licence: string) { |
232 | if (!licence) return undefined | 268 | if (!licence) return undefined |
233 | 269 | ||
@@ -259,6 +295,7 @@ function fetchObject (info: any) { | |||
259 | const url = buildUrl(info) | 295 | const url = buildUrl(info) |
260 | 296 | ||
261 | return new Promise<any>(async (res, rej) => { | 297 | return new Promise<any>(async (res, rej) => { |
298 | const youtubeDL = await safeGetYoutubeDL() | ||
262 | youtubeDL.getInfo(url, undefined, processOptions, async (err, videoInfo) => { | 299 | youtubeDL.getInfo(url, undefined, processOptions, async (err, videoInfo) => { |
263 | if (err) return rej(err) | 300 | if (err) return rej(err) |
264 | 301 | ||
diff --git a/server/tools/upload.ts b/server/tools/peertube-upload.ts index 9b104d308..1f871e660 100644 --- a/server/tools/upload.ts +++ b/server/tools/peertube-upload.ts | |||
@@ -4,18 +4,20 @@ import { isAbsolute } from 'path' | |||
4 | import { getClient, login } from '../tests/utils' | 4 | import { getClient, login } from '../tests/utils' |
5 | import { uploadVideo } from '../tests/utils/index' | 5 | import { uploadVideo } from '../tests/utils/index' |
6 | import { VideoPrivacy } from '../../shared/models/videos' | 6 | import { VideoPrivacy } from '../../shared/models/videos' |
7 | import { netrc, getSettings } from './cli' | ||
7 | 8 | ||
8 | program | 9 | program |
10 | .name('upload') | ||
9 | .option('-u, --url <url>', 'Server url') | 11 | .option('-u, --url <url>', 'Server url') |
10 | .option('-U, --username <username>', 'Username') | 12 | .option('-U, --username <username>', 'Username') |
11 | .option('-p, --password <token>', 'Password') | 13 | .option('-p, --password <token>', 'Password') |
12 | .option('-n, --video-name <name>', 'Video name') | 14 | .option('-n, --video-name <name>', 'Video name') |
13 | .option('-P, --privacy <privacy number>', 'Privacy') | 15 | .option('-P, --privacy <privacy_number>', 'Privacy') |
14 | .option('-N, --nsfw', 'Video is Not Safe For Work') | 16 | .option('-N, --nsfw', 'Video is Not Safe For Work') |
15 | .option('-c, --category <category number>', 'Category number') | 17 | .option('-c, --category <category_number>', 'Category number') |
16 | .option('-m, --comments-enabled', 'Enable comments') | 18 | .option('-m, --comments-enabled', 'Enable comments') |
17 | .option('-l, --licence <licence number>', 'Licence number') | 19 | .option('-l, --licence <licence_number>', 'Licence number') |
18 | .option('-L, --language <language code>', 'Language ISO 639 code (fr or en...)') | 20 | .option('-L, --language <language_code>', 'Language ISO 639 code (fr or en...)') |
19 | .option('-d, --video-description <description>', 'Video description') | 21 | .option('-d, --video-description <description>', 'Video description') |
20 | .option('-t, --tags <tags>', 'Video tags', list) | 22 | .option('-t, --tags <tags>', 'Video tags', list) |
21 | .option('-b, --thumbnail <thumbnailPath>', 'Thumbnail path') | 23 | .option('-b, --thumbnail <thumbnailPath>', 'Thumbnail path') |
@@ -28,27 +30,53 @@ if (!program['nsfw']) program['nsfw'] = false | |||
28 | if (!program['privacy']) program['privacy'] = VideoPrivacy.PUBLIC | 30 | if (!program['privacy']) program['privacy'] = VideoPrivacy.PUBLIC |
29 | if (!program['commentsEnabled']) program['commentsEnabled'] = false | 31 | if (!program['commentsEnabled']) program['commentsEnabled'] = false |
30 | 32 | ||
31 | if ( | 33 | getSettings() |
32 | !program['url'] || | 34 | .then(settings => { |
33 | !program['username'] || | 35 | if ( |
34 | !program['password'] || | 36 | (!program['url'] || |
35 | !program['videoName'] || | 37 | !program['username'] || |
36 | !program['file'] | 38 | !program['password']) && |
37 | ) { | 39 | (settings.remotes.length === 0) |
38 | if (!program['url']) console.error('--url field is required.') | 40 | ) { |
39 | if (!program['username']) console.error('--username field is required.') | 41 | if (!program['url']) console.error('--url field is required.') |
40 | if (!program['password']) console.error('--password field is required.') | 42 | if (!program['username']) console.error('--username field is required.') |
41 | if (!program['videoName']) console.error('--video-name field is required.') | 43 | if (!program['password']) console.error('--password field is required.') |
42 | if (!program['file']) console.error('--file field is required.') | 44 | if (!program['videoName']) console.error('--video-name field is required.') |
43 | process.exit(-1) | 45 | if (!program['file']) console.error('--file field is required.') |
44 | } | 46 | process.exit(-1) |
47 | } | ||
45 | 48 | ||
46 | if (isAbsolute(program['file']) === false) { | 49 | if ( |
47 | console.error('File path should be absolute.') | 50 | (!program['url'] || |
48 | process.exit(-1) | 51 | !program['username'] || |
49 | } | 52 | !program['password']) && |
53 | (settings.remotes.length > 0) | ||
54 | ) { | ||
55 | if (!program['url']) { | ||
56 | program['url'] = (settings.default !== -1) ? | ||
57 | settings.remotes[settings.default] : | ||
58 | settings.remotes[0] | ||
59 | } | ||
60 | if (!program['username']) program['username'] = netrc.machines[program['url']].login | ||
61 | if (!program['password']) program['password'] = netrc.machines[program['url']].password | ||
62 | } | ||
63 | |||
64 | if ( | ||
65 | !program['videoName'] || | ||
66 | !program['file'] | ||
67 | ) { | ||
68 | if (!program['videoName']) console.error('--video-name field is required.') | ||
69 | if (!program['file']) console.error('--file field is required.') | ||
70 | process.exit(-1) | ||
71 | } | ||
72 | |||
73 | if (isAbsolute(program['file']) === false) { | ||
74 | console.error('File path should be absolute.') | ||
75 | process.exit(-1) | ||
76 | } | ||
50 | 77 | ||
51 | run().catch(err => console.error(err)) | 78 | run().catch(err => console.error(err)) |
79 | }) | ||
52 | 80 | ||
53 | async function run () { | 81 | async function run () { |
54 | const res = await getClient(program[ 'url' ]) | 82 | const res = await getClient(program[ 'url' ]) |
diff --git a/server/tools/peertube-watch.ts b/server/tools/peertube-watch.ts new file mode 100644 index 000000000..bf7274aab --- /dev/null +++ b/server/tools/peertube-watch.ts | |||
@@ -0,0 +1,61 @@ | |||
1 | import * as program from 'commander' | ||
2 | import * as summon from 'summon-install' | ||
3 | import { join } from 'path' | ||
4 | import { execSync } from 'child_process' | ||
5 | import { root } from '../helpers/core-utils' | ||
6 | |||
7 | let videoURL | ||
8 | |||
9 | program | ||
10 | .name('watch') | ||
11 | .arguments('<url>') | ||
12 | .option('-g, --gui <player>', 'player type', /^(airplay|stdout|chromecast|mpv|vlc|mplayer|ascii|xbmc)$/i, 'ascii') | ||
13 | .option('-i, --invert', 'invert colors (ascii player only)', true) | ||
14 | .option('-r, --resolution <res>', 'video resolution', /^(240|360|720|1080)$/i, '720') | ||
15 | .on('--help', function () { | ||
16 | console.log(' Available Players:') | ||
17 | console.log() | ||
18 | console.log(' - ascii') | ||
19 | console.log(' - mpv') | ||
20 | console.log(' - mplayer') | ||
21 | console.log(' - vlc') | ||
22 | console.log(' - stdout') | ||
23 | console.log(' - xbmc') | ||
24 | console.log(' - airplay') | ||
25 | console.log(' - chromecast') | ||
26 | console.log() | ||
27 | console.log(' Note: \'ascii\' is the only option not using WebTorrent and not seeding back the video.') | ||
28 | console.log() | ||
29 | console.log(' Examples:') | ||
30 | console.log() | ||
31 | console.log(' $ peertube watch -g mpv https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10') | ||
32 | console.log(' $ peertube watch --gui stdout https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10') | ||
33 | console.log(' $ peertube watch https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10') | ||
34 | console.log() | ||
35 | }) | ||
36 | .action((url) => { | ||
37 | videoURL = url | ||
38 | }) | ||
39 | .parse(process.argv) | ||
40 | |||
41 | if (!videoURL) { | ||
42 | console.error('<url> positional argument is required.') | ||
43 | process.exit(-1) | ||
44 | } else { program['url'] = videoURL } | ||
45 | |||
46 | handler(program) | ||
47 | |||
48 | function handler (argv) { | ||
49 | if (argv['gui'] === 'ascii') { | ||
50 | summon('peerterminal') | ||
51 | const peerterminal = summon('peerterminal') | ||
52 | peerterminal([ '--link', videoURL, '--invert', argv['invert'] ]) | ||
53 | } else { | ||
54 | summon('webtorrent-hybrid') | ||
55 | const CMD = 'node ' + join(root(), 'node_modules', 'webtorrent-hybrid', 'bin', 'cmd.js') | ||
56 | const CMDargs = ` --${argv.gui} ` + | ||
57 | argv['url'].replace('videos/watch', 'download/torrents') + | ||
58 | `-${argv.resolution}.torrent` | ||
59 | execSync(CMD + CMDargs) | ||
60 | } | ||
61 | } | ||
diff --git a/server/tools/peertube.ts b/server/tools/peertube.ts new file mode 100755 index 000000000..7441161b1 --- /dev/null +++ b/server/tools/peertube.ts | |||
@@ -0,0 +1,81 @@ | |||
1 | #!/usr/bin/env node | ||
2 | |||
3 | import * as program from 'commander' | ||
4 | import { | ||
5 | version, | ||
6 | getSettings | ||
7 | } from './cli' | ||
8 | |||
9 | program | ||
10 | .version(version(), '-v, --version') | ||
11 | .usage('[command] [options]') | ||
12 | |||
13 | /* Subcommands automatically loaded in the directory and beginning by peertube-* */ | ||
14 | program | ||
15 | .command('auth [action]', 'register your accounts on remote instances to use them with other commands') | ||
16 | .command('upload', 'upload a video').alias('up') | ||
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') | ||
19 | .command('watch', 'watch a video in the terminal ✩°。⋆').alias('w') | ||
20 | |||
21 | /* Not Yet Implemented */ | ||
22 | program | ||
23 | .command('plugins [action]', | ||
24 | 'manage plugins on a local instance', | ||
25 | { noHelp: true } as program.CommandOptions | ||
26 | ).alias('p') | ||
27 | .command('diagnostic [action]', | ||
28 | 'like couple therapy, but for your instance', | ||
29 | { noHelp: true } as program.CommandOptions | ||
30 | ).alias('d') | ||
31 | .command('admin', | ||
32 | 'manage an instance where you have elevated rights', | ||
33 | { noHelp: true } as program.CommandOptions | ||
34 | ).alias('a') | ||
35 | |||
36 | // help on no command | ||
37 | if (!process.argv.slice(2).length) { | ||
38 | const logo = '░P░e░e░r░T░u░b░e░' | ||
39 | console.log(` | ||
40 | ___/),.._ ` + logo + ` | ||
41 | /' ,. ."'._ | ||
42 | ( "' '-.__"-._ ,- | ||
43 | \\'='='), "\\ -._-"-. -"/ | ||
44 | / ""/"\\,_\\,__"" _" /,- | ||
45 | / / -" _/"/ | ||
46 | / | ._\\\\ |\\ |_.".-" / | ||
47 | / | __\\)|)|),/|_." _,." | ||
48 | / \_." " ") | ).-""---''-- | ||
49 | ( "/.""7__-""'' | ||
50 | | " ."._--._ | ||
51 | \\ \\ (_ __ "" ".,_ | ||
52 | \\.,. \\ "" -"".-" | ||
53 | ".,_, (",_-,,,-".- | ||
54 | "'-,\\_ __,-" | ||
55 | ",)" ") | ||
56 | /"\\-" | ||
57 | ,"\\/ | ||
58 | _,.__/"\\/_ (the CLI for red chocobos) | ||
59 | / \\) "./, ". | ||
60 | --/---"---" "-) )---- by Chocobozzz et al.`) | ||
61 | } | ||
62 | |||
63 | getSettings() | ||
64 | .then(settings => { | ||
65 | const state = (settings.default === -1) ? | ||
66 | 'no instance selected, commands will require explicit arguments' : | ||
67 | ('instance ' + settings.remotes[settings.default] + ' selected') | ||
68 | program | ||
69 | .on('--help', function () { | ||
70 | console.log() | ||
71 | console.log(' State: ' + state) | ||
72 | console.log() | ||
73 | console.log(' Examples:') | ||
74 | console.log() | ||
75 | console.log(' $ peertube auth add -u "PEERTUBE_URL" -U "PEERTUBE_USER" --password "PEERTUBE_PASSWORD"') | ||
76 | console.log(' $ peertube up <videoFile>') | ||
77 | console.log(' $ peertube watch https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10') | ||
78 | console.log() | ||
79 | }) | ||
80 | .parse(process.argv) | ||
81 | }) | ||
diff --git a/support/doc/tools.md b/support/doc/tools.md index 0a2f1f11b..1db29edc0 100644 --- a/support/doc/tools.md +++ b/support/doc/tools.md | |||
@@ -1,14 +1,60 @@ | |||
1 | # CLI tools guide | 1 | # CLI tools guide |
2 | 2 | - [CLI wrapper](#cli-wrapper) | |
3 | - [Remote tools](#remote-tools) | 3 | - [Remote tools](#remote-tools) |
4 | - [import-videos.js](#import-videosjs) | 4 | - [peertube-import-videos.js](#peertube-import-videosjs) |
5 | - [upload.js](#uploadjs) | 5 | - [peertube-upload.js](#peertube-uploadjs) |
6 | - [peertube-watch.js](#peertube-watch) | ||
6 | - [Server tools](#server-tools) | 7 | - [Server tools](#server-tools) |
7 | - [parse-log](#parse-log) | 8 | - [parse-log](#parse-log) |
8 | - [create-transcoding-job.js](#create-transcoding-jobjs) | 9 | - [create-transcoding-job.js](#create-transcoding-jobjs) |
9 | - [create-import-video-file-job.js](#create-import-video-file-jobjs) | 10 | - [create-import-video-file-job.js](#create-import-video-file-jobjs) |
10 | - [prune-storage.js](#prune-storagejs) | 11 | - [prune-storage.js](#prune-storagejs) |
11 | 12 | ||
13 | ## CLI wrapper | ||
14 | |||
15 | The wrapper provides a convenient interface to most scripts, and requires the [same dependencies](#dependencies). You can access it as `peertube` via an alias in your `.bashrc` like `alias peertube="node ${PEERTUBE_PATH}/dist/server/tools/peertube.js"`: | ||
16 | |||
17 | ``` | ||
18 | Usage: peertube [command] [options] | ||
19 | |||
20 | Options: | ||
21 | |||
22 | -v, --version output the version number | ||
23 | -h, --help output usage information | ||
24 | |||
25 | Commands: | ||
26 | |||
27 | auth [action] register your accounts on remote instances to use them with other commands | ||
28 | upload|up upload a video | ||
29 | import-videos|import import a video from a streaming platform | ||
30 | watch|w watch a video in the terminal ✩°。⋆ | ||
31 | help [cmd] display help for [cmd] | ||
32 | ``` | ||
33 | |||
34 | The wrapper can keep track of instances you have an account on. We limit to one account per instance for now. | ||
35 | |||
36 | ```bash | ||
37 | $ peertube auth add -u "PEERTUBE_URL" -U "PEERTUBE_USER" --password "PEERTUBE_PASSWORD" | ||
38 | $ peertube auth list | ||
39 | ┌──────────────────────────────┬──────────────────────────────┐ | ||
40 | │ instance │ login │ | ||
41 | ├──────────────────────────────┼──────────────────────────────┤ | ||
42 | │ "PEERTUBE_URL" │ "PEERTUBE_USER" │ | ||
43 | └──────────────────────────────┴──────────────────────────────┘ | ||
44 | ``` | ||
45 | |||
46 | You can now use that account to upload videos without feeding the same parameters again. | ||
47 | |||
48 | ```bash | ||
49 | $ peertube up <videoFile> | ||
50 | ``` | ||
51 | |||
52 | And now that your video is online, you can watch it from the confort of your terminal (use `peertube watch --help` to see the supported players): | ||
53 | |||
54 | ```bash | ||
55 | $ peertube watch https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10 | ||
56 | ``` | ||
57 | |||
12 | ## Remote Tools | 58 | ## Remote Tools |
13 | 59 | ||
14 | You need at least 512MB RAM to run the script. | 60 | You need at least 512MB RAM to run the script. |
@@ -40,13 +86,13 @@ $ cd ${CLONE} | |||
40 | $ npm run build:server | 86 | $ npm run build:server |
41 | ``` | 87 | ``` |
42 | 88 | ||
43 | ### import-videos.js | 89 | ### peertube-import-videos.js |
44 | 90 | ||
45 | You can use this script to import videos from all [supported sites of youtube-dl](https://rg3.github.io/youtube-dl/supportedsites.html) into PeerTube. | 91 | You can use this script to import videos from all [supported sites of youtube-dl](https://rg3.github.io/youtube-dl/supportedsites.html) into PeerTube. |
46 | Be sure you own the videos or have the author's authorization to do so. | 92 | Be sure you own the videos or have the author's authorization to do so. |
47 | 93 | ||
48 | ```sh | 94 | ```sh |
49 | $ node dist/server/tools/import-videos.js \ | 95 | $ node dist/server/tools/peertube-import-videos.js \ |
50 | -u "PEERTUBE_URL" \ | 96 | -u "PEERTUBE_URL" \ |
51 | -U "PEERTUBE_USER" \ | 97 | -U "PEERTUBE_USER" \ |
52 | --password "PEERTUBE_PASSWORD" \ | 98 | --password "PEERTUBE_PASSWORD" \ |
@@ -70,7 +116,7 @@ Already downloaded videos will not be uploaded twice, so you can run and re-run | |||
70 | Videos will be publicly available after transcoding (you can see them before that in your account on the web interface). | 116 | Videos will be publicly available after transcoding (you can see them before that in your account on the web interface). |
71 | 117 | ||
72 | 118 | ||
73 | ### upload.js | 119 | ### peertube-upload.js |
74 | 120 | ||
75 | You can use this script to import videos directly from the CLI. | 121 | You can use this script to import videos directly from the CLI. |
76 | 122 | ||
@@ -78,9 +124,24 @@ Videos will be publicly available after transcoding (you can see them before tha | |||
78 | 124 | ||
79 | ``` | 125 | ``` |
80 | $ cd ${CLONE} | 126 | $ cd ${CLONE} |
81 | $ node dist/server/tools/upload.js --help | 127 | $ node dist/server/tools/peertube-upload.js --help |
82 | ``` | 128 | ``` |
83 | 129 | ||
130 | ### peertube-watch.js | ||
131 | |||
132 | You can use this script to play videos directly from the CLI. | ||
133 | |||
134 | It provides support for different players: | ||
135 | |||
136 | - ascii (default ; plays in ascii art in your terminal!) | ||
137 | - mpv | ||
138 | - mplayer | ||
139 | - vlc | ||
140 | - stdout | ||
141 | - xbmc | ||
142 | - airplay | ||
143 | - chromecast | ||
144 | |||
84 | 145 | ||
85 | ## Server tools | 146 | ## Server tools |
86 | 147 | ||
diff --git a/tsconfig.json b/tsconfig.json index 7633465b2..c84b179cf 100644 --- a/tsconfig.json +++ b/tsconfig.json | |||
@@ -6,6 +6,7 @@ | |||
6 | "sourceMap": false, | 6 | "sourceMap": false, |
7 | "experimentalDecorators": true, | 7 | "experimentalDecorators": true, |
8 | "emitDecoratorMetadata": true, | 8 | "emitDecoratorMetadata": true, |
9 | "removeComments": true, | ||
9 | "outDir": "./dist", | 10 | "outDir": "./dist", |
10 | "lib": [ | 11 | "lib": [ |
11 | "dom", | 12 | "dom", |