@@ -0,0 +1,88 @@ | |||
{ | |||
"extends": "standard-with-typescript", | |||
"rules": { | |||
"eol-last": [ | |||
"error", | |||
"always" | |||
], | |||
"indent": "off", | |||
"no-lone-blocks": "off", | |||
"no-mixed-operators": "off", | |||
"max-len": [ | |||
"error", | |||
{ | |||
"code": 140 | |||
} | |||
], | |||
"array-bracket-spacing": [ | |||
"error", | |||
"always" | |||
], | |||
"quote-props": [ | |||
"error", | |||
"consistent-as-needed" | |||
], | |||
"padded-blocks": "off", | |||
"no-async-promise-executor": "off", | |||
"dot-notation": "off", | |||
"promise/param-names": "off", | |||
"import/first": "off", | |||
"operator-linebreak": [ | |||
"error", | |||
"after", | |||
{ | |||
"overrides": { | |||
"?": "before", | |||
":": "before" | |||
} | |||
} | |||
], | |||
"@typescript-eslint/indent": [ | |||
"error", | |||
2, | |||
{ | |||
"SwitchCase": 1, | |||
"MemberExpression": "off" | |||
} | |||
], | |||
"@typescript-eslint/consistent-type-assertions": [ | |||
"error", | |||
{ | |||
"assertionStyle": "as" | |||
} | |||
], | |||
"@typescript-eslint/array-type": [ | |||
"error", | |||
{ | |||
"default": "array" | |||
} | |||
], | |||
"@typescript-eslint/restrict-template-expressions": [ | |||
"off", | |||
{ | |||
"allowNumber": "true" | |||
} | |||
], | |||
"@typescript-eslint/quotes": "off", | |||
"@typescript-eslint/no-var-requires": "off", | |||
"@typescript-eslint/explicit-function-return-type": "off", | |||
"@typescript-eslint/promise-function-async": "off", | |||
"@typescript-eslint/no-dynamic-delete": "off", | |||
"@typescript-eslint/strict-boolean-expressions": "off", | |||
"@typescript-eslint/consistent-type-definitions": "off", | |||
"@typescript-eslint/no-misused-promises": "off", | |||
"@typescript-eslint/no-namespace": "off", | |||
"@typescript-eslint/no-extraneous-class": "off", | |||
// bugged but useful | |||
"@typescript-eslint/restrict-plus-operands": "off" | |||
}, | |||
"ignorePatterns": [ | |||
"node_modules/" | |||
], | |||
"parserOptions": { | |||
"project": [ | |||
"./tsconfig.json", | |||
"./server/tools/tsconfig.json" | |||
] | |||
} | |||
} |
@@ -63,7 +63,7 @@ | |||
"ng": "ng", | |||
"nodemon": "nodemon", | |||
"ts-node": "ts-node", | |||
"tslint": "tslint", | |||
"eslint": "eslint", | |||
"concurrently": "concurrently", | |||
"mocha-parallel-tests": "mocha-parallel-tests", | |||
"sasslint": "sass-lint --verbose --no-exit", | |||
@@ -96,7 +96,7 @@ | |||
"express": "^4.12.4", | |||
"express-oauth-server": "^2.0.0", | |||
"express-rate-limit": "^4.0.4", | |||
"express-validator": "^6.1.1", | |||
"express-validator": "^6.4.0", | |||
"flat": "^5.0.0", | |||
"fluent-ffmpeg": "^2.1.0", | |||
"fs-extra": "^8.0.1", | |||
@@ -139,7 +139,7 @@ | |||
"webtorrent": "^0.107.16", | |||
"winston": "3.2.1", | |||
"ws": "^7.0.0", | |||
"youtube-dl": "^3.0.1" | |||
"youtube-dl": "^3.0.2" | |||
}, | |||
"devDependencies": { | |||
"@types/apicache": "^1.2.0", | |||
@@ -180,10 +180,17 @@ | |||
"@types/validator": "^12.0.1", | |||
"@types/webtorrent": "^0.107.0", | |||
"@types/ws": "^7.2.1", | |||
"@typescript-eslint/eslint-plugin": "^2.18.0", | |||
"chai": "^4.1.1", | |||
"chai-json-schema": "^1.5.0", | |||
"chai-xml": "^0.3.2", | |||
"concurrently": "^5.0.0", | |||
"eslint": "^6.8.0", | |||
"eslint-config-standard-with-typescript": "^12.0.1", | |||
"eslint-plugin-import": "^2.20.1", | |||
"eslint-plugin-node": "^11.0.0", | |||
"eslint-plugin-promise": "^4.2.1", | |||
"eslint-plugin-standard": "^4.0.1", | |||
"libxmljs": "0.19.7", | |||
"maildev": "^1.0.0-rc3", | |||
"marked": "^0.8.0", | |||
@@ -195,8 +202,6 @@ | |||
"supertest": "^4.0.2", | |||
"swagger-cli": "^3.0.1", | |||
"ts-node": "8.6.2", | |||
"tslint": "^6.0.0", | |||
"tslint-config-standard": "^9.0.0", | |||
"typescript": "^3.7.2" | |||
}, | |||
"scripty": { | |||
@@ -35,7 +35,7 @@ elif [ "$1" = "api-4" ]; then | |||
npm run build:server | |||
sh ./server/tests/api/ci-4.sh 2 | |||
elif [ "$1" = "lint" ]; then | |||
npm run tslint -- --project ./tsconfig.json -c ./tslint.json server.ts "server/**/*.ts" "shared/**/*.ts" | |||
npm run eslint -- --ext .ts "server/**/*.ts" "shared/**/*.ts" | |||
npm run swagger-cli -- validate support/doc/api/openapi.yaml | |||
( cd client | |||
@@ -38,6 +38,6 @@ async function run () { | |||
} | |||
await JobQueue.Instance.init() | |||
await JobQueue.Instance.createJob({ type: 'video-file-import', payload: dataInput }) | |||
await JobQueue.Instance.createJobWithPromise({ type: 'video-file-import', payload: dataInput }) | |||
console.log('Import job for video %s created.', video.uuid) | |||
} |
@@ -72,7 +72,7 @@ async function run () { | |||
await JobQueue.Instance.init() | |||
for (const d of dataInput) { | |||
await JobQueue.Instance.createJob({ type: 'video-transcoding', payload: d }) | |||
await JobQueue.Instance.createJobWithPromise({ type: 'video-transcoding', payload: d }) | |||
console.log('Transcoding job for video %s created.', video.uuid) | |||
} | |||
} |
@@ -122,7 +122,7 @@ activityPubClientRouter.get('/videos/watch/:videoId/comments/:commentId/activity | |||
activityPubClientRouter.get('/video-channels/:name', | |||
executeIfActivityPub, | |||
asyncMiddleware(localVideoChannelValidator), | |||
asyncMiddleware(videoChannelController) | |||
videoChannelController | |||
) | |||
activityPubClientRouter.get('/video-channels/:name/followers', | |||
executeIfActivityPub, | |||
@@ -154,7 +154,7 @@ activityPubClientRouter.get('/video-playlists/:playlistId', | |||
activityPubClientRouter.get('/video-playlists/:playlistId/:videoId', | |||
executeIfActivityPub, | |||
asyncMiddleware(videoPlaylistElementAPGetValidator), | |||
asyncMiddleware(videoPlaylistElementController) | |||
videoPlaylistElementController | |||
) | |||
// --------------------------------------------------------------------------- | |||
@@ -281,7 +281,7 @@ async function videoCommentsController (req: express.Request, res: express.Respo | |||
return activityPubResponse(activityPubContextify(json), res) | |||
} | |||
async function videoChannelController (req: express.Request, res: express.Response) { | |||
function videoChannelController (req: express.Request, res: express.Response) { | |||
const videoChannel = res.locals.videoChannel | |||
return activityPubResponse(activityPubContextify(videoChannel.toActivityPubObject()), res) | |||
@@ -353,7 +353,7 @@ async function videoPlaylistController (req: express.Request, res: express.Respo | |||
return activityPubResponse(activityPubContextify(object), res) | |||
} | |||
async function videoPlaylistElementController (req: express.Request, res: express.Response) { | |||
function videoPlaylistElementController (req: express.Request, res: express.Response) { | |||
const videoPlaylistElement = res.locals.videoPlaylistElementAP | |||
const json = videoPlaylistElement.toActivityPubObject() | |||
@@ -46,6 +46,10 @@ const inboxQueue = queue<QueueParam, Error>((task, cb) => { | |||
processActivities(task.activities, options) | |||
.then(() => cb()) | |||
.catch(err => { | |||
logger.error('Error in process activities.', { err }) | |||
cb() | |||
}) | |||
}) | |||
function inboxController (req: express.Request, res: express.Response) { | |||
@@ -16,21 +16,17 @@ import { | |||
accountNameWithHostGetValidator, | |||
accountsSortValidator, | |||
ensureAuthUserOwnsAccountValidator, | |||
videosSortValidator, | |||
videoChannelsSortValidator | |||
videoChannelsSortValidator, | |||
videosSortValidator | |||
} from '../../middlewares/validators' | |||
import { AccountModel } from '../../models/account/account' | |||
import { AccountVideoRateModel } from '../../models/account/account-video-rate' | |||
import { VideoModel } from '../../models/video/video' | |||
import { buildNSFWFilter, isUserAbleToSearchRemoteURI, getCountVideos } from '../../helpers/express-utils' | |||
import { buildNSFWFilter, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' | |||
import { VideoChannelModel } from '../../models/video/video-channel' | |||
import { JobQueue } from '../../lib/job-queue' | |||
import { logger } from '../../helpers/logger' | |||
import { VideoPlaylistModel } from '../../models/video/video-playlist' | |||
import { | |||
commonVideoPlaylistFiltersValidator, | |||
videoPlaylistsSearchValidator | |||
} from '../../middlewares/validators/videos/video-playlists' | |||
import { commonVideoPlaylistFiltersValidator, videoPlaylistsSearchValidator } from '../../middlewares/validators/videos/video-playlists' | |||
const accountsRouter = express.Router() | |||
@@ -104,7 +100,6 @@ function getAccount (req: express.Request, res: express.Response) { | |||
if (account.isOutdated()) { | |||
JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: account.Actor.url } }) | |||
.catch(err => logger.error('Cannot create AP refresher job for actor %s.', account.Actor.url, { err })) | |||
} | |||
return res.json(account.toFormattedJSON()) | |||
@@ -31,12 +31,12 @@ configRouter.get('/', | |||
configRouter.get('/custom', | |||
authenticate, | |||
ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), | |||
asyncMiddleware(getCustomConfig) | |||
getCustomConfig | |||
) | |||
configRouter.put('/custom', | |||
authenticate, | |||
ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), | |||
asyncMiddleware(customConfigUpdateValidator), | |||
customConfigUpdateValidator, | |||
asyncMiddleware(updateCustomConfig) | |||
) | |||
configRouter.delete('/custom', | |||
@@ -196,7 +196,7 @@ function getAbout (req: express.Request, res: express.Response) { | |||
return res.json(about).end() | |||
} | |||
async function getCustomConfig (req: express.Request, res: express.Response) { | |||
function getCustomConfig (req: express.Request, res: express.Response) { | |||
const data = customConfig() | |||
return res.json(data).end() | |||
@@ -250,7 +250,7 @@ function getRegisteredThemes () { | |||
function getEnabledResolutions () { | |||
return Object.keys(CONFIG.TRANSCODING.RESOLUTIONS) | |||
.filter(key => CONFIG.TRANSCODING.ENABLED && CONFIG.TRANSCODING.RESOLUTIONS[ key ] === true) | |||
.filter(key => CONFIG.TRANSCODING.ENABLED && CONFIG.TRANSCODING.RESOLUTIONS[key] === true) | |||
.map(r => parseInt(r, 10)) | |||
} | |||
@@ -340,13 +340,13 @@ function customConfig (): CustomConfig { | |||
allowAudioFiles: CONFIG.TRANSCODING.ALLOW_AUDIO_FILES, | |||
threads: CONFIG.TRANSCODING.THREADS, | |||
resolutions: { | |||
'0p': CONFIG.TRANSCODING.RESOLUTIONS[ '0p' ], | |||
'240p': CONFIG.TRANSCODING.RESOLUTIONS[ '240p' ], | |||
'360p': CONFIG.TRANSCODING.RESOLUTIONS[ '360p' ], | |||
'480p': CONFIG.TRANSCODING.RESOLUTIONS[ '480p' ], | |||
'720p': CONFIG.TRANSCODING.RESOLUTIONS[ '720p' ], | |||
'1080p': CONFIG.TRANSCODING.RESOLUTIONS[ '1080p' ], | |||
'2160p': CONFIG.TRANSCODING.RESOLUTIONS[ '2160p' ] | |||
'0p': CONFIG.TRANSCODING.RESOLUTIONS['0p'], | |||
'240p': CONFIG.TRANSCODING.RESOLUTIONS['240p'], | |||
'360p': CONFIG.TRANSCODING.RESOLUTIONS['360p'], | |||
'480p': CONFIG.TRANSCODING.RESOLUTIONS['480p'], | |||
'720p': CONFIG.TRANSCODING.RESOLUTIONS['720p'], | |||
'1080p': CONFIG.TRANSCODING.RESOLUTIONS['1080p'], | |||
'2160p': CONFIG.TRANSCODING.RESOLUTIONS['2160p'] | |||
}, | |||
webtorrent: { | |||
enabled: CONFIG.TRANSCODING.WEBTORRENT.ENABLED | |||
@@ -50,7 +50,7 @@ async function listJobs (req: express.Request, res: express.Response) { | |||
}) | |||
const total = await JobQueue.Instance.count(state) | |||
const result: ResultList<any> = { | |||
const result: ResultList<Job> = { | |||
total, | |||
data: jobs.map(j => formatJob(j, state)) | |||
} | |||
@@ -24,7 +24,7 @@ export { overviewsRouter } | |||
const buildSamples = memoizee(async function () { | |||
const [ categories, channels, tags ] = await Promise.all([ | |||
VideoModel.getRandomFieldSamples('category', OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD, OVERVIEWS.VIDEOS.SAMPLES_COUNT), | |||
VideoModel.getRandomFieldSamples('channelId', OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD ,OVERVIEWS.VIDEOS.SAMPLES_COUNT), | |||
VideoModel.getRandomFieldSamples('channelId', OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD, OVERVIEWS.VIDEOS.SAMPLES_COUNT), | |||
TagModel.getRandomSamples(OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD, OVERVIEWS.VIDEOS.SAMPLES_COUNT) | |||
]) | |||
@@ -1,13 +1,13 @@ | |||
import * as express from 'express' | |||
import { UserRight } from '../../../../shared/models/users' | |||
import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../../middlewares' | |||
import { authenticate, ensureUserHasRight } from '../../../middlewares' | |||
const debugRouter = express.Router() | |||
debugRouter.get('/debug', | |||
authenticate, | |||
ensureUserHasRight(UserRight.MANAGE_DEBUG), | |||
asyncMiddleware(getDebug) | |||
getDebug | |||
) | |||
// --------------------------------------------------------------------------- | |||
@@ -18,7 +18,7 @@ export { | |||
// --------------------------------------------------------------------------- | |||
async function getDebug (req: express.Request, res: express.Response) { | |||
function getDebug (req: express.Request, res: express.Response) { | |||
return res.json({ | |||
ip: req.ip | |||
}).end() | |||
@@ -135,7 +135,6 @@ async function followInstance (req: express.Request, res: express.Response) { | |||
} | |||
JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) | |||
.catch(err => logger.error('Cannot create follow job for %s.', host, err)) | |||
} | |||
return res.status(204).end() | |||
@@ -59,9 +59,9 @@ async function getLogs (req: express.Request, res: express.Response) { | |||
} | |||
async function generateOutput (options: { | |||
startDateQuery: string, | |||
endDateQuery?: string, | |||
level: LogLevel, | |||
startDateQuery: string | |||
endDateQuery?: string | |||
level: LogLevel | |||
nameFilter: RegExp | |||
}) { | |||
const { startDateQuery, level, nameFilter } = options | |||
@@ -111,7 +111,7 @@ async function getOutputFromFile (path: string, startDate: Date, endDate: Date, | |||
const output: any[] = [] | |||
for (let i = lines.length - 1; i >= 0; i--) { | |||
const line = lines[ i ] | |||
const line = lines[i] | |||
let log: any | |||
try { | |||
@@ -122,7 +122,7 @@ async function getOutputFromFile (path: string, startDate: Date, endDate: Date, | |||
} | |||
logTime = new Date(log.timestamp).getTime() | |||
if (logTime >= startTime && logTime <= endTime && logsLevel[ log.level ] >= logsLevel[ level ]) { | |||
if (logTime >= startTime && logTime <= endTime && logsLevel[log.level] >= logsLevel[level]) { | |||
output.push(log) | |||
currentSize += line.length | |||
@@ -84,7 +84,7 @@ async function addVideoRedundancy (req: express.Request, res: express.Response) | |||
videoId: res.locals.onlyVideo.id | |||
} | |||
await JobQueue.Instance.createJob({ | |||
await JobQueue.Instance.createJobWithPromise({ | |||
type: 'video-redundancy', | |||
payload | |||
}) | |||
@@ -39,7 +39,7 @@ meRouter.get('/me', | |||
) | |||
meRouter.delete('/me', | |||
authenticate, | |||
asyncMiddleware(deleteMeValidator), | |||
deleteMeValidator, | |||
asyncMiddleware(deleteMe) | |||
) | |||
@@ -214,7 +214,7 @@ async function updateMe (req: express.Request, res: express.Response) { | |||
} | |||
async function updateMyAvatar (req: express.Request, res: express.Response) { | |||
const avatarPhysicalFile = req.files[ 'avatarfile' ][ 0 ] | |||
const avatarPhysicalFile = req.files['avatarfile'][0] | |||
const user = res.locals.oauth.token.user | |||
const userAccount = await AccountModel.load(user.Account.id) | |||
@@ -19,7 +19,6 @@ import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils' | |||
import { VideoFilter } from '../../../../shared/models/videos/video-query.type' | |||
import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | |||
import { JobQueue } from '../../../lib/job-queue' | |||
import { logger } from '../../../helpers/logger' | |||
import { sequelizeTypescript } from '../../../initializers/database' | |||
const mySubscriptionsRouter = express.Router() | |||
@@ -52,7 +51,7 @@ mySubscriptionsRouter.get('/me/subscriptions', | |||
mySubscriptionsRouter.post('/me/subscriptions', | |||
authenticate, | |||
userSubscriptionAddValidator, | |||
asyncMiddleware(addUserSubscription) | |||
addUserSubscription | |||
) | |||
mySubscriptionsRouter.get('/me/subscriptions/:uri', | |||
@@ -106,7 +105,7 @@ async function areSubscriptionsExist (req: express.Request, res: express.Respons | |||
return res.json(existObject) | |||
} | |||
async function addUserSubscription (req: express.Request, res: express.Response) { | |||
function addUserSubscription (req: express.Request, res: express.Response) { | |||
const user = res.locals.oauth.token.User | |||
const [ name, host ] = req.body.uri.split('@') | |||
@@ -117,7 +116,6 @@ async function addUserSubscription (req: express.Request, res: express.Response) | |||
} | |||
JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) | |||
.catch(err => logger.error('Cannot create follow job for subscription %s.', req.body.uri, err)) | |||
return res.status(204).end() | |||
} | |||
@@ -119,7 +119,7 @@ async function listVideoChannels (req: express.Request, res: express.Response) { | |||
} | |||
async function updateVideoChannelAvatar (req: express.Request, res: express.Response) { | |||
const avatarPhysicalFile = req.files[ 'avatarfile' ][ 0 ] | |||
const avatarPhysicalFile = req.files['avatarfile'][0] | |||
const videoChannel = res.locals.videoChannel | |||
const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannel.toFormattedJSON()) | |||
@@ -232,7 +232,6 @@ async function getVideoChannel (req: express.Request, res: express.Response) { | |||
if (videoChannelWithVideos.isOutdated()) { | |||
JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: videoChannelWithVideos.Actor.url } }) | |||
.catch(err => logger.error('Cannot create AP refresher job for actor %s.', videoChannelWithVideos.Actor.url, { err })) | |||
} | |||
return res.json(videoChannelWithVideos.toFormattedJSON()) | |||
@@ -144,7 +144,6 @@ function getVideoPlaylist (req: express.Request, res: express.Response) { | |||
if (videoPlaylist.isOutdated()) { | |||
JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video-playlist', url: videoPlaylist.url } }) | |||
.catch(err => logger.error('Cannot create AP refresher job for playlist %s.', videoPlaylist.url, { err })) | |||
} | |||
return res.json(videoPlaylist.toFormattedJSON()) | |||
@@ -88,12 +88,12 @@ async function addTorrentImport (req: express.Request, res: express.Response, to | |||
const buf = await readFile(torrentfile.path) | |||
const parsedTorrent = parseTorrent(buf) | |||
videoName = isArray(parsedTorrent.name) ? parsedTorrent.name[ 0 ] : parsedTorrent.name as string | |||
videoName = isArray(parsedTorrent.name) ? parsedTorrent.name[0] : parsedTorrent.name as string | |||
} else { | |||
magnetUri = body.magnetUri | |||
const parsed = magnetUtil.decode(magnetUri) | |||
videoName = isArray(parsed.name) ? parsed.name[ 0 ] : parsed.name as string | |||
videoName = isArray(parsed.name) ? parsed.name[0] : parsed.name as string | |||
} | |||
const video = buildVideo(res.locals.videoChannel.id, body, { name: videoName }) | |||
@@ -124,7 +124,7 @@ async function addTorrentImport (req: express.Request, res: express.Response, to | |||
videoImportId: videoImport.id, | |||
magnetUri | |||
} | |||
await JobQueue.Instance.createJob({ type: 'video-import', payload }) | |||
await JobQueue.Instance.createJobWithPromise({ type: 'video-import', payload }) | |||
auditLogger.create(getAuditIdFromRes(res), new VideoImportAuditView(videoImport.toFormattedJSON())) | |||
@@ -176,7 +176,7 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response) | |||
downloadThumbnail: !thumbnailModel, | |||
downloadPreview: !previewModel | |||
} | |||
await JobQueue.Instance.createJob({ type: 'video-import', payload }) | |||
await JobQueue.Instance.createJobWithPromise({ type: 'video-import', payload }) | |||
auditLogger.create(getAuditIdFromRes(res), new VideoImportAuditView(videoImport.toFormattedJSON())) | |||
@@ -211,7 +211,7 @@ function buildVideo (channelId: number, body: VideoImportCreate, importData: You | |||
async function processThumbnail (req: express.Request, video: VideoModel) { | |||
const thumbnailField = req.files ? req.files['thumbnailfile'] : undefined | |||
if (thumbnailField) { | |||
const thumbnailPhysicalFile = thumbnailField[ 0 ] | |||
const thumbnailPhysicalFile = thumbnailField[0] | |||
return createVideoMiniatureFromExisting(thumbnailPhysicalFile.path, video, ThumbnailType.MINIATURE, false) | |||
} | |||
@@ -231,12 +231,12 @@ async function processPreview (req: express.Request, video: VideoModel) { | |||
} | |||
function insertIntoDB (parameters: { | |||
video: MVideoThumbnailAccountDefault, | |||
thumbnailModel: MThumbnail, | |||
previewModel: MThumbnail, | |||
videoChannel: MChannelAccountDefault, | |||
tags: string[], | |||
videoImportAttributes: Partial<MVideoImport>, | |||
video: MVideoThumbnailAccountDefault | |||
thumbnailModel: MThumbnail | |||
previewModel: MThumbnail | |||
videoChannel: MChannelAccountDefault | |||
tags: string[] | |||
videoImportAttributes: Partial<MVideoImport> | |||
user: MUser | |||
}): Bluebird<MVideoImportFormattable> { | |||
const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters | |||
@@ -12,8 +12,7 @@ import { | |||
VIDEO_CATEGORIES, | |||
VIDEO_LANGUAGES, | |||
VIDEO_LICENCES, | |||
VIDEO_PRIVACIES, | |||
VIDEO_TRANSCODING_FPS | |||
VIDEO_PRIVACIES | |||
} from '../../../initializers/constants' | |||
import { | |||
changeVideoChannelShare, | |||
@@ -308,7 +307,7 @@ async function addVideo (req: express.Request, res: express.Response) { | |||
} | |||
} | |||
await JobQueue.Instance.createJob({ type: 'video-transcoding', payload: dataInput }) | |||
await JobQueue.Instance.createJobWithPromise({ type: 'video-transcoding', payload: dataInput }) | |||
} | |||
Hooks.runAction('action:api.video.uploaded', { video: videoCreated }) | |||
@@ -453,7 +452,6 @@ async function getVideo (req: express.Request, res: express.Response) { | |||
if (video.isOutdated()) { | |||
JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', url: video.url } }) | |||
.catch(err => logger.error('Cannot create AP refresher job for video %s.', video.url, { err })) | |||
} | |||
return res.json(video.toFormattedDetailsJSON()) | |||
@@ -66,7 +66,7 @@ export { | |||
// --------------------------------------------------------------------------- | |||
async function serveServerTranslations (req: express.Request, res: express.Response) { | |||
function serveServerTranslations (req: express.Request, res: express.Response) { | |||
const locale = req.params.locale | |||
const file = req.params.file | |||
@@ -45,12 +45,12 @@ staticRouter.use( | |||
staticRouter.use( | |||
STATIC_DOWNLOAD_PATHS.TORRENTS + ':id-:resolution([0-9]+).torrent', | |||
asyncMiddleware(videosDownloadValidator), | |||
asyncMiddleware(downloadTorrent) | |||
downloadTorrent | |||
) | |||
staticRouter.use( | |||
STATIC_DOWNLOAD_PATHS.TORRENTS + ':id-:resolution([0-9]+)-hls.torrent', | |||
asyncMiddleware(videosDownloadValidator), | |||
asyncMiddleware(downloadHLSVideoFileTorrent) | |||
downloadHLSVideoFileTorrent | |||
) | |||
// Videos path for webseeding | |||
@@ -68,13 +68,13 @@ staticRouter.use( | |||
staticRouter.use( | |||
STATIC_DOWNLOAD_PATHS.VIDEOS + ':id-:resolution([0-9]+).:extension', | |||
asyncMiddleware(videosDownloadValidator), | |||
asyncMiddleware(downloadVideoFile) | |||
downloadVideoFile | |||
) | |||
staticRouter.use( | |||
STATIC_DOWNLOAD_PATHS.HLS_VIDEOS + ':id-:resolution([0-9]+)-fragmented.:extension', | |||
asyncMiddleware(videosDownloadValidator), | |||
asyncMiddleware(downloadHLSVideoFile) | |||
downloadHLSVideoFile | |||
) | |||
// HLS | |||
@@ -325,7 +325,7 @@ async function generateNodeinfo (req: express.Request, res: express.Response) { | |||
return res.send(json).end() | |||
} | |||
async function downloadTorrent (req: express.Request, res: express.Response) { | |||
function downloadTorrent (req: express.Request, res: express.Response) { | |||
const video = res.locals.videoAll | |||
const videoFile = getVideoFile(req, video.VideoFiles) | |||
@@ -334,7 +334,7 @@ async function downloadTorrent (req: express.Request, res: express.Response) { | |||
return res.download(getTorrentFilePath(video, videoFile), `${video.name}-${videoFile.resolution}p.torrent`) | |||
} | |||
async function downloadHLSVideoFileTorrent (req: express.Request, res: express.Response) { | |||
function downloadHLSVideoFileTorrent (req: express.Request, res: express.Response) { | |||
const video = res.locals.videoAll | |||
const playlist = getHLSPlaylist(video) | |||
@@ -346,7 +346,7 @@ async function downloadHLSVideoFileTorrent (req: express.Request, res: express.R | |||
return res.download(getTorrentFilePath(playlist, videoFile), `${video.name}-${videoFile.resolution}p-hls.torrent`) | |||
} | |||
async function downloadVideoFile (req: express.Request, res: express.Response) { | |||
function downloadVideoFile (req: express.Request, res: express.Response) { | |||
const video = res.locals.videoAll | |||
const videoFile = getVideoFile(req, video.VideoFiles) | |||
@@ -355,7 +355,7 @@ async function downloadVideoFile (req: express.Request, res: express.Response) { | |||
return res.download(getVideoFilePath(video, videoFile), `${video.name}-${videoFile.resolution}p${videoFile.extname}`) | |||
} | |||
async function downloadHLSVideoFile (req: express.Request, res: express.Response) { | |||
function downloadHLSVideoFile (req: express.Request, res: express.Response) { | |||
const video = res.locals.videoAll | |||
const playlist = getHLSPlaylist(video) | |||
if (!playlist) return res.status(404).end | |||
@@ -6,7 +6,6 @@ import * as proxyAddr from 'proxy-addr' | |||
import { Server as WebSocketServer } from 'ws' | |||
import { TRACKER_RATE_LIMITS } from '../initializers/constants' | |||
import { VideoFileModel } from '../models/video/video-file' | |||
import { parse } from 'url' | |||
import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' | |||
import { CONFIG } from '../initializers/config' | |||
@@ -38,11 +37,11 @@ const trackerServer = new TrackerServer({ | |||
const key = ip + '-' + infoHash | |||
peersIps[ ip ] = peersIps[ ip ] ? peersIps[ ip ] + 1 : 1 | |||
peersIpInfoHash[ key ] = peersIpInfoHash[ key ] ? peersIpInfoHash[ key ] + 1 : 1 | |||
peersIps[ip] = peersIps[ip] ? peersIps[ip] + 1 : 1 | |||
peersIpInfoHash[key] = peersIpInfoHash[key] ? peersIpInfoHash[key] + 1 : 1 | |||
if (CONFIG.TRACKER.REJECT_TOO_MANY_ANNOUNCES && peersIpInfoHash[ key ] > TRACKER_RATE_LIMITS.ANNOUNCES_PER_IP_PER_INFOHASH) { | |||
return cb(new Error(`Too many requests (${peersIpInfoHash[ key ]} of ip ${ip} for torrent ${infoHash}`)) | |||
if (CONFIG.TRACKER.REJECT_TOO_MANY_ANNOUNCES && peersIpInfoHash[key] > TRACKER_RATE_LIMITS.ANNOUNCES_PER_IP_PER_INFOHASH) { | |||
return cb(new Error(`Too many requests (${peersIpInfoHash[key]} of ip ${ip} for torrent ${infoHash}`)) | |||
} | |||
try { | |||
@@ -87,10 +86,8 @@ function createWebsocketTrackerServer (app: express.Application) { | |||
trackerServer.onWebSocketConnection(ws) | |||
}) | |||
server.on('upgrade', (request, socket, head) => { | |||
const pathname = parse(request.url).pathname | |||
if (pathname === '/tracker/socket') { | |||
server.on('upgrade', (request: express.Request, socket, head) => { | |||
if (request.path === '/tracker/socket') { | |||
wss.handleUpgrade(request, socket, head, ws => wss.emit('connection', ws, request)) | |||
} | |||
@@ -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 <T> (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() | |||
} | |||
@@ -81,7 +81,8 @@ function auditLoggerFactory (domain: string) { | |||
} | |||
abstract class EntityAuditView { | |||
constructor (private keysToKeep: Array<string>, 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) | |||
} | |||
@@ -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<T, A> (func: (arg: T, cb: (err: any, result: A) => void) => | |||
} | |||
} | |||
function promisify1WithVoid<T> (func: (arg: T, cb: (err: any) => void) => void): (arg: T) => Promise<void> { | |||
return function promisified (arg: T): Promise<void> { | |||
return new Promise<void>((resolve: () => void, reject: (err: any) => void) => { | |||
func.apply(null, [ arg, (err: any) => err ? reject(err) : resolve() ]) | |||
}) | |||
} | |||
} | |||
function promisify2<T, U, A> (func: (arg1: T, arg2: U, cb: (err: any, result: A) => void) => void): (arg1: T, arg2: U) => Promise<A> { | |||
return function promisified (arg1: T, arg2: U): Promise<A> { | |||
return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => { | |||
@@ -242,15 +238,7 @@ function promisify2<T, U, A> (func: (arg1: T, arg2: U, cb: (err: any, result: A) | |||
} | |||
} | |||
function promisify2WithVoid<T, U> (func: (arg1: T, arg2: U, cb: (err: any) => void) => void): (arg1: T, arg2: U) => Promise<void> { | |||
return function promisified (arg1: T, arg2: U): Promise<void> { | |||
return new Promise<void>((resolve: () => void, reject: (err: any) => void) => { | |||
func.apply(null, [ arg1, arg2, (err: any) => err ? reject(err) : resolve() ]) | |||
}) | |||
} | |||
} | |||
const pseudoRandomBytesPromise = promisify1<number, Buffer>(pseudoRandomBytes) | |||
const randomBytesPromise = promisify1<number, Buffer>(randomBytes) | |||
const createPrivateKey = promisify1<number, { key: string }>(pem.createPrivateKey) | |||
const getPublicKey = promisify1<string, { publicKey: string }>(pem.getPublicKey) | |||
const execPromise2 = promisify2<string, any, string>(exec) | |||
@@ -280,7 +268,7 @@ export { | |||
promisify1, | |||
promisify2, | |||
pseudoRandomBytesPromise, | |||
randomBytesPromise, | |||
createPrivateKey, | |||
getPublicKey, | |||
execPromise2, | |||
@@ -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 | |||
}) | |||
} | |||
@@ -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) { | |||
@@ -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 { | |||
@@ -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 | |||
@@ -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 { | |||
@@ -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 || | |||
@@ -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 | |||
} | |||
// --------------------------------------------------------------------------- | |||
@@ -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) | |||
@@ -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})`) | |||
@@ -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 | |||
} | |||
// --------------------------------------------------------------------------- | |||
@@ -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) { | |||
@@ -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, | |||
@@ -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<boolean> { | |||
// 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<ffmpeg.FfmpegCommand> { | |||
function presetCopy (command: ffmpeg.FfmpegCommand): ffmpeg.FfmpegCommand { | |||
return command | |||
.format('mp4') | |||
.videoCodec('copy') | |||
.audioCodec('copy') | |||
} | |||
async function presetOnlyAudio (command: ffmpeg.FfmpegCommand): Promise<ffmpeg.FfmpegCommand> { | |||
function presetOnlyAudio (command: ffmpeg.FfmpegCommand): ffmpeg.FfmpegCommand { | |||
return command | |||
.format('mp4') | |||
.audioCodec('copy') | |||
@@ -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'), | |||
@@ -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) { | |||
@@ -1,5 +1,5 @@ | |||
import { resolve } from 'path' | |||
const tsConfigPaths = require('tsconfig-paths') | |||
import tsConfigPaths = require('tsconfig-paths') | |||
const tsConfig = require('../../tsconfig.json') | |||
@@ -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 | |||
@@ -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') | |||
} | |||
@@ -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) | |||
< |