Browse Source

Move to eslint

tags/contain
Chocobozzz 2 years ago
parent
commit
a15871560f
No known key found for this signature in database GPG Key ID: 583A612D890159BE
100 changed files with 620 additions and 535 deletions
  1. +88
    -0
      .eslintrc.json
  2. +10
    -5
      package.json
  3. +1
    -1
      scripts/ci.sh
  4. +1
    -1
      scripts/create-import-video-file-job.ts
  5. +1
    -1
      scripts/create-transcoding-job.ts
  6. +4
    -4
      server/controllers/activitypub/client.ts
  7. +4
    -0
      server/controllers/activitypub/inbox.ts
  8. +4
    -9
      server/controllers/api/accounts.ts
  9. +11
    -11
      server/controllers/api/config.ts
  10. +1
    -1
      server/controllers/api/jobs.ts
  11. +1
    -1
      server/controllers/api/overviews.ts
  12. +3
    -3
      server/controllers/api/server/debug.ts
  13. +0
    -1
      server/controllers/api/server/follows.ts
  14. +5
    -5
      server/controllers/api/server/logs.ts
  15. +1
    -1
      server/controllers/api/server/redundancy.ts
  16. +2
    -2
      server/controllers/api/users/me.ts
  17. +2
    -4
      server/controllers/api/users/my-subscriptions.ts
  18. +1
    -2
      server/controllers/api/video-channel.ts
  19. +0
    -1
      server/controllers/api/video-playlist.ts
  20. +11
    -11
      server/controllers/api/videos/import.ts
  21. +2
    -4
      server/controllers/api/videos/index.ts
  22. +1
    -1
      server/controllers/client.ts
  23. +8
    -8
      server/controllers/static.ts
  24. +6
    -9
      server/controllers/tracker.ts
  25. +3
    -3
      server/helpers/activitypub.ts
  26. +14
    -10
      server/helpers/audit-logger.ts
  27. +27
    -39
      server/helpers/core-utils.ts
  28. +45
    -45
      server/helpers/custom-jsonld-signature.ts
  29. +1
    -3
      server/helpers/custom-validators/activitypub/actor.ts
  30. +0
    -2
      server/helpers/custom-validators/activitypub/video-comments.ts
  31. +2
    -2
      server/helpers/custom-validators/misc.ts
  32. +3
    -3
      server/helpers/custom-validators/plugins.ts
  33. +2
    -1
      server/helpers/custom-validators/user-notifications.ts
  34. +1
    -3
      server/helpers/custom-validators/video-abuses.ts
  35. +1
    -1
      server/helpers/custom-validators/video-captions.ts
  36. +1
    -1
      server/helpers/custom-validators/video-imports.ts
  37. +2
    -4
      server/helpers/custom-validators/video-playlists.ts
  38. +4
    -4
      server/helpers/custom-validators/videos.ts
  39. +8
    -8
      server/helpers/express-utils.ts
  40. +90
    -89
      server/helpers/ffmpeg-utils.ts
  41. +7
    -6
      server/helpers/logger.ts
  42. +1
    -1
      server/helpers/regexp.ts
  43. +1
    -1
      server/helpers/register-ts-paths.ts
  44. +1
    -1
      server/helpers/signup.ts
  45. +2
    -2
      server/helpers/utils.ts
  46. +14
    -6
      server/helpers/webtorrent.ts
  47. +36
    -25
      server/helpers/youtube-dl.ts
  48. +2
    -2
      server/initializers/checker-after-init.ts
  49. +2
    -2
      server/initializers/checker-before-init.ts
  50. +2
    -2
      server/initializers/config.ts
  51. +50
    -50
      server/initializers/constants.ts
  52. +0
    -2
      server/initializers/database.ts
  53. +2
    -2
      server/initializers/migrations/0005-email-pod.ts
  54. +2
    -2
      server/initializers/migrations/0010-email-user.ts
  55. +2
    -2
      server/initializers/migrations/0015-video-views.ts
  56. +2
    -2
      server/initializers/migrations/0020-video-likes.ts
  57. +2
    -2
      server/initializers/migrations/0025-video-dislikes.ts
  58. +2
    -2
      server/initializers/migrations/0030-video-category.ts
  59. +2
    -2
      server/initializers/migrations/0035-video-licence.ts
  60. +2
    -2
      server/initializers/migrations/0040-video-nsfw.ts
  61. +2
    -2
      server/initializers/migrations/0045-user-display-nsfw.ts
  62. +2
    -2
      server/initializers/migrations/0050-video-language.ts
  63. +2
    -2
      server/initializers/migrations/0055-video-uuid.ts
  64. +3
    -3
      server/initializers/migrations/0060-video-file.ts
  65. +3
    -3
      server/initializers/migrations/0065-video-file-size.ts
  66. +3
    -3
      server/initializers/migrations/0070-user-video-quota.ts
  67. +3
    -3
      server/initializers/migrations/0075-video-resolutions.ts
  68. +3
    -3
      server/initializers/migrations/0080-video-channels.ts
  69. +3
    -3
      server/initializers/migrations/0085-user-role.ts
  70. +3
    -3
      server/initializers/migrations/0090-videos-description.ts
  71. +3
    -3
      server/initializers/migrations/0095-videos-privacy.ts
  72. +3
    -3
      server/initializers/migrations/0100-activitypub.ts
  73. +3
    -3
      server/initializers/migrations/0105-server-mail.ts
  74. +3
    -3
      server/initializers/migrations/0110-server-key.ts
  75. +3
    -3
      server/initializers/migrations/0115-account-avatar.ts
  76. +3
    -3
      server/initializers/migrations/0120-video-null.ts
  77. +2
    -2
      server/initializers/migrations/0125-table-lowercase.ts
  78. +2
    -2
      server/initializers/migrations/0130-user-autoplay-video.ts
  79. +21
    -21
      server/initializers/migrations/0135-video-channel-actor.ts
  80. +2
    -2
      server/initializers/migrations/0140-actor-url.ts
  81. +2
    -2
      server/initializers/migrations/0145-delete-author.ts
  82. +2
    -2
      server/initializers/migrations/0150-avatar-cascade.ts
  83. +2
    -2
      server/initializers/migrations/0155-video-comments-enabled.ts
  84. +2
    -2
      server/initializers/migrations/0160-account-route.ts
  85. +2
    -2
      server/initializers/migrations/0165-video-route.ts
  86. +2
    -2
      server/initializers/migrations/0170-actor-follow-score.ts
  87. +2
    -2
      server/initializers/migrations/0175-actor-follow-counts.ts
  88. +2
    -2
      server/initializers/migrations/0180-job-table-delete.ts
  89. +2
    -2
      server/initializers/migrations/0185-video-share-url.ts
  90. +2
    -2
      server/initializers/migrations/0190-video-comment-unique-url.ts
  91. +2
    -2
      server/initializers/migrations/0195-support.ts
  92. +2
    -2
      server/initializers/migrations/0200-video-published-at.ts
  93. +2
    -2
      server/initializers/migrations/0205-user-nsfw-policy.ts
  94. +2
    -2
      server/initializers/migrations/0210-video-language.ts
  95. +2
    -2
      server/initializers/migrations/0215-video-support-length.ts
  96. +0
    -1
      server/initializers/migrations/0255-video-blacklist-reason.ts
  97. +3
    -3
      server/initializers/migrations/0285-description-support.ts
  98. +3
    -3
      server/initializers/migrations/0290-account-video-rate-url.ts
  99. +3
    -3
      server/initializers/migrations/0295-video-file-extname.ts
  100. +3
    -3
      server/initializers/migrations/0300-user-videos-history-enabled.ts

+ 88
- 0
.eslintrc.json View File

@@ -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"
]
}
}

+ 10
- 5
package.json View File

@@ -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": {


+ 1
- 1
scripts/ci.sh View File

@@ -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


+ 1
- 1
scripts/create-import-video-file-job.ts View File

@@ -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)
}

+ 1
- 1
scripts/create-transcoding-job.ts View File

@@ -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)
}
}

+ 4
- 4
server/controllers/activitypub/client.ts View File

@@ -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()


+ 4
- 0
server/controllers/activitypub/inbox.ts View File

@@ -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) {


+ 4
- 9
server/controllers/api/accounts.ts View File

@@ -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())


+ 11
- 11
server/controllers/api/config.ts View File

@@ -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


+ 1
- 1
server/controllers/api/jobs.ts View File

@@ -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))
}


+ 1
- 1
server/controllers/api/overviews.ts View File

@@ -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)
])



+ 3
- 3
server/controllers/api/server/debug.ts View File

@@ -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()


+ 0
- 1
server/controllers/api/server/follows.ts View File

@@ -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()


+ 5
- 5
server/controllers/api/server/logs.ts View File

@@ -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


+ 1
- 1
server/controllers/api/server/redundancy.ts View File

@@ -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
})


+ 2
- 2
server/controllers/api/users/me.ts View File

@@ -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)


+ 2
- 4
server/controllers/api/users/my-subscriptions.ts View File

@@ -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()
}


+ 1
- 2
server/controllers/api/video-channel.ts View File

@@ -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())


+ 0
- 1
server/controllers/api/video-playlist.ts View File

@@ -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())


+ 11
- 11
server/controllers/api/videos/import.ts View File

@@ -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


+ 2
- 4
server/controllers/api/videos/index.ts View File

@@ -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())


+ 1
- 1
server/controllers/client.ts View File

@@ -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



+ 8
- 8
server/controllers/static.ts View 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
- 9
server/controllers/tracker.ts View File

@@ -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))
}



+ 3
- 3
server/helpers/activitypub.ts View File

@@ -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()
}


+ 14
- 10
server/helpers/audit-logger.ts View File

@@ -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)
}


+ 27
- 39
server/helpers/core-utils.ts View File

@@ -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) {
'=': '&#x3D;'
}

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,


+ 45
- 45
server/helpers/custom-jsonld-signature.ts View File

@@ -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
})
}


+ 1
- 3
server/helpers/custom-validators/activitypub/actor.ts View File

@@ -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) {


+ 0
- 2
server/helpers/custom-validators/activitypub/video-comments.ts View File

@@ -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 {


+ 2
- 2
server/helpers/custom-validators/misc.ts View File

@@ -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


+ 3
- 3
server/helpers/custom-validators/plugins.ts View File

@@ -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 {


+ 2
- 1
server/helpers/custom-validators/user-notifications.ts View File

@@ -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
- 3
server/helpers/custom-validators/video-abuses.ts View File

@@ -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
}

// ---------------------------------------------------------------------------


+ 1
- 1
server/helpers/custom-validators/video-captions.ts View File

@@ -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)


+ 1
- 1
server/helpers/custom-validators/video-imports.ts View File

@@ -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})`)


+ 2
- 4
server/helpers/custom-validators/video-playlists.ts View File

@@ -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
}

// ---------------------------------------------------------------------------


+ 4
- 4
server/helpers/custom-validators/videos.ts View File

@@ -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) {


+ 8
- 8
server/helpers/express-utils.ts View File

@@ -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,


+ 90
- 89
server/helpers/ffmpeg-utils.ts View File

@@ -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')


+ 7
- 6
server/helpers/logger.ts View File

@@ -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
- 1
server/helpers/regexp.ts View File

@@ -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
- 1
server/helpers/register-ts-paths.ts View File

@@ -1,5 +1,5 @@
import { resolve } from 'path'
const tsConfigPaths = require('tsconfig-paths')
import tsConfigPaths = require('tsconfig-paths')

const tsConfig = require('../../tsconfig.json')



+ 1
- 1
server/helpers/signup.ts View File

@@ -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


+ 2
- 2
server/helpers/utils.ts View File

@@ -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')
}


+ 14
- 6
server/helpers/webtorrent.ts View File

@@ -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)
<