From a15871560f80e07386c1dabb8370cd2664ecfd1f Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 31 Jan 2020 16:56:52 +0100 Subject: Move to eslint --- server/helpers/activitypub.ts | 6 +- server/helpers/audit-logger.ts | 24 +-- server/helpers/core-utils.ts | 66 ++++---- server/helpers/custom-jsonld-signature.ts | 90 +++++------ .../helpers/custom-validators/activitypub/actor.ts | 4 +- .../activitypub/video-comments.ts | 2 - server/helpers/custom-validators/misc.ts | 4 +- server/helpers/custom-validators/plugins.ts | 6 +- .../custom-validators/user-notifications.ts | 3 +- server/helpers/custom-validators/video-abuses.ts | 4 +- server/helpers/custom-validators/video-captions.ts | 2 +- server/helpers/custom-validators/video-imports.ts | 2 +- .../helpers/custom-validators/video-playlists.ts | 6 +- server/helpers/custom-validators/videos.ts | 8 +- server/helpers/express-utils.ts | 16 +- server/helpers/ffmpeg-utils.ts | 179 +++++++++++---------- server/helpers/logger.ts | 13 +- server/helpers/regexp.ts | 2 +- server/helpers/register-ts-paths.ts | 2 +- server/helpers/signup.ts | 2 +- server/helpers/utils.ts | 4 +- server/helpers/webtorrent.ts | 20 ++- server/helpers/youtube-dl.ts | 61 ++++--- 23 files changed, 266 insertions(+), 260 deletions(-) (limited to 'server/helpers') diff --git a/server/helpers/activitypub.ts b/server/helpers/activitypub.ts index 9f9e8fba7..22f8550ca 100644 --- a/server/helpers/activitypub.ts +++ b/server/helpers/activitypub.ts @@ -5,7 +5,7 @@ import { Activity } from '../../shared/models/activitypub' import { ACTIVITY_PUB, REMOTE_SCHEME } from '../initializers/constants' import { signJsonLDObject } from './peertube-crypto' import { pageToStartAndCount } from './core-utils' -import { parse } from 'url' +import { URL } from 'url' import { MActor, MVideoAccountLight } from '../typings/models' function activityPubContextify (data: T) { @@ -161,8 +161,8 @@ function getAPId (activity: string | { id: string }) { } function checkUrlsSameHost (url1: string, url2: string) { - const idHost = parse(url1).host - const actorHost = parse(url2).host + const idHost = new URL(url1).host + const actorHost = new URL(url2).host return idHost && actorHost && idHost.toLowerCase() === actorHost.toLowerCase() } diff --git a/server/helpers/audit-logger.ts b/server/helpers/audit-logger.ts index 9b258dc3a..a4cfeef76 100644 --- a/server/helpers/audit-logger.ts +++ b/server/helpers/audit-logger.ts @@ -81,7 +81,8 @@ function auditLoggerFactory (domain: string) { } abstract class EntityAuditView { - constructor (private keysToKeep: Array, private prefix: string, private entityInfos: object) { } + constructor (private readonly keysToKeep: string[], private readonly prefix: string, private readonly entityInfos: object) { } + toLogKeys (): object { return chain(flatten(this.entityInfos, { delimiter: '-', safe: true })) .pick(this.keysToKeep) @@ -121,7 +122,7 @@ const videoKeysToKeep = [ 'downloadEnabled' ] class VideoAuditView extends EntityAuditView { - constructor (private video: VideoDetails) { + constructor (private readonly video: VideoDetails) { super(videoKeysToKeep, 'video', video) } } @@ -132,7 +133,7 @@ const videoImportKeysToKeep = [ 'video-name' ] class VideoImportAuditView extends EntityAuditView { - constructor (private videoImport: VideoImport) { + constructor (private readonly videoImport: VideoImport) { super(videoImportKeysToKeep, 'video-import', videoImport) } } @@ -151,7 +152,7 @@ const commentKeysToKeep = [ 'account-name' ] class CommentAuditView extends EntityAuditView { - constructor (private comment: VideoComment) { + constructor (private readonly comment: VideoComment) { super(commentKeysToKeep, 'comment', comment) } } @@ -180,7 +181,7 @@ const userKeysToKeep = [ 'videoChannels' ] class UserAuditView extends EntityAuditView { - constructor (private user: User) { + constructor (private readonly user: User) { super(userKeysToKeep, 'user', user) } } @@ -206,7 +207,7 @@ const channelKeysToKeep = [ 'ownerAccount-displayedName' ] class VideoChannelAuditView extends EntityAuditView { - constructor (private channel: VideoChannel) { + constructor (private readonly channel: VideoChannel) { super(channelKeysToKeep, 'channel', channel) } } @@ -221,7 +222,7 @@ const videoAbuseKeysToKeep = [ 'createdAt' ] class VideoAbuseAuditView extends EntityAuditView { - constructor (private videoAbuse: VideoAbuse) { + constructor (private readonly videoAbuse: VideoAbuse) { super(videoAbuseKeysToKeep, 'abuse', videoAbuse) } } @@ -253,9 +254,12 @@ class CustomConfigAuditView extends EntityAuditView { const infos: any = customConfig const resolutionsDict = infos.transcoding.resolutions const resolutionsArray = [] - Object.entries(resolutionsDict).forEach(([resolution, isEnabled]) => { - if (isEnabled) resolutionsArray.push(resolution) - }) + + Object.entries(resolutionsDict) + .forEach(([ resolution, isEnabled ]) => { + if (isEnabled) resolutionsArray.push(resolution) + }) + Object.assign({}, infos, { transcoding: { resolutions: resolutionsArray } }) super(customConfigKeysToKeep, 'config', infos) } diff --git a/server/helpers/core-utils.ts b/server/helpers/core-utils.ts index 7e8252aa4..2cecea450 100644 --- a/server/helpers/core-utils.ts +++ b/server/helpers/core-utils.ts @@ -1,9 +1,11 @@ +/* eslint-disable no-useless-call */ + /* Different from 'utils' because we don't not import other PeerTube modules. Useful to avoid circular dependencies. */ -import { createHash, HexBase64Latin1Encoding, pseudoRandomBytes } from 'crypto' +import { createHash, HexBase64Latin1Encoding, randomBytes } from 'crypto' import { basename, isAbsolute, join, resolve } from 'path' import * as pem from 'pem' import { URL } from 'url' @@ -22,31 +24,31 @@ const objectConverter = (oldObject: any, keyConverter: (e: string) => string, va const newObject = {} Object.keys(oldObject).forEach(oldKey => { const newKey = keyConverter(oldKey) - newObject[ newKey ] = objectConverter(oldObject[ oldKey ], keyConverter, valueConverter) + newObject[newKey] = objectConverter(oldObject[oldKey], keyConverter, valueConverter) }) return newObject } const timeTable = { - ms: 1, - second: 1000, - minute: 60000, - hour: 3600000, - day: 3600000 * 24, - week: 3600000 * 24 * 7, - month: 3600000 * 24 * 30 + ms: 1, + second: 1000, + minute: 60000, + hour: 3600000, + day: 3600000 * 24, + week: 3600000 * 24 * 7, + month: 3600000 * 24 * 30 } export function parseDurationToMs (duration: number | string): number { if (typeof duration === 'number') return duration if (typeof duration === 'string') { - const split = duration.match(/^([\d\.,]+)\s?(\w+)$/) + const split = duration.match(/^([\d.,]+)\s?(\w+)$/) if (split.length === 3) { const len = parseFloat(split[1]) - let unit = split[2].replace(/s$/i,'').toLowerCase() + let unit = split[2].replace(/s$/i, '').toLowerCase() if (unit === 'm') { unit = 'ms' } @@ -73,21 +75,21 @@ export function parseBytes (value: string | number): number { if (value.match(tgm)) { match = value.match(tgm) - return parseInt(match[1], 10) * 1024 * 1024 * 1024 * 1024 - + parseInt(match[2], 10) * 1024 * 1024 * 1024 - + parseInt(match[3], 10) * 1024 * 1024 + return parseInt(match[1], 10) * 1024 * 1024 * 1024 * 1024 + + parseInt(match[2], 10) * 1024 * 1024 * 1024 + + parseInt(match[3], 10) * 1024 * 1024 } else if (value.match(tg)) { match = value.match(tg) - return parseInt(match[1], 10) * 1024 * 1024 * 1024 * 1024 - + parseInt(match[2], 10) * 1024 * 1024 * 1024 + return parseInt(match[1], 10) * 1024 * 1024 * 1024 * 1024 + + parseInt(match[2], 10) * 1024 * 1024 * 1024 } else if (value.match(tm)) { match = value.match(tm) - return parseInt(match[1], 10) * 1024 * 1024 * 1024 * 1024 - + parseInt(match[2], 10) * 1024 * 1024 + return parseInt(match[1], 10) * 1024 * 1024 * 1024 * 1024 + + parseInt(match[2], 10) * 1024 * 1024 } else if (value.match(gm)) { match = value.match(gm) - return parseInt(match[1], 10) * 1024 * 1024 * 1024 - + parseInt(match[2], 10) * 1024 * 1024 + return parseInt(match[1], 10) * 1024 * 1024 * 1024 + + parseInt(match[2], 10) * 1024 * 1024 } else if (value.match(t)) { match = value.match(t) return parseInt(match[1], 10) * 1024 * 1024 * 1024 * 1024 @@ -137,6 +139,7 @@ function getAppNumber () { } let rootPath: string + function root () { if (rootPath) return rootPath @@ -163,7 +166,7 @@ function escapeHTML (stringParam) { '=': '=' } - return String(stringParam).replace(/[&<>"'`=\/]/g, s => entityMap[s]) + return String(stringParam).replace(/[&<>"'`=/]/g, s => entityMap[s]) } function pageToStartAndCount (page: number, itemsPerPage: number) { @@ -202,6 +205,7 @@ function sha1 (str: string | Buffer, encoding: HexBase64Latin1Encoding = 'hex') function execShell (command: string, options?: ExecOptions) { return new Promise<{ err?: Error, stdout: string, stderr: string }>((res, rej) => { exec(command, options, (err, stdout, stderr) => { + // eslint-disable-next-line prefer-promise-reject-errors if (err) return rej({ err, stdout, stderr }) return res({ stdout, stderr }) @@ -226,14 +230,6 @@ function promisify1 (func: (arg: T, cb: (err: any, result: A) => void) => } } -function promisify1WithVoid (func: (arg: T, cb: (err: any) => void) => void): (arg: T) => Promise { - return function promisified (arg: T): Promise { - return new Promise((resolve: () => void, reject: (err: any) => void) => { - func.apply(null, [ arg, (err: any) => err ? reject(err) : resolve() ]) - }) - } -} - function promisify2 (func: (arg1: T, arg2: U, cb: (err: any, result: A) => void) => void): (arg1: T, arg2: U) => Promise { return function promisified (arg1: T, arg2: U): Promise { return new Promise((resolve: (arg: A) => void, reject: (err: any) => void) => { @@ -242,15 +238,7 @@ function promisify2 (func: (arg1: T, arg2: U, cb: (err: any, result: A) } } -function promisify2WithVoid (func: (arg1: T, arg2: U, cb: (err: any) => void) => void): (arg1: T, arg2: U) => Promise { - return function promisified (arg1: T, arg2: U): Promise { - return new Promise((resolve: () => void, reject: (err: any) => void) => { - func.apply(null, [ arg1, arg2, (err: any) => err ? reject(err) : resolve() ]) - }) - } -} - -const pseudoRandomBytesPromise = promisify1(pseudoRandomBytes) +const randomBytesPromise = promisify1(randomBytes) const createPrivateKey = promisify1(pem.createPrivateKey) const getPublicKey = promisify1(pem.getPublicKey) const execPromise2 = promisify2(exec) @@ -280,7 +268,7 @@ export { promisify1, promisify2, - pseudoRandomBytesPromise, + randomBytesPromise, createPrivateKey, getPublicKey, execPromise2, diff --git a/server/helpers/custom-jsonld-signature.ts b/server/helpers/custom-jsonld-signature.ts index a407a9fec..749c50cb3 100644 --- a/server/helpers/custom-jsonld-signature.ts +++ b/server/helpers/custom-jsonld-signature.ts @@ -5,52 +5,52 @@ import { logger } from './logger' const CACHE = { 'https://w3id.org/security/v1': { '@context': { - 'id': '@id', - 'type': '@type', + id: '@id', + type: '@type', - 'dc': 'http://purl.org/dc/terms/', - 'sec': 'https://w3id.org/security#', - 'xsd': 'http://www.w3.org/2001/XMLSchema#', + dc: 'http://purl.org/dc/terms/', + sec: 'https://w3id.org/security#', + xsd: 'http://www.w3.org/2001/XMLSchema#', - 'EcdsaKoblitzSignature2016': 'sec:EcdsaKoblitzSignature2016', - 'Ed25519Signature2018': 'sec:Ed25519Signature2018', - 'EncryptedMessage': 'sec:EncryptedMessage', - 'GraphSignature2012': 'sec:GraphSignature2012', - 'LinkedDataSignature2015': 'sec:LinkedDataSignature2015', - 'LinkedDataSignature2016': 'sec:LinkedDataSignature2016', - 'CryptographicKey': 'sec:Key', + EcdsaKoblitzSignature2016: 'sec:EcdsaKoblitzSignature2016', + Ed25519Signature2018: 'sec:Ed25519Signature2018', + EncryptedMessage: 'sec:EncryptedMessage', + GraphSignature2012: 'sec:GraphSignature2012', + LinkedDataSignature2015: 'sec:LinkedDataSignature2015', + LinkedDataSignature2016: 'sec:LinkedDataSignature2016', + CryptographicKey: 'sec:Key', - 'authenticationTag': 'sec:authenticationTag', - 'canonicalizationAlgorithm': 'sec:canonicalizationAlgorithm', - 'cipherAlgorithm': 'sec:cipherAlgorithm', - 'cipherData': 'sec:cipherData', - 'cipherKey': 'sec:cipherKey', - 'created': { '@id': 'dc:created', '@type': 'xsd:dateTime' }, - 'creator': { '@id': 'dc:creator', '@type': '@id' }, - 'digestAlgorithm': 'sec:digestAlgorithm', - 'digestValue': 'sec:digestValue', - 'domain': 'sec:domain', - 'encryptionKey': 'sec:encryptionKey', - 'expiration': { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, - 'expires': { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, - 'initializationVector': 'sec:initializationVector', - 'iterationCount': 'sec:iterationCount', - 'nonce': 'sec:nonce', - 'normalizationAlgorithm': 'sec:normalizationAlgorithm', - 'owner': { '@id': 'sec:owner', '@type': '@id' }, - 'password': 'sec:password', - 'privateKey': { '@id': 'sec:privateKey', '@type': '@id' }, - 'privateKeyPem': 'sec:privateKeyPem', - 'publicKey': { '@id': 'sec:publicKey', '@type': '@id' }, - 'publicKeyBase58': 'sec:publicKeyBase58', - 'publicKeyPem': 'sec:publicKeyPem', - 'publicKeyWif': 'sec:publicKeyWif', - 'publicKeyService': { '@id': 'sec:publicKeyService', '@type': '@id' }, - 'revoked': { '@id': 'sec:revoked', '@type': 'xsd:dateTime' }, - 'salt': 'sec:salt', - 'signature': 'sec:signature', - 'signatureAlgorithm': 'sec:signingAlgorithm', - 'signatureValue': 'sec:signatureValue' + authenticationTag: 'sec:authenticationTag', + canonicalizationAlgorithm: 'sec:canonicalizationAlgorithm', + cipherAlgorithm: 'sec:cipherAlgorithm', + cipherData: 'sec:cipherData', + cipherKey: 'sec:cipherKey', + created: { '@id': 'dc:created', '@type': 'xsd:dateTime' }, + creator: { '@id': 'dc:creator', '@type': '@id' }, + digestAlgorithm: 'sec:digestAlgorithm', + digestValue: 'sec:digestValue', + domain: 'sec:domain', + encryptionKey: 'sec:encryptionKey', + expiration: { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, + expires: { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, + initializationVector: 'sec:initializationVector', + iterationCount: 'sec:iterationCount', + nonce: 'sec:nonce', + normalizationAlgorithm: 'sec:normalizationAlgorithm', + owner: { '@id': 'sec:owner', '@type': '@id' }, + password: 'sec:password', + privateKey: { '@id': 'sec:privateKey', '@type': '@id' }, + privateKeyPem: 'sec:privateKeyPem', + publicKey: { '@id': 'sec:publicKey', '@type': '@id' }, + publicKeyBase58: 'sec:publicKeyBase58', + publicKeyPem: 'sec:publicKeyPem', + publicKeyWif: 'sec:publicKeyWif', + publicKeyService: { '@id': 'sec:publicKeyService', '@type': '@id' }, + revoked: { '@id': 'sec:revoked', '@type': 'xsd:dateTime' }, + salt: 'sec:salt', + signature: 'sec:signature', + signatureAlgorithm: 'sec:signingAlgorithm', + signatureValue: 'sec:signatureValue' } } } @@ -60,12 +60,12 @@ const nodeDocumentLoader = jsonld.documentLoaders.node() const lru = new AsyncLRU({ max: 10, load: (url, cb) => { - if (CACHE[ url ] !== undefined) { + if (CACHE[url] !== undefined) { logger.debug('Using cache for JSON-LD %s.', url) return cb(null, { contextUrl: null, - document: CACHE[ url ], + document: CACHE[url], documentUrl: url }) } diff --git a/server/helpers/custom-validators/activitypub/actor.ts b/server/helpers/custom-validators/activitypub/actor.ts index fa58e163f..fec67823d 100644 --- a/server/helpers/custom-validators/activitypub/actor.ts +++ b/server/helpers/custom-validators/activitypub/actor.ts @@ -6,7 +6,7 @@ import { isHostValid } from '../servers' import { peertubeTruncate } from '@server/helpers/core-utils' function isActorEndpointsObjectValid (endpointObject: any) { - if (endpointObject && endpointObject.sharedInbox) { + if (endpointObject?.sharedInbox) { return isActivityPubUrlValid(endpointObject.sharedInbox) } @@ -101,8 +101,6 @@ function normalizeActor (actor: any) { actor.summary = null } } - - return } function isValidActorHandle (handle: string) { diff --git a/server/helpers/custom-validators/activitypub/video-comments.ts b/server/helpers/custom-validators/activitypub/video-comments.ts index aa3c246b5..ea852c491 100644 --- a/server/helpers/custom-validators/activitypub/video-comments.ts +++ b/server/helpers/custom-validators/activitypub/video-comments.ts @@ -48,8 +48,6 @@ function normalizeComment (comment: any) { if (typeof comment.url === 'object') comment.url = comment.url.href || comment.url.url else comment.url = comment.id } - - return } function isCommentTypeValid (comment: any): boolean { diff --git a/server/helpers/custom-validators/misc.ts b/server/helpers/custom-validators/misc.ts index 89149b3e0..cf32201c4 100644 --- a/server/helpers/custom-validators/misc.ts +++ b/server/helpers/custom-validators/misc.ts @@ -94,13 +94,13 @@ function isFileValid ( if (isArray(files)) return optional // Should have a file - const fileArray = files[ field ] + const fileArray = files[field] if (!fileArray || fileArray.length === 0) { return optional } // The file should exist - const file = fileArray[ 0 ] + const file = fileArray[0] if (!file || !file.originalname) return false // Check size diff --git a/server/helpers/custom-validators/plugins.ts b/server/helpers/custom-validators/plugins.ts index 3af72547b..5a4531f72 100644 --- a/server/helpers/custom-validators/plugins.ts +++ b/server/helpers/custom-validators/plugins.ts @@ -14,7 +14,7 @@ function isPluginTypeValid (value: any) { function isPluginNameValid (value: string) { return exists(value) && validator.isLength(value, PLUGINS_CONSTRAINTS_FIELDS.NAME) && - validator.matches(value, /^[a-z\-]+$/) + validator.matches(value, /^[a-z-]+$/) } function isNpmPluginNameValid (value: string) { @@ -146,8 +146,8 @@ function isPackageJSONValid (packageJSON: PluginPackageJson, pluginType: PluginT } function isLibraryCodeValid (library: any) { - return typeof library.register === 'function' - && typeof library.unregister === 'function' + return typeof library.register === 'function' && + typeof library.unregister === 'function' } export { diff --git a/server/helpers/custom-validators/user-notifications.ts b/server/helpers/custom-validators/user-notifications.ts index 5a4d10504..8a33b895b 100644 --- a/server/helpers/custom-validators/user-notifications.ts +++ b/server/helpers/custom-validators/user-notifications.ts @@ -9,7 +9,8 @@ function isUserNotificationTypeValid (value: any) { function isUserNotificationSettingValid (value: any) { return exists(value) && - validator.isInt('' + value) && ( + validator.isInt('' + value) && + ( value === UserNotificationSettingValue.NONE || value === UserNotificationSettingValue.WEB || value === UserNotificationSettingValue.EMAIL || diff --git a/server/helpers/custom-validators/video-abuses.ts b/server/helpers/custom-validators/video-abuses.ts index a9478c76a..5c7bc6fd9 100644 --- a/server/helpers/custom-validators/video-abuses.ts +++ b/server/helpers/custom-validators/video-abuses.ts @@ -1,8 +1,6 @@ -import { Response } from 'express' import validator from 'validator' import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants' import { exists } from './misc' -import { VideoAbuseModel } from '../../models/video/video-abuse' const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES @@ -15,7 +13,7 @@ function isVideoAbuseModerationCommentValid (value: string) { } function isVideoAbuseStateValid (value: string) { - return exists(value) && VIDEO_ABUSE_STATES[ value ] !== undefined + return exists(value) && VIDEO_ABUSE_STATES[value] !== undefined } // --------------------------------------------------------------------------- diff --git a/server/helpers/custom-validators/video-captions.ts b/server/helpers/custom-validators/video-captions.ts index d06eb3695..9abbce04a 100644 --- a/server/helpers/custom-validators/video-captions.ts +++ b/server/helpers/custom-validators/video-captions.ts @@ -2,7 +2,7 @@ import { CONSTRAINTS_FIELDS, MIMETYPES, VIDEO_LANGUAGES } from '../../initialize import { exists, isFileValid } from './misc' function isVideoCaptionLanguageValid (value: any) { - return exists(value) && VIDEO_LANGUAGES[ value ] !== undefined + return exists(value) && VIDEO_LANGUAGES[value] !== undefined } const videoCaptionTypes = Object.keys(MIMETYPES.VIDEO_CAPTIONS.MIMETYPE_EXT) diff --git a/server/helpers/custom-validators/video-imports.ts b/server/helpers/custom-validators/video-imports.ts index ffad482b4..c571f5ddd 100644 --- a/server/helpers/custom-validators/video-imports.ts +++ b/server/helpers/custom-validators/video-imports.ts @@ -20,7 +20,7 @@ function isVideoImportTargetUrlValid (url: string) { } function isVideoImportStateValid (value: any) { - return exists(value) && VIDEO_IMPORT_STATES[ value ] !== undefined + return exists(value) && VIDEO_IMPORT_STATES[value] !== undefined } const videoTorrentImportTypes = Object.keys(MIMETYPES.TORRENT.MIMETYPE_EXT).map(m => `(${m})`) diff --git a/server/helpers/custom-validators/video-playlists.ts b/server/helpers/custom-validators/video-playlists.ts index 4bb8384ab..180018fc5 100644 --- a/server/helpers/custom-validators/video-playlists.ts +++ b/server/helpers/custom-validators/video-playlists.ts @@ -1,8 +1,6 @@ import { exists } from './misc' import validator from 'validator' import { CONSTRAINTS_FIELDS, VIDEO_PLAYLIST_PRIVACIES, VIDEO_PLAYLIST_TYPES } from '../../initializers/constants' -import * as express from 'express' -import { VideoPlaylistModel } from '../../models/video/video-playlist' const PLAYLISTS_CONSTRAINT_FIELDS = CONSTRAINTS_FIELDS.VIDEO_PLAYLISTS @@ -15,7 +13,7 @@ function isVideoPlaylistDescriptionValid (value: any) { } function isVideoPlaylistPrivacyValid (value: number) { - return validator.isInt(value + '') && VIDEO_PLAYLIST_PRIVACIES[ value ] !== undefined + return validator.isInt(value + '') && VIDEO_PLAYLIST_PRIVACIES[value] !== undefined } function isVideoPlaylistTimestampValid (value: any) { @@ -23,7 +21,7 @@ function isVideoPlaylistTimestampValid (value: any) { } function isVideoPlaylistTypeValid (value: any) { - return exists(value) && VIDEO_PLAYLIST_TYPES[ value ] !== undefined + return exists(value) && VIDEO_PLAYLIST_TYPES[value] !== undefined } // --------------------------------------------------------------------------- diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts index a9e859e54..cfb430c63 100644 --- a/server/helpers/custom-validators/videos.ts +++ b/server/helpers/custom-validators/videos.ts @@ -20,15 +20,15 @@ function isVideoFilterValid (filter: VideoFilter) { } function isVideoCategoryValid (value: any) { - return value === null || VIDEO_CATEGORIES[ value ] !== undefined + return value === null || VIDEO_CATEGORIES[value] !== undefined } function isVideoStateValid (value: any) { - return exists(value) && VIDEO_STATES[ value ] !== undefined + return exists(value) && VIDEO_STATES[value] !== undefined } function isVideoLicenceValid (value: any) { - return value === null || VIDEO_LICENCES[ value ] !== undefined + return value === null || VIDEO_LICENCES[value] !== undefined } function isVideoLanguageValid (value: any) { @@ -98,7 +98,7 @@ function isVideoImage (files: { [ fieldname: string ]: Express.Multer.File[] } | } function isVideoPrivacyValid (value: number) { - return VIDEO_PRIVACIES[ value ] !== undefined + return VIDEO_PRIVACIES[value] !== undefined } function isScheduleVideoUpdatePrivacyValid (value: number) { diff --git a/server/helpers/express-utils.ts b/server/helpers/express-utils.ts index 9bf6d85a8..f46812977 100644 --- a/server/helpers/express-utils.ts +++ b/server/helpers/express-utils.ts @@ -12,7 +12,7 @@ function buildNSFWFilter (res?: express.Response, paramNSFW?: string) { if (paramNSFW === 'false') return false if (paramNSFW === 'both') return undefined - if (res && res.locals.oauth) { + if (res?.locals.oauth) { const user = res.locals.oauth.token.User // User does not want NSFW videos @@ -28,7 +28,7 @@ function buildNSFWFilter (res?: express.Response, paramNSFW?: string) { return null } -function cleanUpReqFiles (req: { files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[] }) { +function cleanUpReqFiles (req: { files: { [fieldname: string]: Express.Multer.File[] } | Express.Multer.File[] }) { const files = req.files if (!files) return @@ -39,7 +39,7 @@ function cleanUpReqFiles (req: { files: { [ fieldname: string ]: Express.Multer. } for (const key of Object.keys(files)) { - const file = files[ key ] + const file = files[key] if (isArray(file)) file.forEach(f => deleteFileAsync(f.path)) else deleteFileAsync(file.path) @@ -65,18 +65,18 @@ function badRequest (req: express.Request, res: express.Response) { function createReqFiles ( fieldNames: string[], - mimeTypes: { [ id: string ]: string }, - destinations: { [ fieldName: string ]: string } + mimeTypes: { [id: string]: string }, + destinations: { [fieldName: string]: string } ) { const storage = multer.diskStorage({ destination: (req, file, cb) => { - cb(null, destinations[ file.fieldname ]) + cb(null, destinations[file.fieldname]) }, filename: async (req, file, cb) => { let extension: string const fileExtension = extname(file.originalname) - const extensionFromMimetype = mimeTypes[ file.mimetype ] + const extensionFromMimetype = mimeTypes[file.mimetype] // Take the file extension if we don't understand the mime type // We have the OGG/OGV exception too because firefox sends a bad mime type when sending an OGG file @@ -99,7 +99,7 @@ function createReqFiles ( } }) - let fields: { name: string, maxCount: number }[] = [] + const fields: { name: string, maxCount: number }[] = [] for (const fieldName of fieldNames) { fields.push({ name: fieldName, diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts index 7022d3e03..084516e55 100644 --- a/server/helpers/ffmpeg-utils.ts +++ b/server/helpers/ffmpeg-utils.ts @@ -1,6 +1,6 @@ import * as ffmpeg from 'fluent-ffmpeg' import { dirname, join } from 'path' -import { getTargetBitrate, getMaxBitrate, VideoResolution } from '../../shared/models/videos' +import { getMaxBitrate, getTargetBitrate, VideoResolution } from '../../shared/models/videos' import { FFMPEG_NICE, VIDEO_TRANSCODING_FPS } from '../initializers/constants' import { processImage } from './image-utils' import { logger } from './logger' @@ -8,6 +8,71 @@ import { checkFFmpegEncoders } from '../initializers/checker-before-init' import { readFile, remove, writeFile } from 'fs-extra' import { CONFIG } from '../initializers/config' +/** + * A toolbox to play with audio + */ +namespace audio { + export const get = (videoPath: string) => { + // without position, ffprobe considers the last input only + // we make it consider the first input only + // if you pass a file path to pos, then ffprobe acts on that file directly + return new Promise<{ absolutePath: string, audioStream?: any }>((res, rej) => { + + function parseFfprobe (err: any, data: ffmpeg.FfprobeData) { + if (err) return rej(err) + + if ('streams' in data) { + const audioStream = data.streams.find(stream => stream['codec_type'] === 'audio') + if (audioStream) { + return res({ + absolutePath: data.format.filename, + audioStream + }) + } + } + + return res({ absolutePath: data.format.filename }) + } + + return ffmpeg.ffprobe(videoPath, parseFfprobe) + }) + } + + export namespace bitrate { + const baseKbitrate = 384 + + const toBits = (kbits: number) => kbits * 8000 + + export const aac = (bitrate: number): number => { + switch (true) { + case bitrate > toBits(baseKbitrate): + return baseKbitrate + + default: + return -1 // we interpret it as a signal to copy the audio stream as is + } + } + + export const mp3 = (bitrate: number): number => { + /* + a 192kbit/sec mp3 doesn't hold as much information as a 192kbit/sec aac. + That's why, when using aac, we can go to lower kbit/sec. The equivalences + made here are not made to be accurate, especially with good mp3 encoders. + */ + switch (true) { + case bitrate <= toBits(192): + return 128 + + case bitrate <= toBits(384): + return 256 + + default: + return baseKbitrate + } + } + } +} + function computeResolutionsToTranscode (videoFileHeight: number) { const resolutionsEnabled: number[] = [] const configResolutions = CONFIG.TRANSCODING.RESOLUTIONS @@ -24,7 +89,7 @@ function computeResolutionsToTranscode (videoFileHeight: number) { ] for (const resolution of resolutions) { - if (configResolutions[ resolution + 'p' ] === true && videoFileHeight > resolution) { + if (configResolutions[resolution + 'p'] === true && videoFileHeight > resolution) { resolutionsEnabled.push(resolution) } } @@ -48,9 +113,9 @@ async function getVideoStreamCodec (path: string) { const videoCodec = videoStream.codec_tag_string const baseProfileMatrix = { - 'High': '6400', - 'Main': '4D40', - 'Baseline': '42E0' + High: '6400', + Main: '4D40', + Baseline: '42E0' } let baseProfile = baseProfileMatrix[videoStream.profile] @@ -91,7 +156,7 @@ async function getVideoFileFPS (path: string) { if (videoStream === null) return 0 for (const key of [ 'avg_frame_rate', 'r_frame_rate' ]) { - const valuesText: string = videoStream[ key ] + const valuesText: string = videoStream[key] if (!valuesText) continue const [ frames, seconds ] = valuesText.split('/') @@ -191,7 +256,8 @@ interface OnlyAudioTranscodeOptions extends BaseTranscodeOptions { type: 'only-audio' } -type TranscodeOptions = HLSTranscodeOptions +type TranscodeOptions = + HLSTranscodeOptions | VideoTranscodeOptions | MergeAudioTranscodeOptions | OnlyAudioTranscodeOptions @@ -204,13 +270,13 @@ function transcode (options: TranscodeOptions) { .output(options.outputPath) if (options.type === 'quick-transcode') { - command = await buildQuickTranscodeCommand(command) + command = buildQuickTranscodeCommand(command) } else if (options.type === 'hls') { command = await buildHLSCommand(command, options) } else if (options.type === 'merge-audio') { command = await buildAudioMergeCommand(command, options) } else if (options.type === 'only-audio') { - command = await buildOnlyAudioCommand(command, options) + command = buildOnlyAudioCommand(command, options) } else { command = await buildx264Command(command, options) } @@ -247,17 +313,17 @@ async function canDoQuickTranscode (path: string): Promise { // check video params if (videoStream == null) return false - if (videoStream[ 'codec_name' ] !== 'h264') return false - if (videoStream[ 'pix_fmt' ] !== 'yuv420p') return false + if (videoStream['codec_name'] !== 'h264') return false + if (videoStream['pix_fmt'] !== 'yuv420p') return false if (fps < VIDEO_TRANSCODING_FPS.MIN || fps > VIDEO_TRANSCODING_FPS.MAX) return false if (bitRate > getMaxBitrate(resolution.videoFileResolution, fps, VIDEO_TRANSCODING_FPS)) return false // check audio params (if audio stream exists) if (parsedAudio.audioStream) { - if (parsedAudio.audioStream[ 'codec_name' ] !== 'aac') return false + if (parsedAudio.audioStream['codec_name'] !== 'aac') return false - const maxAudioBitrate = audio.bitrate[ 'aac' ](parsedAudio.audioStream[ 'bit_rate' ]) - if (maxAudioBitrate !== -1 && parsedAudio.audioStream[ 'bit_rate' ] > maxAudioBitrate) return false + const maxAudioBitrate = audio.bitrate['aac'](parsedAudio.audioStream['bit_rate']) + if (maxAudioBitrate !== -1 && parsedAudio.audioStream['bit_rate'] > maxAudioBitrate) return false } return true @@ -333,14 +399,14 @@ async function buildAudioMergeCommand (command: ffmpeg.FfmpegCommand, options: M return command } -async function buildOnlyAudioCommand (command: ffmpeg.FfmpegCommand, options: OnlyAudioTranscodeOptions) { - command = await presetOnlyAudio(command) +function buildOnlyAudioCommand (command: ffmpeg.FfmpegCommand, options: OnlyAudioTranscodeOptions) { + command = presetOnlyAudio(command) return command } -async function buildQuickTranscodeCommand (command: ffmpeg.FfmpegCommand) { - command = await presetCopy(command) +function buildQuickTranscodeCommand (command: ffmpeg.FfmpegCommand) { + command = presetCopy(command) command = command.outputOption('-map_metadata -1') // strip all metadata .outputOption('-movflags faststart') @@ -351,7 +417,7 @@ async function buildQuickTranscodeCommand (command: ffmpeg.FfmpegCommand) { async function buildHLSCommand (command: ffmpeg.FfmpegCommand, options: HLSTranscodeOptions) { const videoPath = getHLSVideoPath(options) - if (options.copyCodecs) command = await presetCopy(command) + if (options.copyCodecs) command = presetCopy(command) else command = await buildx264Command(command, options) command = command.outputOption('-hls_time 4') @@ -418,71 +484,6 @@ async function presetH264VeryFast (command: ffmpeg.FfmpegCommand, input: string, return localCommand } -/** - * A toolbox to play with audio - */ -namespace audio { - export const get = (videoPath: string) => { - // without position, ffprobe considers the last input only - // we make it consider the first input only - // if you pass a file path to pos, then ffprobe acts on that file directly - return new Promise<{ absolutePath: string, audioStream?: any }>((res, rej) => { - - function parseFfprobe (err: any, data: ffmpeg.FfprobeData) { - if (err) return rej(err) - - if ('streams' in data) { - const audioStream = data.streams.find(stream => stream[ 'codec_type' ] === 'audio') - if (audioStream) { - return res({ - absolutePath: data.format.filename, - audioStream - }) - } - } - - return res({ absolutePath: data.format.filename }) - } - - return ffmpeg.ffprobe(videoPath, parseFfprobe) - }) - } - - export namespace bitrate { - const baseKbitrate = 384 - - const toBits = (kbits: number) => kbits * 8000 - - export const aac = (bitrate: number): number => { - switch (true) { - case bitrate > toBits(baseKbitrate): - return baseKbitrate - - default: - return -1 // we interpret it as a signal to copy the audio stream as is - } - } - - export const mp3 = (bitrate: number): number => { - /* - a 192kbit/sec mp3 doesn't hold as much information as a 192kbit/sec aac. - That's why, when using aac, we can go to lower kbit/sec. The equivalences - made here are not made to be accurate, especially with good mp3 encoders. - */ - switch (true) { - case bitrate <= toBits(192): - return 128 - - case bitrate <= toBits(384): - return 256 - - default: - return baseKbitrate - } - } - } -} - /** * Standard profile, with variable bitrate audio and faststart. * @@ -513,10 +514,10 @@ async function presetH264 (command: ffmpeg.FfmpegCommand, input: string, resolut // of course this is far from perfect, but it might save some space in the end localCommand = localCommand.audioCodec('aac') - const audioCodecName = parsedAudio.audioStream[ 'codec_name' ] + const audioCodecName = parsedAudio.audioStream['codec_name'] - if (audio.bitrate[ audioCodecName ]) { - const bitrate = audio.bitrate[ audioCodecName ](parsedAudio.audioStream[ 'bit_rate' ]) + if (audio.bitrate[audioCodecName]) { + const bitrate = audio.bitrate[audioCodecName](parsedAudio.audioStream['bit_rate']) if (bitrate !== undefined && bitrate !== -1) localCommand = localCommand.audioBitrate(bitrate) } } @@ -537,14 +538,14 @@ async function presetH264 (command: ffmpeg.FfmpegCommand, input: string, resolut return localCommand } -async function presetCopy (command: ffmpeg.FfmpegCommand): Promise { +function presetCopy (command: ffmpeg.FfmpegCommand): ffmpeg.FfmpegCommand { return command .format('mp4') .videoCodec('copy') .audioCodec('copy') } -async function presetOnlyAudio (command: ffmpeg.FfmpegCommand): Promise { +function presetOnlyAudio (command: ffmpeg.FfmpegCommand): ffmpeg.FfmpegCommand { return command .format('mp4') .audioCodec('copy') diff --git a/server/helpers/logger.ts b/server/helpers/logger.ts index fd2988ad0..b8ae28b3f 100644 --- a/server/helpers/logger.ts +++ b/server/helpers/logger.ts @@ -27,7 +27,7 @@ function getLoggerReplacer () { if (value instanceof Error) { const error = {} - Object.getOwnPropertyNames(value).forEach(key => error[ key ] = value[ key ]) + Object.getOwnPropertyNames(value).forEach(key => { error[key] = value[key] }) return error } @@ -98,19 +98,20 @@ function bunyanLogFactory (level: string) { let args: any[] = [] args.concat(arguments) - if (arguments[ 0 ] instanceof Error) { - meta = arguments[ 0 ].toString() + if (arguments[0] instanceof Error) { + meta = arguments[0].toString() args = Array.prototype.slice.call(arguments, 1) args.push(meta) - } else if (typeof (args[ 0 ]) !== 'string') { - meta = arguments[ 0 ] + } else if (typeof (args[0]) !== 'string') { + meta = arguments[0] args = Array.prototype.slice.call(arguments, 1) args.push(meta) } - logger[ level ].apply(logger, args) + logger[level].apply(logger, args) } } + const bunyanLogger = { trace: bunyanLogFactory('debug'), debug: bunyanLogFactory('debug'), diff --git a/server/helpers/regexp.ts b/server/helpers/regexp.ts index 2336654b0..cfc2be488 100644 --- a/server/helpers/regexp.ts +++ b/server/helpers/regexp.ts @@ -1,8 +1,8 @@ // Thanks to https://regex101.com function regexpCapture (str: string, regex: RegExp, maxIterations = 100) { + const result: RegExpExecArray[] = [] let m: RegExpExecArray let i = 0 - let result: RegExpExecArray[] = [] // tslint:disable:no-conditional-assignment while ((m = regex.exec(str)) !== null && i < maxIterations) { diff --git a/server/helpers/register-ts-paths.ts b/server/helpers/register-ts-paths.ts index e8db369e3..eec7fed3e 100644 --- a/server/helpers/register-ts-paths.ts +++ b/server/helpers/register-ts-paths.ts @@ -1,5 +1,5 @@ import { resolve } from 'path' -const tsConfigPaths = require('tsconfig-paths') +import tsConfigPaths = require('tsconfig-paths') const tsConfig = require('../../tsconfig.json') diff --git a/server/helpers/signup.ts b/server/helpers/signup.ts index 7c73f7c5c..d34ff2db5 100644 --- a/server/helpers/signup.ts +++ b/server/helpers/signup.ts @@ -21,7 +21,7 @@ async function isSignupAllowed (): Promise<{ allowed: boolean, errorMessage?: st function isSignupAllowedForCurrentIP (ip: string) { const addr = ipaddr.parse(ip) - let excludeList = [ 'blacklist' ] + const excludeList = [ 'blacklist' ] let matched = '' // if there is a valid, non-empty whitelist, we exclude all unknown adresses too diff --git a/server/helpers/utils.ts b/server/helpers/utils.ts index 4c6f200f8..7a4c781cc 100644 --- a/server/helpers/utils.ts +++ b/server/helpers/utils.ts @@ -1,6 +1,6 @@ import { ResultList } from '../../shared' import { ApplicationModel } from '../models/application/application' -import { execPromise, execPromise2, pseudoRandomBytesPromise, sha256 } from './core-utils' +import { execPromise, execPromise2, randomBytesPromise, sha256 } from './core-utils' import { logger } from './logger' import { join } from 'path' import { Instance as ParseTorrent } from 'parse-torrent' @@ -14,7 +14,7 @@ function deleteFileAsync (path: string) { } async function generateRandomString (size: number) { - const raw = await pseudoRandomBytesPromise(size) + const raw = await randomBytesPromise(size) return raw.toString('hex') } diff --git a/server/helpers/webtorrent.ts b/server/helpers/webtorrent.ts index 8a5d030df..b25e44fcd 100644 --- a/server/helpers/webtorrent.ts +++ b/server/helpers/webtorrent.ts @@ -39,7 +39,7 @@ async function downloadWebTorrentVideo (target: { magnetUri: string, torrentName if (torrent.files.length !== 1) { if (timer) clearTimeout(timer) - for (let file of torrent.files) { + for (const file of torrent.files) { deleteDownloadedFile({ directoryPath, filepath: file.path }) } @@ -47,15 +47,16 @@ async function downloadWebTorrentVideo (target: { magnetUri: string, torrentName .then(() => rej(new Error('Cannot import torrent ' + torrentId + ': there are multiple files in it'))) } - file = torrent.files[ 0 ] + file = torrent.files[0] // FIXME: avoid creating another stream when https://github.com/webtorrent/webtorrent/issues/1517 is fixed const writeStream = createWriteStream(path) writeStream.on('finish', () => { if (timer) clearTimeout(timer) - return safeWebtorrentDestroy(webtorrent, torrentId, { directoryPath, filepath: file.path }, target.torrentName) + safeWebtorrentDestroy(webtorrent, torrentId, { directoryPath, filepath: file.path }, target.torrentName) .then(() => res(path)) + .catch(err => logger.error('Cannot destroy webtorrent.', { err })) }) file.createReadStream().pipe(writeStream) @@ -63,9 +64,16 @@ async function downloadWebTorrentVideo (target: { magnetUri: string, torrentName torrent.on('error', err => rej(err)) - timer = setTimeout(async () => { - return safeWebtorrentDestroy(webtorrent, torrentId, file ? { directoryPath, filepath: file.path } : undefined, target.torrentName) - .then(() => rej(new Error('Webtorrent download timeout.'))) + timer = setTimeout(() => { + const err = new Error('Webtorrent download timeout.') + + safeWebtorrentDestroy(webtorrent, torrentId, file ? { directoryPath, filepath: file.path } : undefined, target.torrentName) + .then(() => rej(err)) + .catch(destroyErr => { + logger.error('Cannot destroy webtorrent.', { err: destroyErr }) + rej(err) + }) + }, timeout) }) } diff --git a/server/helpers/youtube-dl.ts b/server/helpers/youtube-dl.ts index 577a59dbf..fc9d416a1 100644 --- a/server/helpers/youtube-dl.ts +++ b/server/helpers/youtube-dl.ts @@ -24,20 +24,23 @@ const processOptions = { } function getYoutubeDLInfo (url: string, opts?: string[]): Promise { - return new Promise(async (res, rej) => { + return new Promise((res, rej) => { let args = opts || [ '-j', '--flat-playlist' ] args = wrapWithProxyOptions(args) - const youtubeDL = await safeGetYoutubeDL() - youtubeDL.getInfo(url, args, processOptions, (err, info) => { - if (err) return rej(err) - if (info.is_live === true) return rej(new Error('Cannot download a live streaming.')) + safeGetYoutubeDL() + .then(youtubeDL => { + youtubeDL.getInfo(url, args, processOptions, (err, info) => { + if (err) return rej(err) + if (info.is_live === true) return rej(new Error('Cannot download a live streaming.')) - const obj = buildVideoInfo(normalizeObject(info)) - if (obj.name && obj.name.length < CONSTRAINTS_FIELDS.VIDEOS.NAME.min) obj.name += ' video' + const obj = buildVideoInfo(normalizeObject(info)) + if (obj.name && obj.name.length < CONSTRAINTS_FIELDS.VIDEOS.NAME.min) obj.name += ' video' - return res(obj) - }) + return res(obj) + }) + }) + .catch(err => rej(err)) }) } @@ -54,26 +57,34 @@ function downloadYoutubeDLVideo (url: string, timeout: number) { options = options.concat([ '--ffmpeg-location', process.env.FFMPEG_PATH ]) } - return new Promise(async (res, rej) => { - const youtubeDL = await safeGetYoutubeDL() - youtubeDL.exec(url, options, processOptions, err => { - clearTimeout(timer) + return new Promise((res, rej) => { + safeGetYoutubeDL() + .then(youtubeDL => { + youtubeDL.exec(url, options, processOptions, err => { + clearTimeout(timer) - if (err) { - remove(path) - .catch(err => logger.error('Cannot delete path on YoutubeDL error.', { err })) + if (err) { + remove(path) + .catch(err => logger.error('Cannot delete path on YoutubeDL error.', { err })) - return rej(err) - } + return rej(err) + } - return res(path) - }) + return res(path) + }) - timer = setTimeout(async () => { - await remove(path) + timer = setTimeout(() => { + const err = new Error('YoutubeDL download timeout.') - return rej(new Error('YoutubeDL download timeout.')) - }, timeout) + remove(path) + .finally(() => rej(err)) + .catch(err => { + logger.error('Cannot remove %s in youtubeDL timeout.', path, { err }) + return rej(err) + }) + }, timeout) + }) + .catch(err => rej(err)) }) } @@ -103,7 +114,7 @@ async function updateYoutubeDLBinary () { const url = result.headers.location const downloadFile = request.get(url) - const newVersion = /yt-dl\.org\/downloads\/(\d{4}\.\d\d\.\d\d(\.\d)?)\/youtube-dl/.exec(url)[ 1 ] + const newVersion = /yt-dl\.org\/downloads\/(\d{4}\.\d\d\.\d\d(\.\d)?)\/youtube-dl/.exec(url)[1] downloadFile.on('response', result => { if (result.statusCode !== 200) { -- cgit v1.2.3