Update server dependencies

pull/3681/head
Chocobozzz 2021-02-03 09:33:05 +01:00
parent 29f148a613
commit ba5a8d89bb
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
42 changed files with 1322 additions and 1553 deletions

View File

@ -97,13 +97,13 @@
"bull": "^3.4.2", "bull": "^3.4.2",
"bytes": "^3.0.0", "bytes": "^3.0.0",
"chokidar": "^3.4.2", "chokidar": "^3.4.2",
"commander": "^6.0.0", "commander": "^7.0.0",
"config": "^3.0.0", "config": "^3.0.0",
"cookie-parser": "^1.4.3", "cookie-parser": "^1.4.3",
"cors": "^2.8.1", "cors": "^2.8.1",
"create-torrent": "^4.0.0", "create-torrent": "^4.0.0",
"deep-object-diff": "^1.1.0", "deep-object-diff": "^1.1.0",
"email-templates": "^7.0.4", "email-templates": "^8.0.3",
"express": "^4.12.4", "express": "^4.12.4",
"express-oauth-server": "^2.0.0", "express-oauth-server": "^2.0.0",
"express-rate-limit": "^5.0.0", "express-rate-limit": "^5.0.0",
@ -118,12 +118,12 @@
"is-cidr": "^4.0.0", "is-cidr": "^4.0.0",
"iso-639-3": "^2.0.0", "iso-639-3": "^2.0.0",
"jimp": "^0.16.0", "jimp": "^0.16.0",
"js-yaml": "^3.5.4", "js-yaml": "^4.0.0",
"jsonld": "~3.2.0", "jsonld": "~3.3.0",
"lodash": "^4.17.10", "lodash": "^4.17.10",
"lru-cache": "^6.0.0", "lru-cache": "^6.0.0",
"magnet-uri": "^6.1.0", "magnet-uri": "^6.1.0",
"markdown-it": "12.0.2", "markdown-it": "12.0.4",
"markdown-it-emoji": "^2.0.0", "markdown-it-emoji": "^2.0.0",
"memoizee": "^0.4.14", "memoizee": "^0.4.14",
"morgan": "^1.5.3", "morgan": "^1.5.3",
@ -143,7 +143,7 @@
"request": "^2.81.0", "request": "^2.81.0",
"sanitize-html": "2.x", "sanitize-html": "2.x",
"scripty": "^2.0.0", "scripty": "^2.0.0",
"sequelize": "6.3.5", "sequelize": "6.5.0",
"sequelize-typescript": "^2.0.0-beta.1", "sequelize-typescript": "^2.0.0-beta.1",
"sitemap": "^6.1.0", "sitemap": "^6.1.0",
"socket.io": "^3.0.2", "socket.io": "^3.0.2",
@ -155,7 +155,7 @@
"uuid": "^8.1.0", "uuid": "^8.1.0",
"validator": "^13.0.0", "validator": "^13.0.0",
"webfinger.js": "^2.6.6", "webfinger.js": "^2.6.6",
"webtorrent": "^0.111.0", "webtorrent": "^0.112.3",
"winston": "3.3.3", "winston": "3.3.3",
"ws": "^7.0.0", "ws": "^7.0.0",
"youtube-dl": "^3.0.2" "youtube-dl": "^3.0.2"
@ -168,13 +168,13 @@
"@types/bcrypt": "^3.0.0", "@types/bcrypt": "^3.0.0",
"@types/bluebird": "3.5.33", "@types/bluebird": "3.5.33",
"@types/body-parser": "^1.16.3", "@types/body-parser": "^1.16.3",
"@types/bull": "3.14.4", "@types/bull": "3.15.0",
"@types/bytes": "^3.0.0", "@types/bytes": "^3.0.0",
"@types/chai": "^4.0.4", "@types/chai": "^4.0.4",
"@types/chai-json-schema": "^1.4.3", "@types/chai-json-schema": "^1.4.3",
"@types/chai-xml": "^0.3.1", "@types/chai-xml": "^0.3.1",
"@types/config": "^0.0.36", "@types/config": "^0.0.38",
"@types/express": "^4.0.35", "@types/express": "4.17.9",
"@types/express-rate-limit": "^5.0.0", "@types/express-rate-limit": "^5.0.0",
"@types/fluent-ffmpeg": "^2.1.16", "@types/fluent-ffmpeg": "^2.1.16",
"@types/fs-extra": "^9.0.1", "@types/fs-extra": "^9.0.1",
@ -188,7 +188,7 @@
"@types/mocha": "^8.0.3", "@types/mocha": "^8.0.3",
"@types/morgan": "^1.7.32", "@types/morgan": "^1.7.32",
"@types/multer": "^1.3.3", "@types/multer": "^1.3.3",
"@types/node": "^14.0.13", "@types/node": "^12",
"@types/nodemailer": "^6.2.0", "@types/nodemailer": "^6.2.0",
"@types/oauth2-server": "^3.0.8", "@types/oauth2-server": "^3.0.8",
"@types/pem": "^1.9.3", "@types/pem": "^1.9.3",
@ -204,11 +204,11 @@
"chai-xml": "^0.4.0", "chai-xml": "^0.4.0",
"concurrently": "^5.0.0", "concurrently": "^5.0.0",
"eslint": "^7.2.0", "eslint": "^7.2.0",
"eslint-config-standard-with-typescript": "^19.0.1", "eslint-config-standard-with-typescript": "^20.0.0",
"eslint-plugin-import": "^2.20.1", "eslint-plugin-import": "^2.20.1",
"eslint-plugin-node": "^11.0.0", "eslint-plugin-node": "^11.0.0",
"eslint-plugin-promise": "^4.2.1", "eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1", "eslint-plugin-standard": "^5.0.0",
"libxmljs": "0.19.7", "libxmljs": "0.19.7",
"maildev": "^1.0.0-rc3", "maildev": "^1.0.0-rc3",
"marked": "^1.1.0", "marked": "^1.1.0",
@ -219,7 +219,7 @@
"source-map-support": "^0.5.0", "source-map-support": "^0.5.0",
"supertest": "^6.0.1", "supertest": "^6.0.1",
"swagger-cli": "^4.0.2", "swagger-cli": "^4.0.2",
"ts-node": "9.0.0", "ts-node": "9.1.1",
"typescript": "^4.0.5" "typescript": "^4.0.5"
}, },
"scripty": { "scripty": {

View File

@ -13,7 +13,9 @@ program
.description('Import a video file to replace an already uploaded file or to add a new resolution') .description('Import a video file to replace an already uploaded file or to add a new resolution')
.parse(process.argv) .parse(process.argv)
if (program['video'] === undefined || program['import'] === undefined) { const options = program.opts()
if (options.video === undefined || options.import === undefined) {
console.error('All parameters are mandatory.') console.error('All parameters are mandatory.')
process.exit(-1) process.exit(-1)
} }
@ -28,13 +30,13 @@ run()
async function run () { async function run () {
await initDatabaseModels(true) await initDatabaseModels(true)
const video = await VideoModel.loadByUUID(program['video']) const video = await VideoModel.loadByUUID(options.video)
if (!video) throw new Error('Video not found.') if (!video) throw new Error('Video not found.')
if (video.isOwned() === false) throw new Error('Cannot import files of a non owned video.') if (video.isOwned() === false) throw new Error('Cannot import files of a non owned video.')
const dataInput = { const dataInput = {
videoUUID: video.uuid, videoUUID: video.uuid,
filePath: resolve(program['import']) filePath: resolve(options.import)
} }
await JobQueue.Instance.init() await JobQueue.Instance.init()

View File

@ -14,12 +14,14 @@ program
.option('--generate-hls', 'Generate HLS playlist') .option('--generate-hls', 'Generate HLS playlist')
.parse(process.argv) .parse(process.argv)
if (program['video'] === undefined) { const options = program.opts()
if (options.video === undefined) {
console.error('All parameters are mandatory.') console.error('All parameters are mandatory.')
process.exit(-1) process.exit(-1)
} }
if (program.resolution !== undefined && Number.isNaN(+program.resolution)) { if (options.resolution !== undefined && Number.isNaN(+options.resolution)) {
console.error('The resolution must be an integer (example: 1080).') console.error('The resolution must be an integer (example: 1080).')
process.exit(-1) process.exit(-1)
} }
@ -34,15 +36,15 @@ run()
async function run () { async function run () {
await initDatabaseModels(true) await initDatabaseModels(true)
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(program['video']) const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(options.video)
if (!video) throw new Error('Video not found.') if (!video) throw new Error('Video not found.')
const dataInput: VideoTranscodingPayload[] = [] const dataInput: VideoTranscodingPayload[] = []
const { videoFileResolution } = await video.getMaxQualityResolution() const { videoFileResolution } = await video.getMaxQualityResolution()
if (program.generateHls) { if (options.generateHls) {
const resolutionsEnabled = program.resolution const resolutionsEnabled = options.resolution
? [ program.resolution ] ? [ options.resolution ]
: computeResolutionsToTranscode(videoFileResolution, 'vod').concat([ videoFileResolution ]) : computeResolutionsToTranscode(videoFileResolution, 'vod').concat([ videoFileResolution ])
for (const resolution of resolutionsEnabled) { for (const resolution of resolutionsEnabled) {
@ -54,12 +56,12 @@ async function run () {
copyCodecs: false copyCodecs: false
}) })
} }
} else if (program.resolution !== undefined) { } else if (options.resolution !== undefined) {
dataInput.push({ dataInput.push({
type: 'new-resolution-to-webtorrent', type: 'new-resolution-to-webtorrent',
videoUUID: video.uuid, videoUUID: video.uuid,
isNewVideo: false, isNewVideo: false,
resolution: program.resolution resolution: options.resolution
}) })
} else { } else {
dataInput.push({ dataInput.push({

View File

@ -17,6 +17,8 @@ program
.option('-f, --files [file...]', 'Files to parse. If not provided, the script will parse the latest log file from config)') .option('-f, --files [file...]', 'Files to parse. If not provided, the script will parse the latest log file from config)')
.parse(process.argv) .parse(process.argv)
const options = program.opts()
const excludedKeys = { const excludedKeys = {
level: true, level: true,
message: true, message: true,
@ -38,7 +40,7 @@ const loggerFormat = winston.format.printf((info) => {
if (CONFIG.LOG.PRETTIFY_SQL) { if (CONFIG.LOG.PRETTIFY_SQL) {
additionalInfos += '\n' + sqlFormat(info.sql, { additionalInfos += '\n' + sqlFormat(info.sql, {
language: 'sql', language: 'sql',
ident: ' ' indent: ' '
}) })
} else { } else {
additionalInfos += ' - ' + info.sql additionalInfos += ' - ' + info.sql
@ -51,7 +53,7 @@ const loggerFormat = winston.format.printf((info) => {
const logger = winston.createLogger({ const logger = winston.createLogger({
transports: [ transports: [
new winston.transports.Console({ new winston.transports.Console({
level: program['level'] || 'debug', level: options.level || 'debug',
stderrLevels: [], stderrLevels: [],
format: winston.format.combine( format: winston.format.combine(
winston.format.splat(), winston.format.splat(),
@ -76,7 +78,7 @@ run()
.catch(err => console.error(err)) .catch(err => console.error(err))
function run () { function run () {
return new Promise(async res => { return new Promise<void>(async res => {
const files = await getFiles() const files = await getFiles()
for (const file of files) { for (const file of files) {
@ -114,7 +116,7 @@ async function getNewestFile (files: string[], basePath: string) {
} }
async function getFiles () { async function getFiles () {
if (program['files']) return program['files'] if (options.files) return options.files
const logFiles = await readdir(CONFIG.STORAGE.LOG_DIR) const logFiles = await readdir(CONFIG.STORAGE.LOG_DIR)

View File

@ -12,12 +12,14 @@ program
.option('-p, --plugin-path [pluginPath]', 'Path of the plugin you want to install') .option('-p, --plugin-path [pluginPath]', 'Path of the plugin you want to install')
.parse(process.argv) .parse(process.argv)
if (!program['npmName'] && !program['pluginPath']) { const options = program.opts()
if (!options.npmName && !options.pluginPath) {
console.error('You need to specify a plugin name with the desired version, or a plugin path.') console.error('You need to specify a plugin name with the desired version, or a plugin path.')
process.exit(-1) process.exit(-1)
} }
if (program['pluginPath'] && !isAbsolute(program['pluginPath'])) { if (options.pluginPath && !isAbsolute(options.pluginPath)) {
console.error('Plugin path should be absolute.') console.error('Plugin path should be absolute.')
process.exit(-1) process.exit(-1)
} }
@ -32,6 +34,6 @@ run()
async function run () { async function run () {
await initDatabaseModels(true) await initDatabaseModels(true)
const toInstall = program['npmName'] || program['pluginPath'] const toInstall = options.npmName || options.pluginPath
await PluginManager.Instance.install(toInstall, program['pluginVersion'], !!program['pluginPath']) await PluginManager.Instance.install(toInstall, options.pluginVersion, !!options.pluginPath)
} }

View File

@ -9,7 +9,9 @@ program
.option('-n, --npm-name [npmName]', 'Package name to install') .option('-n, --npm-name [npmName]', 'Package name to install')
.parse(process.argv) .parse(process.argv)
if (!program['npmName']) { const options = program.opts()
if (!options.npmName) {
console.error('You need to specify the plugin name.') console.error('You need to specify the plugin name.')
process.exit(-1) process.exit(-1)
} }
@ -25,6 +27,6 @@ async function run () {
await initDatabaseModels(true) await initDatabaseModels(true)
const toUninstall = program['npmName'] const toUninstall = options.npmName
await PluginManager.Instance.uninstall(toUninstall) await PluginManager.Instance.uninstall(toUninstall)
} }

View File

@ -10,14 +10,16 @@ program
.option('-u, --user [user]', 'User') .option('-u, --user [user]', 'User')
.parse(process.argv) .parse(process.argv)
if (program['user'] === undefined) { const options = program.opts()
if (options.user === undefined) {
console.error('All parameters are mandatory.') console.error('All parameters are mandatory.')
process.exit(-1) process.exit(-1)
} }
initDatabaseModels(true) initDatabaseModels(true)
.then(() => { .then(() => {
return UserModel.loadByUsername(program['user']) return UserModel.loadByUsername(options.user)
}) })
.then(user => { .then(user => {
if (!user) { if (!user) {
@ -28,7 +30,7 @@ initDatabaseModels(true)
const readline = require('readline') const readline = require('readline')
const Writable = require('stream').Writable const Writable = require('stream').Writable
const mutableStdout = new Writable({ const mutableStdout = new Writable({
write: function (chunk, encoding, callback) { write: function (_chunk, _encoding, callback) {
callback() callback()
} }
}) })

View File

@ -205,7 +205,8 @@ app.use('/', staticRouter)
app.use('/', lazyStaticRouter) app.use('/', lazyStaticRouter)
// Client files, last valid routes! // Client files, last valid routes!
if (cli.client) app.use('/', clientsRouter) const cliOptions = cli.opts()
if (cliOptions.client) app.use('/', clientsRouter)
// ----------- Errors ----------- // ----------- Errors -----------
@ -277,7 +278,7 @@ async function startApplication () {
updateStreamingPlaylistsInfohashesIfNeeded() updateStreamingPlaylistsInfohashesIfNeeded()
.catch(err => logger.error('Cannot update streaming playlist infohashes.', { err })) .catch(err => logger.error('Cannot update streaming playlist infohashes.', { err }))
if (cli.plugins) await PluginManager.Instance.registerPluginsAndThemes() if (cliOptions.plugins) await PluginManager.Instance.registerPluginsAndThemes()
LiveManager.Instance.init() LiveManager.Instance.init()
if (CONFIG.LIVE.ENABLED) LiveManager.Instance.run() if (CONFIG.LIVE.ENABLED) LiveManager.Instance.run()

View File

@ -30,7 +30,7 @@ export {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function convertSrtToVtt (source: string, destination: string) { function convertSrtToVtt (source: string, destination: string) {
return new Promise((res, rej) => { return new Promise<void>((res, rej) => {
const file = createReadStream(source) const file = createReadStream(source)
const converter = srt2vtt() const converter = srt2vtt()
const writer = createWriteStream(destination) const writer = createWriteStream(destination)

View File

@ -5,12 +5,12 @@
Useful to avoid circular dependencies. Useful to avoid circular dependencies.
*/ */
import { createHash, HexBase64Latin1Encoding, randomBytes } from 'crypto' import { exec, ExecOptions } from 'child_process'
import { BinaryToTextEncoding, createHash, randomBytes } from 'crypto'
import { truncate } from 'lodash'
import { basename, isAbsolute, join, resolve } from 'path' import { basename, isAbsolute, join, resolve } from 'path'
import * as pem from 'pem' import * as pem from 'pem'
import { URL } from 'url' import { URL } from 'url'
import { truncate } from 'lodash'
import { exec, ExecOptions } from 'child_process'
const objectConverter = (oldObject: any, keyConverter: (e: string) => string, valueConverter: (e: any) => any) => { const objectConverter = (oldObject: any, keyConverter: (e: string) => string, valueConverter: (e: any) => any) => {
if (!oldObject || typeof oldObject !== 'object') { if (!oldObject || typeof oldObject !== 'object') {
@ -205,11 +205,11 @@ function peertubeTruncate (str: string, options: { length: number, separator?: R
return truncate(str, options) return truncate(str, options)
} }
function sha256 (str: string | Buffer, encoding: HexBase64Latin1Encoding = 'hex') { function sha256 (str: string | Buffer, encoding: BinaryToTextEncoding = 'hex') {
return createHash('sha256').update(str).digest(encoding) return createHash('sha256').update(str).digest(encoding)
} }
function sha1 (str: string | Buffer, encoding: HexBase64Latin1Encoding = 'hex') { function sha1 (str: string | Buffer, encoding: BinaryToTextEncoding = 'hex') {
return createHash('sha1').update(str).digest(encoding) return createHash('sha1').update(str).digest(encoding)
} }

View File

@ -76,6 +76,7 @@ const lru = new AsyncLRU({
} }
}) })
/* eslint-disable no-import-assign */
jsonld.documentLoader = (url) => { jsonld.documentLoader = (url) => {
return new Promise((res, rej) => { return new Promise((res, rej) => {
lru.get(url, (err, value) => { lru.get(url, (err, value) => {

View File

@ -61,7 +61,7 @@ const consoleLoggerFormat = winston.format.printf(info => {
if (CONFIG.LOG.PRETTIFY_SQL) { if (CONFIG.LOG.PRETTIFY_SQL) {
additionalInfos += '\n' + sqlFormat(info.sql, { additionalInfos += '\n' + sqlFormat(info.sql, {
language: 'sql', language: 'sql',
ident: ' ' indent: ' '
}) })
} else { } else {
additionalInfos += ' - ' + info.sql additionalInfos += ' - ' + info.sql

View File

@ -149,7 +149,7 @@ function safeWebtorrentDestroy (
downloadedFile?: { directoryPath: string, filepath: string }, downloadedFile?: { directoryPath: string, filepath: string },
torrentName?: string torrentName?: string
) { ) {
return new Promise(res => { return new Promise<void>(res => {
webtorrent.destroy(err => { webtorrent.destroy(err => {
// Delete torrent file // Delete torrent file
if (torrentName) { if (torrentName) {

View File

@ -195,7 +195,7 @@ async function updateYoutubeDLBinary () {
await ensureDir(binDirectory) await ensureDir(binDirectory)
return new Promise(res => { return new Promise<void>(res => {
request.get(url, { followRedirect: false }, (err, result) => { request.get(url, { followRedirect: false }, (err, result) => {
if (err) { if (err) {
logger.error('Cannot update youtube-dl.', { err }) logger.error('Cannot update youtube-dl.', { err })

View File

@ -41,6 +41,7 @@ class InboxManager {
addInboxMessage (options: QueueParam) { addInboxMessage (options: QueueParam) {
this.inboxQueue.push(options) this.inboxQueue.push(options)
.catch(err => logger.error('Cannot add options in inbox queue.', { options, err }))
} }
static get Instance () { static get Instance () {

View File

@ -65,7 +65,7 @@ const downloadImageQueue = queue<DownloadImageQueueTask, Error>((task, cb) => {
}, QUEUE_CONCURRENCY.AVATAR_PROCESS_IMAGE) }, QUEUE_CONCURRENCY.AVATAR_PROCESS_IMAGE)
function pushAvatarProcessInQueue (task: DownloadImageQueueTask) { function pushAvatarProcessInQueue (task: DownloadImageQueueTask) {
return new Promise((res, rej) => { return new Promise<void>((res, rej) => {
downloadImageQueue.push(task, err => { downloadImageQueue.push(task, err => {
if (err) return rej(err) if (err) return rej(err)

View File

@ -111,7 +111,7 @@ function downloadPlaylistSegments (playlistUrl: string, destinationDir: string,
logger.info('Importing HLS playlist %s', playlistUrl) logger.info('Importing HLS playlist %s', playlistUrl)
return new Promise<string>(async (res, rej) => { return new Promise<void>(async (res, rej) => {
const tmpDirectory = join(CONFIG.STORAGE.TMP_DIR, await generateRandomString(10)) const tmpDirectory = join(CONFIG.STORAGE.TMP_DIR, await generateRandomString(10))
await ensureDir(tmpDirectory) await ensureDir(tmpDirectory)

View File

@ -263,7 +263,7 @@ class Redis {
} }
private addToSet (key: string, value: string) { private addToSet (key: string, value: string) {
return new Promise<string[]>((res, rej) => { return new Promise<void>((res, rej) => {
this.client.sadd(this.prefix + key, value, err => err ? rej(err) : res()) this.client.sadd(this.prefix + key, value, err => err ? rej(err) : res())
}) })
} }

View File

@ -1,12 +1,13 @@
import { eachSeries } from 'async' import { eachSeries } from 'async'
import { NextFunction, Request, RequestHandler, Response } from 'express' import { NextFunction, Request, RequestHandler, Response } from 'express'
import { retryTransactionWrapper } from '../helpers/database-utils'
import { ValidationChain } from 'express-validator' import { ValidationChain } from 'express-validator'
import { ExpressPromiseHandler } from '@server/types/express'
import { retryTransactionWrapper } from '../helpers/database-utils'
// Syntactic sugar to avoid try/catch in express controllers // Syntactic sugar to avoid try/catch in express controllers
// Thanks: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016 // Thanks: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016
export type RequestPromiseHandler = ValidationChain | ((req: Request, res: Response, next: NextFunction) => Promise<any>) export type RequestPromiseHandler = ValidationChain | ExpressPromiseHandler
function asyncMiddleware (fun: RequestPromiseHandler | RequestPromiseHandler[]) { function asyncMiddleware (fun: RequestPromiseHandler | RequestPromiseHandler[]) {
return (req: Request, res: Response, next: NextFunction) => { return (req: Request, res: Response, next: NextFunction) => {

View File

@ -49,7 +49,7 @@ function authenticateSocket (socket: Socket, next: (err?: any) => void) {
} }
function authenticatePromiseIfNeeded (req: express.Request, res: express.Response, authenticateInQuery = false) { function authenticatePromiseIfNeeded (req: express.Request, res: express.Response, authenticateInQuery = false) {
return new Promise(resolve => { return new Promise<void>(resolve => {
// Already authenticated? (or tried to) // Already authenticated? (or tried to)
if (res.locals.oauth?.token.User) return resolve() if (res.locals.oauth?.token.User) return resolve()

View File

@ -14,7 +14,7 @@ import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-code
const startVideoPlaylistsURL = WEBSERVER.SCHEME + '://' + join(WEBSERVER.HOST, 'videos', 'watch', 'playlist') + '/' const startVideoPlaylistsURL = WEBSERVER.SCHEME + '://' + join(WEBSERVER.HOST, 'videos', 'watch', 'playlist') + '/'
const startVideosURL = WEBSERVER.SCHEME + '://' + join(WEBSERVER.HOST, 'videos', 'watch') + '/' const startVideosURL = WEBSERVER.SCHEME + '://' + join(WEBSERVER.HOST, 'videos', 'watch') + '/'
const watchRegex = new RegExp('([^/]+)$') const watchRegex = /([^/]+)$/
const isURLOptions = { const isURLOptions = {
require_host: true, require_host: true,
require_tld: true require_tld: true

View File

@ -1,10 +1,11 @@
import * as express from 'express' import * as express from 'express'
import { body, param, query, ValidationChain } from 'express-validator' import { body, param, query, ValidationChain } from 'express-validator'
import { ExpressPromiseHandler } from '@server/types/express'
import { MUserAccountId } from '@server/types/models'
import { UserRight, VideoPlaylistCreate, VideoPlaylistUpdate } from '../../../../shared' import { UserRight, VideoPlaylistCreate, VideoPlaylistUpdate } from '../../../../shared'
import { logger } from '../../../helpers/logger' import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { areValidationErrors } from '../utils' import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
import { isVideoImage } from '../../../helpers/custom-validators/videos' import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
import { import {
isArrayOf, isArrayOf,
isIdOrUUIDValid, isIdOrUUIDValid,
@ -21,15 +22,15 @@ import {
isVideoPlaylistTimestampValid, isVideoPlaylistTimestampValid,
isVideoPlaylistTypeValid isVideoPlaylistTypeValid
} from '../../../helpers/custom-validators/video-playlists' } from '../../../helpers/custom-validators/video-playlists'
import { isVideoImage } from '../../../helpers/custom-validators/videos'
import { cleanUpReqFiles } from '../../../helpers/express-utils' import { cleanUpReqFiles } from '../../../helpers/express-utils'
import { VideoPlaylistElementModel } from '../../../models/video/video-playlist-element' import { logger } from '../../../helpers/logger'
import { authenticatePromiseIfNeeded } from '../../oauth'
import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
import { doesVideoChannelIdExist, doesVideoExist, doesVideoPlaylistExist, VideoPlaylistFetchType } from '../../../helpers/middlewares' import { doesVideoChannelIdExist, doesVideoExist, doesVideoPlaylistExist, VideoPlaylistFetchType } from '../../../helpers/middlewares'
import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
import { VideoPlaylistElementModel } from '../../../models/video/video-playlist-element'
import { MVideoPlaylist } from '../../../types/models/video/video-playlist' import { MVideoPlaylist } from '../../../types/models/video/video-playlist'
import { MUserAccountId } from '@server/types/models' import { authenticatePromiseIfNeeded } from '../../oauth'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' import { areValidationErrors } from '../utils'
const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([ const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([
body('displayName') body('displayName')
@ -395,7 +396,7 @@ function getCommonPlaylistEditAttributes () {
body('videoChannelId') body('videoChannelId')
.optional() .optional()
.customSanitizer(toIntOrNull) .customSanitizer(toIntOrNull)
] as (ValidationChain | express.Handler)[] ] as (ValidationChain | ExpressPromiseHandler)[]
} }
function checkUserCanManageVideoPlaylist (user: MUserAccountId, videoPlaylist: MVideoPlaylist, right: UserRight, res: express.Response) { function checkUserCanManageVideoPlaylist (user: MUserAccountId, videoPlaylist: MVideoPlaylist, right: UserRight, res: express.Response) {

View File

@ -2,8 +2,10 @@ import * as express from 'express'
import { body, param, query, ValidationChain } from 'express-validator' import { body, param, query, ValidationChain } from 'express-validator'
import { isAbleToUploadVideo } from '@server/lib/user' import { isAbleToUploadVideo } from '@server/lib/user'
import { getServerActor } from '@server/models/application/application' import { getServerActor } from '@server/models/application/application'
import { ExpressPromiseHandler } from '@server/types/express'
import { MVideoFullLight } from '@server/types/models' import { MVideoFullLight } from '@server/types/models'
import { ServerErrorCode, UserRight, VideoChangeOwnershipStatus, VideoPrivacy } from '../../../../shared' import { ServerErrorCode, UserRight, VideoChangeOwnershipStatus, VideoPrivacy } from '../../../../shared'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/video-change-ownership-accept.model' import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/video-change-ownership-accept.model'
import { import {
exists, exists,
@ -54,7 +56,6 @@ import { AccountModel } from '../../../models/account/account'
import { VideoModel } from '../../../models/video/video' import { VideoModel } from '../../../models/video/video'
import { authenticatePromiseIfNeeded } from '../../oauth' import { authenticatePromiseIfNeeded } from '../../oauth'
import { areValidationErrors } from '../utils' import { areValidationErrors } from '../utils'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
const videosAddValidator = getCommonVideoEditAttributes().concat([ const videosAddValidator = getCommonVideoEditAttributes().concat([
body('videofile') body('videofile')
@ -411,7 +412,7 @@ function getCommonVideoEditAttributes () {
.optional() .optional()
.customSanitizer(toIntOrNull) .customSanitizer(toIntOrNull)
.custom(isScheduleVideoUpdatePrivacyValid).withMessage('Should have correct schedule update privacy') .custom(isScheduleVideoUpdatePrivacyValid).withMessage('Should have correct schedule update privacy')
] as (ValidationChain | express.Handler)[] ] as (ValidationChain | ExpressPromiseHandler)[]
} }
const commonVideosFiltersValidator = [ const commonVideosFiltersValidator = [

View File

@ -469,27 +469,33 @@ export class UserNotificationModel extends Model {
? Object.assign(this.formatVideo(this.Video), { channel: this.formatActor(this.Video.VideoChannel) }) ? Object.assign(this.formatVideo(this.Video), { channel: this.formatActor(this.Video.VideoChannel) })
: undefined : undefined
const videoImport = this.VideoImport ? { const videoImport = this.VideoImport
id: this.VideoImport.id, ? {
video: this.VideoImport.Video ? this.formatVideo(this.VideoImport.Video) : undefined, id: this.VideoImport.id,
torrentName: this.VideoImport.torrentName, video: this.VideoImport.Video ? this.formatVideo(this.VideoImport.Video) : undefined,
magnetUri: this.VideoImport.magnetUri, torrentName: this.VideoImport.torrentName,
targetUrl: this.VideoImport.targetUrl magnetUri: this.VideoImport.magnetUri,
} : undefined targetUrl: this.VideoImport.targetUrl
}
: undefined
const comment = this.Comment ? { const comment = this.Comment
id: this.Comment.id, ? {
threadId: this.Comment.getThreadId(), id: this.Comment.id,
account: this.formatActor(this.Comment.Account), threadId: this.Comment.getThreadId(),
video: this.formatVideo(this.Comment.Video) account: this.formatActor(this.Comment.Account),
} : undefined video: this.formatVideo(this.Comment.Video)
}
: undefined
const abuse = this.Abuse ? this.formatAbuse(this.Abuse) : undefined const abuse = this.Abuse ? this.formatAbuse(this.Abuse) : undefined
const videoBlacklist = this.VideoBlacklist ? { const videoBlacklist = this.VideoBlacklist
id: this.VideoBlacklist.id, ? {
video: this.formatVideo(this.VideoBlacklist.Video) id: this.VideoBlacklist.id,
} : undefined video: this.formatVideo(this.VideoBlacklist.Video)
}
: undefined
const account = this.Account ? this.formatActor(this.Account) : undefined const account = this.Account ? this.formatActor(this.Account) : undefined
@ -498,23 +504,25 @@ export class UserNotificationModel extends Model {
Group: 'channel' as 'channel', Group: 'channel' as 'channel',
Person: 'account' as 'account' Person: 'account' as 'account'
} }
const actorFollow = this.ActorFollow ? { const actorFollow = this.ActorFollow
id: this.ActorFollow.id, ? {
state: this.ActorFollow.state, id: this.ActorFollow.id,
follower: { state: this.ActorFollow.state,
id: this.ActorFollow.ActorFollower.Account.id, follower: {
displayName: this.ActorFollow.ActorFollower.Account.getDisplayName(), id: this.ActorFollow.ActorFollower.Account.id,
name: this.ActorFollow.ActorFollower.preferredUsername, displayName: this.ActorFollow.ActorFollower.Account.getDisplayName(),
avatar: this.ActorFollow.ActorFollower.Avatar ? { path: this.ActorFollow.ActorFollower.Avatar.getStaticPath() } : undefined, name: this.ActorFollow.ActorFollower.preferredUsername,
host: this.ActorFollow.ActorFollower.getHost() avatar: this.ActorFollow.ActorFollower.Avatar ? { path: this.ActorFollow.ActorFollower.Avatar.getStaticPath() } : undefined,
}, host: this.ActorFollow.ActorFollower.getHost()
following: { },
type: actorFollowingType[this.ActorFollow.ActorFollowing.type], following: {
displayName: (this.ActorFollow.ActorFollowing.VideoChannel || this.ActorFollow.ActorFollowing.Account).getDisplayName(), type: actorFollowingType[this.ActorFollow.ActorFollowing.type],
name: this.ActorFollow.ActorFollowing.preferredUsername, displayName: (this.ActorFollow.ActorFollowing.VideoChannel || this.ActorFollow.ActorFollowing.Account).getDisplayName(),
host: this.ActorFollow.ActorFollowing.getHost() name: this.ActorFollow.ActorFollowing.preferredUsername,
host: this.ActorFollow.ActorFollowing.getHost()
}
} }
} : undefined : undefined
return { return {
id: this.id, id: this.id,
@ -541,15 +549,17 @@ export class UserNotificationModel extends Model {
} }
formatAbuse (this: UserNotificationModelForApi, abuse: UserNotificationIncludes.AbuseInclude) { formatAbuse (this: UserNotificationModelForApi, abuse: UserNotificationIncludes.AbuseInclude) {
const commentAbuse = abuse.VideoCommentAbuse?.VideoComment ? { const commentAbuse = abuse.VideoCommentAbuse?.VideoComment
threadId: abuse.VideoCommentAbuse.VideoComment.getThreadId(), ? {
threadId: abuse.VideoCommentAbuse.VideoComment.getThreadId(),
video: { video: {
id: abuse.VideoCommentAbuse.VideoComment.Video.id, id: abuse.VideoCommentAbuse.VideoComment.Video.id,
name: abuse.VideoCommentAbuse.VideoComment.Video.name, name: abuse.VideoCommentAbuse.VideoComment.Video.name,
uuid: abuse.VideoCommentAbuse.VideoComment.Video.uuid uuid: abuse.VideoCommentAbuse.VideoComment.Video.uuid
}
} }
} : undefined : undefined
const videoAbuse = abuse.VideoAbuse?.Video ? this.formatVideo(abuse.VideoAbuse.Video) : undefined const videoAbuse = abuse.VideoAbuse?.Video ? this.formatVideo(abuse.VideoAbuse.Video) : undefined

View File

@ -82,9 +82,9 @@ function videoModelToFormattedJSON (video: MVideoFormattable, options?: VideoFor
account: video.VideoChannel.Account.toFormattedSummaryJSON(), account: video.VideoChannel.Account.toFormattedSummaryJSON(),
channel: video.VideoChannel.toFormattedSummaryJSON(), channel: video.VideoChannel.toFormattedSummaryJSON(),
userHistory: userHistory ? { userHistory: userHistory
currentTime: userHistory.currentTime ? { currentTime: userHistory.currentTime }
} : undefined, : undefined,
// Can be added by external plugins // Can be added by external plugins
pluginData: (video as any).pluginData pluginData: (video as any).pluginData

View File

@ -69,23 +69,25 @@ function deleteSettings () {
} }
function getRemoteObjectOrDie ( function getRemoteObjectOrDie (
program: any, program: CommanderStatic,
settings: Settings, settings: Settings,
netrc: Netrc netrc: Netrc
): { url: string, username: string, password: string } { ): { url: string, username: string, password: string } {
if (!program['url'] || !program['username'] || !program['password']) { const options = program.opts()
if (!options.url || !options.username || !options.password) {
// No remote and we don't have program parameters: quit // No remote and we don't have program parameters: quit
if (settings.remotes.length === 0 || Object.keys(netrc.machines).length === 0) { if (settings.remotes.length === 0 || Object.keys(netrc.machines).length === 0) {
if (!program['url']) console.error('--url field is required.') if (!options.url) console.error('--url field is required.')
if (!program['username']) console.error('--username field is required.') if (!options.username) console.error('--username field is required.')
if (!program['password']) console.error('--password field is required.') if (!options.password) console.error('--password field is required.')
return process.exit(-1) return process.exit(-1)
} }
let url: string = program['url'] let url: string = options.url
let username: string = program['username'] let username: string = options.username
let password: string = program['password'] let password: string = options.password
if (!url && settings.default !== -1) url = settings.remotes[settings.default] if (!url && settings.default !== -1) url = settings.remotes[settings.default]
@ -98,9 +100,9 @@ function getRemoteObjectOrDie (
} }
return { return {
url: program['url'], url: options.url,
username: program['username'], username: options.username,
password: program['password'] password: options.password
} }
} }
@ -127,6 +129,8 @@ function buildCommonVideoOptions (command: CommanderStatic) {
} }
async function buildVideoAttributesFromCommander (url: string, command: CommanderStatic, defaultAttributes: any = {}) { async function buildVideoAttributesFromCommander (url: string, command: CommanderStatic, defaultAttributes: any = {}) {
const options = command.opts()
const defaultBooleanAttributes = { const defaultBooleanAttributes = {
nsfw: false, nsfw: false,
commentsEnabled: true, commentsEnabled: true,
@ -137,8 +141,8 @@ async function buildVideoAttributesFromCommander (url: string, command: Commande
const booleanAttributes: { [id in keyof typeof defaultBooleanAttributes]: boolean } | {} = {} const booleanAttributes: { [id in keyof typeof defaultBooleanAttributes]: boolean } | {} = {}
for (const key of Object.keys(defaultBooleanAttributes)) { for (const key of Object.keys(defaultBooleanAttributes)) {
if (command[key] !== undefined) { if (options[key] !== undefined) {
booleanAttributes[key] = command[key] booleanAttributes[key] = options[key]
} else if (defaultAttributes[key] !== undefined) { } else if (defaultAttributes[key] !== undefined) {
booleanAttributes[key] = defaultAttributes[key] booleanAttributes[key] = defaultAttributes[key]
} else { } else {
@ -147,20 +151,20 @@ async function buildVideoAttributesFromCommander (url: string, command: Commande
} }
const videoAttributes = { const videoAttributes = {
name: command['videoName'] || defaultAttributes.name, name: options.videoName || defaultAttributes.name,
category: command['category'] || defaultAttributes.category || undefined, category: options.category || defaultAttributes.category || undefined,
licence: command['licence'] || defaultAttributes.licence || undefined, licence: options.licence || defaultAttributes.licence || undefined,
language: command['language'] || defaultAttributes.language || undefined, language: options.language || defaultAttributes.language || undefined,
privacy: command['privacy'] || defaultAttributes.privacy || VideoPrivacy.PUBLIC, privacy: options.privacy || defaultAttributes.privacy || VideoPrivacy.PUBLIC,
support: command['support'] || defaultAttributes.support || undefined, support: options.support || defaultAttributes.support || undefined,
description: command['videoDescription'] || defaultAttributes.description || undefined, description: options.videoDescription || defaultAttributes.description || undefined,
tags: command['tags'] || defaultAttributes.tags || undefined tags: options.tags || defaultAttributes.tags || undefined
} }
Object.assign(videoAttributes, booleanAttributes) Object.assign(videoAttributes, booleanAttributes)
if (command['channelName']) { if (options.channelName) {
const res = await getVideoChannel(url, command['channelName']) const res = await getVideoChannel(url, options.channelName)
const videoChannel: VideoChannel = res.body const videoChannel: VideoChannel = res.body
Object.assign(videoAttributes, { channelId: videoChannel.id }) Object.assign(videoAttributes, { channelId: videoChannel.id })
@ -173,7 +177,7 @@ async function buildVideoAttributesFromCommander (url: string, command: Commande
return videoAttributes return videoAttributes
} }
function getServerCredentials (program: any) { function getServerCredentials (program: CommanderStatic) {
return Promise.all([ getSettings(), getNetrc() ]) return Promise.all([ getSettings(), getNetrc() ])
.then(([ settings, netrc ]) => { .then(([ settings, netrc ]) => {
return getRemoteObjectOrDie(program, settings, netrc) return getRemoteObjectOrDie(program, settings, netrc)

View File

@ -66,7 +66,8 @@ program
.option('-U, --username <username>', 'Username') .option('-U, --username <username>', 'Username')
.option('-p, --password <token>', 'Password') .option('-p, --password <token>', 'Password')
.option('--default', 'add the entry as the new default') .option('--default', 'add the entry as the new default')
.action(options => { .action((options: program.OptionValues) => {
/* eslint-disable no-import-assign */
prompt.override = options prompt.override = options
prompt.start() prompt.start()
prompt.get({ prompt.get({
@ -102,7 +103,7 @@ program
process.exit(-1) process.exit(-1)
} }
await setInstance(result.url, result.username, result.password, program['default']) await setInstance(result.url, result.username, result.password, options.default)
process.exit(0) process.exit(0)
}) })
@ -160,15 +161,12 @@ program
} }
}) })
program.on('--help', function () { program.addHelpText('after', '\n\n Examples:\n\n' +
console.log(' Examples:') ' $ peertube auth add -u https://peertube.cpy.re -U "PEERTUBE_USER" --password "PEERTUBE_PASSWORD"\n' +
console.log() ' $ peertube auth add -u https://peertube.cpy.re -U root\n' +
console.log(' $ peertube auth add -u https://peertube.cpy.re -U "PEERTUBE_USER" --password "PEERTUBE_PASSWORD"') ' $ peertube auth list\n' +
console.log(' $ peertube auth add -u https://peertube.cpy.re -U root') ' $ peertube auth del https://peertube.cpy.re\n'
console.log(' $ peertube auth list') )
console.log(' $ peertube auth del https://peertube.cpy.re')
console.log()
})
if (!process.argv.slice(2).length) { if (!process.argv.slice(2).length) {
program.outputHelp() program.outputHelp()

View File

@ -10,25 +10,27 @@ program
.option('-p, --password <token>', 'Password') .option('-p, --password <token>', 'Password')
.parse(process.argv) .parse(process.argv)
const options = program.opts()
if ( if (
!program['url'] || !options.url ||
!program['username'] || !options.username ||
!program['password'] !options.password
) { ) {
if (!program['url']) console.error('--url field is required.') if (!options.url) console.error('--url field is required.')
if (!program['username']) console.error('--username field is required.') if (!options.username) console.error('--username field is required.')
if (!program['password']) console.error('--password field is required.') if (!options.password) console.error('--password field is required.')
process.exit(-1) process.exit(-1)
} }
getClient(program.url) getClient(options.url)
.then(res => { .then(res => {
const server = { const server = {
url: program['url'], url: options.url,
user: { user: {
username: program['username'], username: options.username,
password: program['password'] password: options.password
}, },
client: { client: {
id: res.body.client_id, id: res.body.client_id,

View File

@ -45,22 +45,24 @@ command
.usage("[global options] [ -- youtube-dl options]") .usage("[global options] [ -- youtube-dl options]")
.parse(process.argv) .parse(process.argv)
const log = getLogger(program['verbose']) const options = command.opts()
const log = getLogger(options.verbose)
getServerCredentials(command) getServerCredentials(command)
.then(({ url, username, password }) => { .then(({ url, username, password }) => {
if (!program['targetUrl']) { if (!options.targetUrl) {
exitError('--target-url field is required.') exitError('--target-url field is required.')
} }
try { try {
accessSync(program['tmpdir'], constants.R_OK | constants.W_OK) accessSync(options.tmpdir, constants.R_OK | constants.W_OK)
} catch (e) { } catch (e) {
exitError('--tmpdir %s: directory does not exist or is not accessible', program['tmpdir']) exitError('--tmpdir %s: directory does not exist or is not accessible', options.tmpdir)
} }
url = normalizeTargetUrl(url) url = normalizeTargetUrl(url)
program['targetUrl'] = normalizeTargetUrl(program['targetUrl']) options.targetUrl = normalizeTargetUrl(options.targetUrl)
const user = { username, password } const user = { username, password }
@ -76,7 +78,7 @@ async function run (url: string, user: UserInfo) {
const youtubeDL = await safeGetYoutubeDL() const youtubeDL = await safeGetYoutubeDL()
let info = await getYoutubeDLInfo(youtubeDL, program['targetUrl'], command.args) let info = await getYoutubeDLInfo(youtubeDL, options.targetUrl, command.args)
if (!Array.isArray(info)) info = [ info ] if (!Array.isArray(info)) info = [ info ]
@ -92,10 +94,10 @@ async function run (url: string, user: UserInfo) {
let infoArray: any[] let infoArray: any[]
infoArray = [].concat(info) infoArray = [].concat(info)
if (program['first']) { if (options.first) {
infoArray = infoArray.slice(0, program['first']) infoArray = infoArray.slice(0, options.first)
} else if (program['last']) { } else if (options.last) {
infoArray = infoArray.slice(-program['last']) infoArray = infoArray.slice(-options.last)
} }
// Normalize utf8 fields // Normalize utf8 fields
infoArray = infoArray.map(i => normalizeObject(i)) infoArray = infoArray.map(i => normalizeObject(i))
@ -104,12 +106,12 @@ async function run (url: string, user: UserInfo) {
for (const [ index, info ] of infoArray.entries()) { for (const [ index, info ] of infoArray.entries()) {
try { try {
if (index > 0 && program['waitInterval']) { if (index > 0 && options.waitInterval) {
log.info("Wait for %d seconds before continuing.", program['waitInterval'] / 1000) log.info("Wait for %d seconds before continuing.", options.waitInterval / 1000)
await new Promise(res => setTimeout(res, program['waitInterval'])) await new Promise(res => setTimeout(res, options.waitInterval))
} }
await processVideo({ await processVideo({
cwd: program['tmpdir'], cwd: options.tmpdir,
url, url,
user, user,
youtubeInfo: info youtubeInfo: info
@ -119,7 +121,7 @@ async function run (url: string, user: UserInfo) {
} }
} }
log.info('Video/s for user %s imported: %s', user.username, program['targetUrl']) log.info('Video/s for user %s imported: %s', user.username, options.targetUrl)
process.exit(0) process.exit(0)
} }
@ -137,14 +139,14 @@ async function processVideo (parameters: {
log.debug('Fetched object.', videoInfo) log.debug('Fetched object.', videoInfo)
const originallyPublishedAt = buildOriginallyPublishedAt(videoInfo) const originallyPublishedAt = buildOriginallyPublishedAt(videoInfo)
if (program['since'] && originallyPublishedAt && originallyPublishedAt.getTime() < program['since'].getTime()) { if (options.since && originallyPublishedAt && originallyPublishedAt.getTime() < options.since.getTime()) {
log.info('Video "%s" has been published before "%s", don\'t upload it.\n', log.info('Video "%s" has been published before "%s", don\'t upload it.\n',
videoInfo.title, formatDate(program['since'])) videoInfo.title, formatDate(options.since))
return return
} }
if (program['until'] && originallyPublishedAt && originallyPublishedAt.getTime() > program['until'].getTime()) { if (options.until && originallyPublishedAt && originallyPublishedAt.getTime() > options.until.getTime()) {
log.info('Video "%s" has been published after "%s", don\'t upload it.\n', log.info('Video "%s" has been published after "%s", don\'t upload it.\n',
videoInfo.title, formatDate(program['until'])) videoInfo.title, formatDate(options.until))
return return
} }
@ -161,11 +163,11 @@ async function processVideo (parameters: {
log.info('Downloading video "%s"...', videoInfo.title) log.info('Downloading video "%s"...', videoInfo.title)
const options = [ '-f', getYoutubeDLVideoFormat(), ...command.args, '-o', path ] const youtubeDLOptions = [ '-f', getYoutubeDLVideoFormat(), ...command.args, '-o', path ]
try { try {
const youtubeDL = await safeGetYoutubeDL() const youtubeDL = await safeGetYoutubeDL()
const youtubeDLExec = promisify(youtubeDL.exec).bind(youtubeDL) const youtubeDLExec = promisify(youtubeDL.exec).bind(youtubeDL)
const output = await youtubeDLExec(videoInfo.url, options, processOptions) const output = await youtubeDLExec(videoInfo.url, youtubeDLOptions, processOptions)
log.info(output.join('\n')) log.info(output.join('\n'))
await uploadVideoOnPeerTube({ await uploadVideoOnPeerTube({
cwd, cwd,

View File

@ -10,6 +10,7 @@ import { getAdminTokenOrDie, getServerCredentials } from './cli'
import { PeerTubePlugin } from '../../shared/models/plugins/peertube-plugin.model' import { PeerTubePlugin } from '../../shared/models/plugins/peertube-plugin.model'
import { isAbsolute } from 'path' import { isAbsolute } from 'path'
import * as CliTable3 from 'cli-table3' import * as CliTable3 from 'cli-table3'
import commander = require('commander')
program program
.name('plugins') .name('plugins')
@ -33,7 +34,7 @@ program
.option('-p, --password <token>', 'Password') .option('-p, --password <token>', 'Password')
.option('-P --path <path>', 'Install from a path') .option('-P --path <path>', 'Install from a path')
.option('-n, --npm-name <npmName>', 'Install from npm') .option('-n, --npm-name <npmName>', 'Install from npm')
.action((options) => installPluginCLI(options)) .action((options, command) => installPluginCLI(command, options))
program program
.command('update') .command('update')
@ -43,7 +44,7 @@ program
.option('-p, --password <token>', 'Password') .option('-p, --password <token>', 'Password')
.option('-P --path <path>', 'Update from a path') .option('-P --path <path>', 'Update from a path')
.option('-n, --npm-name <npmName>', 'Update from npm') .option('-n, --npm-name <npmName>', 'Update from npm')
.action((options) => updatePluginCLI(options)) .action((options, command) => updatePluginCLI(command, options))
program program
.command('uninstall') .command('uninstall')
@ -52,7 +53,7 @@ program
.option('-U, --username <username>', 'Username') .option('-U, --username <username>', 'Username')
.option('-p, --password <token>', 'Password') .option('-p, --password <token>', 'Password')
.option('-n, --npm-name <npmName>', 'NPM plugin/theme name') .option('-n, --npm-name <npmName>', 'NPM plugin/theme name')
.action(options => uninstallPluginCLI(options)) .action((options, command) => uninstallPluginCLI(command, options))
if (!process.argv.slice(2).length) { if (!process.argv.slice(2).length) {
program.outputHelp() program.outputHelp()
@ -60,6 +61,8 @@ if (!process.argv.slice(2).length) {
program.parse(process.argv) program.parse(process.argv)
const options = program.opts()
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
async function pluginsListCLI () { async function pluginsListCLI () {
@ -67,8 +70,8 @@ async function pluginsListCLI () {
const accessToken = await getAdminTokenOrDie(url, username, password) const accessToken = await getAdminTokenOrDie(url, username, password)
let pluginType: PluginType let pluginType: PluginType
if (program['onlyThemes']) pluginType = PluginType.THEME if (options.onlyThemes) pluginType = PluginType.THEME
if (program['onlyPlugins']) pluginType = PluginType.PLUGIN if (options.onlyPlugins) pluginType = PluginType.PLUGIN
const res = await listPlugins({ const res = await listPlugins({
url, url,
@ -101,27 +104,27 @@ async function pluginsListCLI () {
process.exit(0) process.exit(0)
} }
async function installPluginCLI (options: any) { async function installPluginCLI (command: commander.CommanderStatic, options: commander.OptionValues) {
if (!options['path'] && !options['npmName']) { if (!options.path && !options.npmName) {
console.error('You need to specify the npm name or the path of the plugin you want to install.\n') console.error('You need to specify the npm name or the path of the plugin you want to install.\n')
program.outputHelp() program.outputHelp()
process.exit(-1) process.exit(-1)
} }
if (options['path'] && !isAbsolute(options['path'])) { if (options.path && !isAbsolute(options.path)) {
console.error('Path should be absolute.') console.error('Path should be absolute.')
process.exit(-1) process.exit(-1)
} }
const { url, username, password } = await getServerCredentials(options) const { url, username, password } = await getServerCredentials(command)
const accessToken = await getAdminTokenOrDie(url, username, password) const accessToken = await getAdminTokenOrDie(url, username, password)
try { try {
await installPlugin({ await installPlugin({
url, url,
accessToken, accessToken,
npmName: options['npmName'], npmName: options.npmName,
path: options['path'] path: options.path
}) })
} catch (err) { } catch (err) {
console.error('Cannot install plugin.', err) console.error('Cannot install plugin.', err)
@ -132,27 +135,27 @@ async function installPluginCLI (options: any) {
process.exit(0) process.exit(0)
} }
async function updatePluginCLI (options: any) { async function updatePluginCLI (command: commander.CommanderStatic, options: commander.OptionValues) {
if (!options['path'] && !options['npmName']) { if (!options.path && !options.npmName) {
console.error('You need to specify the npm name or the path of the plugin you want to update.\n') console.error('You need to specify the npm name or the path of the plugin you want to update.\n')
program.outputHelp() program.outputHelp()
process.exit(-1) process.exit(-1)
} }
if (options['path'] && !isAbsolute(options['path'])) { if (options.path && !isAbsolute(options.path)) {
console.error('Path should be absolute.') console.error('Path should be absolute.')
process.exit(-1) process.exit(-1)
} }
const { url, username, password } = await getServerCredentials(options) const { url, username, password } = await getServerCredentials(command)
const accessToken = await getAdminTokenOrDie(url, username, password) const accessToken = await getAdminTokenOrDie(url, username, password)
try { try {
await updatePlugin({ await updatePlugin({
url, url,
accessToken, accessToken,
npmName: options['npmName'], npmName: options.npmName,
path: options['path'] path: options.path
}) })
} catch (err) { } catch (err) {
console.error('Cannot update plugin.', err) console.error('Cannot update plugin.', err)
@ -163,21 +166,21 @@ async function updatePluginCLI (options: any) {
process.exit(0) process.exit(0)
} }
async function uninstallPluginCLI (options: any) { async function uninstallPluginCLI (command: commander.CommanderStatic, options: commander.OptionValues) {
if (!options['npmName']) { if (!options.npmName) {
console.error('You need to specify the npm name of the plugin/theme you want to uninstall.\n') console.error('You need to specify the npm name of the plugin/theme you want to uninstall.\n')
program.outputHelp() program.outputHelp()
process.exit(-1) process.exit(-1)
} }
const { url, username, password } = await getServerCredentials(options) const { url, username, password } = await getServerCredentials(command)
const accessToken = await getAdminTokenOrDie(url, username, password) const accessToken = await getAdminTokenOrDie(url, username, password)
try { try {
await uninstallPlugin({ await uninstallPlugin({
url, url,
accessToken, accessToken,
npmName: options['npmName'] npmName: options.npmName
}) })
} catch (err) { } catch (err) {
console.error('Cannot uninstall plugin.', err) console.error('Cannot uninstall plugin.', err)

View File

@ -14,6 +14,7 @@ import { URL } from 'url'
import { uniq } from 'lodash' import { uniq } from 'lodash'
import bytes = require('bytes') import bytes = require('bytes')
import commander = require('commander')
program program
.name('plugins') .name('plugins')
@ -42,7 +43,7 @@ program
.option('-U, --username <username>', 'Username') .option('-U, --username <username>', 'Username')
.option('-p, --password <token>', 'Password') .option('-p, --password <token>', 'Password')
.option('-v, --video <videoId>', 'Video id to duplicate') .option('-v, --video <videoId>', 'Video id to duplicate')
.action((options) => addRedundancyCLI(options)) .action((options, command) => addRedundancyCLI(options, command))
program program
.command('remove') .command('remove')
@ -51,7 +52,7 @@ program
.option('-U, --username <username>', 'Username') .option('-U, --username <username>', 'Username')
.option('-p, --password <token>', 'Password') .option('-p, --password <token>', 'Password')
.option('-v, --video <videoId>', 'Video id to remove from redundancies') .option('-v, --video <videoId>', 'Video id to remove from redundancies')
.action((options) => removeRedundancyCLI(options)) .action((options, command) => removeRedundancyCLI(options, command))
if (!process.argv.slice(2).length) { if (!process.argv.slice(2).length) {
program.outputHelp() program.outputHelp()
@ -104,13 +105,13 @@ async function listRedundanciesCLI (target: VideoRedundanciesTarget) {
process.exit(0) process.exit(0)
} }
async function addRedundancyCLI (options: { videoId: number }) { async function addRedundancyCLI (options: { video: number }, command: commander.CommanderStatic) {
const { url, username, password } = await getServerCredentials(program) const { url, username, password } = await getServerCredentials(command)
const accessToken = await getAdminTokenOrDie(url, username, password) const accessToken = await getAdminTokenOrDie(url, username, password)
if (!options['video'] || validator.isInt('' + options['video']) === false) { if (!options.video || validator.isInt('' + options.video) === false) {
console.error('You need to specify the video id to duplicate and it should be a number.\n') console.error('You need to specify the video id to duplicate and it should be a number.\n')
program.outputHelp() command.outputHelp()
process.exit(-1) process.exit(-1)
} }
@ -118,7 +119,7 @@ async function addRedundancyCLI (options: { videoId: number }) {
await addVideoRedundancy({ await addVideoRedundancy({
url, url,
accessToken, accessToken,
videoId: options['video'] videoId: options.video
}) })
console.log('Video will be duplicated by your instance!') console.log('Video will be duplicated by your instance!')
@ -137,17 +138,17 @@ async function addRedundancyCLI (options: { videoId: number }) {
} }
} }
async function removeRedundancyCLI (options: { videoId: number }) { async function removeRedundancyCLI (options: { video: number }, command: commander.CommanderStatic) {
const { url, username, password } = await getServerCredentials(program) const { url, username, password } = await getServerCredentials(command)
const accessToken = await getAdminTokenOrDie(url, username, password) const accessToken = await getAdminTokenOrDie(url, username, password)
if (!options['video'] || validator.isInt('' + options['video']) === false) { if (!options.video || validator.isInt('' + options.video) === false) {
console.error('You need to specify the video id to remove from your redundancies.\n') console.error('You need to specify the video id to remove from your redundancies.\n')
program.outputHelp() command.outputHelp()
process.exit(-1) process.exit(-1)
} }
const videoId = parseInt(options['video'] + '', 10) const videoId = parseInt(options.video + '', 10)
let redundancies = await listVideoRedundanciesData(url, accessToken, 'my-videos') let redundancies = await listVideoRedundanciesData(url, accessToken, 'my-videos')
let videoRedundancy = redundancies.find(r => videoId === r.id) let videoRedundancy = redundancies.find(r => videoId === r.id)

View File

@ -82,7 +82,6 @@ const start = async () => {
} }
replServer.defineCommand('reset', resetCommand) replServer.defineCommand('reset', resetCommand)
replServer.defineCommand('r', resetCommand) replServer.defineCommand('r', resetCommand)
} }
start() start()

View File

@ -22,16 +22,18 @@ command
.option('-f, --file <file>', 'Video absolute file path') .option('-f, --file <file>', 'Video absolute file path')
.parse(process.argv) .parse(process.argv)
const options = command.opts()
getServerCredentials(command) getServerCredentials(command)
.then(({ url, username, password }) => { .then(({ url, username, password }) => {
if (!program['videoName'] || !program['file']) { if (!options.videoName || !options.file) {
if (!program['videoName']) console.error('--video-name is required.') if (!options.videoName) console.error('--video-name is required.')
if (!program['file']) console.error('--file is required.') if (!options.file) console.error('--file is required.')
process.exit(-1) process.exit(-1)
} }
if (isAbsolute(program['file']) === false) { if (isAbsolute(options.file) === false) {
console.error('File path should be absolute.') console.error('File path should be absolute.')
process.exit(-1) process.exit(-1)
} }
@ -46,21 +48,21 @@ getServerCredentials(command)
async function run (url: string, username: string, password: string) { async function run (url: string, username: string, password: string) {
const accessToken = await getAccessToken(url, username, password) const accessToken = await getAccessToken(url, username, password)
await access(program['file'], constants.F_OK) await access(options.file, constants.F_OK)
console.log('Uploading %s video...', program['videoName']) console.log('Uploading %s video...', options.videoName)
const videoAttributes = await buildVideoAttributesFromCommander(url, program) const videoAttributes = await buildVideoAttributesFromCommander(url, program)
Object.assign(videoAttributes, { Object.assign(videoAttributes, {
fixture: program['file'], fixture: options.file,
thumbnailfile: program['thumbnail'], thumbnailfile: options.thumbnail,
previewfile: program['preview'] previewfile: options.preview
}) })
try { try {
await uploadVideo(url, accessToken, videoAttributes) await uploadVideo(url, accessToken, videoAttributes)
console.log(`Video ${program['videoName']} uploaded.`) console.log(`Video ${options.videoName} uploaded.`)
process.exit(0) process.exit(0)
} catch (err) { } catch (err) {
console.error(require('util').inspect(err)) console.error(require('util').inspect(err))

View File

@ -8,40 +8,30 @@ import { execSync } from 'child_process'
program program
.name('watch') .name('watch')
.arguments('<url>') .arguments('<url>')
.option('-g, --gui <player>', 'player type', /^(airplay|stdout|chromecast|mpv|vlc|mplayer|xbmc)$/i, 'vlc') .addOption(
new program.Option('-g, --gui <player>', 'player type')
.default('vlc')
.choices([ 'airplay', 'stdout', 'chromecast', 'mpv', 'vlc', 'mplayer', 'xbmc' ])
)
.option('-r, --resolution <res>', 'video resolution', '480') .option('-r, --resolution <res>', 'video resolution', '480')
.on('--help', function () { .addHelpText('after', '\n\n Examples:\n\n' +
console.log(' Available Players:') ' $ peertube watch -g mpv https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10\n' +
console.log() ' $ peertube watch --gui stdout https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10\n' +
console.log(' - mpv') ' $ peertube watch https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10\n'
console.log(' - mplayer') )
console.log(' - vlc') .action((url, options) => run(url, options))
console.log(' - stdout')
console.log(' - xbmc')
console.log(' - airplay')
console.log(' - chromecast')
console.log()
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, cmd) => run(url, cmd))
.parse(process.argv) .parse(process.argv)
function run (url: string, program: any) { function run (url: string, options: program.OptionValues) {
if (!url) { if (!url) {
console.error('<url> positional argument is required.') console.error('<url> positional argument is required.')
process.exit(-1) process.exit(-1)
} }
const cmd = 'node ' + join(__dirname, 'node_modules', 'webtorrent-hybrid', 'bin', 'cmd.js') const cmd = 'node ' + join(__dirname, 'node_modules', 'webtorrent-hybrid', 'bin', 'cmd.js')
const args = ` --${program.gui} ` + const args = ` --${options.gui} ` +
url.replace('videos/watch', 'download/torrents') + url.replace('videos/watch', 'download/torrents') +
`-${program.resolution}.torrent` `-${options.resolution}.torrent`
try { try {
execSync(cmd + args) execSync(cmd + args)

View File

@ -69,17 +69,12 @@ getSettings()
: 'instance ' + settings.remotes[settings.default] + ' selected' : 'instance ' + settings.remotes[settings.default] + ' selected'
program program
.on('--help', function () { .addHelpText('after', '\n\n State: ' + state + '\n\n' +
console.log() ' Examples:\n\n' +
console.log(' State: ' + state) ' $ peertube auth add -u "PEERTUBE_URL" -U "PEERTUBE_USER" --password "PEERTUBE_PASSWORD"\n' +
console.log() ' $ peertube up <videoFile>\n' +
console.log(' Examples:') ' $ peertube watch https://peertube.cpy.re/videos/watch/e8a1af4e-414a-4d58-bfe6-2146eed06d10\n'
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) .parse(process.argv)
}) })
.catch(err => console.error(err)) .catch(err => console.error(err))

3
server/types/express.ts Normal file
View File

@ -0,0 +1,3 @@
import { NextFunction, Request, Response } from 'express'
export type ExpressPromiseHandler = (req: Request<any>, res: Response, next: NextFunction) => Promise<any>

View File

@ -4,7 +4,7 @@ export class MockInstancesIndex {
private readonly indexInstances: { host: string, createdAt: string }[] = [] private readonly indexInstances: { host: string, createdAt: string }[] = []
initialize () { initialize () {
return new Promise(res => { return new Promise<void>(res => {
const app = express() const app = express()
app.use('/', (req: express.Request, res: express.Response, next: express.NextFunction) => { app.use('/', (req: express.Request, res: express.Response, next: express.NextFunction) => {

View File

@ -1,4 +1,5 @@
import { ChildProcess, fork } from 'child_process' import { ChildProcess, fork } from 'child_process'
import { join } from 'path'
import { randomInt } from '../../core-utils/miscs/miscs' import { randomInt } from '../../core-utils/miscs/miscs'
import { parallelTests } from '../server/servers' import { parallelTests } from '../server/servers'
@ -10,7 +11,7 @@ class MockSmtpServer {
private emails: object[] private emails: object[]
private constructor () { private constructor () {
this.emailChildProcess = fork(`${__dirname}/email-child-process`, []) this.emailChildProcess = fork(join(__dirname, 'email-child-process'), [])
this.emailChildProcess.on('message', (msg: any) => { this.emailChildProcess.on('message', (msg: any) => {
if (msg.email) { if (msg.email) {
@ -27,7 +28,7 @@ class MockSmtpServer {
if (this.started) { if (this.started) {
this.emails = emailsCollection this.emails = emailsCollection
return res() return res(undefined)
} }
// ensure maildev isn't started until // ensure maildev isn't started until

View File

@ -21,19 +21,21 @@ function reportAbuse (options: {
}) { }) {
const path = '/api/v1/abuses' const path = '/api/v1/abuses'
const video = options.videoId ? { const video = options.videoId
id: options.videoId, ? {
startAt: options.startAt, id: options.videoId,
endAt: options.endAt startAt: options.startAt,
} : undefined endAt: options.endAt
}
: undefined
const comment = options.commentId ? { const comment = options.commentId
id: options.commentId ? { id: options.commentId }
} : undefined : undefined
const account = options.accountId ? { const account = options.accountId
id: options.accountId ? { id: options.accountId }
} : undefined : undefined
const body = { const body = {
account, account,

View File

@ -14,7 +14,7 @@ export class MockBlocklist {
private server: Server private server: Server
initialize () { initialize () {
return new Promise(res => { return new Promise<void>(res => {
const app = express() const app = express()
app.get('/blocklist', (req: express.Request, res: express.Response) => { app.get('/blocklist', (req: express.Request, res: express.Response) => {

View File

@ -98,7 +98,7 @@ function sendRTMPStream (rtmpBaseUrl: string, streamKey: string, fixtureName = '
} }
function waitFfmpegUntilError (command: ffmpeg.FfmpegCommand, successAfterMS = 10000) { function waitFfmpegUntilError (command: ffmpeg.FfmpegCommand, successAfterMS = 10000) {
return new Promise((res, rej) => { return new Promise<void>((res, rej) => {
command.on('error', err => { command.on('error', err => {
return rej(err) return rej(err)
}) })

2299
yarn.lock

File diff suppressed because it is too large Load Diff