aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/activitypub/actor.ts22
-rw-r--r--server/lib/activitypub/crawl.ts6
-rw-r--r--server/lib/activitypub/follow.ts1
-rw-r--r--server/lib/activitypub/send/send-accept.ts2
-rw-r--r--server/lib/activitypub/send/send-create.ts8
-rw-r--r--server/lib/activitypub/send/send-dislike.ts2
-rw-r--r--server/lib/activitypub/send/send-flag.ts2
-rw-r--r--server/lib/activitypub/send/send-like.ts2
-rw-r--r--server/lib/activitypub/send/send-reject.ts2
-rw-r--r--server/lib/activitypub/send/send-undo.ts10
-rw-r--r--server/lib/activitypub/send/send-update.ts3
-rw-r--r--server/lib/activitypub/send/utils.ts4
-rw-r--r--server/lib/activitypub/video-comments.ts14
-rw-r--r--server/lib/activitypub/video-rates.ts2
-rw-r--r--server/lib/activitypub/videos.ts50
-rw-r--r--server/lib/client-html.ts24
-rw-r--r--server/lib/emailer.ts101
-rw-r--r--server/lib/files-cache/videos-preview-cache.ts3
-rw-r--r--server/lib/job-queue/handlers/utils/activitypub-http-utils.ts2
-rw-r--r--server/lib/job-queue/handlers/video-file-import.ts2
-rw-r--r--server/lib/job-queue/handlers/video-import.ts2
-rw-r--r--server/lib/job-queue/handlers/video-transcoding.ts17
-rw-r--r--server/lib/job-queue/job-queue.ts34
-rw-r--r--server/lib/moderation.ts22
-rw-r--r--server/lib/notifier.ts47
-rw-r--r--server/lib/plugins/plugin-index.ts4
-rw-r--r--server/lib/plugins/plugin-manager.ts44
-rw-r--r--server/lib/redis.ts41
-rw-r--r--server/lib/schedulers/auto-follow-index-instances.ts3
-rw-r--r--server/lib/schedulers/plugins-check-scheduler.ts2
-rw-r--r--server/lib/schedulers/remove-old-views-scheduler.ts2
-rw-r--r--server/lib/schedulers/videos-redundancy-scheduler.ts6
-rw-r--r--server/lib/thumbnail.ts18
-rw-r--r--server/lib/user.ts16
-rw-r--r--server/lib/video-blacklist.ts20
-rw-r--r--server/lib/video-channel.ts3
-rw-r--r--server/lib/video-comment.ts6
37 files changed, 278 insertions, 271 deletions
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts
index f802658cf..3f6edc070 100644
--- a/server/lib/activitypub/actor.ts
+++ b/server/lib/activitypub/actor.ts
@@ -1,6 +1,6 @@
1import * as Bluebird from 'bluebird' 1import * as Bluebird from 'bluebird'
2import { Transaction } from 'sequelize' 2import { Transaction } from 'sequelize'
3import * as url from 'url' 3import { URL } from 'url'
4import * as uuidv4 from 'uuid/v4' 4import * as uuidv4 from 'uuid/v4'
5import { ActivityPubActor, ActivityPubActorType } from '../../../shared/models/activitypub' 5import { ActivityPubActor, ActivityPubActorType } from '../../../shared/models/activitypub'
6import { ActivityPubAttributedTo } from '../../../shared/models/activitypub/objects' 6import { ActivityPubAttributedTo } from '../../../shared/models/activitypub/objects'
@@ -33,8 +33,7 @@ import {
33 MActorFull, 33 MActorFull,
34 MActorFullActor, 34 MActorFullActor,
35 MActorId, 35 MActorId,
36 MChannel, 36 MChannel
37 MChannelAccountDefault
38} from '../../typings/models' 37} from '../../typings/models'
39 38
40// Set account keys, this could be long so process after the account creation and do not block the client 39// Set account keys, this could be long so process after the account creation and do not block the client
@@ -121,13 +120,13 @@ async function getOrCreateActorAndServerAndModel (
121 120
122 if ((created === true || refreshed === true) && updateCollections === true) { 121 if ((created === true || refreshed === true) && updateCollections === true) {
123 const payload = { uri: actor.outboxUrl, type: 'activity' as 'activity' } 122 const payload = { uri: actor.outboxUrl, type: 'activity' as 'activity' }
124 await JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload }) 123 await JobQueue.Instance.createJobWithPromise({ type: 'activitypub-http-fetcher', payload })
125 } 124 }
126 125
127 // We created a new account: fetch the playlists 126 // We created a new account: fetch the playlists
128 if (created === true && actor.Account && accountPlaylistsUrl) { 127 if (created === true && actor.Account && accountPlaylistsUrl) {
129 const payload = { uri: accountPlaylistsUrl, accountId: actor.Account.id, type: 'account-playlists' as 'account-playlists' } 128 const payload = { uri: accountPlaylistsUrl, accountId: actor.Account.id, type: 'account-playlists' as 'account-playlists' }
130 await JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload }) 129 await JobQueue.Instance.createJobWithPromise({ type: 'activitypub-http-fetcher', payload })
131 } 130 }
132 131
133 return actorRefreshed 132 return actorRefreshed
@@ -215,7 +214,7 @@ async function fetchActorTotalItems (url: string) {
215 } 214 }
216} 215}
217 216
218async function getAvatarInfoIfExists (actorJSON: ActivityPubActor) { 217function getAvatarInfoIfExists (actorJSON: ActivityPubActor) {
219 if ( 218 if (
220 actorJSON.icon && actorJSON.icon.type === 'Image' && MIMETYPES.IMAGE.MIMETYPE_EXT[actorJSON.icon.mediaType] !== undefined && 219 actorJSON.icon && actorJSON.icon.type === 'Image' && MIMETYPES.IMAGE.MIMETYPE_EXT[actorJSON.icon.mediaType] !== undefined &&
221 isActivityPubUrlValid(actorJSON.icon.url) 220 isActivityPubUrlValid(actorJSON.icon.url)
@@ -271,7 +270,10 @@ async function refreshActorIfNeeded <T extends MActorFull | MActorAccountChannel
271 270
272 if (statusCode === 404) { 271 if (statusCode === 404) {
273 logger.info('Deleting actor %s because there is a 404 in refresh actor.', actor.url) 272 logger.info('Deleting actor %s because there is a 404 in refresh actor.', actor.url)
274 actor.Account ? actor.Account.destroy() : actor.VideoChannel.destroy() 273 actor.Account
274 ? await actor.Account.destroy()
275 : await actor.VideoChannel.destroy()
276
275 return { actor: undefined, refreshed: false } 277 return { actor: undefined, refreshed: false }
276 } 278 }
277 279
@@ -337,14 +339,14 @@ function saveActorAndServerAndModelIfNotExist (
337 ownerActor?: MActorFullActor, 339 ownerActor?: MActorFullActor,
338 t?: Transaction 340 t?: Transaction
339): Bluebird<MActorFullActor> | Promise<MActorFullActor> { 341): Bluebird<MActorFullActor> | Promise<MActorFullActor> {
340 let actor = result.actor 342 const actor = result.actor
341 343
342 if (t !== undefined) return save(t) 344 if (t !== undefined) return save(t)
343 345
344 return sequelizeTypescript.transaction(t => save(t)) 346 return sequelizeTypescript.transaction(t => save(t))
345 347
346 async function save (t: Transaction) { 348 async function save (t: Transaction) {
347 const actorHost = url.parse(actor.url).host 349 const actorHost = new URL(actor.url).host
348 350
349 const serverOptions = { 351 const serverOptions = {
350 where: { 352 where: {
@@ -402,7 +404,7 @@ type FetchRemoteActorResult = {
402 support?: string 404 support?: string
403 playlists?: string 405 playlists?: string
404 avatar?: { 406 avatar?: {
405 name: string, 407 name: string
406 fileUrl: string 408 fileUrl: string
407 } 409 }
408 attributedTo: ActivityPubAttributedTo[] 410 attributedTo: ActivityPubAttributedTo[]
diff --git a/server/lib/activitypub/crawl.ts b/server/lib/activitypub/crawl.ts
index 9e469e3e6..eeafdf4ba 100644
--- a/server/lib/activitypub/crawl.ts
+++ b/server/lib/activitypub/crawl.ts
@@ -3,7 +3,7 @@ import { doRequest } from '../../helpers/requests'
3import { logger } from '../../helpers/logger' 3import { logger } from '../../helpers/logger'
4import * as Bluebird from 'bluebird' 4import * as Bluebird from 'bluebird'
5import { ActivityPubOrderedCollection } from '../../../shared/models/activitypub' 5import { ActivityPubOrderedCollection } from '../../../shared/models/activitypub'
6import { parse } from 'url' 6import { URL } from 'url'
7 7
8type HandlerFunction<T> = (items: T[]) => (Promise<any> | Bluebird<any>) 8type HandlerFunction<T> = (items: T[]) => (Promise<any> | Bluebird<any>)
9type CleanerFunction = (startedDate: Date) => (Promise<any> | Bluebird<any>) 9type CleanerFunction = (startedDate: Date) => (Promise<any> | Bluebird<any>)
@@ -24,7 +24,7 @@ async function crawlCollectionPage <T> (uri: string, handler: HandlerFunction<T>
24 const response = await doRequest<ActivityPubOrderedCollection<T>>(options) 24 const response = await doRequest<ActivityPubOrderedCollection<T>>(options)
25 const firstBody = response.body 25 const firstBody = response.body
26 26
27 let limit = ACTIVITY_PUB.FETCH_PAGE_LIMIT 27 const limit = ACTIVITY_PUB.FETCH_PAGE_LIMIT
28 let i = 0 28 let i = 0
29 let nextLink = firstBody.first 29 let nextLink = firstBody.first
30 while (nextLink && i < limit) { 30 while (nextLink && i < limit) {
@@ -32,7 +32,7 @@ async function crawlCollectionPage <T> (uri: string, handler: HandlerFunction<T>
32 32
33 if (typeof nextLink === 'string') { 33 if (typeof nextLink === 'string') {
34 // Don't crawl ourselves 34 // Don't crawl ourselves
35 const remoteHost = parse(nextLink).host 35 const remoteHost = new URL(nextLink).host
36 if (remoteHost === WEBSERVER.HOST) continue 36 if (remoteHost === WEBSERVER.HOST) continue
37 37
38 options.uri = nextLink 38 options.uri = nextLink
diff --git a/server/lib/activitypub/follow.ts b/server/lib/activitypub/follow.ts
index 1abf43cd4..a1c95504e 100644
--- a/server/lib/activitypub/follow.ts
+++ b/server/lib/activitypub/follow.ts
@@ -27,7 +27,6 @@ async function autoFollowBackIfNeeded (actorFollow: MActorFollowActors) {
27 } 27 }
28 28
29 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) 29 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload })
30 .catch(err => logger.error('Cannot create auto follow back job for %s.', host, err))
31 } 30 }
32} 31}
33 32
diff --git a/server/lib/activitypub/send/send-accept.ts b/server/lib/activitypub/send/send-accept.ts
index 9f0225b64..c4c6b849b 100644
--- a/server/lib/activitypub/send/send-accept.ts
+++ b/server/lib/activitypub/send/send-accept.ts
@@ -5,7 +5,7 @@ import { buildFollowActivity } from './send-follow'
5import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { MActor, MActorFollowActors } from '../../../typings/models' 6import { MActor, MActorFollowActors } from '../../../typings/models'
7 7
8async function sendAccept (actorFollow: MActorFollowActors) { 8function sendAccept (actorFollow: MActorFollowActors) {
9 const follower = actorFollow.ActorFollower 9 const follower = actorFollow.ActorFollower
10 const me = actorFollow.ActorFollowing 10 const me = actorFollow.ActorFollowing
11 11
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts
index 1709d8348..3585d704a 100644
--- a/server/lib/activitypub/send/send-create.ts
+++ b/server/lib/activitypub/send/send-create.ts
@@ -130,10 +130,10 @@ export {
130// --------------------------------------------------------------------------- 130// ---------------------------------------------------------------------------
131 131
132async function sendVideoRelatedCreateActivity (options: { 132async function sendVideoRelatedCreateActivity (options: {
133 byActor: MActorLight, 133 byActor: MActorLight
134 video: MVideoAccountLight, 134 video: MVideoAccountLight
135 url: string, 135 url: string
136 object: any, 136 object: any
137 transaction?: Transaction 137 transaction?: Transaction
138}) { 138}) {
139 const activityBuilder = (audience: ActivityAudience) => { 139 const activityBuilder = (audience: ActivityAudience) => {
diff --git a/server/lib/activitypub/send/send-dislike.ts b/server/lib/activitypub/send/send-dislike.ts
index 6e41f241f..600469c71 100644
--- a/server/lib/activitypub/send/send-dislike.ts
+++ b/server/lib/activitypub/send/send-dislike.ts
@@ -6,7 +6,7 @@ import { sendVideoRelatedActivity } from './utils'
6import { audiencify, getAudience } from '../audience' 6import { audiencify, getAudience } from '../audience'
7import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../typings/models' 7import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../typings/models'
8 8
9async function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { 9function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
10 logger.info('Creating job to dislike %s.', video.url) 10 logger.info('Creating job to dislike %s.', video.url)
11 11
12 const activityBuilder = (audience: ActivityAudience) => { 12 const activityBuilder = (audience: ActivityAudience) => {
diff --git a/server/lib/activitypub/send/send-flag.ts b/server/lib/activitypub/send/send-flag.ts
index da7638a7b..e4e523631 100644
--- a/server/lib/activitypub/send/send-flag.ts
+++ b/server/lib/activitypub/send/send-flag.ts
@@ -7,7 +7,7 @@ import { Transaction } from 'sequelize'
7import { MActor, MVideoFullLight } from '../../../typings/models' 7import { MActor, MVideoFullLight } from '../../../typings/models'
8import { MVideoAbuseVideo } from '../../../typings/models/video' 8import { MVideoAbuseVideo } from '../../../typings/models/video'
9 9
10async function sendVideoAbuse (byActor: MActor, videoAbuse: MVideoAbuseVideo, video: MVideoFullLight, t: Transaction) { 10function sendVideoAbuse (byActor: MActor, videoAbuse: MVideoAbuseVideo, video: MVideoFullLight, t: Transaction) {
11 if (!video.VideoChannel.Account.Actor.serverId) return // Local user 11 if (!video.VideoChannel.Account.Actor.serverId) return // Local user
12 12
13 const url = getVideoAbuseActivityPubUrl(videoAbuse) 13 const url = getVideoAbuseActivityPubUrl(videoAbuse)
diff --git a/server/lib/activitypub/send/send-like.ts b/server/lib/activitypub/send/send-like.ts
index e84a6f98b..5db252325 100644
--- a/server/lib/activitypub/send/send-like.ts
+++ b/server/lib/activitypub/send/send-like.ts
@@ -6,7 +6,7 @@ import { audiencify, getAudience } from '../audience'
6import { logger } from '../../../helpers/logger' 6import { logger } from '../../../helpers/logger'
7import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../typings/models' 7import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../typings/models'
8 8
9async function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { 9function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
10 logger.info('Creating job to like %s.', video.url) 10 logger.info('Creating job to like %s.', video.url)
11 11
12 const activityBuilder = (audience: ActivityAudience) => { 12 const activityBuilder = (audience: ActivityAudience) => {
diff --git a/server/lib/activitypub/send/send-reject.ts b/server/lib/activitypub/send/send-reject.ts
index 4258a3c36..643c468a9 100644
--- a/server/lib/activitypub/send/send-reject.ts
+++ b/server/lib/activitypub/send/send-reject.ts
@@ -5,7 +5,7 @@ import { buildFollowActivity } from './send-follow'
5import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { MActor } from '../../../typings/models' 6import { MActor } from '../../../typings/models'
7 7
8async function sendReject (follower: MActor, following: MActor) { 8function sendReject (follower: MActor, following: MActor) {
9 if (!follower.serverId) { // This should never happen 9 if (!follower.serverId) { // This should never happen
10 logger.warn('Do not sending reject to local follower.') 10 logger.warn('Do not sending reject to local follower.')
11 return 11 return
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts
index e9ab5b3c5..33f1d4921 100644
--- a/server/lib/activitypub/send/send-undo.ts
+++ b/server/lib/activitypub/send/send-undo.ts
@@ -28,7 +28,7 @@ import {
28 MVideoShare 28 MVideoShare
29} from '../../../typings/models' 29} from '../../../typings/models'
30 30
31async function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) { 31function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) {
32 const me = actorFollow.ActorFollower 32 const me = actorFollow.ActorFollower
33 const following = actorFollow.ActorFollowing 33 const following = actorFollow.ActorFollowing
34 34
@@ -118,10 +118,10 @@ function undoActivityData (
118} 118}
119 119
120async function sendUndoVideoRelatedActivity (options: { 120async function sendUndoVideoRelatedActivity (options: {
121 byActor: MActor, 121 byActor: MActor
122 video: MVideoAccountLight, 122 video: MVideoAccountLight
123 url: string, 123 url: string
124 activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce, 124 activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce
125 transaction: Transaction 125 transaction: Transaction
126}) { 126}) {
127 const activityBuilder = (audience: ActivityAudience) => { 127 const activityBuilder = (audience: ActivityAudience) => {
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts
index 9c76671b5..cb500bd34 100644
--- a/server/lib/activitypub/send/send-update.ts
+++ b/server/lib/activitypub/send/send-update.ts
@@ -8,7 +8,6 @@ import { getUpdateActivityPubUrl } from '../url'
8import { broadcastToFollowers, sendVideoRelatedActivity } from './utils' 8import { broadcastToFollowers, sendVideoRelatedActivity } from './utils'
9import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience' 9import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience'
10import { logger } from '../../../helpers/logger' 10import { logger } from '../../../helpers/logger'
11import { VideoCaptionModel } from '../../../models/video/video-caption'
12import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' 11import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
13import { getServerActor } from '../../../helpers/utils' 12import { getServerActor } from '../../../helpers/utils'
14import { 13import {
@@ -29,7 +28,7 @@ async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, t: Transaction
29 28
30 logger.info('Creating job to update video %s.', video.url) 29 logger.info('Creating job to update video %s.', video.url)
31 30
32 const byActor = overrodeByActor ? overrodeByActor : video.VideoChannel.Account.Actor 31 const byActor = overrodeByActor || video.VideoChannel.Account.Actor
33 32
34 const url = getUpdateActivityPubUrl(video.url, video.updatedAt.toISOString()) 33 const url = getUpdateActivityPubUrl(video.url, video.updatedAt.toISOString())
35 34
diff --git a/server/lib/activitypub/send/utils.ts b/server/lib/activitypub/send/utils.ts
index 77b723479..6fb4efd60 100644
--- a/server/lib/activitypub/send/utils.ts
+++ b/server/lib/activitypub/send/utils.ts
@@ -10,8 +10,8 @@ import { afterCommitIfTransaction } from '../../../helpers/database-utils'
10import { MActorWithInboxes, MActor, MActorId, MActorLight, MVideo, MVideoAccountLight } from '../../../typings/models' 10import { MActorWithInboxes, MActor, MActorId, MActorLight, MVideo, MVideoAccountLight } from '../../../typings/models'
11 11
12async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { 12async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: {
13 byActor: MActorLight, 13 byActor: MActorLight
14 video: MVideoAccountLight, 14 video: MVideoAccountLight
15 transaction?: Transaction 15 transaction?: Transaction
16}) { 16}) {
17 const { byActor, video, transaction } = options 17 const { byActor, video, transaction } = options
diff --git a/server/lib/activitypub/video-comments.ts b/server/lib/activitypub/video-comments.ts
index d5c078a29..8642d2432 100644
--- a/server/lib/activitypub/video-comments.ts
+++ b/server/lib/activitypub/video-comments.ts
@@ -10,9 +10,9 @@ import { checkUrlsSameHost } from '../../helpers/activitypub'
10import { MCommentOwner, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFiles } from '../../typings/models/video' 10import { MCommentOwner, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFiles } from '../../typings/models/video'
11 11
12type ResolveThreadParams = { 12type ResolveThreadParams = {
13 url: string, 13 url: string
14 comments?: MCommentOwner[], 14 comments?: MCommentOwner[]
15 isVideo?: boolean, 15 isVideo?: boolean
16 commentCreated?: boolean 16 commentCreated?: boolean
17} 17}
18type ResolveThreadResult = Promise<{ video: MVideoAccountLightBlacklistAllFiles, comment: MCommentOwnerVideo, commentCreated: boolean }> 18type ResolveThreadResult = Promise<{ video: MVideoAccountLightBlacklistAllFiles, comment: MCommentOwnerVideo, commentCreated: boolean }>
@@ -28,7 +28,7 @@ async function resolveThread (params: ResolveThreadParams): ResolveThreadResult
28 if (params.commentCreated === undefined) params.commentCreated = false 28 if (params.commentCreated === undefined) params.commentCreated = false
29 if (params.comments === undefined) params.comments = [] 29 if (params.comments === undefined) params.comments = []
30 30
31 // Already have this comment? 31 // Already have this comment?
32 if (isVideo !== true) { 32 if (isVideo !== true) {
33 const result = await resolveCommentFromDB(params) 33 const result = await resolveCommentFromDB(params)
34 if (result) return result 34 if (result) return result
@@ -87,7 +87,7 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) {
87 87
88 let resultComment: MCommentOwnerVideo 88 let resultComment: MCommentOwnerVideo
89 if (comments.length !== 0) { 89 if (comments.length !== 0) {
90 const firstReply = comments[ comments.length - 1 ] as MCommentOwnerVideo 90 const firstReply = comments[comments.length - 1] as MCommentOwnerVideo
91 firstReply.inReplyToCommentId = null 91 firstReply.inReplyToCommentId = null
92 firstReply.originCommentId = null 92 firstReply.originCommentId = null
93 firstReply.videoId = video.id 93 firstReply.videoId = video.id
@@ -97,9 +97,9 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) {
97 comments[comments.length - 1] = await firstReply.save() 97 comments[comments.length - 1] = await firstReply.save()
98 98
99 for (let i = comments.length - 2; i >= 0; i--) { 99 for (let i = comments.length - 2; i >= 0; i--) {
100 const comment = comments[ i ] as MCommentOwnerVideo 100 const comment = comments[i] as MCommentOwnerVideo
101 comment.originCommentId = firstReply.id 101 comment.originCommentId = firstReply.id
102 comment.inReplyToCommentId = comments[ i + 1 ].id 102 comment.inReplyToCommentId = comments[i + 1].id
103 comment.videoId = video.id 103 comment.videoId = video.id
104 comment.changed('updatedAt', true) 104 comment.changed('updatedAt', true)
105 comment.Video = video 105 comment.Video = video
diff --git a/server/lib/activitypub/video-rates.ts b/server/lib/activitypub/video-rates.ts
index 6bd46bb58..79ccfbc7e 100644
--- a/server/lib/activitypub/video-rates.ts
+++ b/server/lib/activitypub/video-rates.ts
@@ -58,8 +58,6 @@ async function createRates (ratesUrl: string[], video: MVideo, rate: VideoRateTy
58 const field = rate === 'like' ? 'likes' : 'dislikes' 58 const field = rate === 'like' ? 'likes' : 'dislikes'
59 await video.increment(field, { by: rateCounts }) 59 await video.increment(field, { by: rateCounts })
60 } 60 }
61
62 return
63} 61}
64 62
65async function sendVideoRateChange ( 63async function sendVideoRateChange (
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts
index 6bc2258cc..9e43caa20 100644
--- a/server/lib/activitypub/videos.ts
+++ b/server/lib/activitypub/videos.ts
@@ -197,25 +197,25 @@ async function syncVideoExternalAttributes (video: MVideo, fetchedVideo: VideoTo
197 jobPayloads.push({ uri: fetchedVideo.comments, videoId: video.id, type: 'video-comments' as 'video-comments' }) 197 jobPayloads.push({ uri: fetchedVideo.comments, videoId: video.id, type: 'video-comments' as 'video-comments' })
198 } 198 }
199 199
200 await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })) 200 await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJobWithPromise({ type: 'activitypub-http-fetcher', payload }))
201} 201}
202 202
203function getOrCreateVideoAndAccountAndChannel (options: { 203function getOrCreateVideoAndAccountAndChannel (options: {
204 videoObject: { id: string } | string, 204 videoObject: { id: string } | string
205 syncParam?: SyncParam, 205 syncParam?: SyncParam
206 fetchType?: 'all', 206 fetchType?: 'all'
207 allowRefresh?: boolean 207 allowRefresh?: boolean
208}): Promise<{ video: MVideoAccountLightBlacklistAllFiles, created: boolean, autoBlacklisted?: boolean }> 208}): Promise<{ video: MVideoAccountLightBlacklistAllFiles, created: boolean, autoBlacklisted?: boolean }>
209function getOrCreateVideoAndAccountAndChannel (options: { 209function getOrCreateVideoAndAccountAndChannel (options: {
210 videoObject: { id: string } | string, 210 videoObject: { id: string } | string
211 syncParam?: SyncParam, 211 syncParam?: SyncParam
212 fetchType?: VideoFetchByUrlType, 212 fetchType?: VideoFetchByUrlType
213 allowRefresh?: boolean 213 allowRefresh?: boolean
214}): Promise<{ video: MVideoAccountLightBlacklistAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }> 214}): Promise<{ video: MVideoAccountLightBlacklistAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }>
215async function getOrCreateVideoAndAccountAndChannel (options: { 215async function getOrCreateVideoAndAccountAndChannel (options: {
216 videoObject: { id: string } | string, 216 videoObject: { id: string } | string
217 syncParam?: SyncParam, 217 syncParam?: SyncParam
218 fetchType?: VideoFetchByUrlType, 218 fetchType?: VideoFetchByUrlType
219 allowRefresh?: boolean // true by default 219 allowRefresh?: boolean // true by default
220}): Promise<{ video: MVideoAccountLightBlacklistAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }> { 220}): Promise<{ video: MVideoAccountLightBlacklistAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }> {
221 // Default params 221 // Default params
@@ -235,8 +235,14 @@ async function getOrCreateVideoAndAccountAndChannel (options: {
235 syncParam 235 syncParam
236 } 236 }
237 237
238 if (syncParam.refreshVideo === true) videoFromDatabase = await refreshVideoIfNeeded(refreshOptions) 238 if (syncParam.refreshVideo === true) {
239 else await JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', url: videoFromDatabase.url } }) 239 videoFromDatabase = await refreshVideoIfNeeded(refreshOptions)
240 } else {
241 await JobQueue.Instance.createJobWithPromise({
242 type: 'activitypub-refresher',
243 payload: { type: 'video', url: videoFromDatabase.url }
244 })
245 }
240 } 246 }
241 247
242 return { video: videoFromDatabase, created: false } 248 return { video: videoFromDatabase, created: false }
@@ -255,10 +261,10 @@ async function getOrCreateVideoAndAccountAndChannel (options: {
255} 261}
256 262
257async function updateVideoFromAP (options: { 263async function updateVideoFromAP (options: {
258 video: MVideoAccountLightBlacklistAllFiles, 264 video: MVideoAccountLightBlacklistAllFiles
259 videoObject: VideoTorrentObject, 265 videoObject: VideoTorrentObject
260 account: MAccountIdActor, 266 account: MAccountIdActor
261 channel: MChannelDefault, 267 channel: MChannelDefault
262 overrideTo?: string[] 268 overrideTo?: string[]
263}) { 269}) {
264 const { video, videoObject, account, channel, overrideTo } = options 270 const { video, videoObject, account, channel, overrideTo } = options
@@ -289,7 +295,7 @@ async function updateVideoFromAP (options: {
289 throw new Error('Account ' + account.Actor.url + ' does not own video channel ' + videoChannel.Actor.url) 295 throw new Error('Account ' + account.Actor.url + ' does not own video channel ' + videoChannel.Actor.url)
290 } 296 }
291 297
292 const to = overrideTo ? overrideTo : videoObject.to 298 const to = overrideTo || videoObject.to
293 const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, to) 299 const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, to)
294 video.name = videoData.name 300 video.name = videoData.name
295 video.uuid = videoData.uuid 301 video.uuid = videoData.uuid
@@ -412,8 +418,8 @@ async function updateVideoFromAP (options: {
412} 418}
413 419
414async function refreshVideoIfNeeded (options: { 420async function refreshVideoIfNeeded (options: {
415 video: MVideoThumbnail, 421 video: MVideoThumbnail
416 fetchedType: VideoFetchByUrlType, 422 fetchedType: VideoFetchByUrlType
417 syncParam: SyncParam 423 syncParam: SyncParam
418}): Promise<MVideoThumbnail> { 424}): Promise<MVideoThumbnail> {
419 if (!options.video.isOutdated()) return options.video 425 if (!options.video.isOutdated()) return options.video
@@ -582,13 +588,13 @@ async function createVideo (videoObject: VideoTorrentObject, channel: MChannelAc
582 thumbnailModel = videoCreated.id 588 thumbnailModel = videoCreated.id
583 589
584 return thumbnailModel.save() 590 return thumbnailModel.save()
585 }) 591 }).catch(err => logger.error('Cannot create miniature from url.', { err }))
586 } 592 }
587 593
588 return { autoBlacklisted, videoCreated } 594 return { autoBlacklisted, videoCreated }
589} 595}
590 596
591async function videoActivityObjectToDBAttributes (videoChannel: MChannelId, videoObject: VideoTorrentObject, to: string[] = []) { 597function videoActivityObjectToDBAttributes (videoChannel: MChannelId, videoObject: VideoTorrentObject, to: string[] = []) {
592 const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED 598 const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED
593 const duration = videoObject.duration.replace(/[^\d]+/, '') 599 const duration = videoObject.duration.replace(/[^\d]+/, '')
594 600
@@ -661,7 +667,7 @@ function videoFileActivityUrlToDBAttributes (
661 667
662 const mediaType = fileUrl.mediaType 668 const mediaType = fileUrl.mediaType
663 const attribute = { 669 const attribute = {
664 extname: MIMETYPES.VIDEO.MIMETYPE_EXT[ mediaType ], 670 extname: MIMETYPES.VIDEO.MIMETYPE_EXT[mediaType],
665 infoHash: parsed.infoHash, 671 infoHash: parsed.infoHash,
666 resolution: fileUrl.height, 672 resolution: fileUrl.height,
667 size: fileUrl.size, 673 size: fileUrl.size,
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts
index 1d8a08ed0..572bd03bd 100644
--- a/server/lib/client-html.ts
+++ b/server/lib/client-html.ts
@@ -17,7 +17,7 @@ import { MAccountActor, MChannelActor, MVideo } from '../typings/models'
17 17
18export class ClientHtml { 18export class ClientHtml {
19 19
20 private static htmlCache: { [ path: string ]: string } = {} 20 private static htmlCache: { [path: string]: string } = {}
21 21
22 static invalidCache () { 22 static invalidCache () {
23 logger.info('Cleaning HTML cache.') 23 logger.info('Cleaning HTML cache.')
@@ -94,7 +94,7 @@ export class ClientHtml {
94 94
95 private static async getIndexHTML (req: express.Request, res: express.Response, paramLang?: string) { 95 private static async getIndexHTML (req: express.Request, res: express.Response, paramLang?: string) {
96 const path = ClientHtml.getIndexPath(req, res, paramLang) 96 const path = ClientHtml.getIndexPath(req, res, paramLang)
97 if (ClientHtml.htmlCache[ path ]) return ClientHtml.htmlCache[ path ] 97 if (ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path]
98 98
99 const buffer = await readFile(path) 99 const buffer = await readFile(path)
100 100
@@ -104,7 +104,7 @@ export class ClientHtml {
104 html = ClientHtml.addCustomCSS(html) 104 html = ClientHtml.addCustomCSS(html)
105 html = await ClientHtml.addAsyncPluginCSS(html) 105 html = await ClientHtml.addAsyncPluginCSS(html)
106 106
107 ClientHtml.htmlCache[ path ] = html 107 ClientHtml.htmlCache[path] = html
108 108
109 return html 109 return html
110 } 110 }
@@ -214,21 +214,21 @@ export class ClientHtml {
214 const schemaTags = { 214 const schemaTags = {
215 '@context': 'http://schema.org', 215 '@context': 'http://schema.org',
216 '@type': 'VideoObject', 216 '@type': 'VideoObject',
217 name: videoNameEscaped, 217 'name': videoNameEscaped,
218 description: videoDescriptionEscaped, 218 'description': videoDescriptionEscaped,
219 thumbnailUrl: previewUrl, 219 'thumbnailUrl': previewUrl,
220 uploadDate: video.createdAt.toISOString(), 220 'uploadDate': video.createdAt.toISOString(),
221 duration: getActivityStreamDuration(video.duration), 221 'duration': getActivityStreamDuration(video.duration),
222 contentUrl: videoUrl, 222 'contentUrl': videoUrl,
223 embedUrl: embedUrl, 223 'embedUrl': embedUrl,
224 interactionCount: video.views 224 'interactionCount': video.views
225 } 225 }
226 226
227 let tagsString = '' 227 let tagsString = ''
228 228
229 // Opengraph 229 // Opengraph
230 Object.keys(openGraphMetaTags).forEach(tagName => { 230 Object.keys(openGraphMetaTags).forEach(tagName => {
231 const tagValue = openGraphMetaTags[ tagName ] 231 const tagValue = openGraphMetaTags[tagName]
232 232
233 tagsString += `<meta property="${tagName}" content="${tagValue}" />` 233 tagsString += `<meta property="${tagName}" content="${tagValue}" />`
234 }) 234 })
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts
index 7484524a4..9ce6186b1 100644
--- a/server/lib/emailer.ts
+++ b/server/lib/emailer.ts
@@ -32,7 +32,8 @@ class Emailer {
32 private initialized = false 32 private initialized = false
33 private transporter: Transporter 33 private transporter: Transporter
34 34
35 private constructor () {} 35 private constructor () {
36 }
36 37
37 init () { 38 init () {
38 // Already initialized 39 // Already initialized
@@ -97,12 +98,12 @@ class Emailer {
97 const channelName = video.VideoChannel.getDisplayName() 98 const channelName = video.VideoChannel.getDisplayName()
98 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() 99 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
99 100
100 const text = `Hi dear user,\n\n` + 101 const text = 'Hi dear user,\n\n' +
101 `Your subscription ${channelName} just published a new video: ${video.name}` + 102 `Your subscription ${channelName} just published a new video: ${video.name}` +
102 `\n\n` + 103 '\n\n' +
103 `You can view it on ${videoUrl} ` + 104 `You can view it on ${videoUrl} ` +
104 `\n\n` + 105 '\n\n' +
105 `Cheers,\n` + 106 'Cheers,\n' +
106 `${CONFIG.EMAIL.BODY.SIGNATURE}` 107 `${CONFIG.EMAIL.BODY.SIGNATURE}`
107 108
108 const emailPayload: EmailPayload = { 109 const emailPayload: EmailPayload = {
@@ -118,10 +119,10 @@ class Emailer {
118 const followerName = actorFollow.ActorFollower.Account.getDisplayName() 119 const followerName = actorFollow.ActorFollower.Account.getDisplayName()
119 const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName() 120 const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName()
120 121
121 const text = `Hi dear user,\n\n` + 122 const text = 'Hi dear user,\n\n' +
122 `Your ${followType} ${followingName} has a new subscriber: ${followerName}` + 123 `Your ${followType} ${followingName} has a new subscriber: ${followerName}` +
123 `\n\n` + 124 '\n\n' +
124 `Cheers,\n` + 125 'Cheers,\n' +
125 `${CONFIG.EMAIL.BODY.SIGNATURE}` 126 `${CONFIG.EMAIL.BODY.SIGNATURE}`
126 127
127 const emailPayload: EmailPayload = { 128 const emailPayload: EmailPayload = {
@@ -136,10 +137,10 @@ class Emailer {
136 addNewInstanceFollowerNotification (to: string[], actorFollow: MActorFollowActors) { 137 addNewInstanceFollowerNotification (to: string[], actorFollow: MActorFollowActors) {
137 const awaitingApproval = actorFollow.state === 'pending' ? ' awaiting manual approval.' : '' 138 const awaitingApproval = actorFollow.state === 'pending' ? ' awaiting manual approval.' : ''
138 139
139 const text = `Hi dear admin,\n\n` + 140 const text = 'Hi dear admin,\n\n' +
140 `Your instance has a new follower: ${actorFollow.ActorFollower.url}${awaitingApproval}` + 141 `Your instance has a new follower: ${actorFollow.ActorFollower.url}${awaitingApproval}` +
141 `\n\n` + 142 '\n\n' +
142 `Cheers,\n` + 143 'Cheers,\n' +
143 `${CONFIG.EMAIL.BODY.SIGNATURE}` 144 `${CONFIG.EMAIL.BODY.SIGNATURE}`
144 145
145 const emailPayload: EmailPayload = { 146 const emailPayload: EmailPayload = {
@@ -152,10 +153,10 @@ class Emailer {
152 } 153 }
153 154
154 addAutoInstanceFollowingNotification (to: string[], actorFollow: MActorFollowActors) { 155 addAutoInstanceFollowingNotification (to: string[], actorFollow: MActorFollowActors) {
155 const text = `Hi dear admin,\n\n` + 156 const text = 'Hi dear admin,\n\n' +
156 `Your instance automatically followed a new instance: ${actorFollow.ActorFollowing.url}` + 157 `Your instance automatically followed a new instance: ${actorFollow.ActorFollowing.url}` +
157 `\n\n` + 158 '\n\n' +
158 `Cheers,\n` + 159 'Cheers,\n' +
159 `${CONFIG.EMAIL.BODY.SIGNATURE}` 160 `${CONFIG.EMAIL.BODY.SIGNATURE}`
160 161
161 const emailPayload: EmailPayload = { 162 const emailPayload: EmailPayload = {
@@ -170,12 +171,12 @@ class Emailer {
170 myVideoPublishedNotification (to: string[], video: MVideo) { 171 myVideoPublishedNotification (to: string[], video: MVideo) {
171 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() 172 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
172 173
173 const text = `Hi dear user,\n\n` + 174 const text = 'Hi dear user,\n\n' +
174 `Your video ${video.name} has been published.` + 175 `Your video ${video.name} has been published.` +
175 `\n\n` + 176 '\n\n' +
176 `You can view it on ${videoUrl} ` + 177 `You can view it on ${videoUrl} ` +
177 `\n\n` + 178 '\n\n' +
178 `Cheers,\n` + 179 'Cheers,\n' +
179 `${CONFIG.EMAIL.BODY.SIGNATURE}` 180 `${CONFIG.EMAIL.BODY.SIGNATURE}`
180 181
181 const emailPayload: EmailPayload = { 182 const emailPayload: EmailPayload = {
@@ -190,12 +191,12 @@ class Emailer {
190 myVideoImportSuccessNotification (to: string[], videoImport: MVideoImportVideo) { 191 myVideoImportSuccessNotification (to: string[], videoImport: MVideoImportVideo) {
191 const videoUrl = WEBSERVER.URL + videoImport.Video.getWatchStaticPath() 192 const videoUrl = WEBSERVER.URL + videoImport.Video.getWatchStaticPath()
192 193
193 const text = `Hi dear user,\n\n` + 194 const text = 'Hi dear user,\n\n' +
194 `Your video import ${videoImport.getTargetIdentifier()} is finished.` + 195 `Your video import ${videoImport.getTargetIdentifier()} is finished.` +
195 `\n\n` + 196 '\n\n' +
196 `You can view the imported video on ${videoUrl} ` + 197 `You can view the imported video on ${videoUrl} ` +
197 `\n\n` + 198 '\n\n' +
198 `Cheers,\n` + 199 'Cheers,\n' +
199 `${CONFIG.EMAIL.BODY.SIGNATURE}` 200 `${CONFIG.EMAIL.BODY.SIGNATURE}`
200 201
201 const emailPayload: EmailPayload = { 202 const emailPayload: EmailPayload = {
@@ -210,12 +211,12 @@ class Emailer {
210 myVideoImportErrorNotification (to: string[], videoImport: MVideoImport) { 211 myVideoImportErrorNotification (to: string[], videoImport: MVideoImport) {
211 const importUrl = WEBSERVER.URL + '/my-account/video-imports' 212 const importUrl = WEBSERVER.URL + '/my-account/video-imports'
212 213
213 const text = `Hi dear user,\n\n` + 214 const text = 'Hi dear user,\n\n' +
214 `Your video import ${videoImport.getTargetIdentifier()} encountered an error.` + 215 `Your video import ${videoImport.getTargetIdentifier()} encountered an error.` +
215 `\n\n` + 216 '\n\n' +
216 `See your videos import dashboard for more information: ${importUrl}` + 217 `See your videos import dashboard for more information: ${importUrl}` +
217 `\n\n` + 218 '\n\n' +
218 `Cheers,\n` + 219 'Cheers,\n' +
219 `${CONFIG.EMAIL.BODY.SIGNATURE}` 220 `${CONFIG.EMAIL.BODY.SIGNATURE}`
220 221
221 const emailPayload: EmailPayload = { 222 const emailPayload: EmailPayload = {
@@ -232,12 +233,12 @@ class Emailer {
232 const video = comment.Video 233 const video = comment.Video
233 const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() 234 const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath()
234 235
235 const text = `Hi dear user,\n\n` + 236 const text = 'Hi dear user,\n\n' +
236 `A new comment has been posted by ${accountName} on your video ${video.name}` + 237 `A new comment has been posted by ${accountName} on your video ${video.name}` +
237 `\n\n` + 238 '\n\n' +
238 `You can view it on ${commentUrl} ` + 239 `You can view it on ${commentUrl} ` +
239 `\n\n` + 240 '\n\n' +
240 `Cheers,\n` + 241 'Cheers,\n' +
241 `${CONFIG.EMAIL.BODY.SIGNATURE}` 242 `${CONFIG.EMAIL.BODY.SIGNATURE}`
242 243
243 const emailPayload: EmailPayload = { 244 const emailPayload: EmailPayload = {
@@ -254,12 +255,12 @@ class Emailer {
254 const video = comment.Video 255 const video = comment.Video
255 const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() 256 const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath()
256 257
257 const text = `Hi dear user,\n\n` + 258 const text = 'Hi dear user,\n\n' +
258 `${accountName} mentioned you on video ${video.name}` + 259 `${accountName} mentioned you on video ${video.name}` +
259 `\n\n` + 260 '\n\n' +
260 `You can view the comment on ${commentUrl} ` + 261 `You can view the comment on ${commentUrl} ` +
261 `\n\n` + 262 '\n\n' +
262 `Cheers,\n` + 263 'Cheers,\n' +
263 `${CONFIG.EMAIL.BODY.SIGNATURE}` 264 `${CONFIG.EMAIL.BODY.SIGNATURE}`
264 265
265 const emailPayload: EmailPayload = { 266 const emailPayload: EmailPayload = {
@@ -274,9 +275,9 @@ class Emailer {
274 addVideoAbuseModeratorsNotification (to: string[], videoAbuse: MVideoAbuseVideo) { 275 addVideoAbuseModeratorsNotification (to: string[], videoAbuse: MVideoAbuseVideo) {
275 const videoUrl = WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath() 276 const videoUrl = WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath()
276 277
277 const text = `Hi,\n\n` + 278 const text = 'Hi,\n\n' +
278 `${WEBSERVER.HOST} received an abuse for the following video ${videoUrl}\n\n` + 279 `${WEBSERVER.HOST} received an abuse for the following video ${videoUrl}\n\n` +
279 `Cheers,\n` + 280 'Cheers,\n' +
280 `${CONFIG.EMAIL.BODY.SIGNATURE}` 281 `${CONFIG.EMAIL.BODY.SIGNATURE}`
281 282
282 const emailPayload: EmailPayload = { 283 const emailPayload: EmailPayload = {
@@ -292,14 +293,14 @@ class Emailer {
292 const VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list' 293 const VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list'
293 const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() 294 const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath()
294 295
295 const text = `Hi,\n\n` + 296 const text = 'Hi,\n\n' +
296 `A recently added video was auto-blacklisted and requires moderator review before publishing.` + 297 'A recently added video was auto-blacklisted and requires moderator review before publishing.' +
297 `\n\n` + 298 '\n\n' +
298 `You can view it and take appropriate action on ${videoUrl}` + 299 `You can view it and take appropriate action on ${videoUrl}` +
299 `\n\n` + 300 '\n\n' +
300 `A full list of auto-blacklisted videos can be reviewed here: ${VIDEO_AUTO_BLACKLIST_URL}` + 301 `A full list of auto-blacklisted videos can be reviewed here: ${VIDEO_AUTO_BLACKLIST_URL}` +
301 `\n\n` + 302 '\n\n' +
302 `Cheers,\n` + 303 'Cheers,\n' +
303 `${CONFIG.EMAIL.BODY.SIGNATURE}` 304 `${CONFIG.EMAIL.BODY.SIGNATURE}`
304 305
305 const emailPayload: EmailPayload = { 306 const emailPayload: EmailPayload = {
@@ -312,9 +313,9 @@ class Emailer {
312 } 313 }
313 314
314 addNewUserRegistrationNotification (to: string[], user: MUser) { 315 addNewUserRegistrationNotification (to: string[], user: MUser) {
315 const text = `Hi,\n\n` + 316 const text = 'Hi,\n\n' +
316 `User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` + 317 `User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` +
317 `Cheers,\n` + 318 'Cheers,\n' +
318 `${CONFIG.EMAIL.BODY.SIGNATURE}` 319 `${CONFIG.EMAIL.BODY.SIGNATURE}`
319 320
320 const emailPayload: EmailPayload = { 321 const emailPayload: EmailPayload = {
@@ -367,11 +368,11 @@ class Emailer {
367 } 368 }
368 369
369 addPasswordResetEmailJob (to: string, resetPasswordUrl: string) { 370 addPasswordResetEmailJob (to: string, resetPasswordUrl: string) {
370 const text = `Hi dear user,\n\n` + 371 const text = 'Hi dear user,\n\n' +
371 `A reset password procedure for your account ${to} has been requested on ${WEBSERVER.HOST} ` + 372 `A reset password procedure for your account ${to} has been requested on ${WEBSERVER.HOST} ` +
372 `Please follow this link to reset it: ${resetPasswordUrl} (the link will expire within 1 hour)\n\n` + 373 `Please follow this link to reset it: ${resetPasswordUrl} (the link will expire within 1 hour)\n\n` +
373 `If you are not the person who initiated this request, please ignore this email.\n\n` + 374 'If you are not the person who initiated this request, please ignore this email.\n\n' +
374 `Cheers,\n` + 375 'Cheers,\n' +
375 `${CONFIG.EMAIL.BODY.SIGNATURE}` 376 `${CONFIG.EMAIL.BODY.SIGNATURE}`
376 377
377 const emailPayload: EmailPayload = { 378 const emailPayload: EmailPayload = {
@@ -384,11 +385,11 @@ class Emailer {
384 } 385 }
385 386
386 addVerifyEmailJob (to: string, verifyEmailUrl: string) { 387 addVerifyEmailJob (to: string, verifyEmailUrl: string) {
387 const text = `Welcome to PeerTube,\n\n` + 388 const text = 'Welcome to PeerTube,\n\n' +
388 `To start using PeerTube on ${WEBSERVER.HOST} you must verify your email! ` + 389 `To start using PeerTube on ${WEBSERVER.HOST} you must verify your email! ` +
389 `Please follow this link to verify this email belongs to you: ${verifyEmailUrl}\n\n` + 390 `Please follow this link to verify this email belongs to you: ${verifyEmailUrl}\n\n` +
390 `If you are not the person who initiated this request, please ignore this email.\n\n` + 391 'If you are not the person who initiated this request, please ignore this email.\n\n' +
391 `Cheers,\n` + 392 'Cheers,\n' +
392 `${CONFIG.EMAIL.BODY.SIGNATURE}` 393 `${CONFIG.EMAIL.BODY.SIGNATURE}`
393 394
394 const emailPayload: EmailPayload = { 395 const emailPayload: EmailPayload = {
diff --git a/server/lib/files-cache/videos-preview-cache.ts b/server/lib/files-cache/videos-preview-cache.ts
index 7bfeb5783..d0d4fc5b5 100644
--- a/server/lib/files-cache/videos-preview-cache.ts
+++ b/server/lib/files-cache/videos-preview-cache.ts
@@ -1,9 +1,8 @@
1import { join } from 'path' 1import { join } from 'path'
2import { FILES_CACHE, STATIC_PATHS } from '../../initializers/constants' 2import { FILES_CACHE } from '../../initializers/constants'
3import { VideoModel } from '../../models/video/video' 3import { VideoModel } from '../../models/video/video'
4import { AbstractVideoStaticFileCache } from './abstract-video-static-file-cache' 4import { AbstractVideoStaticFileCache } from './abstract-video-static-file-cache'
5import { doRequestAndSaveToFile } from '@server/helpers/requests' 5import { doRequestAndSaveToFile } from '@server/helpers/requests'
6import { buildRemoteVideoBaseUrl } from '@server/helpers/activitypub'
7 6
8class VideosPreviewCache extends AbstractVideoStaticFileCache <string> { 7class VideosPreviewCache extends AbstractVideoStaticFileCache <string> {
9 8
diff --git a/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts b/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts
index d3bde6e6a..a28f3596f 100644
--- a/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts
+++ b/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts
@@ -42,7 +42,7 @@ async function buildSignedRequestOptions (payload: Payload) {
42 42
43function buildGlobalHeaders (body: any) { 43function buildGlobalHeaders (body: any) {
44 return { 44 return {
45 'Digest': buildDigest(body) 45 Digest: buildDigest(body)
46 } 46 }
47} 47}
48 48
diff --git a/server/lib/job-queue/handlers/video-file-import.ts b/server/lib/job-queue/handlers/video-file-import.ts
index 99c991e72..be9e7d181 100644
--- a/server/lib/job-queue/handlers/video-file-import.ts
+++ b/server/lib/job-queue/handlers/video-file-import.ts
@@ -11,7 +11,7 @@ import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
11import { getVideoFilePath } from '@server/lib/video-paths' 11import { getVideoFilePath } from '@server/lib/video-paths'
12 12
13export type VideoFileImportPayload = { 13export type VideoFileImportPayload = {
14 videoUUID: string, 14 videoUUID: string
15 filePath: string 15 filePath: string
16} 16}
17 17
diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts
index 1fca17584..09f225cec 100644
--- a/server/lib/job-queue/handlers/video-import.ts
+++ b/server/lib/job-queue/handlers/video-import.ts
@@ -221,7 +221,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: MVid
221 isNewVideo: true 221 isNewVideo: true
222 } 222 }
223 223
224 await JobQueue.Instance.createJob({ type: 'video-transcoding', payload: dataInput }) 224 await JobQueue.Instance.createJobWithPromise({ type: 'video-transcoding', payload: dataInput })
225 } 225 }
226 226
227 } catch (err) { 227 } catch (err) {
diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts
index 39b9fac98..c020057c9 100644
--- a/server/lib/job-queue/handlers/video-transcoding.ts
+++ b/server/lib/job-queue/handlers/video-transcoding.ts
@@ -6,7 +6,6 @@ import { JobQueue } from '../job-queue'
6import { federateVideoIfNeeded } from '../../activitypub' 6import { federateVideoIfNeeded } from '../../activitypub'
7import { retryTransactionWrapper } from '../../../helpers/database-utils' 7import { retryTransactionWrapper } from '../../../helpers/database-utils'
8import { sequelizeTypescript } from '../../../initializers' 8import { sequelizeTypescript } from '../../../initializers'
9import * as Bluebird from 'bluebird'
10import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils' 9import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils'
11import { generateHlsPlaylist, mergeAudioVideofile, optimizeOriginalVideofile, transcodeNewResolution } from '../../video-transcoding' 10import { generateHlsPlaylist, mergeAudioVideofile, optimizeOriginalVideofile, transcodeNewResolution } from '../../video-transcoding'
12import { Notifier } from '../../notifier' 11import { Notifier } from '../../notifier'
@@ -40,8 +39,11 @@ interface OptimizeTranscodingPayload extends BaseTranscodingPayload {
40 type: 'optimize' 39 type: 'optimize'
41} 40}
42 41
43export type VideoTranscodingPayload = HLSTranscodingPayload | NewResolutionTranscodingPayload 42export type VideoTranscodingPayload =
44 | OptimizeTranscodingPayload | MergeAudioTranscodingPayload 43 HLSTranscodingPayload
44 | NewResolutionTranscodingPayload
45 | OptimizeTranscodingPayload
46 | MergeAudioTranscodingPayload
45 47
46async function processVideoTranscoding (job: Bull.Job) { 48async function processVideoTranscoding (job: Bull.Job) {
47 const payload = job.data as VideoTranscodingPayload 49 const payload = job.data as VideoTranscodingPayload
@@ -105,7 +107,7 @@ async function onVideoFileOptimizerSuccess (videoArg: MVideoWithFile, payload: O
105 107
106 const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => { 108 const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => {
107 // Maybe the video changed in database, refresh it 109 // Maybe the video changed in database, refresh it
108 let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid, t) 110 const videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid, t)
109 // Video does not exist anymore 111 // Video does not exist anymore
110 if (!videoDatabase) return undefined 112 if (!videoDatabase) return undefined
111 113
@@ -122,8 +124,6 @@ async function onVideoFileOptimizerSuccess (videoArg: MVideoWithFile, payload: O
122 await createHlsJobIfEnabled(hlsPayload) 124 await createHlsJobIfEnabled(hlsPayload)
123 125
124 if (resolutionsEnabled.length !== 0) { 126 if (resolutionsEnabled.length !== 0) {
125 const tasks: (Bluebird<Bull.Job<any>> | Promise<Bull.Job<any>>)[] = []
126
127 for (const resolution of resolutionsEnabled) { 127 for (const resolution of resolutionsEnabled) {
128 let dataInput: VideoTranscodingPayload 128 let dataInput: VideoTranscodingPayload
129 129
@@ -143,12 +143,9 @@ async function onVideoFileOptimizerSuccess (videoArg: MVideoWithFile, payload: O
143 } 143 }
144 } 144 }
145 145
146 const p = JobQueue.Instance.createJob({ type: 'video-transcoding', payload: dataInput }) 146 JobQueue.Instance.createJob({ type: 'video-transcoding', payload: dataInput })
147 tasks.push(p)
148 } 147 }
149 148
150 await Promise.all(tasks)
151
152 logger.info('Transcoding jobs created for uuid %s.', videoDatabase.uuid, { resolutionsEnabled }) 149 logger.info('Transcoding jobs created for uuid %s.', videoDatabase.uuid, { resolutionsEnabled })
153 } else { 150 } else {
154 // No transcoding to do, it's now published 151 // No transcoding to do, it's now published
diff --git a/server/lib/job-queue/job-queue.ts b/server/lib/job-queue/job-queue.ts
index 61f07c487..14acace7d 100644
--- a/server/lib/job-queue/job-queue.ts
+++ b/server/lib/job-queue/job-queue.ts
@@ -28,7 +28,7 @@ type CreateJobArgument =
28 { type: 'videos-views', payload: {} } | 28 { type: 'videos-views', payload: {} } |
29 { type: 'video-redundancy', payload: VideoRedundancyPayload } 29 { type: 'video-redundancy', payload: VideoRedundancyPayload }
30 30
31const handlers: { [ id in JobType ]: (job: Bull.Job) => Promise<any>} = { 31const handlers: { [id in JobType]: (job: Bull.Job) => Promise<any> } = {
32 'activitypub-http-broadcast': processActivityPubHttpBroadcast, 32 'activitypub-http-broadcast': processActivityPubHttpBroadcast,
33 'activitypub-http-unicast': processActivityPubHttpUnicast, 33 'activitypub-http-unicast': processActivityPubHttpUnicast,
34 'activitypub-http-fetcher': processActivityPubHttpFetcher, 34 'activitypub-http-fetcher': processActivityPubHttpFetcher,
@@ -60,13 +60,14 @@ class JobQueue {
60 60
61 private static instance: JobQueue 61 private static instance: JobQueue
62 62
63 private queues: { [ id in JobType ]?: Bull.Queue } = {} 63 private queues: { [id in JobType]?: Bull.Queue } = {}
64 private initialized = false 64 private initialized = false
65 private jobRedisPrefix: string 65 private jobRedisPrefix: string
66 66
67 private constructor () {} 67 private constructor () {
68 }
68 69
69 async init () { 70 init () {
70 // Already initialized 71 // Already initialized
71 if (this.initialized === true) return 72 if (this.initialized === true) return
72 this.initialized = true 73 this.initialized = true
@@ -108,11 +109,16 @@ class JobQueue {
108 } 109 }
109 } 110 }
110 111
111 createJob (obj: CreateJobArgument) { 112 createJob (obj: CreateJobArgument): void {
113 this.createJobWithPromise(obj)
114 .catch(err => logger.error('Cannot create job.', { err, obj }))
115 }
116
117 createJobWithPromise (obj: CreateJobArgument) {
112 const queue = this.queues[obj.type] 118 const queue = this.queues[obj.type]
113 if (queue === undefined) { 119 if (queue === undefined) {
114 logger.error('Unknown queue %s: cannot create job.', obj.type) 120 logger.error('Unknown queue %s: cannot create job.', obj.type)
115 throw Error('Unknown queue, cannot create job') 121 return
116 } 122 }
117 123
118 const jobArgs: Bull.JobOptions = { 124 const jobArgs: Bull.JobOptions = {
@@ -125,10 +131,10 @@ class JobQueue {
125 } 131 }
126 132
127 async listForApi (options: { 133 async listForApi (options: {
128 state: JobState, 134 state: JobState
129 start: number, 135 start: number
130 count: number, 136 count: number
131 asc?: boolean, 137 asc?: boolean
132 jobType: JobType 138 jobType: JobType
133 }): Promise<Bull.Job[]> { 139 }): Promise<Bull.Job[]> {
134 const { state, start, count, asc, jobType } = options 140 const { state, start, count, asc, jobType } = options
@@ -137,7 +143,7 @@ class JobQueue {
137 const filteredJobTypes = this.filterJobTypes(jobType) 143 const filteredJobTypes = this.filterJobTypes(jobType)
138 144
139 for (const jobType of filteredJobTypes) { 145 for (const jobType of filteredJobTypes) {
140 const queue = this.queues[ jobType ] 146 const queue = this.queues[jobType]
141 if (queue === undefined) { 147 if (queue === undefined) {
142 logger.error('Unknown queue %s to list jobs.', jobType) 148 logger.error('Unknown queue %s to list jobs.', jobType)
143 continue 149 continue
@@ -165,7 +171,7 @@ class JobQueue {
165 const filteredJobTypes = this.filterJobTypes(jobType) 171 const filteredJobTypes = this.filterJobTypes(jobType)
166 172
167 for (const type of filteredJobTypes) { 173 for (const type of filteredJobTypes) {
168 const queue = this.queues[ type ] 174 const queue = this.queues[type]
169 if (queue === undefined) { 175 if (queue === undefined) {
170 logger.error('Unknown queue %s to count jobs.', type) 176 logger.error('Unknown queue %s to count jobs.', type)
171 continue 177 continue
@@ -173,7 +179,7 @@ class JobQueue {
173 179
174 const counts = await queue.getJobCounts() 180 const counts = await queue.getJobCounts()
175 181
176 total += counts[ state ] 182 total += counts[state]
177 } 183 }
178 184
179 return total 185 return total
@@ -189,7 +195,7 @@ class JobQueue {
189 private addRepeatableJobs () { 195 private addRepeatableJobs () {
190 this.queues['videos-views'].add({}, { 196 this.queues['videos-views'].add({}, {
191 repeat: REPEAT_JOBS['videos-views'] 197 repeat: REPEAT_JOBS['videos-views']
192 }) 198 }).catch(err => logger.error('Cannot add repeatable job.', { err }))
193 } 199 }
194 200
195 private filterJobTypes (jobType?: JobType) { 201 private filterJobTypes (jobType?: JobType) {
diff --git a/server/lib/moderation.ts b/server/lib/moderation.ts
index b609f4585..55f7a985d 100644
--- a/server/lib/moderation.ts
+++ b/server/lib/moderation.ts
@@ -15,41 +15,41 @@ export type AcceptResult = {
15 15
16// Can be filtered by plugins 16// Can be filtered by plugins
17function isLocalVideoAccepted (object: { 17function isLocalVideoAccepted (object: {
18 videoBody: VideoCreate, 18 videoBody: VideoCreate
19 videoFile: Express.Multer.File & { duration?: number }, 19 videoFile: Express.Multer.File & { duration?: number }
20 user: UserModel 20 user: UserModel
21}): AcceptResult { 21}): AcceptResult {
22 return { accepted: true } 22 return { accepted: true }
23} 23}
24 24
25function isLocalVideoThreadAccepted (_object: { 25function isLocalVideoThreadAccepted (_object: {
26 commentBody: VideoCommentCreate, 26 commentBody: VideoCommentCreate
27 video: VideoModel, 27 video: VideoModel
28 user: UserModel 28 user: UserModel
29}): AcceptResult { 29}): AcceptResult {
30 return { accepted: true } 30 return { accepted: true }
31} 31}
32 32
33function isLocalVideoCommentReplyAccepted (_object: { 33function isLocalVideoCommentReplyAccepted (_object: {
34 commentBody: VideoCommentCreate, 34 commentBody: VideoCommentCreate
35 parentComment: VideoCommentModel, 35 parentComment: VideoCommentModel
36 video: VideoModel, 36 video: VideoModel
37 user: UserModel 37 user: UserModel
38}): AcceptResult { 38}): AcceptResult {
39 return { accepted: true } 39 return { accepted: true }
40} 40}
41 41
42function isRemoteVideoAccepted (_object: { 42function isRemoteVideoAccepted (_object: {
43 activity: ActivityCreate, 43 activity: ActivityCreate
44 videoAP: VideoTorrentObject, 44 videoAP: VideoTorrentObject
45 byActor: ActorModel 45 byActor: ActorModel
46}): AcceptResult { 46}): AcceptResult {
47 return { accepted: true } 47 return { accepted: true }
48} 48}
49 49
50function isRemoteVideoCommentAccepted (_object: { 50function isRemoteVideoCommentAccepted (_object: {
51 activity: ActivityCreate, 51 activity: ActivityCreate
52 commentAP: VideoCommentObject, 52 commentAP: VideoCommentObject
53 byActor: ActorModel 53 byActor: ActorModel
54}): AcceptResult { 54}): AcceptResult {
55 return { accepted: true } 55 return { accepted: true }
diff --git a/server/lib/notifier.ts b/server/lib/notifier.ts
index 679b9bcf6..63197eee1 100644
--- a/server/lib/notifier.ts
+++ b/server/lib/notifier.ts
@@ -6,7 +6,6 @@ import { UserModel } from '../models/account/user'
6import { PeerTubeSocket } from './peertube-socket' 6import { PeerTubeSocket } from './peertube-socket'
7import { CONFIG } from '../initializers/config' 7import { CONFIG } from '../initializers/config'
8import { VideoPrivacy, VideoState } from '../../shared/models/videos' 8import { VideoPrivacy, VideoState } from '../../shared/models/videos'
9import * as Bluebird from 'bluebird'
10import { AccountBlocklistModel } from '../models/account/account-blocklist' 9import { AccountBlocklistModel } from '../models/account/account-blocklist'
11import { 10import {
12 MCommentOwnerVideo, 11 MCommentOwnerVideo,
@@ -17,7 +16,8 @@ import {
17 MVideoFullLight 16 MVideoFullLight
18} from '../typings/models/video' 17} from '../typings/models/video'
19import { 18import {
20 MUser, MUserAccount, 19 MUser,
20 MUserAccount,
21 MUserDefault, 21 MUserDefault,
22 MUserNotifSettingAccount, 22 MUserNotifSettingAccount,
23 MUserWithNotificationSetting, 23 MUserWithNotificationSetting,
@@ -32,14 +32,15 @@ class Notifier {
32 32
33 private static instance: Notifier 33 private static instance: Notifier
34 34
35 private constructor () {} 35 private constructor () {
36 }
36 37
37 notifyOnNewVideoIfNeeded (video: MVideoAccountLight): void { 38 notifyOnNewVideoIfNeeded (video: MVideoAccountLight): void {
38 // Only notify on public and published videos which are not blacklisted 39 // Only notify on public and published videos which are not blacklisted
39 if (video.privacy !== VideoPrivacy.PUBLIC || video.state !== VideoState.PUBLISHED || video.isBlacklisted()) return 40 if (video.privacy !== VideoPrivacy.PUBLIC || video.state !== VideoState.PUBLISHED || video.isBlacklisted()) return
40 41
41 this.notifySubscribersOfNewVideo(video) 42 this.notifySubscribersOfNewVideo(video)
42 .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err })) 43 .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err }))
43 } 44 }
44 45
45 notifyOnVideoPublishedAfterTranscoding (video: MVideoFullLight): void { 46 notifyOnVideoPublishedAfterTranscoding (video: MVideoFullLight): void {
@@ -63,7 +64,9 @@ class Notifier {
63 if (video.ScheduleVideoUpdate || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return 64 if (video.ScheduleVideoUpdate || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return
64 65
65 this.notifyOwnedVideoHasBeenPublished(video) 66 this.notifyOwnedVideoHasBeenPublished(video)
66 .catch(err => logger.error('Cannot notify owner that its video %s has been published after removed from auto-blacklist.', video.url, { err })) // tslint:disable-line:max-line-length 67 .catch(err => {
68 logger.error('Cannot notify owner that its video %s has been published after removed from auto-blacklist.', video.url, { err })
69 })
67 } 70 }
68 71
69 notifyOnNewComment (comment: MCommentOwnerVideo): void { 72 notifyOnNewComment (comment: MCommentOwnerVideo): void {
@@ -76,17 +79,17 @@ class Notifier {
76 79
77 notifyOnNewVideoAbuse (videoAbuse: MVideoAbuseVideo): void { 80 notifyOnNewVideoAbuse (videoAbuse: MVideoAbuseVideo): void {
78 this.notifyModeratorsOfNewVideoAbuse(videoAbuse) 81 this.notifyModeratorsOfNewVideoAbuse(videoAbuse)
79 .catch(err => logger.error('Cannot notify of new video abuse of video %s.', videoAbuse.Video.url, { err })) 82 .catch(err => logger.error('Cannot notify of new video abuse of video %s.', videoAbuse.Video.url, { err }))
80 } 83 }
81 84
82 notifyOnVideoAutoBlacklist (videoBlacklist: MVideoBlacklistLightVideo): void { 85 notifyOnVideoAutoBlacklist (videoBlacklist: MVideoBlacklistLightVideo): void {
83 this.notifyModeratorsOfVideoAutoBlacklist(videoBlacklist) 86 this.notifyModeratorsOfVideoAutoBlacklist(videoBlacklist)
84 .catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', videoBlacklist.Video.url, { err })) 87 .catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', videoBlacklist.Video.url, { err }))
85 } 88 }
86 89
87 notifyOnVideoBlacklist (videoBlacklist: MVideoBlacklistVideo): void { 90 notifyOnVideoBlacklist (videoBlacklist: MVideoBlacklistVideo): void {
88 this.notifyVideoOwnerOfBlacklist(videoBlacklist) 91 this.notifyVideoOwnerOfBlacklist(videoBlacklist)
89 .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err })) 92 .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err }))
90 } 93 }
91 94
92 notifyOnVideoUnblacklist (video: MVideoFullLight): void { 95 notifyOnVideoUnblacklist (video: MVideoFullLight): void {
@@ -96,7 +99,7 @@ class Notifier {
96 99
97 notifyOnFinishedVideoImport (videoImport: MVideoImportVideo, success: boolean): void { 100 notifyOnFinishedVideoImport (videoImport: MVideoImportVideo, success: boolean): void {
98 this.notifyOwnerVideoImportIsFinished(videoImport, success) 101 this.notifyOwnerVideoImportIsFinished(videoImport, success)
99 .catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err })) 102 .catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err }))
100 } 103 }
101 104
102 notifyOnNewUserRegistration (user: MUserDefault): void { 105 notifyOnNewUserRegistration (user: MUserDefault): void {
@@ -106,14 +109,14 @@ class Notifier {
106 109
107 notifyOfNewUserFollow (actorFollow: MActorFollowFull): void { 110 notifyOfNewUserFollow (actorFollow: MActorFollowFull): void {
108 this.notifyUserOfNewActorFollow(actorFollow) 111 this.notifyUserOfNewActorFollow(actorFollow)
109 .catch(err => { 112 .catch(err => {
110 logger.error( 113 logger.error(
111 'Cannot notify owner of channel %s of a new follow by %s.', 114 'Cannot notify owner of channel %s of a new follow by %s.',
112 actorFollow.ActorFollowing.VideoChannel.getDisplayName(), 115 actorFollow.ActorFollowing.VideoChannel.getDisplayName(),
113 actorFollow.ActorFollower.Account.getDisplayName(), 116 actorFollow.ActorFollower.Account.getDisplayName(),
114 { err } 117 { err }
115 ) 118 )
116 }) 119 })
117 } 120 }
118 121
119 notifyOfNewInstanceFollow (actorFollow: MActorFollowFull): void { 122 notifyOfNewInstanceFollow (actorFollow: MActorFollowFull): void {
@@ -548,10 +551,10 @@ class Notifier {
548 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) 551 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender })
549 } 552 }
550 553
551 private async notify <T extends MUserWithNotificationSetting> (options: { 554 private async notify<T extends MUserWithNotificationSetting> (options: {
552 users: T[], 555 users: T[]
553 notificationCreator: (user: T) => Promise<UserNotificationModelForApi>, 556 notificationCreator: (user: T) => Promise<UserNotificationModelForApi>
554 emailSender: (emails: string[]) => Promise<any> | Bluebird<any>, 557 emailSender: (emails: string[]) => void
555 settingGetter: (user: T) => UserNotificationSettingValue 558 settingGetter: (user: T) => UserNotificationSettingValue
556 }) { 559 }) {
557 const emails: string[] = [] 560 const emails: string[] = []
@@ -569,7 +572,7 @@ class Notifier {
569 } 572 }
570 573
571 if (emails.length !== 0) { 574 if (emails.length !== 0) {
572 await options.emailSender(emails) 575 options.emailSender(emails)
573 } 576 }
574 } 577 }
575 578
diff --git a/server/lib/plugins/plugin-index.ts b/server/lib/plugins/plugin-index.ts
index 25b4f3c61..dcdfba28c 100644
--- a/server/lib/plugins/plugin-index.ts
+++ b/server/lib/plugins/plugin-index.ts
@@ -31,7 +31,7 @@ async function listAvailablePluginsFromIndex (options: PeertubePluginIndexList)
31 31
32 logger.debug('Got result from PeerTube index.', { body }) 32 logger.debug('Got result from PeerTube index.', { body })
33 33
34 await addInstanceInformation(body) 34 addInstanceInformation(body)
35 35
36 return body as ResultList<PeerTubePluginIndex> 36 return body as ResultList<PeerTubePluginIndex>
37 } catch (err) { 37 } catch (err) {
@@ -40,7 +40,7 @@ async function listAvailablePluginsFromIndex (options: PeertubePluginIndexList)
40 } 40 }
41} 41}
42 42
43async function addInstanceInformation (result: ResultList<PeerTubePluginIndex>) { 43function addInstanceInformation (result: ResultList<PeerTubePluginIndex>) {
44 for (const d of result.data) { 44 for (const d of result.data) {
45 d.installed = PluginManager.Instance.isRegistered(d.npmName) 45 d.installed = PluginManager.Instance.isRegistered(d.npmName)
46 d.name = PluginModel.normalizePluginName(d.npmName) 46 d.name = PluginModel.normalizePluginName(d.npmName)
diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts
index 7ebdabd34..73f7a71ce 100644
--- a/server/lib/plugins/plugin-manager.ts
+++ b/server/lib/plugins/plugin-manager.ts
@@ -55,30 +55,30 @@ export interface HookInformationValue {
55} 55}
56 56
57type AlterableVideoConstant = 'language' | 'licence' | 'category' 57type AlterableVideoConstant = 'language' | 'licence' | 'category'
58type VideoConstant = { [ key in number | string ]: string } 58type VideoConstant = { [key in number | string]: string }
59type UpdatedVideoConstant = { 59type UpdatedVideoConstant = {
60 [ name in AlterableVideoConstant ]: { 60 [name in AlterableVideoConstant]: {
61 [ npmName: string ]: { 61 [npmName: string]: {
62 added: { key: number | string, label: string }[], 62 added: { key: number | string, label: string }[]
63 deleted: { key: number | string, label: string }[] 63 deleted: { key: number | string, label: string }[]
64 } 64 }
65 } 65 }
66} 66}
67 67
68type PluginLocalesTranslations = { 68type PluginLocalesTranslations = {
69 [ locale: string ]: PluginTranslation 69 [locale: string]: PluginTranslation
70} 70}
71 71
72export class PluginManager implements ServerHook { 72export class PluginManager implements ServerHook {
73 73
74 private static instance: PluginManager 74 private static instance: PluginManager
75 75
76 private registeredPlugins: { [ name: string ]: RegisteredPlugin } = {} 76 private registeredPlugins: { [name: string]: RegisteredPlugin } = {}
77 private settings: { [ name: string ]: RegisterServerSettingOptions[] } = {} 77 private settings: { [name: string]: RegisterServerSettingOptions[] } = {}
78 private hooks: { [ name: string ]: HookInformationValue[] } = {} 78 private hooks: { [name: string]: HookInformationValue[] } = {}
79 private translations: PluginLocalesTranslations = {} 79 private translations: PluginLocalesTranslations = {}
80 80
81 private updatedVideoConstants: UpdatedVideoConstant = { 81 private readonly updatedVideoConstants: UpdatedVideoConstant = {
82 language: {}, 82 language: {},
83 licence: {}, 83 licence: {},
84 category: {} 84 category: {}
@@ -133,7 +133,7 @@ export class PluginManager implements ServerHook {
133 133
134 // ###################### Hooks ###################### 134 // ###################### Hooks ######################
135 135
136 async runHook <T> (hookName: ServerHookName, result?: T, params?: any): Promise<T> { 136 async runHook<T> (hookName: ServerHookName, result?: T, params?: any): Promise<T> {
137 if (!this.hooks[hookName]) return Promise.resolve(result) 137 if (!this.hooks[hookName]) return Promise.resolve(result)
138 138
139 const hookType = getHookType(hookName) 139 const hookType = getHookType(hookName)
@@ -312,7 +312,7 @@ export class PluginManager implements ServerHook {
312 clientScripts[c.script] = c 312 clientScripts[c.script] = c
313 } 313 }
314 314
315 this.registeredPlugins[ npmName ] = { 315 this.registeredPlugins[npmName] = {
316 npmName, 316 npmName,
317 name: plugin.name, 317 name: plugin.name,
318 type: plugin.type, 318 type: plugin.type,
@@ -438,7 +438,7 @@ export class PluginManager implements ServerHook {
438 const plugins: RegisteredPlugin[] = [] 438 const plugins: RegisteredPlugin[] = []
439 439
440 for (const npmName of Object.keys(this.registeredPlugins)) { 440 for (const npmName of Object.keys(this.registeredPlugins)) {
441 const plugin = this.registeredPlugins[ npmName ] 441 const plugin = this.registeredPlugins[npmName]
442 if (plugin.type !== type) continue 442 if (plugin.type !== type) continue
443 443
444 plugins.push(plugin) 444 plugins.push(plugin)
@@ -518,11 +518,11 @@ export class PluginManager implements ServerHook {
518 } 518 }
519 } 519 }
520 520
521 private addConstant <T extends string | number> (parameters: { 521 private addConstant<T extends string | number> (parameters: {
522 npmName: string, 522 npmName: string
523 type: AlterableVideoConstant, 523 type: AlterableVideoConstant
524 obj: VideoConstant, 524 obj: VideoConstant
525 key: T, 525 key: T
526 label: string 526 label: string
527 }) { 527 }) {
528 const { npmName, type, obj, key, label } = parameters 528 const { npmName, type, obj, key, label } = parameters
@@ -545,10 +545,10 @@ export class PluginManager implements ServerHook {
545 return true 545 return true
546 } 546 }
547 547
548 private deleteConstant <T extends string | number> (parameters: { 548 private deleteConstant<T extends string | number> (parameters: {
549 npmName: string, 549 npmName: string
550 type: AlterableVideoConstant, 550 type: AlterableVideoConstant
551 obj: VideoConstant, 551 obj: VideoConstant
552 key: T 552 key: T
553 }) { 553 }) {
554 const { npmName, type, obj, key } = parameters 554 const { npmName, type, obj, key } = parameters
@@ -604,7 +604,7 @@ export class PluginManager implements ServerHook {
604 const { result: packageJSONValid, badFields } = isPackageJSONValid(packageJSON, pluginType) 604 const { result: packageJSONValid, badFields } = isPackageJSONValid(packageJSON, pluginType)
605 if (!packageJSONValid) { 605 if (!packageJSONValid) {
606 const formattedFields = badFields.map(f => `"${f}"`) 606 const formattedFields = badFields.map(f => `"${f}"`)
607 .join(', ') 607 .join(', ')
608 608
609 throw new Error(`PackageJSON is invalid (invalid fields: ${formattedFields}).`) 609 throw new Error(`PackageJSON is invalid (invalid fields: ${formattedFields}).`)
610 } 610 }
diff --git a/server/lib/redis.ts b/server/lib/redis.ts
index f77d0b62c..0c5dbdd3e 100644
--- a/server/lib/redis.ts
+++ b/server/lib/redis.ts
@@ -12,7 +12,7 @@ import {
12import { CONFIG } from '../initializers/config' 12import { CONFIG } from '../initializers/config'
13 13
14type CachedRoute = { 14type CachedRoute = {
15 body: string, 15 body: string
16 contentType?: string 16 contentType?: string
17 statusCode?: string 17 statusCode?: string
18} 18}
@@ -24,7 +24,8 @@ class Redis {
24 private client: RedisClient 24 private client: RedisClient
25 private prefix: string 25 private prefix: string
26 26
27 private constructor () {} 27 private constructor () {
28 }
28 29
29 init () { 30 init () {
30 // Already initialized 31 // Already initialized
@@ -49,9 +50,9 @@ class Redis {
49 return Object.assign({}, 50 return Object.assign({},
50 (CONFIG.REDIS.AUTH && CONFIG.REDIS.AUTH != null) ? { password: CONFIG.REDIS.AUTH } : {}, 51 (CONFIG.REDIS.AUTH && CONFIG.REDIS.AUTH != null) ? { password: CONFIG.REDIS.AUTH } : {},
51 (CONFIG.REDIS.DB) ? { db: CONFIG.REDIS.DB } : {}, 52 (CONFIG.REDIS.DB) ? { db: CONFIG.REDIS.DB } : {},
52 (CONFIG.REDIS.HOSTNAME && CONFIG.REDIS.PORT) ? 53 (CONFIG.REDIS.HOSTNAME && CONFIG.REDIS.PORT)
53 { host: CONFIG.REDIS.HOSTNAME, port: CONFIG.REDIS.PORT } : 54 ? { host: CONFIG.REDIS.HOSTNAME, port: CONFIG.REDIS.PORT }
54 { path: CONFIG.REDIS.SOCKET } 55 : { path: CONFIG.REDIS.SOCKET }
55 ) 56 )
56 } 57 }
57 58
@@ -63,7 +64,7 @@ class Redis {
63 return this.prefix 64 return this.prefix
64 } 65 }
65 66
66 /************* Forgot password *************/ 67 /* ************ Forgot password ************ */
67 68
68 async setResetPasswordVerificationString (userId: number) { 69 async setResetPasswordVerificationString (userId: number) {
69 const generatedString = await generateRandomString(32) 70 const generatedString = await generateRandomString(32)
@@ -77,7 +78,7 @@ class Redis {
77 return this.getValue(this.generateResetPasswordKey(userId)) 78 return this.getValue(this.generateResetPasswordKey(userId))
78 } 79 }
79 80
80 /************* Email verification *************/ 81 /* ************ Email verification ************ */
81 82
82 async setVerifyEmailVerificationString (userId: number) { 83 async setVerifyEmailVerificationString (userId: number) {
83 const generatedString = await generateRandomString(32) 84 const generatedString = await generateRandomString(32)
@@ -91,7 +92,7 @@ class Redis {
91 return this.getValue(this.generateVerifyEmailKey(userId)) 92 return this.getValue(this.generateVerifyEmailKey(userId))
92 } 93 }
93 94
94 /************* Contact form per IP *************/ 95 /* ************ Contact form per IP ************ */
95 96
96 async setContactFormIp (ip: string) { 97 async setContactFormIp (ip: string) {
97 return this.setValue(this.generateContactFormKey(ip), '1', CONTACT_FORM_LIFETIME) 98 return this.setValue(this.generateContactFormKey(ip), '1', CONTACT_FORM_LIFETIME)
@@ -101,7 +102,7 @@ class Redis {
101 return this.exists(this.generateContactFormKey(ip)) 102 return this.exists(this.generateContactFormKey(ip))
102 } 103 }
103 104
104 /************* Views per IP *************/ 105 /* ************ Views per IP ************ */
105 106
106 setIPVideoView (ip: string, videoUUID: string) { 107 setIPVideoView (ip: string, videoUUID: string) {
107 return this.setValue(this.generateViewKey(ip, videoUUID), '1', VIDEO_VIEW_LIFETIME) 108 return this.setValue(this.generateViewKey(ip, videoUUID), '1', VIDEO_VIEW_LIFETIME)
@@ -111,7 +112,7 @@ class Redis {
111 return this.exists(this.generateViewKey(ip, videoUUID)) 112 return this.exists(this.generateViewKey(ip, videoUUID))
112 } 113 }
113 114
114 /************* API cache *************/ 115 /* ************ API cache ************ */
115 116
116 async getCachedRoute (req: express.Request) { 117 async getCachedRoute (req: express.Request) {
117 const cached = await this.getObject(this.generateCachedRouteKey(req)) 118 const cached = await this.getObject(this.generateCachedRouteKey(req))
@@ -120,17 +121,17 @@ class Redis {
120 } 121 }
121 122
122 setCachedRoute (req: express.Request, body: any, lifetime: number, contentType?: string, statusCode?: number) { 123 setCachedRoute (req: express.Request, body: any, lifetime: number, contentType?: string, statusCode?: number) {
123 const cached: CachedRoute = Object.assign({}, { 124 const cached: CachedRoute = Object.assign(
124 body: body.toString() 125 {},
125 }, 126 { body: body.toString() },
126 (contentType) ? { contentType } : null, 127 (contentType) ? { contentType } : null,
127 (statusCode) ? { statusCode: statusCode.toString() } : null 128 (statusCode) ? { statusCode: statusCode.toString() } : null
128 ) 129 )
129 130
130 return this.setObject(this.generateCachedRouteKey(req), cached, lifetime) 131 return this.setObject(this.generateCachedRouteKey(req), cached, lifetime)
131 } 132 }
132 133
133 /************* Video views *************/ 134 /* ************ Video views ************ */
134 135
135 addVideoView (videoId: number) { 136 addVideoView (videoId: number) {
136 const keyIncr = this.generateVideoViewKey(videoId) 137 const keyIncr = this.generateVideoViewKey(videoId)
@@ -173,7 +174,7 @@ class Redis {
173 ]) 174 ])
174 } 175 }
175 176
176 /************* Keys generation *************/ 177 /* ************ Keys generation ************ */
177 178
178 generateCachedRouteKey (req: express.Request) { 179 generateCachedRouteKey (req: express.Request) {
179 return req.method + '-' + req.originalUrl 180 return req.method + '-' + req.originalUrl
@@ -207,7 +208,7 @@ class Redis {
207 return 'contact-form-' + ip 208 return 'contact-form-' + ip
208 } 209 }
209 210
210 /************* Redis helpers *************/ 211 /* ************ Redis helpers ************ */
211 212
212 private getValue (key: string) { 213 private getValue (key: string) {
213 return new Promise<string>((res, rej) => { 214 return new Promise<string>((res, rej) => {
@@ -265,7 +266,7 @@ class Redis {
265 }) 266 })
266 } 267 }
267 268
268 private setObject (key: string, obj: { [ id: string ]: string }, expirationMilliseconds: number) { 269 private setObject (key: string, obj: { [id: string]: string }, expirationMilliseconds: number) {
269 return new Promise<void>((res, rej) => { 270 return new Promise<void>((res, rej) => {
270 this.client.hmset(this.prefix + key, obj, (err, ok) => { 271 this.client.hmset(this.prefix + key, obj, (err, ok) => {
271 if (err) return rej(err) 272 if (err) return rej(err)
@@ -282,7 +283,7 @@ class Redis {
282 } 283 }
283 284
284 private getObject (key: string) { 285 private getObject (key: string) {
285 return new Promise<{ [ id: string ]: string }>((res, rej) => { 286 return new Promise<{ [id: string]: string }>((res, rej) => {
286 this.client.hgetall(this.prefix + key, (err, value) => { 287 this.client.hgetall(this.prefix + key, (err, value) => {
287 if (err) return rej(err) 288 if (err) return rej(err)
288 289
diff --git a/server/lib/schedulers/auto-follow-index-instances.ts b/server/lib/schedulers/auto-follow-index-instances.ts
index dd326bc1e..d700a99f0 100644
--- a/server/lib/schedulers/auto-follow-index-instances.ts
+++ b/server/lib/schedulers/auto-follow-index-instances.ts
@@ -57,8 +57,7 @@ export class AutoFollowIndexInstances extends AbstractScheduler {
57 isAutoFollow: true 57 isAutoFollow: true
58 } 58 }
59 59
60 await JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) 60 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload })
61 .catch(err => logger.error('Cannot create follow job for %s.', unfollowedHost, err))
62 } 61 }
63 } 62 }
64 63
diff --git a/server/lib/schedulers/plugins-check-scheduler.ts b/server/lib/schedulers/plugins-check-scheduler.ts
index 7ff41e639..014993e94 100644
--- a/server/lib/schedulers/plugins-check-scheduler.ts
+++ b/server/lib/schedulers/plugins-check-scheduler.ts
@@ -43,7 +43,7 @@ export class PluginsCheckScheduler extends AbstractScheduler {
43 const results = await getLatestPluginsVersion(npmNames) 43 const results = await getLatestPluginsVersion(npmNames)
44 44
45 for (const result of results) { 45 for (const result of results) {
46 const plugin = pluginIndex[ result.npmName ] 46 const plugin = pluginIndex[result.npmName]
47 if (!result.latestVersion) continue 47 if (!result.latestVersion) continue
48 48
49 if ( 49 if (
diff --git a/server/lib/schedulers/remove-old-views-scheduler.ts b/server/lib/schedulers/remove-old-views-scheduler.ts
index 39fbb9163..5ae87fe50 100644
--- a/server/lib/schedulers/remove-old-views-scheduler.ts
+++ b/server/lib/schedulers/remove-old-views-scheduler.ts
@@ -1,9 +1,7 @@
1import { logger } from '../../helpers/logger' 1import { logger } from '../../helpers/logger'
2import { AbstractScheduler } from './abstract-scheduler' 2import { AbstractScheduler } from './abstract-scheduler'
3import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants' 3import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants'
4import { UserVideoHistoryModel } from '../../models/account/user-video-history'
5import { CONFIG } from '../../initializers/config' 4import { CONFIG } from '../../initializers/config'
6import { isTestInstance } from '../../helpers/core-utils'
7import { VideoViewModel } from '../../models/video/video-views' 5import { VideoViewModel } from '../../models/video/video-views'
8 6
9export class RemoveOldViewsScheduler extends AbstractScheduler { 7export class RemoveOldViewsScheduler extends AbstractScheduler {
diff --git a/server/lib/schedulers/videos-redundancy-scheduler.ts b/server/lib/schedulers/videos-redundancy-scheduler.ts
index 6e61cbe7d..e33a4133a 100644
--- a/server/lib/schedulers/videos-redundancy-scheduler.ts
+++ b/server/lib/schedulers/videos-redundancy-scheduler.ts
@@ -28,9 +28,9 @@ import { getVideoFilename } from '../video-paths'
28import { VideoModel } from '@server/models/video/video' 28import { VideoModel } from '@server/models/video/video'
29 29
30type CandidateToDuplicate = { 30type CandidateToDuplicate = {
31 redundancy: VideosRedundancyStrategy, 31 redundancy: VideosRedundancyStrategy
32 video: MVideoWithAllFiles, 32 video: MVideoWithAllFiles
33 files: MVideoFile[], 33 files: MVideoFile[]
34 streamingPlaylists: MStreamingPlaylistFiles[] 34 streamingPlaylists: MStreamingPlaylistFiles[]
35} 35}
36 36
diff --git a/server/lib/thumbnail.ts b/server/lib/thumbnail.ts
index a99f71629..8dbd41771 100644
--- a/server/lib/thumbnail.ts
+++ b/server/lib/thumbnail.ts
@@ -69,7 +69,7 @@ function generateVideoMiniature (video: MVideoThumbnail, videoFile: MVideoFile,
69function createPlaceholderThumbnail (fileUrl: string, video: MVideoThumbnail, type: ThumbnailType, size: ImageSize) { 69function createPlaceholderThumbnail (fileUrl: string, video: MVideoThumbnail, type: ThumbnailType, size: ImageSize) {
70 const { filename, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size) 70 const { filename, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size)
71 71
72 const thumbnail = existingThumbnail ? existingThumbnail : new ThumbnailModel() 72 const thumbnail = existingThumbnail || new ThumbnailModel()
73 73
74 thumbnail.filename = filename 74 thumbnail.filename = filename
75 thumbnail.height = height 75 thumbnail.height = height
@@ -142,18 +142,18 @@ function buildMetadataFromVideo (video: MVideoThumbnail, type: ThumbnailType, si
142} 142}
143 143
144async function createThumbnailFromFunction (parameters: { 144async function createThumbnailFromFunction (parameters: {
145 thumbnailCreator: () => Promise<any>, 145 thumbnailCreator: () => Promise<any>
146 filename: string, 146 filename: string
147 height: number, 147 height: number
148 width: number, 148 width: number
149 type: ThumbnailType, 149 type: ThumbnailType
150 automaticallyGenerated?: boolean, 150 automaticallyGenerated?: boolean
151 fileUrl?: string, 151 fileUrl?: string
152 existingThumbnail?: MThumbnail 152 existingThumbnail?: MThumbnail
153}) { 153}) {
154 const { thumbnailCreator, filename, width, height, type, existingThumbnail, automaticallyGenerated = null, fileUrl = null } = parameters 154 const { thumbnailCreator, filename, width, height, type, existingThumbnail, automaticallyGenerated = null, fileUrl = null } = parameters
155 155
156 const thumbnail = existingThumbnail ? existingThumbnail : new ThumbnailModel() 156 const thumbnail = existingThumbnail || new ThumbnailModel()
157 157
158 thumbnail.filename = filename 158 thumbnail.filename = filename
159 thumbnail.height = height 159 thumbnail.height = height
diff --git a/server/lib/user.ts b/server/lib/user.ts
index c45438d95..88e60a7df 100644
--- a/server/lib/user.ts
+++ b/server/lib/user.ts
@@ -18,9 +18,9 @@ import { MUser, MUserDefault, MUserId } from '../typings/models/user'
18type ChannelNames = { name: string, displayName: string } 18type ChannelNames = { name: string, displayName: string }
19 19
20async function createUserAccountAndChannelAndPlaylist (parameters: { 20async function createUserAccountAndChannelAndPlaylist (parameters: {
21 userToCreate: MUser, 21 userToCreate: MUser
22 userDisplayName?: string, 22 userDisplayName?: string
23 channelNames?: ChannelNames, 23 channelNames?: ChannelNames
24 validateUser?: boolean 24 validateUser?: boolean
25}): Promise<{ user: MUserDefault, account: MAccountDefault, videoChannel: MChannelActor }> { 25}): Promise<{ user: MUserDefault, account: MAccountDefault, videoChannel: MChannelActor }> {
26 const { userToCreate, userDisplayName, channelNames, validateUser = true } = parameters 26 const { userToCreate, userDisplayName, channelNames, validateUser = true } = parameters
@@ -63,11 +63,11 @@ async function createUserAccountAndChannelAndPlaylist (parameters: {
63} 63}
64 64
65async function createLocalAccountWithoutKeys (parameters: { 65async function createLocalAccountWithoutKeys (parameters: {
66 name: string, 66 name: string
67 displayName?: string, 67 displayName?: string
68 userId: number | null, 68 userId: number | null
69 applicationId: number | null, 69 applicationId: number | null
70 t: Transaction | undefined, 70 t: Transaction | undefined
71 type?: ActivityPubActorType 71 type?: ActivityPubActorType
72}) { 72}) {
73 const { name, displayName, userId, applicationId, t, type = 'Person' } = parameters 73 const { name, displayName, userId, applicationId, t, type = 'Person' } = parameters
diff --git a/server/lib/video-blacklist.ts b/server/lib/video-blacklist.ts
index 1dd45b76d..3b90b1b94 100644
--- a/server/lib/video-blacklist.ts
+++ b/server/lib/video-blacklist.ts
@@ -9,15 +9,15 @@ import { Notifier } from './notifier'
9import { MUser, MVideoBlacklistVideo, MVideoWithBlacklistLight } from '@server/typings/models' 9import { MUser, MVideoBlacklistVideo, MVideoWithBlacklistLight } from '@server/typings/models'
10 10
11async function autoBlacklistVideoIfNeeded (parameters: { 11async function autoBlacklistVideoIfNeeded (parameters: {
12 video: MVideoWithBlacklistLight, 12 video: MVideoWithBlacklistLight
13 user?: MUser, 13 user?: MUser
14 isRemote: boolean, 14 isRemote: boolean
15 isNew: boolean, 15 isNew: boolean
16 notify?: boolean, 16 notify?: boolean
17 transaction?: Transaction 17 transaction?: Transaction
18}) { 18}) {
19 const { video, user, isRemote, isNew, notify = true, transaction } = parameters 19 const { video, user, isRemote, isNew, notify = true, transaction } = parameters
20 const doAutoBlacklist = await Hooks.wrapPromiseFun( 20 const doAutoBlacklist = await Hooks.wrapFun(
21 autoBlacklistNeeded, 21 autoBlacklistNeeded,
22 { video, user, isRemote, isNew }, 22 { video, user, isRemote, isNew },
23 'filter:video.auto-blacklist.result' 23 'filter:video.auto-blacklist.result'
@@ -49,10 +49,10 @@ async function autoBlacklistVideoIfNeeded (parameters: {
49 return true 49 return true
50} 50}
51 51
52async function autoBlacklistNeeded (parameters: { 52function autoBlacklistNeeded (parameters: {
53 video: MVideoWithBlacklistLight, 53 video: MVideoWithBlacklistLight
54 isRemote: boolean, 54 isRemote: boolean
55 isNew: boolean, 55 isNew: boolean
56 user?: MUser 56 user?: MUser
57}) { 57}) {
58 const { user, video, isRemote, isNew } = parameters 58 const { user, video, isRemote, isNew } = parameters
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts
index 41eab456b..14829c9d6 100644
--- a/server/lib/video-channel.ts
+++ b/server/lib/video-channel.ts
@@ -6,8 +6,7 @@ import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUr
6import { VideoModel } from '../models/video/video' 6import { VideoModel } from '../models/video/video'
7import { MAccountId, MChannelDefault, MChannelId } from '../typings/models' 7import { MAccountId, MChannelDefault, MChannelId } from '../typings/models'
8 8
9type CustomVideoChannelModelAccount <T extends MAccountId> = MChannelDefault & 9type CustomVideoChannelModelAccount <T extends MAccountId> = MChannelDefault & { Account?: T }
10 { Account?: T }
11 10
12async function createLocalVideoChannel <T extends MAccountId> ( 11async function createLocalVideoChannel <T extends MAccountId> (
13 videoChannelInfo: VideoChannelCreate, 12 videoChannelInfo: VideoChannelCreate,
diff --git a/server/lib/video-comment.ts b/server/lib/video-comment.ts
index b8074e6d2..fe83d23e7 100644
--- a/server/lib/video-comment.ts
+++ b/server/lib/video-comment.ts
@@ -7,9 +7,9 @@ import { sendCreateVideoComment } from './activitypub/send'
7import { MAccountDefault, MComment, MCommentOwnerVideoReply, MVideoFullLight } from '../typings/models' 7import { MAccountDefault, MComment, MCommentOwnerVideoReply, MVideoFullLight } from '../typings/models'
8 8
9async function createVideoComment (obj: { 9async function createVideoComment (obj: {
10 text: string, 10 text: string
11 inReplyToComment: MComment | null, 11 inReplyToComment: MComment | null
12 video: MVideoFullLight, 12 video: MVideoFullLight
13 account: MAccountDefault 13 account: MAccountDefault
14}, t: Sequelize.Transaction) { 14}, t: Sequelize.Transaction) {
15 let originCommentId: number | null = null 15 let originCommentId: number | null = null