"engines": {
"node": ">=8.x"
},
+ "bin": {
+ "peertube": "dist/server/tools/peertube.js"
+ },
"author": {
"name": "Florian Bigard",
"email": "florian.bigard@gmail.com",
"@types/bluebird": "3.5.21"
},
"dependencies": {
+ "application-config": "^1.0.1",
"async": "^2.0.0",
"async-lock": "^1.1.2",
"async-lru": "^1.1.1",
"bluebird": "^3.5.0",
"body-parser": "^1.12.4",
"bull": "^3.4.2",
+ "cli-table": "^0.3.1",
"bytes": "^3.0.0",
"commander": "^2.13.0",
"concurrently": "^4.0.1",
"magnet-uri": "^5.1.4",
"morgan": "^1.5.3",
"multer": "^1.1.0",
+ "netrc-parser": "^3.1.6",
"nodemailer": "^4.4.2",
"parse-torrent": "^6.0.0",
"password-generator": "^2.0.2",
"sequelize-typescript": "0.6.6",
"sharp": "^0.20.0",
"srt-to-vtt": "^1.1.2",
+ "summon-install": "^0.4.3",
"useragent": "^2.3.0",
"uuid": "^3.1.0",
"validator": "^10.2.0",
"scripty": {
"silent": true
},
+ "summon": {
+ "silent": true
+ },
"sasslintConfig": "client/.sass-lint.yml"
}
thumbnailUrl?: string
}
-function getYoutubeDLInfo (url: string): Promise<YoutubeDLInfo> {
+function getYoutubeDLInfo (url: string, opts?: string[]): Promise<YoutubeDLInfo> {
return new Promise<YoutubeDLInfo>(async (res, rej) => {
- const options = [ '-j', '--flat-playlist' ]
+ const options = opts || [ '-j', '--flat-playlist' ]
const youtubeDL = await safeGetYoutubeDL()
youtubeDL.getInfo(url, options, (err, info) => {
})
}
-// ---------------------------------------------------------------------------
-
-export {
- downloadYoutubeDLVideo,
- getYoutubeDLInfo
-}
-
-// ---------------------------------------------------------------------------
-
async function safeGetYoutubeDL () {
let youtubeDL
return youtubeDL
}
+// ---------------------------------------------------------------------------
+
+export {
+ downloadYoutubeDLVideo,
+ getYoutubeDLInfo,
+ safeGetYoutubeDL
+}
+
+// ---------------------------------------------------------------------------
+
function normalizeObject (obj: any) {
const newObj: any = {}
// Order of the tests we want to execute
import './create-transcoding-job'
import './create-import-video-file-job'
+import './peertube'
import './reset-password'
import './update-host'
--- /dev/null
+import 'mocha'
+import {
+ expect
+} from 'chai'
+import {
+ createUser,
+ execCLI,
+ flushTests,
+ getEnvCli,
+ killallServers,
+ runServer,
+ ServerInfo,
+ setAccessTokensToServers
+} from '../utils'
+
+describe('Test CLI wrapper', function () {
+ let server: ServerInfo
+ const cmd = 'node ./dist/server/tools/peertube.js'
+
+ before(async function () {
+ this.timeout(30000)
+
+ await flushTests()
+ server = await runServer(1)
+ await setAccessTokensToServers([ server ])
+
+ await createUser(server.url, server.accessToken, 'user_1', 'super password')
+ })
+
+ it('Should display no selected instance', async function () {
+ this.timeout(60000)
+
+ const env = getEnvCli(server)
+ const stdout = await execCLI(`${env} ${cmd} --help`)
+
+ expect(stdout).to.contain('selected')
+ })
+
+ it('Should remember the authentifying material of the user', async function () {
+ this.timeout(60000)
+
+ const env = getEnvCli(server)
+ const stdout = await execCLI(`${env} ` + cmd + ` auth add --url ${server.url} -U user_1 -p "super password"`)
+ })
+
+ after(async function () {
+ await execCLI(cmd + ` auth del ${server.url}`)
+
+ killallServers([ server ])
+ })
+})
--- /dev/null
+const config = require('application-config')('PeerTube/CLI')
+const netrc = require('netrc-parser').default
+
+const version = () => {
+ const tag = require('child_process')
+ .execSync('[[ ! -d .git ]] || git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || true', { stdio: [0,1,2] })
+ if (tag) return tag
+
+ const version = require('child_process')
+ .execSync('[[ ! -d .git ]] || git rev-parse --short HEAD').toString().trim()
+ if (version) return version
+
+ return require('../../../package.json').version
+}
+
+let settings = {
+ remotes: [],
+ default: 0
+}
+
+interface Settings {
+ remotes: any[],
+ default: number
+}
+
+async function getSettings () {
+ return new Promise<Settings>((res, rej) => {
+ let settings = {
+ remotes: [],
+ default: 0
+ } as Settings
+ config.read((err, data) => {
+ if (err) {
+ return rej(err)
+ }
+ return res(data || settings)
+ })
+ })
+}
+
+async function writeSettings (settings) {
+ return new Promise((res, rej) => {
+ config.write(settings, function (err) {
+ if (err) {
+ return rej(err)
+ }
+ return res()
+ })
+ })
+}
+
+netrc.loadSync()
+
+// ---------------------------------------------------------------------------
+
+export {
+ version,
+ config,
+ settings,
+ getSettings,
+ writeSettings,
+ netrc
+}
--- /dev/null
+import * as program from 'commander'
+import * as prompt from 'prompt'
+const Table = require('cli-table')
+import { getSettings, writeSettings, netrc } from './cli'
+import { isHostValid } from '../helpers/custom-validators/servers'
+import { isUserUsernameValid } from '../helpers/custom-validators/users'
+
+function delInstance (url: string) {
+ return new Promise((res, rej): void => {
+ getSettings()
+ .then(async (settings) => {
+ settings.remotes.splice(settings.remotes.indexOf(url))
+ await writeSettings(settings)
+ delete netrc.machines[url]
+ netrc.save()
+ res()
+ })
+ .catch(err => rej(err))
+ })
+}
+
+async function setInstance (url: string, username: string, password: string) {
+ return new Promise((res, rej): void => {
+ getSettings()
+ .then(async settings => {
+ if (settings.remotes.indexOf(url) === -1) {
+ settings.remotes.push(url)
+ }
+ await writeSettings(settings)
+ netrc.machines[url] = { login: username, password }
+ netrc.save()
+ res()
+ })
+ .catch(err => rej(err))
+ })
+}
+
+function isURLaPeerTubeInstance (url: string) {
+ return isHostValid(url) || (url.includes('localhost'))
+}
+
+program
+ .name('auth')
+ .usage('[command] [options]')
+
+program
+ .command('add')
+ .description('remember your accounts on remote instances for easier use')
+ .option('-u, --url <url>', 'Server url')
+ .option('-U, --username <username>', 'Username')
+ .option('-p, --password <token>', 'Password')
+ .option('--default', 'add the entry as the new default')
+ .action(options => {
+ prompt.override = options
+ prompt.start()
+ prompt.get({
+ properties: {
+ url: {
+ description: 'instance url',
+ conform: (value) => isURLaPeerTubeInstance(value),
+ required: true
+ },
+ username: {
+ conform: (value) => isUserUsernameValid(value),
+ message: 'Name must be only letters, spaces, or dashes',
+ required: true
+ },
+ password: {
+ hidden: true,
+ replace: '*',
+ required: true
+ }
+ }
+ }, (_, result) => {
+ setInstance(result.url, result.username, result.password)
+ })
+ })
+
+program
+ .command('del <url>')
+ .description('unregisters a remote instance')
+ .action((url) => {
+ delInstance(url)
+ })
+
+program
+ .command('list')
+ .description('lists registered remote instances')
+ .action(() => {
+ getSettings()
+ .then(settings => {
+ const table = new Table({
+ head: ['instance', 'login'],
+ colWidths: [30, 30]
+ })
+ netrc.loadSync()
+ settings.remotes.forEach(element => {
+ table.push([
+ element,
+ netrc.machines[element].login
+ ])
+ })
+
+ console.log(table.toString())
+ })
+ })
+
+program
+ .command('set-default <url>')
+ .description('set an existing entry as default')
+ .action((url) => {
+ getSettings()
+ .then(settings => {
+ const instanceExists = settings.remotes.indexOf(url) !== -1
+
+ if (instanceExists) {
+ settings.default = settings.remotes.indexOf(url)
+ writeSettings(settings)
+ } else {
+ console.log('<url> is not a registered instance.')
+ process.exit(-1)
+ }
+ })
+ })
+
+program.on('--help', function () {
+ console.log(' Examples:')
+ console.log()
+ console.log(' $ peertube add -u peertube.cpy.re -U "PEERTUBE_USER" --password "PEERTUBE_PASSWORD"')
+ console.log(' $ peertube add -u peertube.cpy.re -U root')
+ console.log(' $ peertube list')
+ console.log(' $ peertube del peertube.cpy.re')
+ console.log()
+})
+
+if (!process.argv.slice(2).length) {
+ program.outputHelp()
+}
+
+program.parse(process.argv)
!program['username'] ||
!program['password']
) {
- throw new Error('All arguments are required.')
+ if (!program['url']) console.error('--url field is required.')
+ if (!program['username']) console.error('--username field is required.')
+ if (!program['password']) console.error('--password field is required.')
+ process.exit(-1)
}
getClient(program.url)
import * as program from 'commander'
import { join } from 'path'
-import * as youtubeDL from 'youtube-dl'
import { VideoPrivacy } from '../../shared/models/videos'
import { doRequestAndSaveToFile } from '../helpers/requests'
import { CONSTRAINTS_FIELDS } from '../initializers'
import { truncate } from 'lodash'
import * as prompt from 'prompt'
import { remove } from 'fs-extra'
+import { safeGetYoutubeDL } from '../helpers/youtube-dl'
+import { getSettings, netrc } from './cli'
+
+let accessToken: string
+let client: { id: string, secret: string }
+
+const processOptions = {
+ cwd: __dirname,
+ maxBuffer: Infinity
+}
program
+ .name('import-videos')
.option('-u, --url <url>', 'Server url')
.option('-U, --username <username>', 'Username')
.option('-p, --password <token>', 'Password')
.option('-v, --verbose', 'Verbose mode')
.parse(process.argv)
-if (
- !program['url'] ||
- !program['username'] ||
- !program['targetUrl']
-) {
- console.error('All arguments are required.')
- process.exit(-1)
-}
+getSettings()
+.then(settings => {
+ if (
+ (!program['url'] ||
+ !program['username'] ||
+ !program['password']) &&
+ (settings.remotes.length === 0)
+ ) {
+ if (!program['url']) console.error('--url field is required.')
+ if (!program['username']) console.error('--username field is required.')
+ if (!program['password']) console.error('--password field is required.')
+ if (!program['targetUrl']) console.error('--targetUrl field is required.')
+ process.exit(-1)
+ }
-const user = {
- username: program['username'],
- password: program['password']
-}
+ if (
+ (!program['url'] ||
+ !program['username'] ||
+ !program['password']) &&
+ (settings.remotes.length > 0)
+ ) {
+ if (!program['url']) {
+ program['url'] = (settings.default !== -1) ?
+ settings.remotes[settings.default] :
+ settings.remotes[0]
+ }
+ if (!program['username']) program['username'] = netrc.machines[program['url']].login
+ if (!program['password']) program['password'] = netrc.machines[program['url']].password
+ }
-run().catch(err => console.error(err))
+ if (
+ !program['targetUrl']
+ ) {
+ if (!program['targetUrl']) console.error('--targetUrl field is required.')
+ process.exit(-1)
+ }
-let accessToken: string
-let client: { id: string, secret: string }
+ const user = {
+ username: program['username'],
+ password: program['password']
+ }
-const processOptions = {
- cwd: __dirname,
- maxBuffer: Infinity
-}
+ run(user, program['url']).catch(err => console.error(err))
+})
async function promptPassword () {
return new Promise((res, rej) => {
})
}
-async function run () {
+async function run (user, url: string) {
if (!user.password) {
user.password = await promptPassword()
}
- const res = await getClient(program['url'])
+ const res = await getClient(url)
client = {
id: res.body.client_id,
secret: res.body.client_secret
}
- const res2 = await login(program['url'], client, user)
+ const res2 = await login(url, client, user)
accessToken = res2.body.access_token
+ const youtubeDL = await safeGetYoutubeDL()
+
const options = [ '-j', '--flat-playlist', '--playlist-reverse' ]
youtubeDL.getInfo(program['targetUrl'], options, processOptions, async (err, info) => {
if (err) {
console.log('Will download and upload %d videos.\n', infoArray.length)
for (const info of infoArray) {
- await processVideo(info, program['language'])
+ await processVideo(info, program['language'], processOptions.cwd, url, user)
}
// https://www.youtube.com/watch?v=2Upx39TBc1s
})
}
-function processVideo (info: any, languageCode: string) {
+function processVideo (info: any, languageCode: string, cwd: string, url: string, user) {
return new Promise(async res => {
if (program['verbose']) console.log('Fetching object.', info)
const videoInfo = await fetchObject(info)
if (program['verbose']) console.log('Fetched object.', videoInfo)
- const result = await searchVideoWithSort(program['url'], videoInfo.title, '-match')
+ const result = await searchVideoWithSort(url, videoInfo.title, '-match')
console.log('############################################################\n')
return res()
}
- const path = join(__dirname, new Date().getTime() + '.mp4')
+ const path = join(cwd, new Date().getTime() + '.mp4')
console.log('Downloading video "%s"...', videoInfo.title)
const options = [ '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best', '-o', path ]
try {
+ const youtubeDL = await safeGetYoutubeDL()
youtubeDL.exec(videoInfo.url, options, processOptions, async (err, output) => {
if (err) {
console.error(err)
}
console.log(output.join('\n'))
- await uploadVideoOnPeerTube(normalizeObject(videoInfo), path, languageCode)
+ await uploadVideoOnPeerTube(normalizeObject(videoInfo), path, cwd, url, user, languageCode)
return res()
})
} catch (err) {
})
}
-async function uploadVideoOnPeerTube (videoInfo: any, videoPath: string, language?: string) {
- const category = await getCategory(videoInfo.categories)
+async function uploadVideoOnPeerTube (videoInfo: any, videoPath: string, cwd: string, url: string, user, language?: string) {
+ const category = await getCategory(videoInfo.categories, url)
const licence = getLicence(videoInfo.license)
let tags = []
if (Array.isArray(videoInfo.tags)) {
let thumbnailfile
if (videoInfo.thumbnail) {
- thumbnailfile = join(__dirname, 'thumbnail.jpg')
+ thumbnailfile = join(cwd, 'thumbnail.jpg')
await doRequestAndSaveToFile({
method: 'GET',
console.log('\nUploading on PeerTube video "%s".', videoAttributes.name)
try {
- await uploadVideo(program['url'], accessToken, videoAttributes)
+ await uploadVideo(url, accessToken, videoAttributes)
} catch (err) {
if (err.message.indexOf('401') !== -1) {
console.log('Got 401 Unauthorized, token may have expired, renewing token and retry.')
- const res = await login(program['url'], client, user)
+ const res = await login(url, client, user)
accessToken = res.body.access_token
- await uploadVideo(program['url'], accessToken, videoAttributes)
+ await uploadVideo(url, accessToken, videoAttributes)
} else {
console.log(err.message)
process.exit(1)
console.log('Uploaded video "%s"!\n', videoAttributes.name)
}
-async function getCategory (categories: string[]) {
+async function getCategory (categories: string[], url: string) {
if (!categories) return undefined
const categoryString = categories[0]
if (categoryString === 'News & Politics') return 11
- const res = await getVideoCategories(program['url'])
+ const res = await getVideoCategories(url)
const categoriesServer = res.body
for (const key of Object.keys(categoriesServer)) {
return undefined
}
+/* ---------------------------------------------------------- */
+
function getLicence (licence: string) {
if (!licence) return undefined
const url = buildUrl(info)
return new Promise<any>(async (res, rej) => {
+ const youtubeDL = await safeGetYoutubeDL()
youtubeDL.getInfo(url, undefined, processOptions, async (err, videoInfo) => {
if (err) return rej(err)
import { getClient, login } from '../tests/utils'
import { uploadVideo } from '../tests/utils/index'
import { VideoPrivacy } from '../../shared/models/videos'
+import { netrc, getSettings } from './cli'
program
+ .name('upload')
.option('-u, --url <url>', 'Server url')
.option('-U, --username <username>', 'Username')
.option('-p, --password <token>', 'Password')
.option('-n, --video-name <name>', 'Video name')
- .option('-P, --privacy <privacy number>', 'Privacy')
+ .option('-P, --privacy <privacy_number>', 'Privacy')
.option('-N, --nsfw', 'Video is Not Safe For Work')
- .option('-c, --category <category number>', 'Category number')
+ .option('-c, --category <category_number>', 'Category number')
.option('-m, --comments-enabled', 'Enable comments')
- .option('-l, --licence <licence number>', 'Licence number')
- .option('-L, --language <language code>', 'Language ISO 639 code (fr or en...)')
+ .option('-l, --licence <licence_number>', 'Licence number')
+ .option('-L, --language <language_code>', 'Language ISO 639 code (fr or en...)')
.option('-d, --video-description <description>', 'Video description')
.option('-t, --tags <tags>', 'Video tags', list)
.option('-b, --thumbnail <thumbnailPath>', 'Thumbnail path')
if (!program['privacy']) program['privacy'] = VideoPrivacy.PUBLIC
if (!program['commentsEnabled']) program['commentsEnabled'] = false
-if (
- !program['url'] ||
- !program['username'] ||
- !program['password'] ||
- !program['videoName'] ||
- !program['file']
-) {
- if (!program['url']) console.error('--url field is required.')
- if (!program['username']) console.error('--username field is required.')
- if (!program['password']) console.error('--password field is required.')
- if (!program['videoName']) console.error('--video-name field is required.')
- if (!program['file']) console.error('--file field is required.')
- process.exit(-1)
-}
+getSettings()
+ .then(settings => {
+ if (
+ (!program['url'] ||
+ !program['username'] ||
+ !program['password']) &&
+ (settings.remotes.length === 0)
+ ) {
+ if (!program['url']) console.error('--url field is required.')
+ if (!program['username']) console.error('--username field is required.')
+ if (!program['password']) console.error('--password field is required.')
+ if (!program['videoName']) console.error('--video-name field is required.')
+ if (!program['file']) console.error('--file field is required.')
+ process.exit(-1)
+ }
-if (isAbsolute(program['file']) === false) {
- console.error('File path should be absolute.')
- process.exit(-1)
-}
+ if (
+ (!program['url'] ||
+ !program['username'] ||
+ !program['password']) &&
+ (settings.remotes.length > 0)
+ ) {
+ if (!program['url']) {
+ program['url'] = (settings.default !== -1) ?
+ settings.remotes[settings.default] :
+ settings.remotes[0]
+ }
+ if (!program['username']) program['username'] = netrc.machines[program['url']].login
+ if (!program['password']) program['password'] = netrc.machines[program['url']].password
+ }
+
+ if (
+ !program['videoName'] ||
+ !program['file']
+ ) {
+ if (!program['videoName']) console.error('--video-name field is required.')
+ if (!program['file']) console.error('--file field is required.')
+ process.exit(-1)
+ }
+
+ if (isAbsolute(program['file']) === false) {
+ console.error('File path should be absolute.')
+ process.exit(-1)
+ }
-run().catch(err => console.error(err))
+ run().catch(err => console.error(err))
+ })
async function run () {
const res = await getClient(program[ 'url' ])
--- /dev/null
+import * as program from 'commander'
+import * as summon from 'summon-install'
+import { join } from 'path'
+import { execSync } from 'child_process'
+import { root } from '../helpers/core-utils'
+
+let videoURL
+
+program
+ .name('watch')
+ .arguments('<url>')
+ .option('-g, --gui <player>', 'player type', /^(airplay|stdout|chromecast|mpv|vlc|mplayer|ascii|xbmc)$/i, 'ascii')
+ .option('-i, --invert', 'invert colors (ascii player only)', true)
+ .option('-r, --resolution <res>', 'video resolution', /^(240|360|720|1080)$/i, '720')
+ .on('--help', function () {
+ console.log(' Available Players:')
+ console.log()
+ console.log(' - ascii')
+ console.log(' - mpv')
+ console.log(' - mplayer')
+ console.log(' - vlc')
+ console.log(' - stdout')
+ console.log(' - xbmc')
+ console.log(' - airplay')
+ console.log(' - chromecast')
+ console.log()
+ console.log(' Note: \'ascii\' is the only option not using WebTorrent and not seeding back the video.')
+ console.log()
+ console.log(' Examples:')
+ console.log()
+ console.log(' $ peertube watch -g mpv https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10')
+ console.log(' $ peertube watch --gui stdout https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10')
+ console.log(' $ peertube watch https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10')
+ console.log()
+ })
+ .action((url) => {
+ videoURL = url
+ })
+ .parse(process.argv)
+
+if (!videoURL) {
+ console.error('<url> positional argument is required.')
+ process.exit(-1)
+} else { program['url'] = videoURL }
+
+handler(program)
+
+function handler (argv) {
+ if (argv['gui'] === 'ascii') {
+ summon('peerterminal')
+ const peerterminal = summon('peerterminal')
+ peerterminal([ '--link', videoURL, '--invert', argv['invert'] ])
+ } else {
+ summon('webtorrent-hybrid')
+ const CMD = 'node ' + join(root(), 'node_modules', 'webtorrent-hybrid', 'bin', 'cmd.js')
+ const CMDargs = ` --${argv.gui} ` +
+ argv['url'].replace('videos/watch', 'download/torrents') +
+ `-${argv.resolution}.torrent`
+ execSync(CMD + CMDargs)
+ }
+}
--- /dev/null
+#!/usr/bin/env node
+
+import * as program from 'commander'
+import {
+ version,
+ getSettings
+} from './cli'
+
+program
+ .version(version(), '-v, --version')
+ .usage('[command] [options]')
+
+/* Subcommands automatically loaded in the directory and beginning by peertube-* */
+program
+ .command('auth [action]', 'register your accounts on remote instances to use them with other commands')
+ .command('upload', 'upload a video').alias('up')
+ .command('import-videos', 'import a video from a streaming platform').alias('import')
+ .command('get-access-token', 'get a peertube access token', { noHelp: true }).alias('token')
+ .command('watch', 'watch a video in the terminal ✩°。⋆').alias('w')
+
+/* Not Yet Implemented */
+program
+ .command('plugins [action]',
+ 'manage plugins on a local instance',
+ { noHelp: true } as program.CommandOptions
+ ).alias('p')
+ .command('diagnostic [action]',
+ 'like couple therapy, but for your instance',
+ { noHelp: true } as program.CommandOptions
+ ).alias('d')
+ .command('admin',
+ 'manage an instance where you have elevated rights',
+ { noHelp: true } as program.CommandOptions
+ ).alias('a')
+
+// help on no command
+if (!process.argv.slice(2).length) {
+ const logo = '░P░e░e░r░T░u░b░e░'
+ console.log(`
+ ___/),.._ ` + logo + `
+/' ,. ."'._
+( "' '-.__"-._ ,-
+\\'='='), "\\ -._-"-. -"/
+ / ""/"\\,_\\,__"" _" /,-
+ / / -" _/"/
+ / | ._\\\\ |\\ |_.".-" /
+ / | __\\)|)|),/|_." _,."
+ / \_." " ") | ).-""---''--
+ ( "/.""7__-""''
+ | " ."._--._
+ \\ \\ (_ __ "" ".,_
+ \\.,. \\ "" -"".-"
+ ".,_, (",_-,,,-".-
+ "'-,\\_ __,-"
+ ",)" ")
+ /"\\-"
+ ,"\\/
+ _,.__/"\\/_ (the CLI for red chocobos)
+ / \\) "./, ".
+ --/---"---" "-) )---- by Chocobozzz et al.`)
+}
+
+getSettings()
+ .then(settings => {
+ const state = (settings.default === -1) ?
+ 'no instance selected, commands will require explicit arguments' :
+ ('instance ' + settings.remotes[settings.default] + ' selected')
+ program
+ .on('--help', function () {
+ console.log()
+ console.log(' State: ' + state)
+ console.log()
+ console.log(' Examples:')
+ console.log()
+ console.log(' $ peertube auth add -u "PEERTUBE_URL" -U "PEERTUBE_USER" --password "PEERTUBE_PASSWORD"')
+ console.log(' $ peertube up <videoFile>')
+ console.log(' $ peertube watch https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10')
+ console.log()
+ })
+ .parse(process.argv)
+ })
# CLI tools guide
-
+ - [CLI wrapper](#cli-wrapper)
- [Remote tools](#remote-tools)
- - [import-videos.js](#import-videosjs)
- - [upload.js](#uploadjs)
+ - [peertube-import-videos.js](#peertube-import-videosjs)
+ - [peertube-upload.js](#peertube-uploadjs)
+ - [peertube-watch.js](#peertube-watch)
- [Server tools](#server-tools)
- [parse-log](#parse-log)
- [create-transcoding-job.js](#create-transcoding-jobjs)
- [create-import-video-file-job.js](#create-import-video-file-jobjs)
- [prune-storage.js](#prune-storagejs)
+## CLI wrapper
+
+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"`:
+
+```
+ Usage: peertube [command] [options]
+
+ Options:
+
+ -v, --version output the version number
+ -h, --help output usage information
+
+ Commands:
+
+ auth [action] register your accounts on remote instances to use them with other commands
+ upload|up upload a video
+ import-videos|import import a video from a streaming platform
+ watch|w watch a video in the terminal ✩°。⋆
+ help [cmd] display help for [cmd]
+```
+
+The wrapper can keep track of instances you have an account on. We limit to one account per instance for now.
+
+```bash
+$ peertube auth add -u "PEERTUBE_URL" -U "PEERTUBE_USER" --password "PEERTUBE_PASSWORD"
+$ peertube auth list
+┌──────────────────────────────┬──────────────────────────────┐
+│ instance │ login │
+├──────────────────────────────┼──────────────────────────────┤
+│ "PEERTUBE_URL" │ "PEERTUBE_USER" │
+└──────────────────────────────┴──────────────────────────────┘
+```
+
+You can now use that account to upload videos without feeding the same parameters again.
+
+```bash
+$ peertube up <videoFile>
+```
+
+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):
+
+```bash
+$ peertube watch https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10
+```
+
## Remote Tools
You need at least 512MB RAM to run the script.
$ npm run build:server
```
-### import-videos.js
+### peertube-import-videos.js
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.
Be sure you own the videos or have the author's authorization to do so.
```sh
-$ node dist/server/tools/import-videos.js \
+$ node dist/server/tools/peertube-import-videos.js \
-u "PEERTUBE_URL" \
-U "PEERTUBE_USER" \
--password "PEERTUBE_PASSWORD" \
Videos will be publicly available after transcoding (you can see them before that in your account on the web interface).
-### upload.js
+### peertube-upload.js
You can use this script to import videos directly from the CLI.
```
$ cd ${CLONE}
-$ node dist/server/tools/upload.js --help
+$ node dist/server/tools/peertube-upload.js --help
```
+### peertube-watch.js
+
+You can use this script to play videos directly from the CLI.
+
+It provides support for different players:
+
+- ascii (default ; plays in ascii art in your terminal!)
+- mpv
+- mplayer
+- vlc
+- stdout
+- xbmc
+- airplay
+- chromecast
+
## Server tools
"sourceMap": false,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
+ "removeComments": true,
"outDir": "./dist",
"lib": [
"dom",