aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/controllers/activitypub/client.ts37
-rw-r--r--server/controllers/activitypub/inbox.ts5
-rw-r--r--server/controllers/activitypub/outbox.ts10
-rw-r--r--server/controllers/api/search.ts5
-rw-r--r--server/controllers/api/users/index.ts3
-rw-r--r--server/controllers/api/users/me.ts2
-rw-r--r--server/controllers/api/users/my-history.ts1
-rw-r--r--server/controllers/api/video-channel.ts2
-rw-r--r--server/controllers/api/video-playlist.ts36
-rw-r--r--server/controllers/api/videos/abuse.ts7
-rw-r--r--server/controllers/api/videos/blacklist.ts17
-rw-r--r--server/controllers/api/videos/captions.ts9
-rw-r--r--server/controllers/api/videos/comment.ts17
-rw-r--r--server/controllers/api/videos/import.ts34
-rw-r--r--server/controllers/api/videos/index.ts15
-rw-r--r--server/controllers/api/videos/ownership.ts5
-rw-r--r--server/controllers/api/videos/rate.ts2
-rw-r--r--server/controllers/api/videos/watching.ts2
-rw-r--r--server/controllers/feeds.ts2
-rw-r--r--server/controllers/services.ts2
-rw-r--r--server/controllers/static.ts6
-rw-r--r--server/controllers/webfinger.ts2
-rw-r--r--server/helpers/activitypub.ts3
-rw-r--r--server/helpers/actor.ts9
-rw-r--r--server/helpers/captions-utils.ts6
-rw-r--r--server/helpers/custom-validators/video-ownership.ts22
-rw-r--r--server/helpers/middlewares/accounts.ts7
-rw-r--r--server/helpers/middlewares/video-abuses.ts44
-rw-r--r--server/helpers/middlewares/video-captions.ts4
-rw-r--r--server/helpers/middlewares/video-channels.ts45
-rw-r--r--server/helpers/middlewares/video-playlists.ts35
-rw-r--r--server/helpers/middlewares/videos.ts26
-rw-r--r--server/helpers/peertube-crypto.ts9
-rw-r--r--server/helpers/video.ts29
-rw-r--r--server/helpers/webfinger.ts3
-rw-r--r--server/lib/activitypub/actor.ts75
-rw-r--r--server/lib/activitypub/audience.ts27
-rw-r--r--server/lib/activitypub/cache-file.ts14
-rw-r--r--server/lib/activitypub/playlist.ts19
-rw-r--r--server/lib/activitypub/process/process-accept.ts5
-rw-r--r--server/lib/activitypub/process/process-announce.ts7
-rw-r--r--server/lib/activitypub/process/process-create.ts14
-rw-r--r--server/lib/activitypub/process/process-delete.ts26
-rw-r--r--server/lib/activitypub/process/process-dislike.ts4
-rw-r--r--server/lib/activitypub/process/process-flag.ts6
-rw-r--r--server/lib/activitypub/process/process-follow.ts21
-rw-r--r--server/lib/activitypub/process/process-like.ts4
-rw-r--r--server/lib/activitypub/process/process-reject.ts4
-rw-r--r--server/lib/activitypub/process/process-undo.ts12
-rw-r--r--server/lib/activitypub/process/process-update.ts12
-rw-r--r--server/lib/activitypub/process/process-view.ts6
-rw-r--r--server/lib/activitypub/process/process.ts11
-rw-r--r--server/lib/activitypub/send/send-accept.ts7
-rw-r--r--server/lib/activitypub/send/send-announce.ts15
-rw-r--r--server/lib/activitypub/send/send-create.ts34
-rw-r--r--server/lib/activitypub/send/send-delete.ts12
-rw-r--r--server/lib/activitypub/send/send-dislike.ts7
-rw-r--r--server/lib/activitypub/send/send-flag.ts9
-rw-r--r--server/lib/activitypub/send/send-follow.ts6
-rw-r--r--server/lib/activitypub/send/send-like.ts7
-rw-r--r--server/lib/activitypub/send/send-reject.ts7
-rw-r--r--server/lib/activitypub/send/send-undo.ts34
-rw-r--r--server/lib/activitypub/send/send-update.ts31
-rw-r--r--server/lib/activitypub/send/send-view.ts6
-rw-r--r--server/lib/activitypub/send/utils.ts35
-rw-r--r--server/lib/activitypub/share.ts21
-rw-r--r--server/lib/activitypub/url.ts60
-rw-r--r--server/lib/activitypub/video-comments.ts16
-rw-r--r--server/lib/activitypub/video-rates.ts24
-rw-r--r--server/lib/activitypub/videos.ts143
-rw-r--r--server/lib/avatar.ts8
-rw-r--r--server/lib/blocklist.ts5
-rw-r--r--server/lib/client-html.ts7
-rw-r--r--server/lib/emailer.ts36
-rw-r--r--server/lib/hls.ts6
-rw-r--r--server/lib/job-queue/handlers/activitypub-follow.ts9
-rw-r--r--server/lib/job-queue/handlers/activitypub-http-fetcher.ts5
-rw-r--r--server/lib/job-queue/handlers/utils/activitypub-http-utils.ts4
-rw-r--r--server/lib/job-queue/handlers/video-file-import.ts11
-rw-r--r--server/lib/job-queue/handlers/video-import.ts38
-rw-r--r--server/lib/job-queue/handlers/video-transcoding.ts7
-rw-r--r--server/lib/notifier.ts101
-rw-r--r--server/lib/oauth-model.ts3
-rw-r--r--server/lib/peertube-socket.ts4
-rw-r--r--server/lib/redundancy.ts3
-rw-r--r--server/lib/schedulers/videos-redundancy-scheduler.ts52
-rw-r--r--server/lib/thumbnail.ts26
-rw-r--r--server/lib/user.ts26
-rw-r--r--server/lib/video-blacklist.ts13
-rw-r--r--server/lib/video-channel.ts17
-rw-r--r--server/lib/video-comment.ts11
-rw-r--r--server/lib/video-playlist.ts7
-rw-r--r--server/lib/video-transcoding.ts16
-rw-r--r--server/middlewares/validators/follows.ts3
-rw-r--r--server/middlewares/validators/redundancy.ts4
-rw-r--r--server/middlewares/validators/users.ts5
-rw-r--r--server/middlewares/validators/videos/video-abuses.ts4
-rw-r--r--server/middlewares/validators/videos/video-blacklist.ts6
-rw-r--r--server/middlewares/validators/videos/video-captions.ts6
-rw-r--r--server/middlewares/validators/videos/video-channels.ts5
-rw-r--r--server/middlewares/validators/videos/video-comments.ts40
-rw-r--r--server/middlewares/validators/videos/video-playlists.ts84
-rw-r--r--server/middlewares/validators/videos/video-shares.ts2
-rw-r--r--server/middlewares/validators/videos/videos.ts20
-rw-r--r--server/middlewares/validators/webfinger.ts3
-rw-r--r--server/models/account/account-blocklist.ts6
-rw-r--r--server/models/account/account-video-rate.ts21
-rw-r--r--server/models/account/account.ts17
-rw-r--r--server/models/account/user-notification.ts10
-rw-r--r--server/models/account/user-video-history.ts7
-rw-r--r--server/models/account/user.ts30
-rw-r--r--server/models/activitypub/actor-follow.ts25
-rw-r--r--server/models/activitypub/actor.ts20
-rw-r--r--server/models/oauth/oauth-token.ts19
-rw-r--r--server/models/redundancy/video-redundancy.ts9
-rw-r--r--server/models/server/plugin.ts10
-rw-r--r--server/models/server/server-blocklist.ts6
-rw-r--r--server/models/server/server.ts5
-rw-r--r--server/models/video/video-abuse.ts8
-rw-r--r--server/models/video/video-blacklist.ts6
-rw-r--r--server/models/video/video-caption.ts8
-rw-r--r--server/models/video/video-change-ownership.ts6
-rw-r--r--server/models/video/video-channel.ts35
-rw-r--r--server/models/video/video-comment.ts83
-rw-r--r--server/models/video/video-file.ts3
-rw-r--r--server/models/video/video-format-utils.ts15
-rw-r--r--server/models/video/video-import.ts12
-rw-r--r--server/models/video/video-playlist-element.ts13
-rw-r--r--server/models/video/video-playlist.ts18
-rw-r--r--server/models/video/video-share.ts14
-rw-r--r--server/models/video/video-streaming-playlist.ts12
-rw-r--r--server/models/video/video.ts111
-rw-r--r--server/typings/activitypub-processor.model.ts7
-rw-r--r--server/typings/express.ts123
-rw-r--r--server/typings/models/account/account-blocklist.ts11
-rw-r--r--server/typings/models/account/account.ts56
-rw-r--r--server/typings/models/account/actor-follow.ts27
-rw-r--r--server/typings/models/account/actor.ts74
-rw-r--r--server/typings/models/account/avatar.ts3
-rw-r--r--server/typings/models/account/index.d.ts5
-rw-r--r--server/typings/models/actor-follow.ts8
-rw-r--r--server/typings/models/actor.ts22
-rw-r--r--server/typings/models/index.d.ts5
-rw-r--r--server/typings/models/oauth/oauth-client.ts3
-rw-r--r--server/typings/models/oauth/oauth-token.ts9
-rw-r--r--server/typings/models/server/index.d.ts3
-rw-r--r--server/typings/models/server/plugin.ts3
-rw-r--r--server/typings/models/server/server-blocklist.ts9
-rw-r--r--server/typings/models/server/server.ts10
-rw-r--r--server/typings/models/user/index.d.ts3
-rw-r--r--server/typings/models/user/user-notification-setting.ts3
-rw-r--r--server/typings/models/user/user-notification.ts69
-rw-r--r--server/typings/models/user/user-video-history.ts5
-rw-r--r--server/typings/models/user/user.ts32
-rw-r--r--server/typings/models/video-share.ts3
-rw-r--r--server/typings/models/video/index.d.ts14
-rw-r--r--server/typings/models/video/schedule-video-update.ts3
-rw-r--r--server/typings/models/video/tag.ts3
-rw-r--r--server/typings/models/video/thumbnail.ts3
-rw-r--r--server/typings/models/video/video-abuse.ts15
-rw-r--r--server/typings/models/video/video-blacklist.ts11
-rw-r--r--server/typings/models/video/video-caption.ts10
-rw-r--r--server/typings/models/video/video-change-ownership.ts10
-rw-r--r--server/typings/models/video/video-channels.ts70
-rw-r--r--server/typings/models/video/video-comment.ts29
-rw-r--r--server/typings/models/video/video-file.ts15
-rw-r--r--server/typings/models/video/video-import.ts15
-rw-r--r--server/typings/models/video/video-playlist-element.ts15
-rw-r--r--server/typings/models/video/video-playlist.ts42
-rw-r--r--server/typings/models/video/video-rate.ts12
-rw-r--r--server/typings/models/video/video-redundancy.ts18
-rw-r--r--server/typings/models/video/video-share.ts12
-rw-r--r--server/typings/models/video/video-streaming-playlist.ts12
-rw-r--r--server/typings/models/video/video.ts103
-rw-r--r--server/typings/utils.ts14
175 files changed, 2115 insertions, 1134 deletions
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts
index 11504b354..98f5c8865 100644
--- a/server/controllers/activitypub/client.ts
+++ b/server/controllers/activitypub/client.ts
@@ -16,7 +16,6 @@ import {
16} from '../../middlewares' 16} from '../../middlewares'
17import { getAccountVideoRateValidator, videoCommentGetValidator } from '../../middlewares/validators' 17import { getAccountVideoRateValidator, videoCommentGetValidator } from '../../middlewares/validators'
18import { AccountModel } from '../../models/account/account' 18import { AccountModel } from '../../models/account/account'
19import { ActorModel } from '../../models/activitypub/actor'
20import { ActorFollowModel } from '../../models/activitypub/actor-follow' 19import { ActorFollowModel } from '../../models/activitypub/actor-follow'
21import { VideoModel } from '../../models/video/video' 20import { VideoModel } from '../../models/video/video'
22import { VideoCommentModel } from '../../models/video/video-comment' 21import { VideoCommentModel } from '../../models/video/video-comment'
@@ -38,6 +37,7 @@ import { buildDislikeActivity } from '../../lib/activitypub/send/send-dislike'
38import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists' 37import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists'
39import { VideoPlaylistModel } from '../../models/video/video-playlist' 38import { VideoPlaylistModel } from '../../models/video/video-playlist'
40import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' 39import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
40import { MAccountId, MActorId, MVideo, MVideoAPWithoutCaption } from '@server/typings/models'
41 41
42const activityPubClientRouter = express.Router() 42const activityPubClientRouter = express.Router()
43 43
@@ -148,7 +148,7 @@ activityPubClientRouter.get('/redundancy/streaming-playlists/:streamingPlaylistT
148 148
149activityPubClientRouter.get('/video-playlists/:playlistId', 149activityPubClientRouter.get('/video-playlists/:playlistId',
150 executeIfActivityPub, 150 executeIfActivityPub,
151 asyncMiddleware(videoPlaylistsGetValidator), 151 asyncMiddleware(videoPlaylistsGetValidator('all')),
152 asyncMiddleware(videoPlaylistController) 152 asyncMiddleware(videoPlaylistController)
153) 153)
154activityPubClientRouter.get('/video-playlists/:playlistId/:videoId', 154activityPubClientRouter.get('/video-playlists/:playlistId/:videoId',
@@ -208,18 +208,19 @@ function getAccountVideoRate (rateType: VideoRateType) {
208 208
209async function videoController (req: express.Request, res: express.Response) { 209async function videoController (req: express.Request, res: express.Response) {
210 // We need more attributes 210 // We need more attributes
211 const video = await VideoModel.loadForGetAPI({ id: res.locals.video.id }) 211 const video = await VideoModel.loadForGetAPI({ id: res.locals.onlyVideoWithRights.id }) as MVideoAPWithoutCaption
212 212
213 if (video.url.startsWith(WEBSERVER.URL) === false) return res.redirect(video.url) 213 if (video.url.startsWith(WEBSERVER.URL) === false) return res.redirect(video.url)
214 214
215 // We need captions to render AP object 215 // We need captions to render AP object
216 video.VideoCaptions = await VideoCaptionModel.listVideoCaptions(video.id) 216 const captions = await VideoCaptionModel.listVideoCaptions(video.id)
217 const videoWithCaptions: MVideoAPWithoutCaption = Object.assign(video, { VideoCaptions: captions })
217 218
218 const audience = getAudience(video.VideoChannel.Account.Actor, video.privacy === VideoPrivacy.PUBLIC) 219 const audience = getAudience(videoWithCaptions.VideoChannel.Account.Actor, videoWithCaptions.privacy === VideoPrivacy.PUBLIC)
219 const videoObject = audiencify(video.toActivityPubObject(), audience) 220 const videoObject = audiencify(videoWithCaptions.toActivityPubObject(), audience)
220 221
221 if (req.path.endsWith('/activity')) { 222 if (req.path.endsWith('/activity')) {
222 const data = buildCreateActivity(video.url, video.VideoChannel.Account.Actor, videoObject, audience) 223 const data = buildCreateActivity(videoWithCaptions.url, video.VideoChannel.Account.Actor, videoObject, audience)
223 return activityPubResponse(activityPubContextify(data), res) 224 return activityPubResponse(activityPubContextify(data), res)
224 } 225 }
225 226
@@ -231,13 +232,13 @@ async function videoAnnounceController (req: express.Request, res: express.Respo
231 232
232 if (share.url.startsWith(WEBSERVER.URL) === false) return res.redirect(share.url) 233 if (share.url.startsWith(WEBSERVER.URL) === false) return res.redirect(share.url)
233 234
234 const { activity } = await buildAnnounceWithVideoAudience(share.Actor, share, res.locals.video, undefined) 235 const { activity } = await buildAnnounceWithVideoAudience(share.Actor, share, res.locals.videoAll, undefined)
235 236
236 return activityPubResponse(activityPubContextify(activity), res) 237 return activityPubResponse(activityPubContextify(activity), res)
237} 238}
238 239
239async function videoAnnouncesController (req: express.Request, res: express.Response) { 240async function videoAnnouncesController (req: express.Request, res: express.Response) {
240 const video = res.locals.video 241 const video = res.locals.onlyVideo
241 242
242 const handler = async (start: number, count: number) => { 243 const handler = async (start: number, count: number) => {
243 const result = await VideoShareModel.listAndCountByVideoId(video.id, start, count) 244 const result = await VideoShareModel.listAndCountByVideoId(video.id, start, count)
@@ -252,21 +253,21 @@ async function videoAnnouncesController (req: express.Request, res: express.Resp
252} 253}
253 254
254async function videoLikesController (req: express.Request, res: express.Response) { 255async function videoLikesController (req: express.Request, res: express.Response) {
255 const video = res.locals.video 256 const video = res.locals.onlyVideo
256 const json = await videoRates(req, 'like', video, getVideoLikesActivityPubUrl(video)) 257 const json = await videoRates(req, 'like', video, getVideoLikesActivityPubUrl(video))
257 258
258 return activityPubResponse(activityPubContextify(json), res) 259 return activityPubResponse(activityPubContextify(json), res)
259} 260}
260 261
261async function videoDislikesController (req: express.Request, res: express.Response) { 262async function videoDislikesController (req: express.Request, res: express.Response) {
262 const video = res.locals.video 263 const video = res.locals.onlyVideo
263 const json = await videoRates(req, 'dislike', video, getVideoDislikesActivityPubUrl(video)) 264 const json = await videoRates(req, 'dislike', video, getVideoDislikesActivityPubUrl(video))
264 265
265 return activityPubResponse(activityPubContextify(json), res) 266 return activityPubResponse(activityPubContextify(json), res)
266} 267}
267 268
268async function videoCommentsController (req: express.Request, res: express.Response) { 269async function videoCommentsController (req: express.Request, res: express.Response) {
269 const video = res.locals.video 270 const video = res.locals.onlyVideo
270 271
271 const handler = async (start: number, count: number) => { 272 const handler = async (start: number, count: number) => {
272 const result = await VideoCommentModel.listAndCountByVideoId(video.id, start, count) 273 const result = await VideoCommentModel.listAndCountByVideoId(video.id, start, count)
@@ -301,7 +302,7 @@ async function videoChannelFollowingController (req: express.Request, res: expre
301} 302}
302 303
303async function videoCommentController (req: express.Request, res: express.Response) { 304async function videoCommentController (req: express.Request, res: express.Response) {
304 const videoComment = res.locals.videoComment 305 const videoComment = res.locals.videoCommentFull
305 306
306 if (videoComment.url.startsWith(WEBSERVER.URL) === false) return res.redirect(videoComment.url) 307 if (videoComment.url.startsWith(WEBSERVER.URL) === false) return res.redirect(videoComment.url)
307 308
@@ -337,7 +338,7 @@ async function videoRedundancyController (req: express.Request, res: express.Res
337} 338}
338 339
339async function videoPlaylistController (req: express.Request, res: express.Response) { 340async function videoPlaylistController (req: express.Request, res: express.Response) {
340 const playlist = res.locals.videoPlaylist 341 const playlist = res.locals.videoPlaylistFull
341 342
342 // We need more attributes 343 // We need more attributes
343 playlist.OwnerAccount = await AccountModel.load(playlist.ownerAccountId) 344 playlist.OwnerAccount = await AccountModel.load(playlist.ownerAccountId)
@@ -358,7 +359,7 @@ async function videoPlaylistElementController (req: express.Request, res: expres
358 359
359// --------------------------------------------------------------------------- 360// ---------------------------------------------------------------------------
360 361
361async function actorFollowing (req: express.Request, actor: ActorModel) { 362async function actorFollowing (req: express.Request, actor: MActorId) {
362 const handler = (start: number, count: number) => { 363 const handler = (start: number, count: number) => {
363 return ActorFollowModel.listAcceptedFollowingUrlsForApi([ actor.id ], undefined, start, count) 364 return ActorFollowModel.listAcceptedFollowingUrlsForApi([ actor.id ], undefined, start, count)
364 } 365 }
@@ -366,7 +367,7 @@ async function actorFollowing (req: express.Request, actor: ActorModel) {
366 return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page) 367 return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page)
367} 368}
368 369
369async function actorFollowers (req: express.Request, actor: ActorModel) { 370async function actorFollowers (req: express.Request, actor: MActorId) {
370 const handler = (start: number, count: number) => { 371 const handler = (start: number, count: number) => {
371 return ActorFollowModel.listAcceptedFollowerUrlsForAP([ actor.id ], undefined, start, count) 372 return ActorFollowModel.listAcceptedFollowerUrlsForAP([ actor.id ], undefined, start, count)
372 } 373 }
@@ -374,7 +375,7 @@ async function actorFollowers (req: express.Request, actor: ActorModel) {
374 return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page) 375 return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page)
375} 376}
376 377
377async function actorPlaylists (req: express.Request, account: AccountModel) { 378async function actorPlaylists (req: express.Request, account: MAccountId) {
378 const handler = (start: number, count: number) => { 379 const handler = (start: number, count: number) => {
379 return VideoPlaylistModel.listPublicUrlsOfForAP(account.id, start, count) 380 return VideoPlaylistModel.listPublicUrlsOfForAP(account.id, start, count)
380 } 381 }
@@ -382,7 +383,7 @@ async function actorPlaylists (req: express.Request, account: AccountModel) {
382 return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page) 383 return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page)
383} 384}
384 385
385function videoRates (req: express.Request, rateType: VideoRateType, video: VideoModel, url: string) { 386function videoRates (req: express.Request, rateType: VideoRateType, video: MVideo, url: string) {
386 const handler = async (start: number, count: number) => { 387 const handler = async (start: number, count: number) => {
387 const result = await AccountVideoRateModel.listAndCountAccountUrlsByVideoId(rateType, video.id, start, count) 388 const result = await AccountVideoRateModel.listAndCountAccountUrlsByVideoId(rateType, video.id, start, count)
388 return { 389 return {
diff --git a/server/controllers/activitypub/inbox.ts b/server/controllers/activitypub/inbox.ts
index 2d3eef222..d9df253aa 100644
--- a/server/controllers/activitypub/inbox.ts
+++ b/server/controllers/activitypub/inbox.ts
@@ -7,7 +7,7 @@ import { asyncMiddleware, checkSignature, localAccountValidator, localVideoChann
7import { activityPubValidator } from '../../middlewares/validators/activitypub/activity' 7import { activityPubValidator } from '../../middlewares/validators/activitypub/activity'
8import { queue } from 'async' 8import { queue } from 'async'
9import { ActorModel } from '../../models/activitypub/actor' 9import { ActorModel } from '../../models/activitypub/actor'
10import { SignatureActorModel } from '../../typings/models' 10import { MActorDefault, MActorSignature } from '../../typings/models'
11 11
12const inboxRouter = express.Router() 12const inboxRouter = express.Router()
13 13
@@ -41,7 +41,8 @@ export {
41 41
42// --------------------------------------------------------------------------- 42// ---------------------------------------------------------------------------
43 43
44const inboxQueue = queue<{ activities: Activity[], signatureActor?: SignatureActorModel, inboxActor?: ActorModel }, Error>((task, cb) => { 44type QueueParam = { activities: Activity[], signatureActor?: MActorSignature, inboxActor?: MActorDefault }
45const inboxQueue = queue<QueueParam, Error>((task, cb) => {
45 const options = { signatureActor: task.signatureActor, inboxActor: task.inboxActor } 46 const options = { signatureActor: task.signatureActor, inboxActor: task.inboxActor }
46 47
47 processActivities(task.activities, options) 48 processActivities(task.activities, options)
diff --git a/server/controllers/activitypub/outbox.ts b/server/controllers/activitypub/outbox.ts
index 38b6ec976..f3dd2ad7d 100644
--- a/server/controllers/activitypub/outbox.ts
+++ b/server/controllers/activitypub/outbox.ts
@@ -6,11 +6,9 @@ import { logger } from '../../helpers/logger'
6import { buildAnnounceActivity, buildCreateActivity } from '../../lib/activitypub/send' 6import { buildAnnounceActivity, buildCreateActivity } from '../../lib/activitypub/send'
7import { buildAudience } from '../../lib/activitypub/audience' 7import { buildAudience } from '../../lib/activitypub/audience'
8import { asyncMiddleware, localAccountValidator, localVideoChannelValidator } from '../../middlewares' 8import { asyncMiddleware, localAccountValidator, localVideoChannelValidator } from '../../middlewares'
9import { AccountModel } from '../../models/account/account'
10import { ActorModel } from '../../models/activitypub/actor'
11import { VideoModel } from '../../models/video/video' 9import { VideoModel } from '../../models/video/video'
12import { activityPubResponse } from './utils' 10import { activityPubResponse } from './utils'
13import { VideoChannelModel } from '../../models/video/video-channel' 11import { MActorLight } from '@server/typings/models'
14 12
15const outboxRouter = express.Router() 13const outboxRouter = express.Router()
16 14
@@ -45,14 +43,10 @@ async function outboxController (req: express.Request, res: express.Response) {
45 return activityPubResponse(activityPubContextify(json), res) 43 return activityPubResponse(activityPubContextify(json), res)
46} 44}
47 45
48async function buildActivities (actor: ActorModel, start: number, count: number) { 46async function buildActivities (actor: MActorLight, start: number, count: number) {
49 const data = await VideoModel.listAllAndSharedByActorForOutbox(actor.id, start, count) 47 const data = await VideoModel.listAllAndSharedByActorForOutbox(actor.id, start, count)
50 const activities: Activity[] = [] 48 const activities: Activity[] = []
51 49
52 // Avoid too many SQL requests
53 const actors = data.data.map(v => v.VideoChannel.Account.Actor)
54 actors.push(actor)
55
56 for (const video of data.data) { 50 for (const video of data.data) {
57 const byActor = video.VideoChannel.Account.Actor 51 const byActor = video.VideoChannel.Account.Actor
58 const createActivityAudience = buildAudience([ byActor.followersUrl ], video.privacy === VideoPrivacy.PUBLIC) 52 const createActivityAudience = buildAudience([ byActor.followersUrl ], video.privacy === VideoPrivacy.PUBLIC)
diff --git a/server/controllers/api/search.ts b/server/controllers/api/search.ts
index 9a1e30b83..7fef7c173 100644
--- a/server/controllers/api/search.ts
+++ b/server/controllers/api/search.ts
@@ -19,6 +19,7 @@ import { getOrCreateActorAndServerAndModel, getOrCreateVideoAndAccountAndChannel
19import { logger } from '../../helpers/logger' 19import { logger } from '../../helpers/logger'
20import { VideoChannelModel } from '../../models/video/video-channel' 20import { VideoChannelModel } from '../../models/video/video-channel'
21import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' 21import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
22import { MChannelAccountDefault, MVideoAccountAllFiles } from '../../typings/models'
22 23
23const searchRouter = express.Router() 24const searchRouter = express.Router()
24 25
@@ -84,7 +85,7 @@ async function searchVideoChannelsDB (query: VideoChannelsSearchQuery, res: expr
84} 85}
85 86
86async function searchVideoChannelURI (search: string, isWebfingerSearch: boolean, res: express.Response) { 87async function searchVideoChannelURI (search: string, isWebfingerSearch: boolean, res: express.Response) {
87 let videoChannel: VideoChannelModel 88 let videoChannel: MChannelAccountDefault
88 let uri = search 89 let uri = search
89 90
90 if (isWebfingerSearch) { 91 if (isWebfingerSearch) {
@@ -137,7 +138,7 @@ async function searchVideosDB (query: VideosSearchQuery, res: express.Response)
137} 138}
138 139
139async function searchVideoURI (url: string, res: express.Response) { 140async function searchVideoURI (url: string, res: express.Response) {
140 let video: VideoModel 141 let video: MVideoAccountAllFiles
141 142
142 // Check if we can fetch a remote video with the URL 143 // Check if we can fetch a remote video with the URL
143 if (isUserAbleToSearchRemoteURI(res)) { 144 if (isUserAbleToSearchRemoteURI(res)) {
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts
index ae40e86f8..e6b678f3a 100644
--- a/server/controllers/api/users/index.ts
+++ b/server/controllers/api/users/index.ts
@@ -48,6 +48,7 @@ import { CONFIG } from '../../../initializers/config'
48import { sequelizeTypescript } from '../../../initializers/database' 48import { sequelizeTypescript } from '../../../initializers/database'
49import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' 49import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
50import { UserRegister } from '../../../../shared/models/users/user-register.model' 50import { UserRegister } from '../../../../shared/models/users/user-register.model'
51import { MUser, MUserAccountDefault } from '@server/typings/models'
51 52
52const auditLogger = auditLoggerFactory('users') 53const auditLogger = auditLoggerFactory('users')
53 54
@@ -359,7 +360,7 @@ function success (req: express.Request, res: express.Response) {
359 res.end() 360 res.end()
360} 361}
361 362
362async function changeUserBlock (res: express.Response, user: UserModel, block: boolean, reason?: string) { 363async function changeUserBlock (res: express.Response, user: MUserAccountDefault, block: boolean, reason?: string) {
363 const oldUserAuditView = new UserAuditView(user.toFormattedJSON()) 364 const oldUserAuditView = new UserAuditView(user.toFormattedJSON())
364 365
365 user.blocked = block 366 user.blocked = block
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts
index e7ed3de64..af054f620 100644
--- a/server/controllers/api/users/me.ts
+++ b/server/controllers/api/users/me.ts
@@ -147,7 +147,7 @@ async function getUserVideoQuotaUsed (req: express.Request, res: express.Respons
147} 147}
148 148
149async function getUserVideoRating (req: express.Request, res: express.Response) { 149async function getUserVideoRating (req: express.Request, res: express.Response) {
150 const videoId = res.locals.video.id 150 const videoId = res.locals.videoId.id
151 const accountId = +res.locals.oauth.token.User.Account.id 151 const accountId = +res.locals.oauth.token.User.Account.id
152 152
153 const ratingObj = await AccountVideoRateModel.load(accountId, videoId, null) 153 const ratingObj = await AccountVideoRateModel.load(accountId, videoId, null)
diff --git a/server/controllers/api/users/my-history.ts b/server/controllers/api/users/my-history.ts
index 7025c0ff1..4da1f3496 100644
--- a/server/controllers/api/users/my-history.ts
+++ b/server/controllers/api/users/my-history.ts
@@ -7,7 +7,6 @@ import {
7 setDefaultPagination, 7 setDefaultPagination,
8 userHistoryRemoveValidator 8 userHistoryRemoveValidator
9} from '../../../middlewares' 9} from '../../../middlewares'
10import { UserModel } from '../../../models/account/user'
11import { getFormattedObjects } from '../../../helpers/utils' 10import { getFormattedObjects } from '../../../helpers/utils'
12import { UserVideoHistoryModel } from '../../../models/account/user-video-history' 11import { UserVideoHistoryModel } from '../../../models/account/user-video-history'
13import { sequelizeTypescript } from '../../../initializers' 12import { sequelizeTypescript } from '../../../initializers'
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts
index 81a03a62b..2b6184a83 100644
--- a/server/controllers/api/video-channel.ts
+++ b/server/controllers/api/video-channel.ts
@@ -136,7 +136,7 @@ async function updateVideoChannelAvatar (req: express.Request, res: express.Resp
136async function addVideoChannel (req: express.Request, res: express.Response) { 136async function addVideoChannel (req: express.Request, res: express.Response) {
137 const videoChannelInfo: VideoChannelCreate = req.body 137 const videoChannelInfo: VideoChannelCreate = req.body
138 138
139 const videoChannelCreated: VideoChannelModel = await sequelizeTypescript.transaction(async t => { 139 const videoChannelCreated = await sequelizeTypescript.transaction(async t => {
140 const account = await AccountModel.load(res.locals.oauth.token.User.Account.id, t) 140 const account = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
141 141
142 return createVideoChannel(videoChannelInfo, account, t) 142 return createVideoChannel(videoChannelInfo, account, t)
diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts
index bd454f553..d9f0ff925 100644
--- a/server/controllers/api/video-playlist.ts
+++ b/server/controllers/api/video-playlist.ts
@@ -40,7 +40,7 @@ import { JobQueue } from '../../lib/job-queue'
40import { CONFIG } from '../../initializers/config' 40import { CONFIG } from '../../initializers/config'
41import { sequelizeTypescript } from '../../initializers/database' 41import { sequelizeTypescript } from '../../initializers/database'
42import { createPlaylistMiniatureFromExisting } from '../../lib/thumbnail' 42import { createPlaylistMiniatureFromExisting } from '../../lib/thumbnail'
43import { VideoModel } from '../../models/video/video' 43import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/typings/models'
44 44
45const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { thumbnailfile: CONFIG.STORAGE.TMP_DIR }) 45const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { thumbnailfile: CONFIG.STORAGE.TMP_DIR })
46 46
@@ -58,7 +58,7 @@ videoPlaylistRouter.get('/',
58) 58)
59 59
60videoPlaylistRouter.get('/:playlistId', 60videoPlaylistRouter.get('/:playlistId',
61 asyncMiddleware(videoPlaylistsGetValidator), 61 asyncMiddleware(videoPlaylistsGetValidator('summary')),
62 getVideoPlaylist 62 getVideoPlaylist
63) 63)
64 64
@@ -83,7 +83,7 @@ videoPlaylistRouter.delete('/:playlistId',
83) 83)
84 84
85videoPlaylistRouter.get('/:playlistId/videos', 85videoPlaylistRouter.get('/:playlistId/videos',
86 asyncMiddleware(videoPlaylistsGetValidator), 86 asyncMiddleware(videoPlaylistsGetValidator('summary')),
87 paginationValidator, 87 paginationValidator,
88 setDefaultPagination, 88 setDefaultPagination,
89 optionalAuthenticate, 89 optionalAuthenticate,
@@ -140,7 +140,7 @@ async function listVideoPlaylists (req: express.Request, res: express.Response)
140} 140}
141 141
142function getVideoPlaylist (req: express.Request, res: express.Response) { 142function getVideoPlaylist (req: express.Request, res: express.Response) {
143 const videoPlaylist = res.locals.videoPlaylist 143 const videoPlaylist = res.locals.videoPlaylistSummary
144 144
145 if (videoPlaylist.isOutdated()) { 145 if (videoPlaylist.isOutdated()) {
146 JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video-playlist', url: videoPlaylist.url } }) 146 JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video-playlist', url: videoPlaylist.url } })
@@ -159,7 +159,7 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) {
159 description: videoPlaylistInfo.description, 159 description: videoPlaylistInfo.description,
160 privacy: videoPlaylistInfo.privacy || VideoPlaylistPrivacy.PRIVATE, 160 privacy: videoPlaylistInfo.privacy || VideoPlaylistPrivacy.PRIVATE,
161 ownerAccountId: user.Account.id 161 ownerAccountId: user.Account.id
162 }) 162 }) as MVideoPlaylistFull
163 163
164 videoPlaylist.url = getVideoPlaylistActivityPubUrl(videoPlaylist) // We use the UUID, so set the URL after building the object 164 videoPlaylist.url = getVideoPlaylistActivityPubUrl(videoPlaylist) // We use the UUID, so set the URL after building the object
165 165
@@ -175,8 +175,8 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) {
175 ? await createPlaylistMiniatureFromExisting(thumbnailField[0].path, videoPlaylist, false) 175 ? await createPlaylistMiniatureFromExisting(thumbnailField[0].path, videoPlaylist, false)
176 : undefined 176 : undefined
177 177
178 const videoPlaylistCreated: VideoPlaylistModel = await sequelizeTypescript.transaction(async t => { 178 const videoPlaylistCreated = await sequelizeTypescript.transaction(async t => {
179 const videoPlaylistCreated = await videoPlaylist.save({ transaction: t }) 179 const videoPlaylistCreated = await videoPlaylist.save({ transaction: t }) as MVideoPlaylistFull
180 180
181 if (thumbnailModel) { 181 if (thumbnailModel) {
182 thumbnailModel.automaticallyGenerated = false 182 thumbnailModel.automaticallyGenerated = false
@@ -201,7 +201,7 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) {
201} 201}
202 202
203async function updateVideoPlaylist (req: express.Request, res: express.Response) { 203async function updateVideoPlaylist (req: express.Request, res: express.Response) {
204 const videoPlaylistInstance = res.locals.videoPlaylist 204 const videoPlaylistInstance = res.locals.videoPlaylistFull
205 const videoPlaylistFieldsSave = videoPlaylistInstance.toJSON() 205 const videoPlaylistFieldsSave = videoPlaylistInstance.toJSON()
206 const videoPlaylistInfoToUpdate = req.body as VideoPlaylistUpdate 206 const videoPlaylistInfoToUpdate = req.body as VideoPlaylistUpdate
207 207
@@ -275,7 +275,7 @@ async function updateVideoPlaylist (req: express.Request, res: express.Response)
275} 275}
276 276
277async function removeVideoPlaylist (req: express.Request, res: express.Response) { 277async function removeVideoPlaylist (req: express.Request, res: express.Response) {
278 const videoPlaylistInstance = res.locals.videoPlaylist 278 const videoPlaylistInstance = res.locals.videoPlaylistSummary
279 279
280 await sequelizeTypescript.transaction(async t => { 280 await sequelizeTypescript.transaction(async t => {
281 await videoPlaylistInstance.destroy({ transaction: t }) 281 await videoPlaylistInstance.destroy({ transaction: t })
@@ -290,10 +290,10 @@ async function removeVideoPlaylist (req: express.Request, res: express.Response)
290 290
291async function addVideoInPlaylist (req: express.Request, res: express.Response) { 291async function addVideoInPlaylist (req: express.Request, res: express.Response) {
292 const body: VideoPlaylistElementCreate = req.body 292 const body: VideoPlaylistElementCreate = req.body
293 const videoPlaylist = res.locals.videoPlaylist 293 const videoPlaylist = res.locals.videoPlaylistFull
294 const video = res.locals.video 294 const video = res.locals.onlyVideo
295 295
296 const playlistElement: VideoPlaylistElementModel = await sequelizeTypescript.transaction(async t => { 296 const playlistElement = await sequelizeTypescript.transaction(async t => {
297 const position = await VideoPlaylistElementModel.getNextPositionOf(videoPlaylist.id, t) 297 const position = await VideoPlaylistElementModel.getNextPositionOf(videoPlaylist.id, t)
298 298
299 const playlistElement = await VideoPlaylistElementModel.create({ 299 const playlistElement = await VideoPlaylistElementModel.create({
@@ -330,7 +330,7 @@ async function addVideoInPlaylist (req: express.Request, res: express.Response)
330 330
331async function updateVideoPlaylistElement (req: express.Request, res: express.Response) { 331async function updateVideoPlaylistElement (req: express.Request, res: express.Response) {
332 const body: VideoPlaylistElementUpdate = req.body 332 const body: VideoPlaylistElementUpdate = req.body
333 const videoPlaylist = res.locals.videoPlaylist 333 const videoPlaylist = res.locals.videoPlaylistFull
334 const videoPlaylistElement = res.locals.videoPlaylistElement 334 const videoPlaylistElement = res.locals.videoPlaylistElement
335 335
336 const playlistElement: VideoPlaylistElementModel = await sequelizeTypescript.transaction(async t => { 336 const playlistElement: VideoPlaylistElementModel = await sequelizeTypescript.transaction(async t => {
@@ -354,7 +354,7 @@ async function updateVideoPlaylistElement (req: express.Request, res: express.Re
354 354
355async function removeVideoFromPlaylist (req: express.Request, res: express.Response) { 355async function removeVideoFromPlaylist (req: express.Request, res: express.Response) {
356 const videoPlaylistElement = res.locals.videoPlaylistElement 356 const videoPlaylistElement = res.locals.videoPlaylistElement
357 const videoPlaylist = res.locals.videoPlaylist 357 const videoPlaylist = res.locals.videoPlaylistFull
358 const positionToDelete = videoPlaylistElement.position 358 const positionToDelete = videoPlaylistElement.position
359 359
360 await sequelizeTypescript.transaction(async t => { 360 await sequelizeTypescript.transaction(async t => {
@@ -381,7 +381,7 @@ async function removeVideoFromPlaylist (req: express.Request, res: express.Respo
381} 381}
382 382
383async function reorderVideosPlaylist (req: express.Request, res: express.Response) { 383async function reorderVideosPlaylist (req: express.Request, res: express.Response) {
384 const videoPlaylist = res.locals.videoPlaylist 384 const videoPlaylist = res.locals.videoPlaylistFull
385 const body: VideoPlaylistReorder = req.body 385 const body: VideoPlaylistReorder = req.body
386 386
387 const start: number = body.startPosition 387 const start: number = body.startPosition
@@ -434,7 +434,7 @@ async function reorderVideosPlaylist (req: express.Request, res: express.Respons
434} 434}
435 435
436async function getVideoPlaylistVideos (req: express.Request, res: express.Response) { 436async function getVideoPlaylistVideos (req: express.Request, res: express.Response) {
437 const videoPlaylistInstance = res.locals.videoPlaylist 437 const videoPlaylistInstance = res.locals.videoPlaylistSummary
438 const user = res.locals.oauth ? res.locals.oauth.token.User : undefined 438 const user = res.locals.oauth ? res.locals.oauth.token.User : undefined
439 const server = await getServerActor() 439 const server = await getServerActor()
440 440
@@ -453,7 +453,7 @@ async function getVideoPlaylistVideos (req: express.Request, res: express.Respon
453 return res.json(getFormattedObjects(resultList.data, resultList.total, options)) 453 return res.json(getFormattedObjects(resultList.data, resultList.total, options))
454} 454}
455 455
456async function regeneratePlaylistThumbnail (videoPlaylist: VideoPlaylistModel) { 456async function regeneratePlaylistThumbnail (videoPlaylist: MVideoPlaylistThumbnail) {
457 await videoPlaylist.Thumbnail.destroy() 457 await videoPlaylist.Thumbnail.destroy()
458 videoPlaylist.Thumbnail = null 458 videoPlaylist.Thumbnail = null
459 459
@@ -461,7 +461,7 @@ async function regeneratePlaylistThumbnail (videoPlaylist: VideoPlaylistModel) {
461 if (firstElement) await generateThumbnailForPlaylist(videoPlaylist, firstElement.Video) 461 if (firstElement) await generateThumbnailForPlaylist(videoPlaylist, firstElement.Video)
462} 462}
463 463
464async function generateThumbnailForPlaylist (videoPlaylist: VideoPlaylistModel, video: VideoModel) { 464async function generateThumbnailForPlaylist (videoPlaylist: MVideoPlaylistThumbnail, video: MVideoThumbnail) {
465 logger.info('Generating default thumbnail to playlist %s.', videoPlaylist.url) 465 logger.info('Generating default thumbnail to playlist %s.', videoPlaylist.url)
466 466
467 const inputPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, video.getMiniature().filename) 467 const inputPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, video.getMiniature().filename)
diff --git a/server/controllers/api/videos/abuse.ts b/server/controllers/api/videos/abuse.ts
index 77808466c..39c841ffe 100644
--- a/server/controllers/api/videos/abuse.ts
+++ b/server/controllers/api/videos/abuse.ts
@@ -21,6 +21,7 @@ import { VideoAbuseModel } from '../../../models/video/video-abuse'
21import { auditLoggerFactory, VideoAbuseAuditView } from '../../../helpers/audit-logger' 21import { auditLoggerFactory, VideoAbuseAuditView } from '../../../helpers/audit-logger'
22import { Notifier } from '../../../lib/notifier' 22import { Notifier } from '../../../lib/notifier'
23import { sendVideoAbuse } from '../../../lib/activitypub/send/send-flag' 23import { sendVideoAbuse } from '../../../lib/activitypub/send/send-flag'
24import { MVideoAbuseAccountVideo } from '../../../typings/models/video'
24 25
25const auditLogger = auditLoggerFactory('abuse') 26const auditLogger = auditLoggerFactory('abuse')
26const abuseVideoRouter = express.Router() 27const abuseVideoRouter = express.Router()
@@ -94,10 +95,10 @@ async function deleteVideoAbuse (req: express.Request, res: express.Response) {
94} 95}
95 96
96async function reportVideoAbuse (req: express.Request, res: express.Response) { 97async function reportVideoAbuse (req: express.Request, res: express.Response) {
97 const videoInstance = res.locals.video 98 const videoInstance = res.locals.videoAll
98 const body: VideoAbuseCreate = req.body 99 const body: VideoAbuseCreate = req.body
99 100
100 const videoAbuse: VideoAbuseModel = await sequelizeTypescript.transaction(async t => { 101 const videoAbuse = await sequelizeTypescript.transaction(async t => {
101 const reporterAccount = await AccountModel.load(res.locals.oauth.token.User.Account.id, t) 102 const reporterAccount = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
102 103
103 const abuseToCreate = { 104 const abuseToCreate = {
@@ -107,7 +108,7 @@ async function reportVideoAbuse (req: express.Request, res: express.Response) {
107 state: VideoAbuseState.PENDING 108 state: VideoAbuseState.PENDING
108 } 109 }
109 110
110 const videoAbuseInstance = await VideoAbuseModel.create(abuseToCreate, { transaction: t }) 111 const videoAbuseInstance: MVideoAbuseAccountVideo = await VideoAbuseModel.create(abuseToCreate, { transaction: t })
111 videoAbuseInstance.Video = videoInstance 112 videoAbuseInstance.Video = videoInstance
112 videoAbuseInstance.Account = reporterAccount 113 videoAbuseInstance.Account = reporterAccount
113 114
diff --git a/server/controllers/api/videos/blacklist.ts b/server/controllers/api/videos/blacklist.ts
index 9ff494def..2a667480d 100644
--- a/server/controllers/api/videos/blacklist.ts
+++ b/server/controllers/api/videos/blacklist.ts
@@ -1,5 +1,5 @@
1import * as express from 'express' 1import * as express from 'express'
2import { VideoBlacklist, UserRight, VideoBlacklistCreate, VideoBlacklistType } from '../../../../shared' 2import { UserRight, VideoBlacklistCreate, VideoBlacklistType } from '../../../../shared'
3import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
4import { getFormattedObjects } from '../../../helpers/utils' 4import { getFormattedObjects } from '../../../helpers/utils'
5import { 5import {
@@ -11,15 +11,16 @@ import {
11 setBlacklistSort, 11 setBlacklistSort,
12 setDefaultPagination, 12 setDefaultPagination,
13 videosBlacklistAddValidator, 13 videosBlacklistAddValidator,
14 videosBlacklistFiltersValidator,
14 videosBlacklistRemoveValidator, 15 videosBlacklistRemoveValidator,
15 videosBlacklistUpdateValidator, 16 videosBlacklistUpdateValidator
16 videosBlacklistFiltersValidator
17} from '../../../middlewares' 17} from '../../../middlewares'
18import { VideoBlacklistModel } from '../../../models/video/video-blacklist' 18import { VideoBlacklistModel } from '../../../models/video/video-blacklist'
19import { sequelizeTypescript } from '../../../initializers' 19import { sequelizeTypescript } from '../../../initializers'
20import { Notifier } from '../../../lib/notifier' 20import { Notifier } from '../../../lib/notifier'
21import { sendDeleteVideo } from '../../../lib/activitypub/send' 21import { sendDeleteVideo } from '../../../lib/activitypub/send'
22import { federateVideoIfNeeded } from '../../../lib/activitypub' 22import { federateVideoIfNeeded } from '../../../lib/activitypub'
23import { MVideoBlacklistVideo } from '@server/typings/models'
23 24
24const blacklistRouter = express.Router() 25const blacklistRouter = express.Router()
25 26
@@ -64,7 +65,7 @@ export {
64// --------------------------------------------------------------------------- 65// ---------------------------------------------------------------------------
65 66
66async function addVideoToBlacklist (req: express.Request, res: express.Response) { 67async function addVideoToBlacklist (req: express.Request, res: express.Response) {
67 const videoInstance = res.locals.video 68 const videoInstance = res.locals.videoAll
68 const body: VideoBlacklistCreate = req.body 69 const body: VideoBlacklistCreate = req.body
69 70
70 const toCreate = { 71 const toCreate = {
@@ -74,7 +75,7 @@ async function addVideoToBlacklist (req: express.Request, res: express.Response)
74 type: VideoBlacklistType.MANUAL 75 type: VideoBlacklistType.MANUAL
75 } 76 }
76 77
77 const blacklist = await VideoBlacklistModel.create(toCreate) 78 const blacklist: MVideoBlacklistVideo = await VideoBlacklistModel.create(toCreate)
78 blacklist.Video = videoInstance 79 blacklist.Video = videoInstance
79 80
80 if (body.unfederate === true) { 81 if (body.unfederate === true) {
@@ -83,7 +84,7 @@ async function addVideoToBlacklist (req: express.Request, res: express.Response)
83 84
84 Notifier.Instance.notifyOnVideoBlacklist(blacklist) 85 Notifier.Instance.notifyOnVideoBlacklist(blacklist)
85 86
86 logger.info('Video %s blacklisted.', res.locals.video.uuid) 87 logger.info('Video %s blacklisted.', videoInstance.uuid)
87 88
88 return res.type('json').status(204).end() 89 return res.type('json').status(204).end()
89} 90}
@@ -108,7 +109,7 @@ async function listBlacklist (req: express.Request, res: express.Response) {
108 109
109async function removeVideoFromBlacklistController (req: express.Request, res: express.Response) { 110async function removeVideoFromBlacklistController (req: express.Request, res: express.Response) {
110 const videoBlacklist = res.locals.videoBlacklist 111 const videoBlacklist = res.locals.videoBlacklist
111 const video = res.locals.video 112 const video = res.locals.videoAll
112 113
113 const videoBlacklistType = await sequelizeTypescript.transaction(async t => { 114 const videoBlacklistType = await sequelizeTypescript.transaction(async t => {
114 const unfederated = videoBlacklist.unfederated 115 const unfederated = videoBlacklist.unfederated
@@ -135,7 +136,7 @@ async function removeVideoFromBlacklistController (req: express.Request, res: ex
135 Notifier.Instance.notifyOnNewVideoIfNeeded(video) 136 Notifier.Instance.notifyOnNewVideoIfNeeded(video)
136 } 137 }
137 138
138 logger.info('Video %s removed from blacklist.', res.locals.video.uuid) 139 logger.info('Video %s removed from blacklist.', video.uuid)
139 140
140 return res.type('json').status(204).end() 141 return res.type('json').status(204).end()
141} 142}
diff --git a/server/controllers/api/videos/captions.ts b/server/controllers/api/videos/captions.ts
index 44c255232..37481d12f 100644
--- a/server/controllers/api/videos/captions.ts
+++ b/server/controllers/api/videos/captions.ts
@@ -10,6 +10,7 @@ import { federateVideoIfNeeded } from '../../../lib/activitypub'
10import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' 10import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils'
11import { CONFIG } from '../../../initializers/config' 11import { CONFIG } from '../../../initializers/config'
12import { sequelizeTypescript } from '../../../initializers/database' 12import { sequelizeTypescript } from '../../../initializers/database'
13import { MVideoCaptionVideo } from '@server/typings/models'
13 14
14const reqVideoCaptionAdd = createReqFiles( 15const reqVideoCaptionAdd = createReqFiles(
15 [ 'captionfile' ], 16 [ 'captionfile' ],
@@ -46,19 +47,19 @@ export {
46// --------------------------------------------------------------------------- 47// ---------------------------------------------------------------------------
47 48
48async function listVideoCaptions (req: express.Request, res: express.Response) { 49async function listVideoCaptions (req: express.Request, res: express.Response) {
49 const data = await VideoCaptionModel.listVideoCaptions(res.locals.video.id) 50 const data = await VideoCaptionModel.listVideoCaptions(res.locals.videoId.id)
50 51
51 return res.json(getFormattedObjects(data, data.length)) 52 return res.json(getFormattedObjects(data, data.length))
52} 53}
53 54
54async function addVideoCaption (req: express.Request, res: express.Response) { 55async function addVideoCaption (req: express.Request, res: express.Response) {
55 const videoCaptionPhysicalFile = req.files['captionfile'][0] 56 const videoCaptionPhysicalFile = req.files['captionfile'][0]
56 const video = res.locals.video 57 const video = res.locals.videoAll
57 58
58 const videoCaption = new VideoCaptionModel({ 59 const videoCaption = new VideoCaptionModel({
59 videoId: video.id, 60 videoId: video.id,
60 language: req.params.captionLanguage 61 language: req.params.captionLanguage
61 }) 62 }) as MVideoCaptionVideo
62 videoCaption.Video = video 63 videoCaption.Video = video
63 64
64 // Move physical file 65 // Move physical file
@@ -75,7 +76,7 @@ async function addVideoCaption (req: express.Request, res: express.Response) {
75} 76}
76 77
77async function deleteVideoCaption (req: express.Request, res: express.Response) { 78async function deleteVideoCaption (req: express.Request, res: express.Response) {
78 const video = res.locals.video 79 const video = res.locals.videoAll
79 const videoCaption = res.locals.videoCaption 80 const videoCaption = res.locals.videoCaption
80 81
81 await sequelizeTypescript.transaction(async t => { 82 await sequelizeTypescript.transaction(async t => {
diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts
index bc6d81a7c..b2b06b170 100644
--- a/server/controllers/api/videos/comment.ts
+++ b/server/controllers/api/videos/comment.ts
@@ -27,9 +27,6 @@ import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../.
27import { AccountModel } from '../../../models/account/account' 27import { AccountModel } from '../../../models/account/account'
28import { Notifier } from '../../../lib/notifier' 28import { Notifier } from '../../../lib/notifier'
29import { Hooks } from '../../../lib/plugins/hooks' 29import { Hooks } from '../../../lib/plugins/hooks'
30import { ActorModel } from '../../../models/activitypub/actor'
31import { VideoChannelModel } from '../../../models/video/video-channel'
32import { VideoModel } from '../../../models/video/video'
33import { sendDeleteVideoComment } from '../../../lib/activitypub/send' 30import { sendDeleteVideoComment } from '../../../lib/activitypub/send'
34 31
35const auditLogger = auditLoggerFactory('comments') 32const auditLogger = auditLoggerFactory('comments')
@@ -75,7 +72,7 @@ export {
75// --------------------------------------------------------------------------- 72// ---------------------------------------------------------------------------
76 73
77async function listVideoThreads (req: express.Request, res: express.Response) { 74async function listVideoThreads (req: express.Request, res: express.Response) {
78 const video = res.locals.video 75 const video = res.locals.onlyVideo
79 const user = res.locals.oauth ? res.locals.oauth.token.User : undefined 76 const user = res.locals.oauth ? res.locals.oauth.token.User : undefined
80 77
81 let resultList: ResultList<VideoCommentModel> 78 let resultList: ResultList<VideoCommentModel>
@@ -86,7 +83,7 @@ async function listVideoThreads (req: express.Request, res: express.Response) {
86 start: req.query.start, 83 start: req.query.start,
87 count: req.query.count, 84 count: req.query.count,
88 sort: req.query.sort, 85 sort: req.query.sort,
89 user: user 86 user
90 }, 'filter:api.video-threads.list.params') 87 }, 'filter:api.video-threads.list.params')
91 88
92 resultList = await Hooks.wrapPromiseFun( 89 resultList = await Hooks.wrapPromiseFun(
@@ -105,7 +102,7 @@ async function listVideoThreads (req: express.Request, res: express.Response) {
105} 102}
106 103
107async function listVideoThreadComments (req: express.Request, res: express.Response) { 104async function listVideoThreadComments (req: express.Request, res: express.Response) {
108 const video = res.locals.video 105 const video = res.locals.onlyVideo
109 const user = res.locals.oauth ? res.locals.oauth.token.User : undefined 106 const user = res.locals.oauth ? res.locals.oauth.token.User : undefined
110 107
111 let resultList: ResultList<VideoCommentModel> 108 let resultList: ResultList<VideoCommentModel>
@@ -141,7 +138,7 @@ async function addVideoCommentThread (req: express.Request, res: express.Respons
141 return createVideoComment({ 138 return createVideoComment({
142 text: videoCommentInfo.text, 139 text: videoCommentInfo.text,
143 inReplyToComment: null, 140 inReplyToComment: null,
144 video: res.locals.video, 141 video: res.locals.videoAll,
145 account 142 account
146 }, t) 143 }, t)
147 }) 144 })
@@ -164,8 +161,8 @@ async function addVideoCommentReply (req: express.Request, res: express.Response
164 161
165 return createVideoComment({ 162 return createVideoComment({
166 text: videoCommentInfo.text, 163 text: videoCommentInfo.text,
167 inReplyToComment: res.locals.videoComment, 164 inReplyToComment: res.locals.videoCommentFull,
168 video: res.locals.video, 165 video: res.locals.videoAll,
169 account 166 account
170 }, t) 167 }, t)
171 }) 168 })
@@ -179,7 +176,7 @@ async function addVideoCommentReply (req: express.Request, res: express.Response
179} 176}
180 177
181async function removeVideoComment (req: express.Request, res: express.Response) { 178async function removeVideoComment (req: express.Request, res: express.Response) {
182 const videoCommentInstance = res.locals.videoComment 179 const videoCommentInstance = res.locals.videoCommentFull
183 180
184 await sequelizeTypescript.transaction(async t => { 181 await sequelizeTypescript.transaction(async t => {
185 await videoCommentInstance.destroy({ transaction: t }) 182 await videoCommentInstance.destroy({ transaction: t })
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts
index 04c9b547b..adc2f9aa2 100644
--- a/server/controllers/api/videos/import.ts
+++ b/server/controllers/api/videos/import.ts
@@ -15,7 +15,6 @@ import { VideoImportModel } from '../../../models/video/video-import'
15import { JobQueue } from '../../../lib/job-queue/job-queue' 15import { JobQueue } from '../../../lib/job-queue/job-queue'
16import { join } from 'path' 16import { join } from 'path'
17import { isArray } from '../../../helpers/custom-validators/misc' 17import { isArray } from '../../../helpers/custom-validators/misc'
18import { VideoChannelModel } from '../../../models/video/video-channel'
19import * as Bluebird from 'bluebird' 18import * as Bluebird from 'bluebird'
20import * as parseTorrent from 'parse-torrent' 19import * as parseTorrent from 'parse-torrent'
21import { getSecureTorrentName } from '../../../helpers/utils' 20import { getSecureTorrentName } from '../../../helpers/utils'
@@ -25,8 +24,14 @@ import { CONFIG } from '../../../initializers/config'
25import { sequelizeTypescript } from '../../../initializers/database' 24import { sequelizeTypescript } from '../../../initializers/database'
26import { createVideoMiniatureFromExisting } from '../../../lib/thumbnail' 25import { createVideoMiniatureFromExisting } from '../../../lib/thumbnail'
27import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' 26import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
28import { ThumbnailModel } from '../../../models/video/thumbnail' 27import {
29import { UserModel } from '../../../models/account/user' 28 MChannelActorAccountDefault,
29 MThumbnail,
30 MUser,
31 MVideoThumbnailAccountDefault,
32 MVideoWithBlacklistLight
33} from '@server/typings/models'
34import { MVideoImport, MVideoImportVideo } from '@server/typings/models/video/video-import'
30 35
31const auditLogger = auditLoggerFactory('video-imports') 36const auditLogger = auditLoggerFactory('video-imports')
32const videoImportsRouter = express.Router() 37const videoImportsRouter = express.Router()
@@ -225,28 +230,28 @@ async function processPreview (req: express.Request, video: VideoModel) {
225} 230}
226 231
227function insertIntoDB (parameters: { 232function insertIntoDB (parameters: {
228 video: VideoModel, 233 video: MVideoThumbnailAccountDefault,
229 thumbnailModel: ThumbnailModel, 234 thumbnailModel: MThumbnail,
230 previewModel: ThumbnailModel, 235 previewModel: MThumbnail,
231 videoChannel: VideoChannelModel, 236 videoChannel: MChannelActorAccountDefault,
232 tags: string[], 237 tags: string[],
233 videoImportAttributes: Partial<VideoImportModel>, 238 videoImportAttributes: Partial<MVideoImport>,
234 user: UserModel 239 user: MUser
235}): Bluebird<VideoImportModel> { 240}): Bluebird<MVideoImportVideo> {
236 const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters 241 const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters
237 242
238 return sequelizeTypescript.transaction(async t => { 243 return sequelizeTypescript.transaction(async t => {
239 const sequelizeOptions = { transaction: t } 244 const sequelizeOptions = { transaction: t }
240 245
241 // Save video object in database 246 // Save video object in database
242 const videoCreated = await video.save(sequelizeOptions) 247 const videoCreated = await video.save(sequelizeOptions) as (MVideoThumbnailAccountDefault & MVideoWithBlacklistLight)
243 videoCreated.VideoChannel = videoChannel 248 videoCreated.VideoChannel = videoChannel
244 249
245 if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) 250 if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t)
246 if (previewModel) await videoCreated.addAndSaveThumbnail(previewModel, t) 251 if (previewModel) await videoCreated.addAndSaveThumbnail(previewModel, t)
247 252
248 await autoBlacklistVideoIfNeeded({ 253 await autoBlacklistVideoIfNeeded({
249 video, 254 video: videoCreated,
250 user, 255 user,
251 notify: false, 256 notify: false,
252 isRemote: false, 257 isRemote: false,
@@ -259,16 +264,13 @@ function insertIntoDB (parameters: {
259 const tagInstances = await TagModel.findOrCreateTags(tags, t) 264 const tagInstances = await TagModel.findOrCreateTags(tags, t)
260 265
261 await videoCreated.$set('Tags', tagInstances, sequelizeOptions) 266 await videoCreated.$set('Tags', tagInstances, sequelizeOptions)
262 videoCreated.Tags = tagInstances
263 } else {
264 videoCreated.Tags = []
265 } 267 }
266 268
267 // Create video import object in database 269 // Create video import object in database
268 const videoImport = await VideoImportModel.create( 270 const videoImport = await VideoImportModel.create(
269 Object.assign({ videoId: videoCreated.id }, videoImportAttributes), 271 Object.assign({ videoId: videoCreated.id }, videoImportAttributes),
270 sequelizeOptions 272 sequelizeOptions
271 ) 273 ) as MVideoImportVideo
272 videoImport.Video = videoCreated 274 videoImport.Video = videoCreated
273 275
274 return videoImport 276 return videoImport
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 155ca4678..9af71d276 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -63,6 +63,7 @@ import { createVideoMiniatureFromExisting, generateVideoMiniature } from '../../
63import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' 63import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
64import { VideoTranscodingPayload } from '../../../lib/job-queue/handlers/video-transcoding' 64import { VideoTranscodingPayload } from '../../../lib/job-queue/handlers/video-transcoding'
65import { Hooks } from '../../../lib/plugins/hooks' 65import { Hooks } from '../../../lib/plugins/hooks'
66import { MVideoFullLight } from '@server/typings/models'
66 67
67const auditLogger = auditLoggerFactory('videos') 68const auditLogger = auditLoggerFactory('videos')
68const videosRouter = express.Router() 69const videosRouter = express.Router()
@@ -238,7 +239,7 @@ async function addVideo (req: express.Request, res: express.Response) {
238 const { videoCreated } = await sequelizeTypescript.transaction(async t => { 239 const { videoCreated } = await sequelizeTypescript.transaction(async t => {
239 const sequelizeOptions = { transaction: t } 240 const sequelizeOptions = { transaction: t }
240 241
241 const videoCreated = await video.save(sequelizeOptions) 242 const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight
242 243
243 await videoCreated.addAndSaveThumbnail(thumbnailModel, t) 244 await videoCreated.addAndSaveThumbnail(thumbnailModel, t)
244 await videoCreated.addAndSaveThumbnail(previewModel, t) 245 await videoCreated.addAndSaveThumbnail(previewModel, t)
@@ -318,7 +319,7 @@ async function addVideo (req: express.Request, res: express.Response) {
318} 319}
319 320
320async function updateVideo (req: express.Request, res: express.Response) { 321async function updateVideo (req: express.Request, res: express.Response) {
321 const videoInstance = res.locals.video 322 const videoInstance = res.locals.videoAll
322 const videoFieldsSave = videoInstance.toJSON() 323 const videoFieldsSave = videoInstance.toJSON()
323 const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON()) 324 const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON())
324 const videoInfoToUpdate: VideoUpdate = req.body 325 const videoInfoToUpdate: VideoUpdate = req.body
@@ -371,7 +372,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
371 } 372 }
372 } 373 }
373 374
374 const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) 375 const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) as MVideoFullLight
375 376
376 if (thumbnailModel) await videoInstanceUpdated.addAndSaveThumbnail(thumbnailModel, t) 377 if (thumbnailModel) await videoInstanceUpdated.addAndSaveThumbnail(thumbnailModel, t)
377 if (previewModel) await videoInstanceUpdated.addAndSaveThumbnail(previewModel, t) 378 if (previewModel) await videoInstanceUpdated.addAndSaveThumbnail(previewModel, t)
@@ -447,7 +448,7 @@ async function getVideo (req: express.Request, res: express.Response) {
447 448
448 const video = await Hooks.wrapPromiseFun( 449 const video = await Hooks.wrapPromiseFun(
449 VideoModel.loadForGetAPI, 450 VideoModel.loadForGetAPI,
450 { id: res.locals.video.id, userId }, 451 { id: res.locals.onlyVideoWithRights.id, userId },
451 'filter:api.video.get.result' 452 'filter:api.video.get.result'
452 ) 453 )
453 454
@@ -460,7 +461,7 @@ async function getVideo (req: express.Request, res: express.Response) {
460} 461}
461 462
462async function viewVideo (req: express.Request, res: express.Response) { 463async function viewVideo (req: express.Request, res: express.Response) {
463 const videoInstance = res.locals.video 464 const videoInstance = res.locals.videoAll
464 465
465 const ip = req.ip 466 const ip = req.ip
466 const exists = await Redis.Instance.doesVideoIPViewExist(ip, videoInstance.uuid) 467 const exists = await Redis.Instance.doesVideoIPViewExist(ip, videoInstance.uuid)
@@ -483,7 +484,7 @@ async function viewVideo (req: express.Request, res: express.Response) {
483} 484}
484 485
485async function getVideoDescription (req: express.Request, res: express.Response) { 486async function getVideoDescription (req: express.Request, res: express.Response) {
486 const videoInstance = res.locals.video 487 const videoInstance = res.locals.videoAll
487 let description = '' 488 let description = ''
488 489
489 if (videoInstance.isOwned()) { 490 if (videoInstance.isOwned()) {
@@ -522,7 +523,7 @@ async function listVideos (req: express.Request, res: express.Response) {
522} 523}
523 524
524async function removeVideo (req: express.Request, res: express.Response) { 525async function removeVideo (req: express.Request, res: express.Response) {
525 const videoInstance = res.locals.video 526 const videoInstance = res.locals.videoAll
526 527
527 await sequelizeTypescript.transaction(async t => { 528 await sequelizeTypescript.transaction(async t => {
528 await videoInstance.destroy({ transaction: t }) 529 await videoInstance.destroy({ transaction: t })
diff --git a/server/controllers/api/videos/ownership.ts b/server/controllers/api/videos/ownership.ts
index 5272c1385..abb34082e 100644
--- a/server/controllers/api/videos/ownership.ts
+++ b/server/controllers/api/videos/ownership.ts
@@ -18,6 +18,7 @@ import { getFormattedObjects } from '../../../helpers/utils'
18import { changeVideoChannelShare } from '../../../lib/activitypub' 18import { changeVideoChannelShare } from '../../../lib/activitypub'
19import { sendUpdateVideo } from '../../../lib/activitypub/send' 19import { sendUpdateVideo } from '../../../lib/activitypub/send'
20import { VideoModel } from '../../../models/video/video' 20import { VideoModel } from '../../../models/video/video'
21import { MVideoFullLight } from '@server/typings/models'
21 22
22const ownershipVideoRouter = express.Router() 23const ownershipVideoRouter = express.Router()
23 24
@@ -56,7 +57,7 @@ export {
56// --------------------------------------------------------------------------- 57// ---------------------------------------------------------------------------
57 58
58async function giveVideoOwnership (req: express.Request, res: express.Response) { 59async function giveVideoOwnership (req: express.Request, res: express.Response) {
59 const videoInstance = res.locals.video 60 const videoInstance = res.locals.videoAll
60 const initiatorAccountId = res.locals.oauth.token.User.Account.id 61 const initiatorAccountId = res.locals.oauth.token.User.Account.id
61 const nextOwner = res.locals.nextOwner 62 const nextOwner = res.locals.nextOwner
62 63
@@ -107,7 +108,7 @@ async function acceptOwnership (req: express.Request, res: express.Response) {
107 108
108 targetVideo.channelId = channel.id 109 targetVideo.channelId = channel.id
109 110
110 const targetVideoUpdated = await targetVideo.save({ transaction: t }) 111 const targetVideoUpdated = await targetVideo.save({ transaction: t }) as MVideoFullLight
111 targetVideoUpdated.VideoChannel = channel 112 targetVideoUpdated.VideoChannel = channel
112 113
113 if (targetVideoUpdated.privacy !== VideoPrivacy.PRIVATE && targetVideoUpdated.state === VideoState.PUBLISHED) { 114 if (targetVideoUpdated.privacy !== VideoPrivacy.PRIVATE && targetVideoUpdated.state === VideoState.PUBLISHED) {
diff --git a/server/controllers/api/videos/rate.ts b/server/controllers/api/videos/rate.ts
index b65babedf..3d2f3d728 100644
--- a/server/controllers/api/videos/rate.ts
+++ b/server/controllers/api/videos/rate.ts
@@ -27,7 +27,7 @@ export {
27async function rateVideo (req: express.Request, res: express.Response) { 27async function rateVideo (req: express.Request, res: express.Response) {
28 const body: UserVideoRateUpdate = req.body 28 const body: UserVideoRateUpdate = req.body
29 const rateType = body.rating 29 const rateType = body.rating
30 const videoInstance = res.locals.video 30 const videoInstance = res.locals.videoAll
31 const userAccount = res.locals.oauth.token.User.Account 31 const userAccount = res.locals.oauth.token.User.Account
32 32
33 await sequelizeTypescript.transaction(async t => { 33 await sequelizeTypescript.transaction(async t => {
diff --git a/server/controllers/api/videos/watching.ts b/server/controllers/api/videos/watching.ts
index dcd1f070d..036e16f3a 100644
--- a/server/controllers/api/videos/watching.ts
+++ b/server/controllers/api/videos/watching.ts
@@ -23,7 +23,7 @@ async function userWatchVideo (req: express.Request, res: express.Response) {
23 const user = res.locals.oauth.token.User 23 const user = res.locals.oauth.token.User
24 24
25 const body: UserWatchingVideo = req.body 25 const body: UserWatchingVideo = req.body
26 const { id: videoId } = res.locals.video as { id: number } 26 const { id: videoId } = res.locals.videoId
27 27
28 await UserVideoHistoryModel.upsert({ 28 await UserVideoHistoryModel.upsert({
29 videoId, 29 videoId,
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts
index d3f581615..468f7a668 100644
--- a/server/controllers/feeds.ts
+++ b/server/controllers/feeds.ts
@@ -43,7 +43,7 @@ export {
43async function generateVideoCommentsFeed (req: express.Request, res: express.Response) { 43async function generateVideoCommentsFeed (req: express.Request, res: express.Response) {
44 const start = 0 44 const start = 0
45 45
46 const video = res.locals.video 46 const video = res.locals.videoAll
47 const videoId: number = video ? video.id : undefined 47 const videoId: number = video ? video.id : undefined
48 48
49 const comments = await VideoCommentModel.listForFeed(start, FEEDS.COUNT, videoId) 49 const comments = await VideoCommentModel.listForFeed(start, FEEDS.COUNT, videoId)
diff --git a/server/controllers/services.ts b/server/controllers/services.ts
index c1c53c3fc..ec057235f 100644
--- a/server/controllers/services.ts
+++ b/server/controllers/services.ts
@@ -23,7 +23,7 @@ export {
23// --------------------------------------------------------------------------- 23// ---------------------------------------------------------------------------
24 24
25function generateOEmbed (req: express.Request, res: express.Response) { 25function generateOEmbed (req: express.Request, res: express.Response) {
26 const video = res.locals.video 26 const video = res.locals.videoAll
27 const webserverUrl = WEBSERVER.URL 27 const webserverUrl = WEBSERVER.URL
28 const maxHeight = parseInt(req.query.maxheight, 10) 28 const maxHeight = parseInt(req.query.maxheight, 10)
29 const maxWidth = parseInt(req.query.maxwidth, 10) 29 const maxWidth = parseInt(req.query.maxwidth, 10)
diff --git a/server/controllers/static.ts b/server/controllers/static.ts
index 8979ef5f3..0f4772310 100644
--- a/server/controllers/static.ts
+++ b/server/controllers/static.ts
@@ -226,14 +226,14 @@ async function generateNodeinfo (req: express.Request, res: express.Response) {
226 return res.send(json).end() 226 return res.send(json).end()
227} 227}
228 228
229async function downloadTorrent (req: express.Request, res: express.Response, next: express.NextFunction) { 229async function downloadTorrent (req: express.Request, res: express.Response) {
230 const { video, videoFile } = getVideoAndFile(req, res) 230 const { video, videoFile } = getVideoAndFile(req, res)
231 if (!videoFile) return res.status(404).end() 231 if (!videoFile) return res.status(404).end()
232 232
233 return res.download(video.getTorrentFilePath(videoFile), `${video.name}-${videoFile.resolution}p.torrent`) 233 return res.download(video.getTorrentFilePath(videoFile), `${video.name}-${videoFile.resolution}p.torrent`)
234} 234}
235 235
236async function downloadVideoFile (req: express.Request, res: express.Response, next: express.NextFunction) { 236async function downloadVideoFile (req: express.Request, res: express.Response) {
237 const { video, videoFile } = getVideoAndFile(req, res) 237 const { video, videoFile } = getVideoAndFile(req, res)
238 if (!videoFile) return res.status(404).end() 238 if (!videoFile) return res.status(404).end()
239 239
@@ -242,7 +242,7 @@ async function downloadVideoFile (req: express.Request, res: express.Response, n
242 242
243function getVideoAndFile (req: express.Request, res: express.Response) { 243function getVideoAndFile (req: express.Request, res: express.Response) {
244 const resolution = parseInt(req.params.resolution, 10) 244 const resolution = parseInt(req.params.resolution, 10)
245 const video = res.locals.video 245 const video = res.locals.videoAll
246 246
247 const videoFile = video.VideoFiles.find(f => f.resolution === resolution) 247 const videoFile = video.VideoFiles.find(f => f.resolution === resolution)
248 248
diff --git a/server/controllers/webfinger.ts b/server/controllers/webfinger.ts
index f2ba3c826..fc9575160 100644
--- a/server/controllers/webfinger.ts
+++ b/server/controllers/webfinger.ts
@@ -18,7 +18,7 @@ export {
18// --------------------------------------------------------------------------- 18// ---------------------------------------------------------------------------
19 19
20function webfingerController (req: express.Request, res: express.Response) { 20function webfingerController (req: express.Request, res: express.Response) {
21 const actor = res.locals.actor 21 const actor = res.locals.actorFull
22 22
23 const json = { 23 const json = {
24 subject: req.query.resource, 24 subject: req.query.resource,
diff --git a/server/helpers/activitypub.ts b/server/helpers/activitypub.ts
index 951a25669..97c809a0c 100644
--- a/server/helpers/activitypub.ts
+++ b/server/helpers/activitypub.ts
@@ -7,6 +7,7 @@ import { ActorModel } from '../models/activitypub/actor'
7import { signJsonLDObject } from './peertube-crypto' 7import { signJsonLDObject } from './peertube-crypto'
8import { pageToStartAndCount } from './core-utils' 8import { pageToStartAndCount } from './core-utils'
9import { parse } from 'url' 9import { parse } from 'url'
10import { MActor } from '../typings/models'
10 11
11function activityPubContextify <T> (data: T) { 12function activityPubContextify <T> (data: T) {
12 return Object.assign(data, { 13 return Object.assign(data, {
@@ -143,7 +144,7 @@ async function activityPubCollectionPagination (baseUrl: string, handler: Activi
143 144
144} 145}
145 146
146function buildSignedActivity (byActor: ActorModel, data: Object) { 147function buildSignedActivity (byActor: MActor, data: Object) {
147 const activity = activityPubContextify(data) 148 const activity = activityPubContextify(data)
148 149
149 return signJsonLDObject(byActor, activity) as Promise<Activity> 150 return signJsonLDObject(byActor, activity) as Promise<Activity>
diff --git a/server/helpers/actor.ts b/server/helpers/actor.ts
index 12a7ace9f..117548a60 100644
--- a/server/helpers/actor.ts
+++ b/server/helpers/actor.ts
@@ -1,10 +1,13 @@
1import { ActorModel } from '../models/activitypub/actor' 1import { ActorModel } from '../models/activitypub/actor'
2import * as Bluebird from 'bluebird'
3import { MActorFull, MActorAccountChannelId } from '../typings/models'
2 4
3type ActorFetchByUrlType = 'all' | 'actor-and-association-ids' 5type ActorFetchByUrlType = 'all' | 'association-ids'
4function fetchActorByUrl (url: string, fetchType: ActorFetchByUrlType) { 6
7function fetchActorByUrl (url: string, fetchType: ActorFetchByUrlType): Bluebird<MActorFull | MActorAccountChannelId> {
5 if (fetchType === 'all') return ActorModel.loadByUrlAndPopulateAccountAndChannel(url) 8 if (fetchType === 'all') return ActorModel.loadByUrlAndPopulateAccountAndChannel(url)
6 9
7 if (fetchType === 'actor-and-association-ids') return ActorModel.loadByUrl(url) 10 if (fetchType === 'association-ids') return ActorModel.loadByUrl(url)
8} 11}
9 12
10export { 13export {
diff --git a/server/helpers/captions-utils.ts b/server/helpers/captions-utils.ts
index 7174d4654..4f29058e5 100644
--- a/server/helpers/captions-utils.ts
+++ b/server/helpers/captions-utils.ts
@@ -1,10 +1,10 @@
1import { join } from 'path' 1import { join } from 'path'
2import { CONFIG } from '../initializers/config' 2import { CONFIG } from '../initializers/config'
3import { VideoCaptionModel } from '../models/video/video-caption'
4import * as srt2vtt from 'srt-to-vtt' 3import * as srt2vtt from 'srt-to-vtt'
5import { createReadStream, createWriteStream, remove, move } from 'fs-extra' 4import { createReadStream, createWriteStream, move, remove } from 'fs-extra'
5import { MVideoCaption } from '@server/typings/models'
6 6
7async function moveAndProcessCaptionFile (physicalFile: { filename: string, path: string }, videoCaption: VideoCaptionModel) { 7async function moveAndProcessCaptionFile (physicalFile: { filename: string, path: string }, videoCaption: MVideoCaption) {
8 const videoCaptionsDir = CONFIG.STORAGE.CAPTIONS_DIR 8 const videoCaptionsDir = CONFIG.STORAGE.CAPTIONS_DIR
9 const destination = join(videoCaptionsDir, videoCaption.getCaptionName()) 9 const destination = join(videoCaptionsDir, videoCaption.getCaptionName())
10 10
diff --git a/server/helpers/custom-validators/video-ownership.ts b/server/helpers/custom-validators/video-ownership.ts
index a7771e07b..9570b2799 100644
--- a/server/helpers/custom-validators/video-ownership.ts
+++ b/server/helpers/custom-validators/video-ownership.ts
@@ -1,10 +1,10 @@
1import { Response } from 'express' 1import { Response } from 'express'
2import * as validator from 'validator'
3import { VideoChangeOwnershipModel } from '../../models/video/video-change-ownership' 2import { VideoChangeOwnershipModel } from '../../models/video/video-change-ownership'
4import { UserModel } from '../../models/account/user' 3import { MVideoChangeOwnershipFull } from '@server/typings/models/video/video-change-ownership'
4import { MUserId } from '@server/typings/models'
5 5
6export async function doesChangeVideoOwnershipExist (id: string, res: Response): Promise<boolean> { 6export async function doesChangeVideoOwnershipExist (id: number, res: Response) {
7 const videoChangeOwnership = await loadVideoChangeOwnership(id) 7 const videoChangeOwnership = await VideoChangeOwnershipModel.load(id)
8 8
9 if (!videoChangeOwnership) { 9 if (!videoChangeOwnership) {
10 res.status(404) 10 res.status(404)
@@ -18,19 +18,7 @@ export async function doesChangeVideoOwnershipExist (id: string, res: Response):
18 return true 18 return true
19} 19}
20 20
21async function loadVideoChangeOwnership (id: string): Promise<VideoChangeOwnershipModel | undefined> { 21export function checkUserCanTerminateOwnershipChange (user: MUserId, videoChangeOwnership: MVideoChangeOwnershipFull, res: Response) {
22 if (validator.isInt(id)) {
23 return VideoChangeOwnershipModel.load(parseInt(id, 10))
24 }
25
26 return undefined
27}
28
29export function checkUserCanTerminateOwnershipChange (
30 user: UserModel,
31 videoChangeOwnership: VideoChangeOwnershipModel,
32 res: Response
33): boolean {
34 if (videoChangeOwnership.NextOwner.userId === user.id) { 22 if (videoChangeOwnership.NextOwner.userId === user.id) {
35 return true 23 return true
36 } 24 }
diff --git a/server/helpers/middlewares/accounts.ts b/server/helpers/middlewares/accounts.ts
index 791022b97..f5aa0bada 100644
--- a/server/helpers/middlewares/accounts.ts
+++ b/server/helpers/middlewares/accounts.ts
@@ -1,6 +1,7 @@
1import { Response } from 'express' 1import { Response } from 'express'
2import { AccountModel } from '../../models/account/account' 2import { AccountModel } from '../../models/account/account'
3import * as Bluebird from 'bluebird' 3import * as Bluebird from 'bluebird'
4import { MAccountDefault } from '../../typings/models'
4 5
5function doesAccountIdExist (id: number, res: Response, sendNotFound = true) { 6function doesAccountIdExist (id: number, res: Response, sendNotFound = true) {
6 const promise = AccountModel.load(id) 7 const promise = AccountModel.load(id)
@@ -15,10 +16,12 @@ function doesLocalAccountNameExist (name: string, res: Response, sendNotFound =
15} 16}
16 17
17function doesAccountNameWithHostExist (nameWithDomain: string, res: Response, sendNotFound = true) { 18function doesAccountNameWithHostExist (nameWithDomain: string, res: Response, sendNotFound = true) {
18 return doesAccountExist(AccountModel.loadByNameWithHost(nameWithDomain), res, sendNotFound) 19 const promise = AccountModel.loadByNameWithHost(nameWithDomain)
20
21 return doesAccountExist(promise, res, sendNotFound)
19} 22}
20 23
21async function doesAccountExist (p: Bluebird<AccountModel>, res: Response, sendNotFound: boolean) { 24async function doesAccountExist (p: Bluebird<MAccountDefault>, res: Response, sendNotFound: boolean) {
22 const account = await p 25 const account = await p
23 26
24 if (!account) { 27 if (!account) {
diff --git a/server/helpers/middlewares/video-abuses.ts b/server/helpers/middlewares/video-abuses.ts
index b23f1f021..1b573ca37 100644
--- a/server/helpers/middlewares/video-abuses.ts
+++ b/server/helpers/middlewares/video-abuses.ts
@@ -1,41 +1,23 @@
1import * as express from 'express' 1import { Response } from 'express'
2import { VideoChannelModel } from '../../models/video/video-channel' 2import { VideoAbuseModel } from '../../models/video/video-abuse'
3 3
4async function doesLocalVideoChannelNameExist (name: string, res: express.Response) { 4async function doesVideoAbuseExist (abuseId: number, videoId: number, res: Response) {
5 const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name) 5 const videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, videoId)
6 6
7 return processVideoChannelExist(videoChannel, res) 7 if (videoAbuse === null) {
8}
9
10async function doesVideoChannelIdExist (id: number, res: express.Response) {
11 const videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id)
12
13 return processVideoChannelExist(videoChannel, res)
14}
15
16async function doesVideoChannelNameWithHostExist (nameWithDomain: string, res: express.Response) {
17 const videoChannel = await VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithDomain)
18
19 return processVideoChannelExist(videoChannel, res)
20}
21
22// ---------------------------------------------------------------------------
23
24export {
25 doesLocalVideoChannelNameExist,
26 doesVideoChannelIdExist,
27 doesVideoChannelNameWithHostExist
28}
29
30function processVideoChannelExist (videoChannel: VideoChannelModel, res: express.Response) {
31 if (!videoChannel) {
32 res.status(404) 8 res.status(404)
33 .json({ error: 'Video channel not found' }) 9 .json({ error: 'Video abuse not found' })
34 .end() 10 .end()
35 11
36 return false 12 return false
37 } 13 }
38 14
39 res.locals.videoChannel = videoChannel 15 res.locals.videoAbuse = videoAbuse
40 return true 16 return true
41} 17}
18
19// ---------------------------------------------------------------------------
20
21export {
22 doesVideoAbuseExist
23}
diff --git a/server/helpers/middlewares/video-captions.ts b/server/helpers/middlewares/video-captions.ts
index dc3d0144b..1b2513b60 100644
--- a/server/helpers/middlewares/video-captions.ts
+++ b/server/helpers/middlewares/video-captions.ts
@@ -1,8 +1,8 @@
1import { VideoModel } from '../../models/video/video'
2import { Response } from 'express' 1import { Response } from 'express'
3import { VideoCaptionModel } from '../../models/video/video-caption' 2import { VideoCaptionModel } from '../../models/video/video-caption'
3import { MVideoId } from '@server/typings/models'
4 4
5async function doesVideoCaptionExist (video: VideoModel, language: string, res: Response) { 5async function doesVideoCaptionExist (video: MVideoId, language: string, res: Response) {
6 const videoCaption = await VideoCaptionModel.loadByVideoIdAndLanguage(video.id, language) 6 const videoCaption = await VideoCaptionModel.loadByVideoIdAndLanguage(video.id, language)
7 7
8 if (!videoCaption) { 8 if (!videoCaption) {
diff --git a/server/helpers/middlewares/video-channels.ts b/server/helpers/middlewares/video-channels.ts
index 1b573ca37..17b7692c5 100644
--- a/server/helpers/middlewares/video-channels.ts
+++ b/server/helpers/middlewares/video-channels.ts
@@ -1,23 +1,42 @@
1import { Response } from 'express' 1import * as express from 'express'
2import { VideoAbuseModel } from '../../models/video/video-abuse' 2import { VideoChannelModel } from '../../models/video/video-channel'
3import { MChannelActorAccountDefault } from '../../typings/models'
3 4
4async function doesVideoAbuseExist (abuseId: number, videoId: number, res: Response) { 5async function doesLocalVideoChannelNameExist (name: string, res: express.Response) {
5 const videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, videoId) 6 const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name)
6 7
7 if (videoAbuse === null) { 8 return processVideoChannelExist(videoChannel, res)
8 res.status(404) 9}
9 .json({ error: 'Video abuse not found' })
10 .end()
11 10
12 return false 11async function doesVideoChannelIdExist (id: number, res: express.Response) {
13 } 12 const videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id)
14 13
15 res.locals.videoAbuse = videoAbuse 14 return processVideoChannelExist(videoChannel, res)
16 return true 15}
16
17async function doesVideoChannelNameWithHostExist (nameWithDomain: string, res: express.Response) {
18 const videoChannel = await VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithDomain)
19
20 return processVideoChannelExist(videoChannel, res)
17} 21}
18 22
19// --------------------------------------------------------------------------- 23// ---------------------------------------------------------------------------
20 24
21export { 25export {
22 doesVideoAbuseExist 26 doesLocalVideoChannelNameExist,
27 doesVideoChannelIdExist,
28 doesVideoChannelNameWithHostExist
29}
30
31function processVideoChannelExist (videoChannel: MChannelActorAccountDefault, res: express.Response) {
32 if (!videoChannel) {
33 res.status(404)
34 .json({ error: 'Video channel not found' })
35 .end()
36
37 return false
38 }
39
40 res.locals.videoChannel = videoChannel
41 return true
23} 42}
diff --git a/server/helpers/middlewares/video-playlists.ts b/server/helpers/middlewares/video-playlists.ts
index 735bf362f..8e7484483 100644
--- a/server/helpers/middlewares/video-playlists.ts
+++ b/server/helpers/middlewares/video-playlists.ts
@@ -1,11 +1,31 @@
1import * as express from 'express' 1import * as express from 'express'
2import { VideoPlaylistModel } from '../../models/video/video-playlist' 2import { VideoPlaylistModel } from '../../models/video/video-playlist'
3import { MVideoPlaylist } from '../../typings/models/video/video-playlist'
3 4
4async function doesVideoPlaylistExist (id: number | string, res: express.Response, fetchType: 'summary' | 'all' = 'summary') { 5export type VideoPlaylistFetchType = 'summary' | 'all'
5 const videoPlaylist = fetchType === 'summary' 6async function doesVideoPlaylistExist (id: number | string, res: express.Response, fetchType: VideoPlaylistFetchType = 'summary') {
6 ? await VideoPlaylistModel.loadWithAccountAndChannelSummary(id, undefined) 7 if (fetchType === 'summary') {
7 : await VideoPlaylistModel.loadWithAccountAndChannel(id, undefined) 8 const videoPlaylist = await VideoPlaylistModel.loadWithAccountAndChannelSummary(id, undefined)
9 res.locals.videoPlaylistSummary = videoPlaylist
8 10
11 return handleVideoPlaylist(videoPlaylist, res)
12 }
13
14 const videoPlaylist = await VideoPlaylistModel.loadWithAccountAndChannel(id, undefined)
15 res.locals.videoPlaylistFull = videoPlaylist
16
17 return handleVideoPlaylist(videoPlaylist, res)
18}
19
20// ---------------------------------------------------------------------------
21
22export {
23 doesVideoPlaylistExist
24}
25
26// ---------------------------------------------------------------------------
27
28function handleVideoPlaylist (videoPlaylist: MVideoPlaylist, res: express.Response) {
9 if (!videoPlaylist) { 29 if (!videoPlaylist) {
10 res.status(404) 30 res.status(404)
11 .json({ error: 'Video playlist not found' }) 31 .json({ error: 'Video playlist not found' })
@@ -14,12 +34,5 @@ async function doesVideoPlaylistExist (id: number | string, res: express.Respons
14 return false 34 return false
15 } 35 }
16 36
17 res.locals.videoPlaylist = videoPlaylist
18 return true 37 return true
19} 38}
20
21// ---------------------------------------------------------------------------
22
23export {
24 doesVideoPlaylistExist
25}
diff --git a/server/helpers/middlewares/videos.ts b/server/helpers/middlewares/videos.ts
index ceb1058ec..964f0c91a 100644
--- a/server/helpers/middlewares/videos.ts
+++ b/server/helpers/middlewares/videos.ts
@@ -1,9 +1,8 @@
1import { Response } from 'express' 1import { Response } from 'express'
2import { fetchVideo, VideoFetchType } from '../video' 2import { fetchVideo, VideoFetchType } from '../video'
3import { UserModel } from '../../models/account/user'
4import { UserRight } from '../../../shared/models/users' 3import { UserRight } from '../../../shared/models/users'
5import { VideoChannelModel } from '../../models/video/video-channel' 4import { VideoChannelModel } from '../../models/video/video-channel'
6import { VideoModel } from '../../models/video/video' 5import { MUser, MUserAccountId, MVideoAccountLight, MVideoFullLight, MVideoWithRights } from '@server/typings/models'
7 6
8async function doesVideoExist (id: number | string, res: Response, fetchType: VideoFetchType = 'all') { 7async function doesVideoExist (id: number | string, res: Response, fetchType: VideoFetchType = 'all') {
9 const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined 8 const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined
@@ -18,11 +17,28 @@ async function doesVideoExist (id: number | string, res: Response, fetchType: Vi
18 return false 17 return false
19 } 18 }
20 19
21 if (fetchType !== 'none') res.locals.video = video 20 switch (fetchType) {
21 case 'all':
22 res.locals.videoAll = video as MVideoFullLight
23 break
24
25 case 'id':
26 res.locals.videoId = video
27 break
28
29 case 'only-video':
30 res.locals.onlyVideo = video
31 break
32
33 case 'only-video-with-rights':
34 res.locals.onlyVideoWithRights = video as MVideoWithRights
35 break
36 }
37
22 return true 38 return true
23} 39}
24 40
25async function doesVideoChannelOfAccountExist (channelId: number, user: UserModel, res: Response) { 41async function doesVideoChannelOfAccountExist (channelId: number, user: MUserAccountId, res: Response) {
26 if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) { 42 if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) {
27 const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId) 43 const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId)
28 if (videoChannel === null) { 44 if (videoChannel === null) {
@@ -50,7 +66,7 @@ async function doesVideoChannelOfAccountExist (channelId: number, user: UserMode
50 return true 66 return true
51} 67}
52 68
53function checkUserCanManageVideo (user: UserModel, video: VideoModel, right: UserRight, res: Response) { 69function checkUserCanManageVideo (user: MUser, video: MVideoAccountLight, right: UserRight, res: Response) {
54 // Retrieve the user who did the request 70 // Retrieve the user who did the request
55 if (video.isOwned() === false) { 71 if (video.isOwned() === false) {
56 res.status(403) 72 res.status(403)
diff --git a/server/helpers/peertube-crypto.ts b/server/helpers/peertube-crypto.ts
index 1424949d0..085cd62c9 100644
--- a/server/helpers/peertube-crypto.ts
+++ b/server/helpers/peertube-crypto.ts
@@ -8,6 +8,7 @@ import { cloneDeep } from 'lodash'
8import { createVerify } from 'crypto' 8import { createVerify } from 'crypto'
9import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils' 9import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils'
10import * as bcrypt from 'bcrypt' 10import * as bcrypt from 'bcrypt'
11import { MActor } from '../typings/models'
11 12
12const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare) 13const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare)
13const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt) 14const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt)
@@ -46,7 +47,7 @@ function isHTTPSignatureDigestValid (rawBody: Buffer, req: Request): boolean {
46 return true 47 return true
47} 48}
48 49
49function isHTTPSignatureVerified (httpSignatureParsed: any, actor: ActorModel): boolean { 50function isHTTPSignatureVerified (httpSignatureParsed: any, actor: MActor): boolean {
50 return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true 51 return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true
51} 52}
52 53
@@ -56,7 +57,7 @@ function parseHTTPSignature (req: Request, clockSkew?: number) {
56 57
57// JSONLD 58// JSONLD
58 59
59async function isJsonLDSignatureVerified (fromActor: ActorModel, signedDocument: any): Promise<boolean> { 60async function isJsonLDSignatureVerified (fromActor: MActor, signedDocument: any): Promise<boolean> {
60 if (signedDocument.signature.type === 'RsaSignature2017') { 61 if (signedDocument.signature.type === 'RsaSignature2017') {
61 // Mastodon algorithm 62 // Mastodon algorithm
62 const res = await isJsonLDRSA2017Verified(fromActor, signedDocument) 63 const res = await isJsonLDRSA2017Verified(fromActor, signedDocument)
@@ -93,7 +94,7 @@ async function isJsonLDSignatureVerified (fromActor: ActorModel, signedDocument:
93} 94}
94 95
95// Backward compatibility with "other" implementations 96// Backward compatibility with "other" implementations
96async function isJsonLDRSA2017Verified (fromActor: ActorModel, signedDocument: any) { 97async function isJsonLDRSA2017Verified (fromActor: MActor, signedDocument: any) {
97 function hash (obj: any): Promise<any> { 98 function hash (obj: any): Promise<any> {
98 return jsonld.promises 99 return jsonld.promises
99 .normalize(obj, { 100 .normalize(obj, {
@@ -130,7 +131,7 @@ async function isJsonLDRSA2017Verified (fromActor: ActorModel, signedDocument: a
130 return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64') 131 return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64')
131} 132}
132 133
133function signJsonLDObject (byActor: ActorModel, data: any) { 134function signJsonLDObject (byActor: MActor, data: any) {
134 const options = { 135 const options = {
135 privateKeyPem: byActor.privateKey, 136 privateKeyPem: byActor.privateKey,
136 creator: byActor.url, 137 creator: byActor.url,
diff --git a/server/helpers/video.ts b/server/helpers/video.ts
index c90fe06c7..26a72ac5c 100644
--- a/server/helpers/video.ts
+++ b/server/helpers/video.ts
@@ -1,8 +1,24 @@
1import { VideoModel } from '../models/video/video' 1import { VideoModel } from '../models/video/video'
2import * as Bluebird from 'bluebird'
3import { MVideoAccountAllFiles, MVideoFullLight, MVideoThumbnail, MVideoWithRights, MVideoIdThumbnail } from '@server/typings/models'
4import { Response } from 'express'
2 5
3type VideoFetchType = 'all' | 'only-video' | 'only-video-with-rights' | 'id' | 'none' 6type VideoFetchType = 'all' | 'only-video' | 'only-video-with-rights' | 'id' | 'none'
4 7
5function fetchVideo (id: number | string, fetchType: VideoFetchType, userId?: number) { 8function fetchVideo (id: number | string, fetchType: 'all', userId?: number): Bluebird<MVideoFullLight>
9function fetchVideo (id: number | string, fetchType: 'only-video', userId?: number): Bluebird<MVideoThumbnail>
10function fetchVideo (id: number | string, fetchType: 'only-video-with-rights', userId?: number): Bluebird<MVideoWithRights>
11function fetchVideo (id: number | string, fetchType: 'id' | 'none', userId?: number): Bluebird<MVideoIdThumbnail>
12function fetchVideo (
13 id: number | string,
14 fetchType: VideoFetchType,
15 userId?: number
16): Bluebird<MVideoFullLight | MVideoThumbnail | MVideoWithRights | MVideoIdThumbnail>
17function fetchVideo (
18 id: number | string,
19 fetchType: VideoFetchType,
20 userId?: number
21): Bluebird<MVideoFullLight | MVideoThumbnail | MVideoWithRights | MVideoIdThumbnail> {
6 if (fetchType === 'all') return VideoModel.loadAndPopulateAccountAndServerAndTags(id, undefined, userId) 22 if (fetchType === 'all') return VideoModel.loadAndPopulateAccountAndServerAndTags(id, undefined, userId)
7 23
8 if (fetchType === 'only-video-with-rights') return VideoModel.loadWithRights(id) 24 if (fetchType === 'only-video-with-rights') return VideoModel.loadWithRights(id)
@@ -13,15 +29,24 @@ function fetchVideo (id: number | string, fetchType: VideoFetchType, userId?: nu
13} 29}
14 30
15type VideoFetchByUrlType = 'all' | 'only-video' 31type VideoFetchByUrlType = 'all' | 'only-video'
16function fetchVideoByUrl (url: string, fetchType: VideoFetchByUrlType) { 32
33function fetchVideoByUrl (url: string, fetchType: 'all'): Bluebird<MVideoAccountAllFiles>
34function fetchVideoByUrl (url: string, fetchType: 'only-video'): Bluebird<MVideoThumbnail>
35function fetchVideoByUrl (url: string, fetchType: VideoFetchByUrlType): Bluebird<MVideoAccountAllFiles> | Bluebird<MVideoThumbnail>
36function fetchVideoByUrl (url: string, fetchType: VideoFetchByUrlType): Bluebird<MVideoAccountAllFiles> | Bluebird<MVideoThumbnail> {
17 if (fetchType === 'all') return VideoModel.loadByUrlAndPopulateAccount(url) 37 if (fetchType === 'all') return VideoModel.loadByUrlAndPopulateAccount(url)
18 38
19 if (fetchType === 'only-video') return VideoModel.loadByUrl(url) 39 if (fetchType === 'only-video') return VideoModel.loadByUrl(url)
20} 40}
21 41
42function getVideo (res: Response) {
43 return res.locals.videoAll || res.locals.onlyVideo || res.locals.onlyVideoWithRights || res.locals.videoId
44}
45
22export { 46export {
23 VideoFetchType, 47 VideoFetchType,
24 VideoFetchByUrlType, 48 VideoFetchByUrlType,
25 fetchVideo, 49 fetchVideo,
50 getVideo,
26 fetchVideoByUrl 51 fetchVideoByUrl
27} 52}
diff --git a/server/helpers/webfinger.ts b/server/helpers/webfinger.ts
index d1229e28f..5443a266b 100644
--- a/server/helpers/webfinger.ts
+++ b/server/helpers/webfinger.ts
@@ -4,6 +4,7 @@ import { ActorModel } from '../models/activitypub/actor'
4import { isTestInstance } from './core-utils' 4import { isTestInstance } from './core-utils'
5import { isActivityPubUrlValid } from './custom-validators/activitypub/misc' 5import { isActivityPubUrlValid } from './custom-validators/activitypub/misc'
6import { WEBSERVER } from '../initializers/constants' 6import { WEBSERVER } from '../initializers/constants'
7import { MActorFull } from '../typings/models'
7 8
8const webfinger = new WebFinger({ 9const webfinger = new WebFinger({
9 webfist_fallback: false, 10 webfist_fallback: false,
@@ -17,7 +18,7 @@ async function loadActorUrlOrGetFromWebfinger (uriArg: string) {
17 const uri = uriArg.startsWith('@') ? uriArg.slice(1) : uriArg 18 const uri = uriArg.startsWith('@') ? uriArg.slice(1) : uriArg
18 19
19 const [ name, host ] = uri.split('@') 20 const [ name, host ] = uri.split('@')
20 let actor: ActorModel 21 let actor: MActorFull
21 22
22 if (!host || host === WEBSERVER.HOST) { 23 if (!host || host === WEBSERVER.HOST) {
23 actor = await ActorModel.loadLocalByName(name) 24 actor = await ActorModel.loadLocalByName(name)
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts
index 9f5d12eb4..7862b0f00 100644
--- a/server/lib/activitypub/actor.ts
+++ b/server/lib/activitypub/actor.ts
@@ -22,13 +22,25 @@ import { JobQueue } from '../job-queue'
22import { getServerActor } from '../../helpers/utils' 22import { getServerActor } from '../../helpers/utils'
23import { ActorFetchByUrlType, fetchActorByUrl } from '../../helpers/actor' 23import { ActorFetchByUrlType, fetchActorByUrl } from '../../helpers/actor'
24import { sequelizeTypescript } from '../../initializers/database' 24import { sequelizeTypescript } from '../../initializers/database'
25import {
26 MAccount,
27 MActor,
28 MActorAccountChannelId,
29 MActorAccountId,
30 MActorDefault,
31 MActorFull,
32 MActorId,
33 MActorAccountChannelIdActor,
34 MChannel,
35 MActorFullActor, MAccountActorDefault, MChannelActorDefault, MChannelActorAccountDefault
36} from '../../typings/models'
25 37
26// Set account keys, this could be long so process after the account creation and do not block the client 38// Set account keys, this could be long so process after the account creation and do not block the client
27function setAsyncActorKeys (actor: ActorModel) { 39function setAsyncActorKeys (actor: MActor) {
28 return createPrivateAndPublicKeys() 40 return createPrivateAndPublicKeys()
29 .then(({ publicKey, privateKey }) => { 41 .then(({ publicKey, privateKey }) => {
30 actor.set('publicKey', publicKey) 42 actor.publicKey = publicKey
31 actor.set('privateKey', privateKey) 43 actor.privateKey = privateKey
32 return actor.save() 44 return actor.save()
33 }) 45 })
34 .catch(err => { 46 .catch(err => {
@@ -37,12 +49,26 @@ function setAsyncActorKeys (actor: ActorModel) {
37 }) 49 })
38} 50}
39 51
52function getOrCreateActorAndServerAndModel (
53 activityActor: string | ActivityPubActor,
54 fetchType: 'all',
55 recurseIfNeeded?: boolean,
56 updateCollections?: boolean
57): Promise<MActorFullActor>
58
59function getOrCreateActorAndServerAndModel (
60 activityActor: string | ActivityPubActor,
61 fetchType?: 'association-ids',
62 recurseIfNeeded?: boolean,
63 updateCollections?: boolean
64): Promise<MActorAccountChannelId>
65
40async function getOrCreateActorAndServerAndModel ( 66async function getOrCreateActorAndServerAndModel (
41 activityActor: string | ActivityPubActor, 67 activityActor: string | ActivityPubActor,
42 fetchType: ActorFetchByUrlType = 'actor-and-association-ids', 68 fetchType: ActorFetchByUrlType = 'association-ids',
43 recurseIfNeeded = true, 69 recurseIfNeeded = true,
44 updateCollections = false 70 updateCollections = false
45) { 71): Promise<MActorFullActor | MActorAccountChannelId> {
46 const actorUrl = getAPId(activityActor) 72 const actorUrl = getAPId(activityActor)
47 let created = false 73 let created = false
48 let accountPlaylistsUrl: string 74 let accountPlaylistsUrl: string
@@ -61,7 +87,7 @@ async function getOrCreateActorAndServerAndModel (
61 87
62 // Create the attributed to actor 88 // Create the attributed to actor
63 // In PeerTube a video channel is owned by an account 89 // In PeerTube a video channel is owned by an account
64 let ownerActor: ActorModel = undefined 90 let ownerActor: MActorFullActor
65 if (recurseIfNeeded === true && result.actor.type === 'Group') { 91 if (recurseIfNeeded === true && result.actor.type === 'Group') {
66 const accountAttributedTo = result.attributedTo.find(a => a.type === 'Person') 92 const accountAttributedTo = result.attributedTo.find(a => a.type === 'Person')
67 if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actor.url) 93 if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actor.url)
@@ -85,8 +111,8 @@ async function getOrCreateActorAndServerAndModel (
85 accountPlaylistsUrl = result.playlists 111 accountPlaylistsUrl = result.playlists
86 } 112 }
87 113
88 if (actor.Account) actor.Account.Actor = actor 114 if (actor.Account) (actor as MActorAccountChannelIdActor).Account.Actor = actor
89 if (actor.VideoChannel) actor.VideoChannel.Actor = actor 115 if (actor.VideoChannel) (actor as MActorAccountChannelIdActor).VideoChannel.Actor = actor
90 116
91 const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor, fetchType) 117 const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor, fetchType)
92 if (!actorRefreshed) throw new Error('Actor ' + actorRefreshed.url + ' does not exist anymore.') 118 if (!actorRefreshed) throw new Error('Actor ' + actorRefreshed.url + ' does not exist anymore.')
@@ -140,7 +166,8 @@ async function updateActorInstance (actorInstance: ActorModel, attributes: Activ
140 actorInstance.followingUrl = attributes.following 166 actorInstance.followingUrl = attributes.following
141} 167}
142 168
143async function updateActorAvatarInstance (actor: ActorModel, info: { name: string, onDisk: boolean, fileUrl: string }, t: Transaction) { 169type AvatarInfo = { name: string, onDisk: boolean, fileUrl: string }
170async function updateActorAvatarInstance (actor: MActorDefault, info: AvatarInfo, t: Transaction) {
144 if (info.name !== undefined) { 171 if (info.name !== undefined) {
145 if (actor.avatarId) { 172 if (actor.avatarId) {
146 try { 173 try {
@@ -212,14 +239,16 @@ async function addFetchOutboxJob (actor: Pick<ActorModel, 'id' | 'outboxUrl'>) {
212 return JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload }) 239 return JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })
213} 240}
214 241
215async function refreshActorIfNeeded ( 242async function refreshActorIfNeeded <T extends MActorFull | MActorAccountChannelId> (
216 actorArg: ActorModel, 243 actorArg: T,
217 fetchedType: ActorFetchByUrlType 244 fetchedType: ActorFetchByUrlType
218): Promise<{ actor: ActorModel, refreshed: boolean }> { 245): Promise<{ actor: T | MActorFull, refreshed: boolean }> {
219 if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false } 246 if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false }
220 247
221 // We need more attributes 248 // We need more attributes
222 const actor = fetchedType === 'all' ? actorArg : await ActorModel.loadByUrlAndPopulateAccountAndChannel(actorArg.url) 249 const actor = fetchedType === 'all'
250 ? actorArg as MActorFull
251 : await ActorModel.loadByUrlAndPopulateAccountAndChannel(actorArg.url)
223 252
224 try { 253 try {
225 let actorUrl: string 254 let actorUrl: string
@@ -297,9 +326,9 @@ export {
297 326
298function saveActorAndServerAndModelIfNotExist ( 327function saveActorAndServerAndModelIfNotExist (
299 result: FetchRemoteActorResult, 328 result: FetchRemoteActorResult,
300 ownerActor?: ActorModel, 329 ownerActor?: MActorFullActor,
301 t?: Transaction 330 t?: Transaction
302): Bluebird<ActorModel> | Promise<ActorModel> { 331): Bluebird<MActorFullActor> | Promise<MActorFullActor> {
303 let actor = result.actor 332 let actor = result.actor
304 333
305 if (t !== undefined) return save(t) 334 if (t !== undefined) return save(t)
@@ -336,7 +365,7 @@ function saveActorAndServerAndModelIfNotExist (
336 365
337 // Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists 366 // Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists
338 // (which could be false in a retried query) 367 // (which could be false in a retried query)
339 const [ actorCreated ] = await ActorModel.findOrCreate({ 368 const [ actorCreated ] = await ActorModel.findOrCreate<MActorFullActor>({
340 defaults: actor.toJSON(), 369 defaults: actor.toJSON(),
341 where: { 370 where: {
342 url: actor.url 371 url: actor.url
@@ -345,10 +374,10 @@ function saveActorAndServerAndModelIfNotExist (
345 }) 374 })
346 375
347 if (actorCreated.type === 'Person' || actorCreated.type === 'Application') { 376 if (actorCreated.type === 'Person' || actorCreated.type === 'Application') {
348 actorCreated.Account = await saveAccount(actorCreated, result, t) 377 actorCreated.Account = await saveAccount(actorCreated, result, t) as MAccountActorDefault
349 actorCreated.Account.Actor = actorCreated 378 actorCreated.Account.Actor = actorCreated
350 } else if (actorCreated.type === 'Group') { // Video channel 379 } else if (actorCreated.type === 'Group') { // Video channel
351 actorCreated.VideoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t) 380 actorCreated.VideoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t) as MChannelActorAccountDefault
352 actorCreated.VideoChannel.Actor = actorCreated 381 actorCreated.VideoChannel.Actor = actorCreated
353 actorCreated.VideoChannel.Account = ownerActor.Account 382 actorCreated.VideoChannel.Account = ownerActor.Account
354 } 383 }
@@ -360,7 +389,7 @@ function saveActorAndServerAndModelIfNotExist (
360} 389}
361 390
362type FetchRemoteActorResult = { 391type FetchRemoteActorResult = {
363 actor: ActorModel 392 actor: MActor
364 name: string 393 name: string
365 summary: string 394 summary: string
366 support?: string 395 support?: string
@@ -429,7 +458,7 @@ async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: numbe
429 } 458 }
430} 459}
431 460
432async function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t: Transaction) { 461async function saveAccount (actor: MActorId, result: FetchRemoteActorResult, t: Transaction) {
433 const [ accountCreated ] = await AccountModel.findOrCreate({ 462 const [ accountCreated ] = await AccountModel.findOrCreate({
434 defaults: { 463 defaults: {
435 name: result.name, 464 name: result.name,
@@ -442,10 +471,10 @@ async function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t
442 transaction: t 471 transaction: t
443 }) 472 })
444 473
445 return accountCreated 474 return accountCreated as MAccount
446} 475}
447 476
448async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResult, ownerActor: ActorModel, t: Transaction) { 477async function saveVideoChannel (actor: MActorId, result: FetchRemoteActorResult, ownerActor: MActorAccountId, t: Transaction) {
449 const [ videoChannelCreated ] = await VideoChannelModel.findOrCreate({ 478 const [ videoChannelCreated ] = await VideoChannelModel.findOrCreate({
450 defaults: { 479 defaults: {
451 name: result.name, 480 name: result.name,
@@ -460,5 +489,5 @@ async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResu
460 transaction: t 489 transaction: t
461 }) 490 })
462 491
463 return videoChannelCreated 492 return videoChannelCreated as MChannel
464} 493}
diff --git a/server/lib/activitypub/audience.ts b/server/lib/activitypub/audience.ts
index 0e3d78590..f2ab54cf7 100644
--- a/server/lib/activitypub/audience.ts
+++ b/server/lib/activitypub/audience.ts
@@ -3,11 +3,10 @@ import { ActivityAudience } from '../../../shared/models/activitypub'
3import { ACTIVITY_PUB } from '../../initializers/constants' 3import { ACTIVITY_PUB } from '../../initializers/constants'
4import { ActorModel } from '../../models/activitypub/actor' 4import { ActorModel } from '../../models/activitypub/actor'
5import { VideoModel } from '../../models/video/video' 5import { VideoModel } from '../../models/video/video'
6import { VideoCommentModel } from '../../models/video/video-comment'
7import { VideoShareModel } from '../../models/video/video-share' 6import { VideoShareModel } from '../../models/video/video-share'
8import { ActorModelOnly } from '../../typings/models' 7import { MActorFollowersUrl, MActorLight, MCommentOwner, MCommentOwnerVideo, MVideo, MVideoAccountLight } from '../../typings/models'
9 8
10function getRemoteVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]): ActivityAudience { 9function getRemoteVideoAudience (video: MVideoAccountLight, actorsInvolvedInVideo: MActorFollowersUrl[]): ActivityAudience {
11 return { 10 return {
12 to: [ video.VideoChannel.Account.Actor.url ], 11 to: [ video.VideoChannel.Account.Actor.url ],
13 cc: actorsInvolvedInVideo.map(a => a.followersUrl) 12 cc: actorsInvolvedInVideo.map(a => a.followersUrl)
@@ -15,9 +14,9 @@ function getRemoteVideoAudience (video: VideoModel, actorsInvolvedInVideo: Actor
15} 14}
16 15
17function getVideoCommentAudience ( 16function getVideoCommentAudience (
18 videoComment: VideoCommentModel, 17 videoComment: MCommentOwnerVideo,
19 threadParentComments: VideoCommentModel[], 18 threadParentComments: MCommentOwner[],
20 actorsInvolvedInVideo: ActorModel[], 19 actorsInvolvedInVideo: MActorFollowersUrl[],
21 isOrigin = false 20 isOrigin = false
22): ActivityAudience { 21): ActivityAudience {
23 const to = [ ACTIVITY_PUB.PUBLIC ] 22 const to = [ ACTIVITY_PUB.PUBLIC ]
@@ -42,26 +41,28 @@ function getVideoCommentAudience (
42 } 41 }
43} 42}
44 43
45function getAudienceFromFollowersOf (actorsInvolvedInObject: ActorModel[]): ActivityAudience { 44function getAudienceFromFollowersOf (actorsInvolvedInObject: MActorFollowersUrl[]): ActivityAudience {
46 return { 45 return {
47 to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)), 46 to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)),
48 cc: [] 47 cc: []
49 } 48 }
50} 49}
51 50
52async function getActorsInvolvedInVideo (video: VideoModel, t: Transaction) { 51async function getActorsInvolvedInVideo (video: MVideo, t: Transaction) {
53 const actors = await VideoShareModel.loadActorsByShare(video.id, t) 52 const actors: MActorLight[] = await VideoShareModel.loadActorsByShare(video.id, t)
54 53
55 const videoActor = video.VideoChannel && video.VideoChannel.Account 54 const videoAll = video as VideoModel
56 ? video.VideoChannel.Account.Actor 55
57 : await ActorModel.loadAccountActorByVideoId(video.id, t) 56 const videoActor = videoAll.VideoChannel && videoAll.VideoChannel.Account
57 ? videoAll.VideoChannel.Account.Actor
58 : await ActorModel.loadFromAccountByVideoId(video.id, t)
58 59
59 actors.push(videoActor) 60 actors.push(videoActor)
60 61
61 return actors 62 return actors
62} 63}
63 64
64function getAudience (actorSender: ActorModelOnly, isPublic = true) { 65function getAudience (actorSender: MActorFollowersUrl, isPublic = true) {
65 return buildAudience([ actorSender.followersUrl ], isPublic) 66 return buildAudience([ actorSender.followersUrl ], isPublic)
66} 67}
67 68
diff --git a/server/lib/activitypub/cache-file.ts b/server/lib/activitypub/cache-file.ts
index de5cc54ac..65b2dcb49 100644
--- a/server/lib/activitypub/cache-file.ts
+++ b/server/lib/activitypub/cache-file.ts
@@ -1,10 +1,10 @@
1import { CacheFileObject } from '../../../shared/index' 1import { CacheFileObject } from '../../../shared/index'
2import { VideoModel } from '../../models/video/video'
3import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' 2import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
4import { Transaction } from 'sequelize' 3import { Transaction } from 'sequelize'
5import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' 4import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
5import { MActorId, MVideoRedundancy, MVideoWithAllFiles } from '@server/typings/models'
6 6
7function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }) { 7function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject, video: MVideoWithAllFiles, byActor: MActorId) {
8 8
9 if (cacheFileObject.url.mediaType === 'application/x-mpegURL') { 9 if (cacheFileObject.url.mediaType === 'application/x-mpegURL') {
10 const url = cacheFileObject.url 10 const url = cacheFileObject.url
@@ -39,7 +39,7 @@ function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject
39 } 39 }
40} 40}
41 41
42async function createOrUpdateCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }, t: Transaction) { 42async function createOrUpdateCacheFile (cacheFileObject: CacheFileObject, video: MVideoWithAllFiles, byActor: MActorId, t: Transaction) {
43 const redundancyModel = await VideoRedundancyModel.loadByUrl(cacheFileObject.id, t) 43 const redundancyModel = await VideoRedundancyModel.loadByUrl(cacheFileObject.id, t)
44 44
45 if (!redundancyModel) { 45 if (!redundancyModel) {
@@ -49,7 +49,7 @@ async function createOrUpdateCacheFile (cacheFileObject: CacheFileObject, video:
49 } 49 }
50} 50}
51 51
52function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }, t: Transaction) { 52function createCacheFile (cacheFileObject: CacheFileObject, video: MVideoWithAllFiles, byActor: MActorId, t: Transaction) {
53 const attributes = cacheFileActivityObjectToDBAttributes(cacheFileObject, video, byActor) 53 const attributes = cacheFileActivityObjectToDBAttributes(cacheFileObject, video, byActor)
54 54
55 return VideoRedundancyModel.create(attributes, { transaction: t }) 55 return VideoRedundancyModel.create(attributes, { transaction: t })
@@ -57,9 +57,9 @@ function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, b
57 57
58function updateCacheFile ( 58function updateCacheFile (
59 cacheFileObject: CacheFileObject, 59 cacheFileObject: CacheFileObject,
60 redundancyModel: VideoRedundancyModel, 60 redundancyModel: MVideoRedundancy,
61 video: VideoModel, 61 video: MVideoWithAllFiles,
62 byActor: { id?: number }, 62 byActor: MActorId,
63 t: Transaction 63 t: Transaction
64) { 64) {
65 if (redundancyModel.actorId !== byActor.id) { 65 if (redundancyModel.actorId !== byActor.id) {
diff --git a/server/lib/activitypub/playlist.ts b/server/lib/activitypub/playlist.ts
index c2e2a3283..c52b715ef 100644
--- a/server/lib/activitypub/playlist.ts
+++ b/server/lib/activitypub/playlist.ts
@@ -1,7 +1,6 @@
1import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' 1import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object'
2import { crawlCollectionPage } from './crawl' 2import { crawlCollectionPage } from './crawl'
3import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' 3import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
4import { AccountModel } from '../../models/account/account'
5import { isArray } from '../../helpers/custom-validators/misc' 4import { isArray } from '../../helpers/custom-validators/misc'
6import { getOrCreateActorAndServerAndModel } from './actor' 5import { getOrCreateActorAndServerAndModel } from './actor'
7import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
@@ -13,14 +12,14 @@ import { PlaylistElementObject } from '../../../shared/models/activitypub/object
13import { getOrCreateVideoAndAccountAndChannel } from './videos' 12import { getOrCreateVideoAndAccountAndChannel } from './videos'
14import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '../../helpers/custom-validators/activitypub/playlist' 13import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '../../helpers/custom-validators/activitypub/playlist'
15import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element' 14import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element'
16import { VideoModel } from '../../models/video/video'
17import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' 15import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
18import { sequelizeTypescript } from '../../initializers/database' 16import { sequelizeTypescript } from '../../initializers/database'
19import { createPlaylistMiniatureFromUrl } from '../thumbnail' 17import { createPlaylistMiniatureFromUrl } from '../thumbnail'
20import { FilteredModelAttributes } from '../../typings/sequelize' 18import { FilteredModelAttributes } from '../../typings/sequelize'
21import { AccountModelId } from '../../typings/models' 19import { MAccountDefault, MAccountId, MVideoId } from '../../typings/models'
20import { MVideoPlaylist, MVideoPlaylistId, MVideoPlaylistOwner } from '../../typings/models/video/video-playlist'
22 21
23function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount: AccountModelId, to: string[]) { 22function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount: MAccountId, to: string[]) {
24 const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPlaylistPrivacy.PUBLIC : VideoPlaylistPrivacy.UNLISTED 23 const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPlaylistPrivacy.PUBLIC : VideoPlaylistPrivacy.UNLISTED
25 24
26 return { 25 return {
@@ -36,7 +35,7 @@ function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount
36 } 35 }
37} 36}
38 37
39function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObject, videoPlaylist: VideoPlaylistModel, video: VideoModel) { 38function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObject, videoPlaylist: MVideoPlaylistId, video: MVideoId) {
40 return { 39 return {
41 position: elementObject.position, 40 position: elementObject.position,
42 url: elementObject.id, 41 url: elementObject.id,
@@ -47,7 +46,7 @@ function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObje
47 } 46 }
48} 47}
49 48
50async function createAccountPlaylists (playlistUrls: string[], account: AccountModel) { 49async function createAccountPlaylists (playlistUrls: string[], account: MAccountDefault) {
51 await Bluebird.map(playlistUrls, async playlistUrl => { 50 await Bluebird.map(playlistUrls, async playlistUrl => {
52 try { 51 try {
53 const exists = await VideoPlaylistModel.doesPlaylistExist(playlistUrl) 52 const exists = await VideoPlaylistModel.doesPlaylistExist(playlistUrl)
@@ -75,7 +74,7 @@ async function createAccountPlaylists (playlistUrls: string[], account: AccountM
75 }, { concurrency: CRAWL_REQUEST_CONCURRENCY }) 74 }, { concurrency: CRAWL_REQUEST_CONCURRENCY })
76} 75}
77 76
78async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAccount: AccountModelId, to: string[]) { 77async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAccount: MAccountId, to: string[]) {
79 const playlistAttributes = playlistObjectToDBAttributes(playlistObject, byAccount, to) 78 const playlistAttributes = playlistObjectToDBAttributes(playlistObject, byAccount, to)
80 79
81 if (isArray(playlistObject.attributedTo) && playlistObject.attributedTo.length === 1) { 80 if (isArray(playlistObject.attributedTo) && playlistObject.attributedTo.length === 1) {
@@ -88,7 +87,7 @@ async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAc
88 } 87 }
89 } 88 }
90 89
91 const [ playlist ] = await VideoPlaylistModel.upsert<VideoPlaylistModel>(playlistAttributes, { returning: true }) 90 const [ playlist ] = await VideoPlaylistModel.upsert<MVideoPlaylist>(playlistAttributes, { returning: true })
92 91
93 let accItems: string[] = [] 92 let accItems: string[] = []
94 await crawlCollectionPage<string>(playlistObject.id, items => { 93 await crawlCollectionPage<string>(playlistObject.id, items => {
@@ -114,7 +113,7 @@ async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAc
114 return resetVideoPlaylistElements(accItems, refreshedPlaylist) 113 return resetVideoPlaylistElements(accItems, refreshedPlaylist)
115} 114}
116 115
117async function refreshVideoPlaylistIfNeeded (videoPlaylist: VideoPlaylistModel): Promise<VideoPlaylistModel> { 116async function refreshVideoPlaylistIfNeeded (videoPlaylist: MVideoPlaylistOwner): Promise<MVideoPlaylistOwner> {
118 if (!videoPlaylist.isOutdated()) return videoPlaylist 117 if (!videoPlaylist.isOutdated()) return videoPlaylist
119 118
120 try { 119 try {
@@ -157,7 +156,7 @@ export {
157 156
158// --------------------------------------------------------------------------- 157// ---------------------------------------------------------------------------
159 158
160async function resetVideoPlaylistElements (elementUrls: string[], playlist: VideoPlaylistModel) { 159async function resetVideoPlaylistElements (elementUrls: string[], playlist: MVideoPlaylist) {
161 const elementsToCreate: FilteredModelAttributes<VideoPlaylistElementModel>[] = [] 160 const elementsToCreate: FilteredModelAttributes<VideoPlaylistElementModel>[] = []
162 161
163 await Bluebird.map(elementUrls, async elementUrl => { 162 await Bluebird.map(elementUrls, async elementUrl => {
diff --git a/server/lib/activitypub/process/process-accept.ts b/server/lib/activitypub/process/process-accept.ts
index cf27e6c32..86f7c764d 100644
--- a/server/lib/activitypub/process/process-accept.ts
+++ b/server/lib/activitypub/process/process-accept.ts
@@ -1,9 +1,8 @@
1import { ActivityAccept } from '../../../../shared/models/activitypub' 1import { ActivityAccept } from '../../../../shared/models/activitypub'
2import { ActorModel } from '../../../models/activitypub/actor'
3import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 2import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
4import { addFetchOutboxJob } from '../actor' 3import { addFetchOutboxJob } from '../actor'
5import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 4import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
6import { SignatureActorModel } from '../../../typings/models' 5import { MActorDefault, MActorSignature } from '../../../typings/models'
7 6
8async function processAcceptActivity (options: APProcessorOptions<ActivityAccept>) { 7async function processAcceptActivity (options: APProcessorOptions<ActivityAccept>) {
9 const { byActor: targetActor, inboxActor } = options 8 const { byActor: targetActor, inboxActor } = options
@@ -20,7 +19,7 @@ export {
20 19
21// --------------------------------------------------------------------------- 20// ---------------------------------------------------------------------------
22 21
23async function processAccept (actor: ActorModel, targetActor: SignatureActorModel) { 22async function processAccept (actor: MActorDefault, targetActor: MActorSignature) {
24 const follow = await ActorFollowModel.loadByActorAndTarget(actor.id, targetActor.id) 23 const follow = await ActorFollowModel.loadByActorAndTarget(actor.id, targetActor.id)
25 if (!follow) throw new Error('Cannot find associated follow.') 24 if (!follow) throw new Error('Cannot find associated follow.')
26 25
diff --git a/server/lib/activitypub/process/process-announce.ts b/server/lib/activitypub/process/process-announce.ts
index b3cdc4441..91a9ad72c 100644
--- a/server/lib/activitypub/process/process-announce.ts
+++ b/server/lib/activitypub/process/process-announce.ts
@@ -5,10 +5,9 @@ import { VideoShareModel } from '../../../models/video/video-share'
5import { forwardVideoRelatedActivity } from '../send/utils' 5import { forwardVideoRelatedActivity } from '../send/utils'
6import { getOrCreateVideoAndAccountAndChannel } from '../videos' 6import { getOrCreateVideoAndAccountAndChannel } from '../videos'
7import { Notifier } from '../../notifier' 7import { Notifier } from '../../notifier'
8import { VideoModel } from '../../../models/video/video'
9import { logger } from '../../../helpers/logger' 8import { logger } from '../../../helpers/logger'
10import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 9import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
11import { SignatureActorModel } from '../../../typings/models' 10import { MActorSignature, MVideoAccountAllFiles } from '../../../typings/models'
12 11
13async function processAnnounceActivity (options: APProcessorOptions<ActivityAnnounce>) { 12async function processAnnounceActivity (options: APProcessorOptions<ActivityAnnounce>) {
14 const { activity, byActor: actorAnnouncer } = options 13 const { activity, byActor: actorAnnouncer } = options
@@ -26,10 +25,10 @@ export {
26 25
27// --------------------------------------------------------------------------- 26// ---------------------------------------------------------------------------
28 27
29async function processVideoShare (actorAnnouncer: SignatureActorModel, activity: ActivityAnnounce, notify: boolean) { 28async function processVideoShare (actorAnnouncer: MActorSignature, activity: ActivityAnnounce, notify: boolean) {
30 const objectUri = typeof activity.object === 'string' ? activity.object : activity.object.id 29 const objectUri = typeof activity.object === 'string' ? activity.object : activity.object.id
31 30
32 let video: VideoModel 31 let video: MVideoAccountAllFiles
33 let videoCreated: boolean 32 let videoCreated: boolean
34 33
35 try { 34 try {
diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts
index 6815c6997..c45f09f52 100644
--- a/server/lib/activitypub/process/process-create.ts
+++ b/server/lib/activitypub/process/process-create.ts
@@ -10,10 +10,8 @@ import { createOrUpdateCacheFile } from '../cache-file'
10import { Notifier } from '../../notifier' 10import { Notifier } from '../../notifier'
11import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' 11import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object'
12import { createOrUpdateVideoPlaylist } from '../playlist' 12import { createOrUpdateVideoPlaylist } from '../playlist'
13import { VideoModel } from '../../../models/video/video'
14import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 13import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
15import { VideoCommentModel } from '../../../models/video/video-comment' 14import { MActorSignature, MCommentOwnerVideo, MVideoAccountAllFiles } from '../../../typings/models'
16import { SignatureActorModel } from '../../../typings/models'
17 15
18async function processCreateActivity (options: APProcessorOptions<ActivityCreate>) { 16async function processCreateActivity (options: APProcessorOptions<ActivityCreate>) {
19 const { activity, byActor } = options 17 const { activity, byActor } = options
@@ -61,7 +59,7 @@ async function processCreateVideo (activity: ActivityCreate, notify: boolean) {
61 return video 59 return video
62} 60}
63 61
64async function processCreateCacheFile (activity: ActivityCreate, byActor: SignatureActorModel) { 62async function processCreateCacheFile (activity: ActivityCreate, byActor: MActorSignature) {
65 const cacheFile = activity.object as CacheFileObject 63 const cacheFile = activity.object as CacheFileObject
66 64
67 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFile.object }) 65 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFile.object })
@@ -77,15 +75,15 @@ async function processCreateCacheFile (activity: ActivityCreate, byActor: Signat
77 } 75 }
78} 76}
79 77
80async function processCreateVideoComment (activity: ActivityCreate, byActor: SignatureActorModel, notify: boolean) { 78async function processCreateVideoComment (activity: ActivityCreate, byActor: MActorSignature, notify: boolean) {
81 const commentObject = activity.object as VideoCommentObject 79 const commentObject = activity.object as VideoCommentObject
82 const byAccount = byActor.Account 80 const byAccount = byActor.Account
83 81
84 if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url) 82 if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url)
85 83
86 let video: VideoModel 84 let video: MVideoAccountAllFiles
87 let created: boolean 85 let created: boolean
88 let comment: VideoCommentModel 86 let comment: MCommentOwnerVideo
89 try { 87 try {
90 const resolveThreadResult = await resolveThread({ url: commentObject.id, isVideo: false }) 88 const resolveThreadResult = await resolveThread({ url: commentObject.id, isVideo: false })
91 video = resolveThreadResult.video 89 video = resolveThreadResult.video
@@ -110,7 +108,7 @@ async function processCreateVideoComment (activity: ActivityCreate, byActor: Sig
110 if (created && notify) Notifier.Instance.notifyOnNewComment(comment) 108 if (created && notify) Notifier.Instance.notifyOnNewComment(comment)
111} 109}
112 110
113async function processCreatePlaylist (activity: ActivityCreate, byActor: SignatureActorModel) { 111async function processCreatePlaylist (activity: ActivityCreate, byActor: MActorSignature) {
114 const playlistObject = activity.object as PlaylistObject 112 const playlistObject = activity.object as PlaylistObject
115 const byAccount = byActor.Account 113 const byAccount = byActor.Account
116 114
diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts
index 344d14322..79d0e0d79 100644
--- a/server/lib/activitypub/process/process-delete.ts
+++ b/server/lib/activitypub/process/process-delete.ts
@@ -2,15 +2,13 @@ import { ActivityDelete } from '../../../../shared/models/activitypub'
2import { retryTransactionWrapper } from '../../../helpers/database-utils' 2import { retryTransactionWrapper } from '../../../helpers/database-utils'
3import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
4import { sequelizeTypescript } from '../../../initializers' 4import { sequelizeTypescript } from '../../../initializers'
5import { AccountModel } from '../../../models/account/account'
6import { ActorModel } from '../../../models/activitypub/actor' 5import { ActorModel } from '../../../models/activitypub/actor'
7import { VideoModel } from '../../../models/video/video' 6import { VideoModel } from '../../../models/video/video'
8import { VideoChannelModel } from '../../../models/video/video-channel'
9import { VideoCommentModel } from '../../../models/video/video-comment' 7import { VideoCommentModel } from '../../../models/video/video-comment'
10import { forwardVideoRelatedActivity } from '../send/utils' 8import { forwardVideoRelatedActivity } from '../send/utils'
11import { VideoPlaylistModel } from '../../../models/video/video-playlist' 9import { VideoPlaylistModel } from '../../../models/video/video-playlist'
12import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 10import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
13import { SignatureActorModel } from '../../../typings/models' 11import { MAccountActor, MActor, MActorSignature, MChannelActor, MChannelActorAccountActor } from '../../../typings/models'
14 12
15async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) { 13async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) {
16 const { activity, byActor } = options 14 const { activity, byActor } = options
@@ -24,13 +22,17 @@ async function processDeleteActivity (options: APProcessorOptions<ActivityDelete
24 if (byActorFull.type === 'Person') { 22 if (byActorFull.type === 'Person') {
25 if (!byActorFull.Account) throw new Error('Actor ' + byActorFull.url + ' is a person but we cannot find it in database.') 23 if (!byActorFull.Account) throw new Error('Actor ' + byActorFull.url + ' is a person but we cannot find it in database.')
26 24
27 byActorFull.Account.Actor = await byActorFull.Account.$get('Actor') as ActorModel 25 const accountToDelete = byActorFull.Account as MAccountActor
28 return retryTransactionWrapper(processDeleteAccount, byActorFull.Account) 26 accountToDelete.Actor = byActorFull
27
28 return retryTransactionWrapper(processDeleteAccount, accountToDelete)
29 } else if (byActorFull.type === 'Group') { 29 } else if (byActorFull.type === 'Group') {
30 if (!byActorFull.VideoChannel) throw new Error('Actor ' + byActorFull.url + ' is a group but we cannot find it in database.') 30 if (!byActorFull.VideoChannel) throw new Error('Actor ' + byActorFull.url + ' is a group but we cannot find it in database.')
31 31
32 byActorFull.VideoChannel.Actor = await byActorFull.VideoChannel.$get('Actor') as ActorModel 32 const channelToDelete = byActorFull.VideoChannel as MChannelActorAccountActor
33 return retryTransactionWrapper(processDeleteVideoChannel, byActorFull.VideoChannel) 33 channelToDelete.Actor = byActorFull
34
35 return retryTransactionWrapper(processDeleteVideoChannel, channelToDelete)
34 } 36 }
35 } 37 }
36 38
@@ -70,7 +72,7 @@ export {
70 72
71// --------------------------------------------------------------------------- 73// ---------------------------------------------------------------------------
72 74
73async function processDeleteVideo (actor: ActorModel, videoToDelete: VideoModel) { 75async function processDeleteVideo (actor: MActor, videoToDelete: VideoModel) {
74 logger.debug('Removing remote video "%s".', videoToDelete.uuid) 76 logger.debug('Removing remote video "%s".', videoToDelete.uuid)
75 77
76 await sequelizeTypescript.transaction(async t => { 78 await sequelizeTypescript.transaction(async t => {
@@ -84,7 +86,7 @@ async function processDeleteVideo (actor: ActorModel, videoToDelete: VideoModel)
84 logger.info('Remote video with uuid %s removed.', videoToDelete.uuid) 86 logger.info('Remote video with uuid %s removed.', videoToDelete.uuid)
85} 87}
86 88
87async function processDeleteVideoPlaylist (actor: ActorModel, playlistToDelete: VideoPlaylistModel) { 89async function processDeleteVideoPlaylist (actor: MActor, playlistToDelete: VideoPlaylistModel) {
88 logger.debug('Removing remote video playlist "%s".', playlistToDelete.uuid) 90 logger.debug('Removing remote video playlist "%s".', playlistToDelete.uuid)
89 91
90 await sequelizeTypescript.transaction(async t => { 92 await sequelizeTypescript.transaction(async t => {
@@ -98,7 +100,7 @@ async function processDeleteVideoPlaylist (actor: ActorModel, playlistToDelete:
98 logger.info('Remote video playlist with uuid %s removed.', playlistToDelete.uuid) 100 logger.info('Remote video playlist with uuid %s removed.', playlistToDelete.uuid)
99} 101}
100 102
101async function processDeleteAccount (accountToRemove: AccountModel) { 103async function processDeleteAccount (accountToRemove: MAccountActor) {
102 logger.debug('Removing remote account "%s".', accountToRemove.Actor.url) 104 logger.debug('Removing remote account "%s".', accountToRemove.Actor.url)
103 105
104 await sequelizeTypescript.transaction(async t => { 106 await sequelizeTypescript.transaction(async t => {
@@ -108,7 +110,7 @@ async function processDeleteAccount (accountToRemove: AccountModel) {
108 logger.info('Remote account %s removed.', accountToRemove.Actor.url) 110 logger.info('Remote account %s removed.', accountToRemove.Actor.url)
109} 111}
110 112
111async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelModel) { 113async function processDeleteVideoChannel (videoChannelToRemove: MChannelActor) {
112 logger.debug('Removing remote video channel "%s".', videoChannelToRemove.Actor.url) 114 logger.debug('Removing remote video channel "%s".', videoChannelToRemove.Actor.url)
113 115
114 await sequelizeTypescript.transaction(async t => { 116 await sequelizeTypescript.transaction(async t => {
@@ -118,7 +120,7 @@ async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelMode
118 logger.info('Remote video channel %s removed.', videoChannelToRemove.Actor.url) 120 logger.info('Remote video channel %s removed.', videoChannelToRemove.Actor.url)
119} 121}
120 122
121function processDeleteVideoComment (byActor: SignatureActorModel, videoComment: VideoCommentModel, activity: ActivityDelete) { 123function processDeleteVideoComment (byActor: MActorSignature, videoComment: VideoCommentModel, activity: ActivityDelete) {
122 logger.debug('Removing remote video comment "%s".', videoComment.url) 124 logger.debug('Removing remote video comment "%s".', videoComment.url)
123 125
124 return sequelizeTypescript.transaction(async t => { 126 return sequelizeTypescript.transaction(async t => {
diff --git a/server/lib/activitypub/process/process-dislike.ts b/server/lib/activitypub/process/process-dislike.ts
index 727fcfee0..debd8a67c 100644
--- a/server/lib/activitypub/process/process-dislike.ts
+++ b/server/lib/activitypub/process/process-dislike.ts
@@ -7,7 +7,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos'
7import { forwardVideoRelatedActivity } from '../send/utils' 7import { forwardVideoRelatedActivity } from '../send/utils'
8import { getVideoDislikeActivityPubUrl } from '../url' 8import { getVideoDislikeActivityPubUrl } from '../url'
9import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 9import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
10import { SignatureActorModel } from '../../../typings/models' 10import { MActorSignature } from '../../../typings/models'
11 11
12async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) { 12async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) {
13 const { activity, byActor } = options 13 const { activity, byActor } = options
@@ -22,7 +22,7 @@ export {
22 22
23// --------------------------------------------------------------------------- 23// ---------------------------------------------------------------------------
24 24
25async function processDislike (activity: ActivityCreate | ActivityDislike, byActor: SignatureActorModel) { 25async function processDislike (activity: ActivityCreate | ActivityDislike, byActor: MActorSignature) {
26 const dislikeObject = activity.type === 'Dislike' ? activity.object : (activity.object as DislikeObject).object 26 const dislikeObject = activity.type === 'Dislike' ? activity.object : (activity.object as DislikeObject).object
27 const byAccount = byActor.Account 27 const byAccount = byActor.Account
28 28
diff --git a/server/lib/activitypub/process/process-flag.ts b/server/lib/activitypub/process/process-flag.ts
index 1f8a80c14..422386540 100644
--- a/server/lib/activitypub/process/process-flag.ts
+++ b/server/lib/activitypub/process/process-flag.ts
@@ -8,7 +8,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos'
8import { Notifier } from '../../notifier' 8import { Notifier } from '../../notifier'
9import { getAPId } from '../../../helpers/activitypub' 9import { getAPId } from '../../../helpers/activitypub'
10import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 10import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
11import { SignatureActorModel } from '../../../typings/models' 11import { MActorSignature, MVideoAbuseVideo } from '../../../typings/models'
12 12
13async function processFlagActivity (options: APProcessorOptions<ActivityCreate | ActivityFlag>) { 13async function processFlagActivity (options: APProcessorOptions<ActivityCreate | ActivityFlag>) {
14 const { activity, byActor } = options 14 const { activity, byActor } = options
@@ -23,7 +23,7 @@ export {
23 23
24// --------------------------------------------------------------------------- 24// ---------------------------------------------------------------------------
25 25
26async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag, byActor: SignatureActorModel) { 26async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag, byActor: MActorSignature) {
27 const flag = activity.type === 'Flag' ? activity : (activity.object as VideoAbuseObject) 27 const flag = activity.type === 'Flag' ? activity : (activity.object as VideoAbuseObject)
28 28
29 logger.debug('Reporting remote abuse for video %s.', getAPId(flag.object)) 29 logger.debug('Reporting remote abuse for video %s.', getAPId(flag.object))
@@ -41,7 +41,7 @@ async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag,
41 state: VideoAbuseState.PENDING 41 state: VideoAbuseState.PENDING
42 } 42 }
43 43
44 const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t }) 44 const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t }) as MVideoAbuseVideo
45 videoAbuseInstance.Video = video 45 videoAbuseInstance.Video = video
46 46
47 logger.info('Remote abuse for video uuid %s created', flag.object) 47 logger.info('Remote abuse for video uuid %s created', flag.object)
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts
index 240aa5799..bc5660395 100644
--- a/server/lib/activitypub/process/process-follow.ts
+++ b/server/lib/activitypub/process/process-follow.ts
@@ -10,8 +10,7 @@ import { getAPId } from '../../../helpers/activitypub'
10import { getServerActor } from '../../../helpers/utils' 10import { getServerActor } from '../../../helpers/utils'
11import { CONFIG } from '../../../initializers/config' 11import { CONFIG } from '../../../initializers/config'
12import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 12import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
13import { SignatureActorModel } from '../../../typings/models' 13import { MAccount, MActorFollowActors, MActorFollowFull, MActorSignature } from '../../../typings/models'
14import { ActorFollowModelLight } from '../../../typings/models/actor-follow'
15 14
16async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) { 15async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) {
17 const { activity, byActor } = options 16 const { activity, byActor } = options
@@ -28,7 +27,7 @@ export {
28 27
29// --------------------------------------------------------------------------- 28// ---------------------------------------------------------------------------
30 29
31async function processFollow (byActor: SignatureActorModel, targetActorURL: string) { 30async function processFollow (byActor: MActorSignature, targetActorURL: string) {
32 const { actorFollow, created, isFollowingInstance } = await sequelizeTypescript.transaction(async t => { 31 const { actorFollow, created, isFollowingInstance } = await sequelizeTypescript.transaction(async t => {
33 const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t) 32 const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t)
34 33
@@ -43,10 +42,10 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri
43 42
44 await sendReject(byActor, targetActor) 43 await sendReject(byActor, targetActor)
45 44
46 return { actorFollow: undefined } 45 return { actorFollow: undefined as MActorFollowActors }
47 } 46 }
48 47
49 const [ actorFollow, created ] = await ActorFollowModel.findOrCreate({ 48 const [ actorFollow, created ] = await ActorFollowModel.findOrCreate<MActorFollowActors>({
50 where: { 49 where: {
51 actorId: byActor.id, 50 actorId: byActor.id,
52 targetActorId: targetActor.id 51 targetActorId: targetActor.id
@@ -57,7 +56,7 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri
57 state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL ? 'pending' : 'accepted' 56 state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL ? 'pending' : 'accepted'
58 }, 57 },
59 transaction: t 58 transaction: t
60 }) as [ ActorFollowModelLight, boolean ] 59 })
61 60
62 if (actorFollow.state !== 'accepted' && CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false) { 61 if (actorFollow.state !== 'accepted' && CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false) {
63 actorFollow.state = 'accepted' 62 actorFollow.state = 'accepted'
@@ -77,8 +76,14 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri
77 if (!actorFollow) return 76 if (!actorFollow) return
78 77
79 if (created) { 78 if (created) {
80 if (isFollowingInstance) Notifier.Instance.notifyOfNewInstanceFollow(actorFollow) 79 if (isFollowingInstance) {
81 else Notifier.Instance.notifyOfNewUserFollow(actorFollow) 80 Notifier.Instance.notifyOfNewInstanceFollow(actorFollow)
81 } else {
82 const actorFollowFull = actorFollow as MActorFollowFull
83 actorFollowFull.ActorFollower.Account = await actorFollow.ActorFollower.$get('Account') as MAccount
84
85 Notifier.Instance.notifyOfNewUserFollow(actorFollowFull)
86 }
82 } 87 }
83 88
84 logger.info('Actor %s is followed by actor %s.', targetActorURL, byActor.url) 89 logger.info('Actor %s is followed by actor %s.', targetActorURL, byActor.url)
diff --git a/server/lib/activitypub/process/process-like.ts b/server/lib/activitypub/process/process-like.ts
index cf559af72..62be0de42 100644
--- a/server/lib/activitypub/process/process-like.ts
+++ b/server/lib/activitypub/process/process-like.ts
@@ -7,7 +7,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos'
7import { getVideoLikeActivityPubUrl } from '../url' 7import { getVideoLikeActivityPubUrl } from '../url'
8import { getAPId } from '../../../helpers/activitypub' 8import { getAPId } from '../../../helpers/activitypub'
9import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 9import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
10import { SignatureActorModel } from '../../../typings/models' 10import { MActorSignature } from '../../../typings/models'
11 11
12async function processLikeActivity (options: APProcessorOptions<ActivityLike>) { 12async function processLikeActivity (options: APProcessorOptions<ActivityLike>) {
13 const { activity, byActor } = options 13 const { activity, byActor } = options
@@ -22,7 +22,7 @@ export {
22 22
23// --------------------------------------------------------------------------- 23// ---------------------------------------------------------------------------
24 24
25async function processLikeVideo (byActor: SignatureActorModel, activity: ActivityLike) { 25async function processLikeVideo (byActor: MActorSignature, activity: ActivityLike) {
26 const videoUrl = getAPId(activity.object) 26 const videoUrl = getAPId(activity.object)
27 27
28 const byAccount = byActor.Account 28 const byAccount = byActor.Account
diff --git a/server/lib/activitypub/process/process-reject.ts b/server/lib/activitypub/process/process-reject.ts
index 22e311ceb..00e9afa10 100644
--- a/server/lib/activitypub/process/process-reject.ts
+++ b/server/lib/activitypub/process/process-reject.ts
@@ -2,7 +2,7 @@ import { ActivityReject } from '../../../../shared/models/activitypub/activity'
2import { sequelizeTypescript } from '../../../initializers' 2import { sequelizeTypescript } from '../../../initializers'
3import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 3import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
4import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 4import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
5import { ActorModelOnly } from '../../../typings/models' 5import { MActor } from '../../../typings/models'
6 6
7async function processRejectActivity (options: APProcessorOptions<ActivityReject>) { 7async function processRejectActivity (options: APProcessorOptions<ActivityReject>) {
8 const { byActor: targetActor, inboxActor } = options 8 const { byActor: targetActor, inboxActor } = options
@@ -19,7 +19,7 @@ export {
19 19
20// --------------------------------------------------------------------------- 20// ---------------------------------------------------------------------------
21 21
22async function processReject (follower: ActorModelOnly, targetActor: ActorModelOnly) { 22async function processReject (follower: MActor, targetActor: MActor) {
23 return sequelizeTypescript.transaction(async t => { 23 return sequelizeTypescript.transaction(async t => {
24 const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, targetActor.id, t) 24 const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, targetActor.id, t)
25 25
diff --git a/server/lib/activitypub/process/process-undo.ts b/server/lib/activitypub/process/process-undo.ts
index c37ee38bb..10643b2e9 100644
--- a/server/lib/activitypub/process/process-undo.ts
+++ b/server/lib/activitypub/process/process-undo.ts
@@ -11,7 +11,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos'
11import { VideoShareModel } from '../../../models/video/video-share' 11import { VideoShareModel } from '../../../models/video/video-share'
12import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' 12import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
13import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 13import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
14import { SignatureActorModel } from '../../../typings/models' 14import { MActorSignature } from '../../../typings/models'
15 15
16async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) { 16async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) {
17 const { activity, byActor } = options 17 const { activity, byActor } = options
@@ -54,7 +54,7 @@ export {
54 54
55// --------------------------------------------------------------------------- 55// ---------------------------------------------------------------------------
56 56
57async function processUndoLike (byActor: SignatureActorModel, activity: ActivityUndo) { 57async function processUndoLike (byActor: MActorSignature, activity: ActivityUndo) {
58 const likeActivity = activity.object as ActivityLike 58 const likeActivity = activity.object as ActivityLike
59 59
60 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: likeActivity.object }) 60 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: likeActivity.object })
@@ -77,7 +77,7 @@ async function processUndoLike (byActor: SignatureActorModel, activity: Activity
77 }) 77 })
78} 78}
79 79
80async function processUndoDislike (byActor: SignatureActorModel, activity: ActivityUndo) { 80async function processUndoDislike (byActor: MActorSignature, activity: ActivityUndo) {
81 const dislike = activity.object.type === 'Dislike' 81 const dislike = activity.object.type === 'Dislike'
82 ? activity.object 82 ? activity.object
83 : activity.object.object as DislikeObject 83 : activity.object.object as DislikeObject
@@ -102,7 +102,7 @@ async function processUndoDislike (byActor: SignatureActorModel, activity: Activ
102 }) 102 })
103} 103}
104 104
105async function processUndoCacheFile (byActor: SignatureActorModel, activity: ActivityUndo) { 105async function processUndoCacheFile (byActor: MActorSignature, activity: ActivityUndo) {
106 const cacheFileObject = activity.object.object as CacheFileObject 106 const cacheFileObject = activity.object.object as CacheFileObject
107 107
108 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFileObject.object }) 108 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFileObject.object })
@@ -127,7 +127,7 @@ async function processUndoCacheFile (byActor: SignatureActorModel, activity: Act
127 }) 127 })
128} 128}
129 129
130function processUndoFollow (follower: SignatureActorModel, followActivity: ActivityFollow) { 130function processUndoFollow (follower: MActorSignature, followActivity: ActivityFollow) {
131 return sequelizeTypescript.transaction(async t => { 131 return sequelizeTypescript.transaction(async t => {
132 const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t) 132 const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t)
133 const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t) 133 const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t)
@@ -140,7 +140,7 @@ function processUndoFollow (follower: SignatureActorModel, followActivity: Activ
140 }) 140 })
141} 141}
142 142
143function processUndoAnnounce (byActor: SignatureActorModel, announceActivity: ActivityAnnounce) { 143function processUndoAnnounce (byActor: MActorSignature, announceActivity: ActivityAnnounce) {
144 return sequelizeTypescript.transaction(async t => { 144 return sequelizeTypescript.transaction(async t => {
145 const share = await VideoShareModel.loadByUrl(announceActivity.id, t) 145 const share = await VideoShareModel.loadByUrl(announceActivity.id, t)
146 if (!share) throw new Error(`Unknown video share ${announceActivity.id}.`) 146 if (!share) throw new Error(`Unknown video share ${announceActivity.id}.`)
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts
index 414f9e375..9f80a0ce9 100644
--- a/server/lib/activitypub/process/process-update.ts
+++ b/server/lib/activitypub/process/process-update.ts
@@ -15,7 +15,7 @@ import { forwardVideoRelatedActivity } from '../send/utils'
15import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' 15import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object'
16import { createOrUpdateVideoPlaylist } from '../playlist' 16import { createOrUpdateVideoPlaylist } from '../playlist'
17import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 17import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
18import { SignatureActorModel } from '../../../typings/models' 18import { MActorSignature } from '../../../typings/models'
19 19
20async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) { 20async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) {
21 const { activity, byActor } = options 21 const { activity, byActor } = options
@@ -53,7 +53,7 @@ export {
53 53
54// --------------------------------------------------------------------------- 54// ---------------------------------------------------------------------------
55 55
56async function processUpdateVideo (actor: SignatureActorModel, activity: ActivityUpdate) { 56async function processUpdateVideo (actor: MActorSignature, activity: ActivityUpdate) {
57 const videoObject = activity.object as VideoTorrentObject 57 const videoObject = activity.object as VideoTorrentObject
58 58
59 if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) { 59 if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) {
@@ -61,20 +61,20 @@ async function processUpdateVideo (actor: SignatureActorModel, activity: Activit
61 return undefined 61 return undefined
62 } 62 }
63 63
64 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoObject.id, allowRefresh: false }) 64 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoObject.id, allowRefresh: false, fetchType: 'all' })
65 const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) 65 const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject)
66 66
67 const updateOptions = { 67 const updateOptions = {
68 video, 68 video,
69 videoObject, 69 videoObject,
70 account: actor.Account, 70 account: channelActor.VideoChannel.Account,
71 channel: channelActor.VideoChannel, 71 channel: channelActor.VideoChannel,
72 overrideTo: activity.to 72 overrideTo: activity.to
73 } 73 }
74 return updateVideoFromAP(updateOptions) 74 return updateVideoFromAP(updateOptions)
75} 75}
76 76
77async function processUpdateCacheFile (byActor: SignatureActorModel, activity: ActivityUpdate) { 77async function processUpdateCacheFile (byActor: MActorSignature, activity: ActivityUpdate) {
78 const cacheFileObject = activity.object as CacheFileObject 78 const cacheFileObject = activity.object as CacheFileObject
79 79
80 if (!isCacheFileObjectValid(cacheFileObject)) { 80 if (!isCacheFileObjectValid(cacheFileObject)) {
@@ -150,7 +150,7 @@ async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate)
150 } 150 }
151} 151}
152 152
153async function processUpdatePlaylist (byActor: SignatureActorModel, activity: ActivityUpdate) { 153async function processUpdatePlaylist (byActor: MActorSignature, activity: ActivityUpdate) {
154 const playlistObject = activity.object as PlaylistObject 154 const playlistObject = activity.object as PlaylistObject
155 const byAccount = byActor.Account 155 const byAccount = byActor.Account
156 156
diff --git a/server/lib/activitypub/process/process-view.ts b/server/lib/activitypub/process/process-view.ts
index e4997b828..df29ee968 100644
--- a/server/lib/activitypub/process/process-view.ts
+++ b/server/lib/activitypub/process/process-view.ts
@@ -3,7 +3,7 @@ import { forwardVideoRelatedActivity } from '../send/utils'
3import { Redis } from '../../redis' 3import { Redis } from '../../redis'
4import { ActivityCreate, ActivityView, ViewObject } from '../../../../shared/models/activitypub' 4import { ActivityCreate, ActivityView, ViewObject } from '../../../../shared/models/activitypub'
5import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 5import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
6import { SignatureActorModel } from '../../../typings/models' 6import { MActorSignature } from '../../../typings/models'
7 7
8async function processViewActivity (options: APProcessorOptions<ActivityCreate | ActivityView>) { 8async function processViewActivity (options: APProcessorOptions<ActivityCreate | ActivityView>) {
9 const { activity, byActor } = options 9 const { activity, byActor } = options
@@ -18,11 +18,11 @@ export {
18 18
19// --------------------------------------------------------------------------- 19// ---------------------------------------------------------------------------
20 20
21async function processCreateView (activity: ActivityView | ActivityCreate, byActor: SignatureActorModel) { 21async function processCreateView (activity: ActivityView | ActivityCreate, byActor: MActorSignature) {
22 const videoObject = activity.type === 'View' ? activity.object : (activity.object as ViewObject).object 22 const videoObject = activity.type === 'View' ? activity.object : (activity.object as ViewObject).object
23 23
24 const options = { 24 const options = {
25 videoObject: videoObject, 25 videoObject,
26 fetchType: 'only-video' as 'only-video' 26 fetchType: 'only-video' as 'only-video'
27 } 27 }
28 const { video } = await getOrCreateVideoAndAccountAndChannel(options) 28 const { video } = await getOrCreateVideoAndAccountAndChannel(options)
diff --git a/server/lib/activitypub/process/process.ts b/server/lib/activitypub/process/process.ts
index d108fe321..c602bf218 100644
--- a/server/lib/activitypub/process/process.ts
+++ b/server/lib/activitypub/process/process.ts
@@ -1,7 +1,6 @@
1import { Activity, ActivityType } from '../../../../shared/models/activitypub' 1import { Activity, ActivityType } from '../../../../shared/models/activitypub'
2import { checkUrlsSameHost, getAPId } from '../../../helpers/activitypub' 2import { checkUrlsSameHost, getAPId } from '../../../helpers/activitypub'
3import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
4import { ActorModel } from '../../../models/activitypub/actor'
5import { processAcceptActivity } from './process-accept' 4import { processAcceptActivity } from './process-accept'
6import { processAnnounceActivity } from './process-announce' 5import { processAnnounceActivity } from './process-announce'
7import { processCreateActivity } from './process-create' 6import { processCreateActivity } from './process-create'
@@ -16,7 +15,7 @@ import { processDislikeActivity } from './process-dislike'
16import { processFlagActivity } from './process-flag' 15import { processFlagActivity } from './process-flag'
17import { processViewActivity } from './process-view' 16import { processViewActivity } from './process-view'
18import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 17import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
19import { SignatureActorModel } from '../../../typings/models' 18import { MActorDefault, MActorSignature } from '../../../typings/models'
20 19
21const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions<Activity>) => Promise<any> } = { 20const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions<Activity>) => Promise<any> } = {
22 Create: processCreateActivity, 21 Create: processCreateActivity,
@@ -36,15 +35,15 @@ const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions<Act
36async function processActivities ( 35async function processActivities (
37 activities: Activity[], 36 activities: Activity[],
38 options: { 37 options: {
39 signatureActor?: SignatureActorModel 38 signatureActor?: MActorSignature
40 inboxActor?: ActorModel 39 inboxActor?: MActorDefault
41 outboxUrl?: string 40 outboxUrl?: string
42 fromFetch?: boolean 41 fromFetch?: boolean
43 } = {} 42 } = {}
44) { 43) {
45 const { outboxUrl, signatureActor, inboxActor, fromFetch = false } = options 44 const { outboxUrl, signatureActor, inboxActor, fromFetch = false } = options
46 45
47 const actorsCache: { [ url: string ]: SignatureActorModel } = {} 46 const actorsCache: { [ url: string ]: MActorSignature } = {}
48 47
49 for (const activity of activities) { 48 for (const activity of activities) {
50 if (!signatureActor && [ 'Create', 'Announce', 'Like' ].includes(activity.type) === false) { 49 if (!signatureActor && [ 'Create', 'Announce', 'Like' ].includes(activity.type) === false) {
@@ -75,7 +74,7 @@ async function processActivities (
75 } 74 }
76 75
77 try { 76 try {
78 await activityProcessor({ activity, byActor, inboxActor: inboxActor, fromFetch }) 77 await activityProcessor({ activity, byActor, inboxActor, fromFetch })
79 } catch (err) { 78 } catch (err) {
80 logger.warn('Cannot process activity %s.', activity.type, { err }) 79 logger.warn('Cannot process activity %s.', activity.type, { err })
81 } 80 }
diff --git a/server/lib/activitypub/send/send-accept.ts b/server/lib/activitypub/send/send-accept.ts
index 813c42e15..9f0225b64 100644
--- a/server/lib/activitypub/send/send-accept.ts
+++ b/server/lib/activitypub/send/send-accept.ts
@@ -3,10 +3,9 @@ import { getActorFollowAcceptActivityPubUrl, getActorFollowActivityPubUrl } from
3import { unicastTo } from './utils' 3import { unicastTo } from './utils'
4import { buildFollowActivity } from './send-follow' 4import { buildFollowActivity } from './send-follow'
5import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { ActorFollowModelLight } from '../../../typings/models/actor-follow' 6import { MActor, MActorFollowActors } from '../../../typings/models'
7import { ActorModelOnly } from '../../../typings/models'
8 7
9async function sendAccept (actorFollow: ActorFollowModelLight) { 8async function sendAccept (actorFollow: MActorFollowActors) {
10 const follower = actorFollow.ActorFollower 9 const follower = actorFollow.ActorFollower
11 const me = actorFollow.ActorFollowing 10 const me = actorFollow.ActorFollowing
12 11
@@ -34,7 +33,7 @@ export {
34 33
35// --------------------------------------------------------------------------- 34// ---------------------------------------------------------------------------
36 35
37function buildAcceptActivity (url: string, byActor: ActorModelOnly, followActivityData: ActivityFollow): ActivityAccept { 36function buildAcceptActivity (url: string, byActor: MActor, followActivityData: ActivityFollow): ActivityAccept {
38 return { 37 return {
39 type: 'Accept', 38 type: 'Accept',
40 id: url, 39 id: url,
diff --git a/server/lib/activitypub/send/send-announce.ts b/server/lib/activitypub/send/send-announce.ts
index 7fe4ca180..a0f33852c 100644
--- a/server/lib/activitypub/send/send-announce.ts
+++ b/server/lib/activitypub/send/send-announce.ts
@@ -1,16 +1,15 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub' 2import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub'
3import { VideoModel } from '../../../models/video/video'
4import { broadcastToFollowers } from './utils' 3import { broadcastToFollowers } from './utils'
5import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience' 4import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience'
6import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
7import { ActorModelOnly } from '../../../typings/models' 6import { MActorLight, MVideo } from '../../../typings/models'
8import { VideoShareModelOnly } from '../../../typings/models/video-share' 7import { MVideoShare } from '../../../typings/models/video'
9 8
10async function buildAnnounceWithVideoAudience ( 9async function buildAnnounceWithVideoAudience (
11 byActor: ActorModelOnly, 10 byActor: MActorLight,
12 videoShare: VideoShareModelOnly, 11 videoShare: MVideoShare,
13 video: VideoModel, 12 video: MVideo,
14 t: Transaction 13 t: Transaction
15) { 14) {
16 const announcedObject = video.url 15 const announcedObject = video.url
@@ -23,7 +22,7 @@ async function buildAnnounceWithVideoAudience (
23 return { activity, actorsInvolvedInVideo } 22 return { activity, actorsInvolvedInVideo }
24} 23}
25 24
26async function sendVideoAnnounce (byActor: ActorModelOnly, videoShare: VideoShareModelOnly, video: VideoModel, t: Transaction) { 25async function sendVideoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) {
27 const { activity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t) 26 const { activity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t)
28 27
29 logger.info('Creating job to send announce %s.', videoShare.url) 28 logger.info('Creating job to send announce %s.', videoShare.url)
@@ -32,7 +31,7 @@ async function sendVideoAnnounce (byActor: ActorModelOnly, videoShare: VideoShar
32 return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, t, followersException) 31 return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, t, followersException)
33} 32}
34 33
35function buildAnnounceActivity (url: string, byActor: ActorModelOnly, object: string, audience?: ActivityAudience): ActivityAnnounce { 34function buildAnnounceActivity (url: string, byActor: MActorLight, object: string, audience?: ActivityAudience): ActivityAnnounce {
36 if (!audience) audience = getAudience(byActor) 35 if (!audience) audience = getAudience(byActor)
37 36
38 return audiencify({ 37 return audiencify({
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts
index 9c21149f2..26ec3e948 100644
--- a/server/lib/activitypub/send/send-create.ts
+++ b/server/lib/activitypub/send/send-create.ts
@@ -1,19 +1,23 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub' 2import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub'
3import { VideoPrivacy } from '../../../../shared/models/videos' 3import { VideoPrivacy } from '../../../../shared/models/videos'
4import { ActorModel } from '../../../models/activitypub/actor'
5import { VideoModel } from '../../../models/video/video'
6import { VideoCommentModel } from '../../../models/video/video-comment' 4import { VideoCommentModel } from '../../../models/video/video-comment'
7import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' 5import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
8import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience' 6import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience'
9import { logger } from '../../../helpers/logger' 7import { logger } from '../../../helpers/logger'
10import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
11import { VideoPlaylistModel } from '../../../models/video/video-playlist'
12import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' 8import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
13import { getServerActor } from '../../../helpers/utils' 9import { getServerActor } from '../../../helpers/utils'
14import * as Bluebird from 'bluebird' 10import {
15 11 MActorLight,
16async function sendCreateVideo (video: VideoModel, t: Transaction) { 12 MCommentOwnerVideo,
13 MVideoAccountLight,
14 MVideoAP,
15 MVideoPlaylistFull,
16 MVideoRedundancyFileVideo,
17 MVideoRedundancyStreamingPlaylistVideo
18} from '../../../typings/models'
19
20async function sendCreateVideo (video: MVideoAP, t: Transaction) {
17 if (video.privacy === VideoPrivacy.PRIVATE) return undefined 21 if (video.privacy === VideoPrivacy.PRIVATE) return undefined
18 22
19 logger.info('Creating job to send video creation of %s.', video.url) 23 logger.info('Creating job to send video creation of %s.', video.url)
@@ -27,7 +31,11 @@ async function sendCreateVideo (video: VideoModel, t: Transaction) {
27 return broadcastToFollowers(createActivity, byActor, [ byActor ], t) 31 return broadcastToFollowers(createActivity, byActor, [ byActor ], t)
28} 32}
29 33
30async function sendCreateCacheFile (byActor: ActorModel, video: VideoModel, fileRedundancy: VideoRedundancyModel) { 34async function sendCreateCacheFile (
35 byActor: MActorLight,
36 video: MVideoAccountLight,
37 fileRedundancy: MVideoRedundancyStreamingPlaylistVideo | MVideoRedundancyFileVideo
38) {
31 logger.info('Creating job to send file cache of %s.', fileRedundancy.url) 39 logger.info('Creating job to send file cache of %s.', fileRedundancy.url)
32 40
33 return sendVideoRelatedCreateActivity({ 41 return sendVideoRelatedCreateActivity({
@@ -38,7 +46,7 @@ async function sendCreateCacheFile (byActor: ActorModel, video: VideoModel, file
38 }) 46 })
39} 47}
40 48
41async function sendCreateVideoPlaylist (playlist: VideoPlaylistModel, t: Transaction) { 49async function sendCreateVideoPlaylist (playlist: MVideoPlaylistFull, t: Transaction) {
42 if (playlist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined 50 if (playlist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined
43 51
44 logger.info('Creating job to send create video playlist of %s.', playlist.url) 52 logger.info('Creating job to send create video playlist of %s.', playlist.url)
@@ -57,7 +65,7 @@ async function sendCreateVideoPlaylist (playlist: VideoPlaylistModel, t: Transac
57 return broadcastToFollowers(createActivity, byActor, toFollowersOf, t) 65 return broadcastToFollowers(createActivity, byActor, toFollowersOf, t)
58} 66}
59 67
60async function sendCreateVideoComment (comment: VideoCommentModel, t: Transaction) { 68async function sendCreateVideoComment (comment: MCommentOwnerVideo, t: Transaction) {
61 logger.info('Creating job to send comment %s.', comment.url) 69 logger.info('Creating job to send comment %s.', comment.url)
62 70
63 const isOrigin = comment.Video.isOwned() 71 const isOrigin = comment.Video.isOwned()
@@ -95,7 +103,7 @@ async function sendCreateVideoComment (comment: VideoCommentModel, t: Transactio
95 t.afterCommit(() => unicastTo(createActivity, byActor, comment.Video.VideoChannel.Account.Actor.sharedInboxUrl)) 103 t.afterCommit(() => unicastTo(createActivity, byActor, comment.Video.VideoChannel.Account.Actor.sharedInboxUrl))
96} 104}
97 105
98function buildCreateActivity (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityCreate { 106function buildCreateActivity (url: string, byActor: MActorLight, object: any, audience?: ActivityAudience): ActivityCreate {
99 if (!audience) audience = getAudience(byActor) 107 if (!audience) audience = getAudience(byActor)
100 108
101 return audiencify( 109 return audiencify(
@@ -122,8 +130,8 @@ export {
122// --------------------------------------------------------------------------- 130// ---------------------------------------------------------------------------
123 131
124async function sendVideoRelatedCreateActivity (options: { 132async function sendVideoRelatedCreateActivity (options: {
125 byActor: ActorModel, 133 byActor: MActorLight,
126 video: VideoModel, 134 video: MVideoAccountLight,
127 url: string, 135 url: string,
128 object: any, 136 object: any,
129 transaction?: Transaction 137 transaction?: Transaction
diff --git a/server/lib/activitypub/send/send-delete.ts b/server/lib/activitypub/send/send-delete.ts
index 6c7fb8449..4b1ff8dc5 100644
--- a/server/lib/activitypub/send/send-delete.ts
+++ b/server/lib/activitypub/send/send-delete.ts
@@ -1,17 +1,17 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub' 2import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub'
3import { ActorModel } from '../../../models/activitypub/actor' 3import { ActorModel } from '../../../models/activitypub/actor'
4import { VideoModel } from '../../../models/video/video'
5import { VideoCommentModel } from '../../../models/video/video-comment' 4import { VideoCommentModel } from '../../../models/video/video-comment'
6import { VideoShareModel } from '../../../models/video/video-share' 5import { VideoShareModel } from '../../../models/video/video-share'
7import { getDeleteActivityPubUrl } from '../url' 6import { getDeleteActivityPubUrl } from '../url'
8import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' 7import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
9import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience' 8import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience'
10import { logger } from '../../../helpers/logger' 9import { logger } from '../../../helpers/logger'
11import { VideoPlaylistModel } from '../../../models/video/video-playlist'
12import { getServerActor } from '../../../helpers/utils' 10import { getServerActor } from '../../../helpers/utils'
11import { MCommentOwnerVideoReply, MVideoAccountLight, MVideoPlaylistFullSummary } from '../../../typings/models/video'
12import { MActorUrl } from '../../../typings/models'
13 13
14async function sendDeleteVideo (video: VideoModel, transaction: Transaction) { 14async function sendDeleteVideo (video: MVideoAccountLight, transaction: Transaction) {
15 logger.info('Creating job to broadcast delete of video %s.', video.url) 15 logger.info('Creating job to broadcast delete of video %s.', video.url)
16 16
17 const byActor = video.VideoChannel.Account.Actor 17 const byActor = video.VideoChannel.Account.Actor
@@ -42,7 +42,7 @@ async function sendDeleteActor (byActor: ActorModel, t: Transaction) {
42 return broadcastToFollowers(activity, byActor, actorsInvolved, t) 42 return broadcastToFollowers(activity, byActor, actorsInvolved, t)
43} 43}
44 44
45async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Transaction) { 45async function sendDeleteVideoComment (videoComment: MCommentOwnerVideoReply, t: Transaction) {
46 logger.info('Creating job to send delete of comment %s.', videoComment.url) 46 logger.info('Creating job to send delete of comment %s.', videoComment.url)
47 47
48 const isVideoOrigin = videoComment.Video.isOwned() 48 const isVideoOrigin = videoComment.Video.isOwned()
@@ -74,7 +74,7 @@ async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Trans
74 t.afterCommit(() => unicastTo(activity, byActor, videoComment.Video.VideoChannel.Account.Actor.sharedInboxUrl)) 74 t.afterCommit(() => unicastTo(activity, byActor, videoComment.Video.VideoChannel.Account.Actor.sharedInboxUrl))
75} 75}
76 76
77async function sendDeleteVideoPlaylist (videoPlaylist: VideoPlaylistModel, t: Transaction) { 77async function sendDeleteVideoPlaylist (videoPlaylist: MVideoPlaylistFullSummary, t: Transaction) {
78 logger.info('Creating job to send delete of playlist %s.', videoPlaylist.url) 78 logger.info('Creating job to send delete of playlist %s.', videoPlaylist.url)
79 79
80 const byActor = videoPlaylist.OwnerAccount.Actor 80 const byActor = videoPlaylist.OwnerAccount.Actor
@@ -101,7 +101,7 @@ export {
101 101
102// --------------------------------------------------------------------------- 102// ---------------------------------------------------------------------------
103 103
104function buildDeleteActivity (url: string, object: string, byActor: ActorModel, audience?: ActivityAudience): ActivityDelete { 104function buildDeleteActivity (url: string, object: string, byActor: MActorUrl, audience?: ActivityAudience): ActivityDelete {
105 const activity = { 105 const activity = {
106 type: 'Delete' as 'Delete', 106 type: 'Delete' as 'Delete',
107 id: url, 107 id: url,
diff --git a/server/lib/activitypub/send/send-dislike.ts b/server/lib/activitypub/send/send-dislike.ts
index a88436f2c..6e41f241f 100644
--- a/server/lib/activitypub/send/send-dislike.ts
+++ b/server/lib/activitypub/send/send-dislike.ts
@@ -1,13 +1,12 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActorModel } from '../../../models/activitypub/actor'
3import { VideoModel } from '../../../models/video/video'
4import { getVideoDislikeActivityPubUrl } from '../url' 2import { getVideoDislikeActivityPubUrl } from '../url'
5import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
6import { ActivityAudience, ActivityDislike } from '../../../../shared/models/activitypub' 4import { ActivityAudience, ActivityDislike } from '../../../../shared/models/activitypub'
7import { sendVideoRelatedActivity } from './utils' 5import { sendVideoRelatedActivity } from './utils'
8import { audiencify, getAudience } from '../audience' 6import { audiencify, getAudience } from '../audience'
7import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../typings/models'
9 8
10async function sendDislike (byActor: ActorModel, video: VideoModel, t: Transaction) { 9async function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
11 logger.info('Creating job to dislike %s.', video.url) 10 logger.info('Creating job to dislike %s.', video.url)
12 11
13 const activityBuilder = (audience: ActivityAudience) => { 12 const activityBuilder = (audience: ActivityAudience) => {
@@ -19,7 +18,7 @@ async function sendDislike (byActor: ActorModel, video: VideoModel, t: Transacti
19 return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) 18 return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t })
20} 19}
21 20
22function buildDislikeActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityDislike { 21function buildDislikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityDislike {
23 if (!audience) audience = getAudience(byActor) 22 if (!audience) audience = getAudience(byActor)
24 23
25 return audiencify( 24 return audiencify(
diff --git a/server/lib/activitypub/send/send-flag.ts b/server/lib/activitypub/send/send-flag.ts
index 61ee389a6..5ae1614ab 100644
--- a/server/lib/activitypub/send/send-flag.ts
+++ b/server/lib/activitypub/send/send-flag.ts
@@ -1,14 +1,13 @@
1import { ActorModel } from '../../../models/activitypub/actor'
2import { VideoModel } from '../../../models/video/video'
3import { VideoAbuseModel } from '../../../models/video/video-abuse'
4import { getVideoAbuseActivityPubUrl } from '../url' 1import { getVideoAbuseActivityPubUrl } from '../url'
5import { unicastTo } from './utils' 2import { unicastTo } from './utils'
6import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
7import { ActivityAudience, ActivityFlag } from '../../../../shared/models/activitypub' 4import { ActivityAudience, ActivityFlag } from '../../../../shared/models/activitypub'
8import { audiencify, getAudience } from '../audience' 5import { audiencify, getAudience } from '../audience'
9import { Transaction } from 'sequelize' 6import { Transaction } from 'sequelize'
7import { MActor, MVideoFullLight } from '../../../typings/models'
8import { MVideoAbuseVideo } from '../../../typings/models/video'
10 9
11async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel, video: VideoModel, t: Transaction) { 10async function sendVideoAbuse (byActor: MActor, videoAbuse: MVideoAbuseVideo, video: MVideoFullLight, t: Transaction) {
12 if (!video.VideoChannel.Account.Actor.serverId) return // Local user 11 if (!video.VideoChannel.Account.Actor.serverId) return // Local user
13 12
14 const url = getVideoAbuseActivityPubUrl(videoAbuse) 13 const url = getVideoAbuseActivityPubUrl(videoAbuse)
@@ -22,7 +21,7 @@ async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel,
22 t.afterCommit(() => unicastTo(flagActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)) 21 t.afterCommit(() => unicastTo(flagActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl))
23} 22}
24 23
25function buildFlagActivity (url: string, byActor: ActorModel, videoAbuse: VideoAbuseModel, audience: ActivityAudience): ActivityFlag { 24function buildFlagActivity (url: string, byActor: MActor, videoAbuse: MVideoAbuseVideo, audience: ActivityAudience): ActivityFlag {
26 if (!audience) audience = getAudience(byActor) 25 if (!audience) audience = getAudience(byActor)
27 26
28 const activity = Object.assign( 27 const activity = Object.assign(
diff --git a/server/lib/activitypub/send/send-follow.ts b/server/lib/activitypub/send/send-follow.ts
index a59ed50cf..6b17b25da 100644
--- a/server/lib/activitypub/send/send-follow.ts
+++ b/server/lib/activitypub/send/send-follow.ts
@@ -4,9 +4,9 @@ import { getActorFollowActivityPubUrl } from '../url'
4import { unicastTo } from './utils' 4import { unicastTo } from './utils'
5import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { Transaction } from 'sequelize' 6import { Transaction } from 'sequelize'
7import { ActorModelOnly } from '../../../typings/models' 7import { MActor, MActorFollowActors } from '../../../typings/models'
8 8
9function sendFollow (actorFollow: ActorFollowModel, t: Transaction) { 9function sendFollow (actorFollow: MActorFollowActors, t: Transaction) {
10 const me = actorFollow.ActorFollower 10 const me = actorFollow.ActorFollower
11 const following = actorFollow.ActorFollowing 11 const following = actorFollow.ActorFollowing
12 12
@@ -21,7 +21,7 @@ function sendFollow (actorFollow: ActorFollowModel, t: Transaction) {
21 t.afterCommit(() => unicastTo(data, me, following.inboxUrl)) 21 t.afterCommit(() => unicastTo(data, me, following.inboxUrl))
22} 22}
23 23
24function buildFollowActivity (url: string, byActor: ActorModelOnly, targetActor: ActorModelOnly): ActivityFollow { 24function buildFollowActivity (url: string, byActor: MActor, targetActor: MActor): ActivityFollow {
25 return { 25 return {
26 type: 'Follow', 26 type: 'Follow',
27 id: url, 27 id: url,
diff --git a/server/lib/activitypub/send/send-like.ts b/server/lib/activitypub/send/send-like.ts
index 35227887a..e84a6f98b 100644
--- a/server/lib/activitypub/send/send-like.ts
+++ b/server/lib/activitypub/send/send-like.ts
@@ -1,13 +1,12 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub' 2import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub'
3import { ActorModel } from '../../../models/activitypub/actor'
4import { VideoModel } from '../../../models/video/video'
5import { getVideoLikeActivityPubUrl } from '../url' 3import { getVideoLikeActivityPubUrl } from '../url'
6import { sendVideoRelatedActivity } from './utils' 4import { sendVideoRelatedActivity } from './utils'
7import { audiencify, getAudience } from '../audience' 5import { audiencify, getAudience } from '../audience'
8import { logger } from '../../../helpers/logger' 6import { logger } from '../../../helpers/logger'
7import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../typings/models'
9 8
10async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction) { 9async function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
11 logger.info('Creating job to like %s.', video.url) 10 logger.info('Creating job to like %s.', video.url)
12 11
13 const activityBuilder = (audience: ActivityAudience) => { 12 const activityBuilder = (audience: ActivityAudience) => {
@@ -19,7 +18,7 @@ async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction)
19 return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) 18 return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t })
20} 19}
21 20
22function buildLikeActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityLike { 21function buildLikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityLike {
23 if (!audience) audience = getAudience(byActor) 22 if (!audience) audience = getAudience(byActor)
24 23
25 return audiencify( 24 return audiencify(
diff --git a/server/lib/activitypub/send/send-reject.ts b/server/lib/activitypub/send/send-reject.ts
index 63110b433..4258a3c36 100644
--- a/server/lib/activitypub/send/send-reject.ts
+++ b/server/lib/activitypub/send/send-reject.ts
@@ -1,12 +1,11 @@
1import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub' 1import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub'
2import { ActorModel } from '../../../models/activitypub/actor'
3import { getActorFollowActivityPubUrl, getActorFollowRejectActivityPubUrl } from '../url' 2import { getActorFollowActivityPubUrl, getActorFollowRejectActivityPubUrl } from '../url'
4import { unicastTo } from './utils' 3import { unicastTo } from './utils'
5import { buildFollowActivity } from './send-follow' 4import { buildFollowActivity } from './send-follow'
6import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
7import { SignatureActorModel } from '../../../typings/models' 6import { MActor } from '../../../typings/models'
8 7
9async function sendReject (follower: SignatureActorModel, following: ActorModel) { 8async function sendReject (follower: MActor, following: MActor) {
10 if (!follower.serverId) { // This should never happen 9 if (!follower.serverId) { // This should never happen
11 logger.warn('Do not sending reject to local follower.') 10 logger.warn('Do not sending reject to local follower.')
12 return 11 return
@@ -31,7 +30,7 @@ export {
31 30
32// --------------------------------------------------------------------------- 31// ---------------------------------------------------------------------------
33 32
34function buildRejectActivity (url: string, byActor: ActorModel, followActivityData: ActivityFollow): ActivityReject { 33function buildRejectActivity (url: string, byActor: MActor, followActivityData: ActivityFollow): ActivityReject {
35 return { 34 return {
36 type: 'Reject', 35 type: 'Reject',
37 id: url, 36 id: url,
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts
index 8fcbbac5c..e9ab5b3c5 100644
--- a/server/lib/activitypub/send/send-undo.ts
+++ b/server/lib/activitypub/send/send-undo.ts
@@ -2,13 +2,12 @@ import { Transaction } from 'sequelize'
2import { 2import {
3 ActivityAnnounce, 3 ActivityAnnounce,
4 ActivityAudience, 4 ActivityAudience,
5 ActivityCreate, ActivityDislike, 5 ActivityCreate,
6 ActivityDislike,
6 ActivityFollow, 7 ActivityFollow,
7 ActivityLike, 8 ActivityLike,
8 ActivityUndo 9 ActivityUndo
9} from '../../../../shared/models/activitypub' 10} from '../../../../shared/models/activitypub'
10import { ActorModel } from '../../../models/activitypub/actor'
11import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
12import { VideoModel } from '../../../models/video/video' 11import { VideoModel } from '../../../models/video/video'
13import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url' 12import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url'
14import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' 13import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
@@ -16,13 +15,20 @@ import { audiencify, getAudience } from '../audience'
16import { buildCreateActivity } from './send-create' 15import { buildCreateActivity } from './send-create'
17import { buildFollowActivity } from './send-follow' 16import { buildFollowActivity } from './send-follow'
18import { buildLikeActivity } from './send-like' 17import { buildLikeActivity } from './send-like'
19import { VideoShareModel } from '../../../models/video/video-share'
20import { buildAnnounceWithVideoAudience } from './send-announce' 18import { buildAnnounceWithVideoAudience } from './send-announce'
21import { logger } from '../../../helpers/logger' 19import { logger } from '../../../helpers/logger'
22import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
23import { buildDislikeActivity } from './send-dislike' 20import { buildDislikeActivity } from './send-dislike'
24 21import {
25async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) { 22 MActor, MActorAudience,
23 MActorFollowActors,
24 MActorLight,
25 MVideo,
26 MVideoAccountLight,
27 MVideoRedundancyVideo,
28 MVideoShare
29} from '../../../typings/models'
30
31async function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) {
26 const me = actorFollow.ActorFollower 32 const me = actorFollow.ActorFollower
27 const following = actorFollow.ActorFollowing 33 const following = actorFollow.ActorFollowing
28 34
@@ -40,7 +46,7 @@ async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) {
40 t.afterCommit(() => unicastTo(undoActivity, me, following.inboxUrl)) 46 t.afterCommit(() => unicastTo(undoActivity, me, following.inboxUrl))
41} 47}
42 48
43async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) { 49async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) {
44 logger.info('Creating job to undo announce %s.', videoShare.url) 50 logger.info('Creating job to undo announce %s.', videoShare.url)
45 51
46 const undoUrl = getUndoActivityPubUrl(videoShare.url) 52 const undoUrl = getUndoActivityPubUrl(videoShare.url)
@@ -52,7 +58,7 @@ async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareMode
52 return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException) 58 return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException)
53} 59}
54 60
55async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transaction) { 61async function sendUndoLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
56 logger.info('Creating job to undo a like of video %s.', video.url) 62 logger.info('Creating job to undo a like of video %s.', video.url)
57 63
58 const likeUrl = getVideoLikeActivityPubUrl(byActor, video) 64 const likeUrl = getVideoLikeActivityPubUrl(byActor, video)
@@ -61,7 +67,7 @@ async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transact
61 return sendUndoVideoRelatedActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t }) 67 return sendUndoVideoRelatedActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t })
62} 68}
63 69
64async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Transaction) { 70async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
65 logger.info('Creating job to undo a dislike of video %s.', video.url) 71 logger.info('Creating job to undo a dislike of video %s.', video.url)
66 72
67 const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video) 73 const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video)
@@ -70,7 +76,7 @@ async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Trans
70 return sendUndoVideoRelatedActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t }) 76 return sendUndoVideoRelatedActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t })
71} 77}
72 78
73async function sendUndoCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel, t: Transaction) { 79async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedundancyVideo, t: Transaction) {
74 logger.info('Creating job to undo cache file %s.', redundancyModel.url) 80 logger.info('Creating job to undo cache file %s.', redundancyModel.url)
75 81
76 const videoId = redundancyModel.getVideo().id 82 const videoId = redundancyModel.getVideo().id
@@ -94,7 +100,7 @@ export {
94 100
95function undoActivityData ( 101function undoActivityData (
96 url: string, 102 url: string,
97 byActor: ActorModel, 103 byActor: MActorAudience,
98 object: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce, 104 object: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce,
99 audience?: ActivityAudience 105 audience?: ActivityAudience
100): ActivityUndo { 106): ActivityUndo {
@@ -112,8 +118,8 @@ function undoActivityData (
112} 118}
113 119
114async function sendUndoVideoRelatedActivity (options: { 120async function sendUndoVideoRelatedActivity (options: {
115 byActor: ActorModel, 121 byActor: MActor,
116 video: VideoModel, 122 video: MVideoAccountLight,
117 url: string, 123 url: string,
118 activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce, 124 activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce,
119 transaction: Transaction 125 transaction: Transaction
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts
index 5bf092894..3a5cc1853 100644
--- a/server/lib/activitypub/send/send-update.ts
+++ b/server/lib/activitypub/send/send-update.ts
@@ -2,21 +2,29 @@ import { Transaction } from 'sequelize'
2import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub' 2import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub'
3import { VideoPrivacy } from '../../../../shared/models/videos' 3import { VideoPrivacy } from '../../../../shared/models/videos'
4import { AccountModel } from '../../../models/account/account' 4import { AccountModel } from '../../../models/account/account'
5import { ActorModel } from '../../../models/activitypub/actor'
6import { VideoModel } from '../../../models/video/video' 5import { VideoModel } from '../../../models/video/video'
7import { VideoChannelModel } from '../../../models/video/video-channel'
8import { VideoShareModel } from '../../../models/video/video-share' 6import { VideoShareModel } from '../../../models/video/video-share'
9import { getUpdateActivityPubUrl } from '../url' 7import { getUpdateActivityPubUrl } from '../url'
10import { broadcastToFollowers, sendVideoRelatedActivity } from './utils' 8import { broadcastToFollowers, sendVideoRelatedActivity } from './utils'
11import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience' 9import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience'
12import { logger } from '../../../helpers/logger' 10import { logger } from '../../../helpers/logger'
13import { VideoCaptionModel } from '../../../models/video/video-caption' 11import { VideoCaptionModel } from '../../../models/video/video-caption'
14import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
15import { VideoPlaylistModel } from '../../../models/video/video-playlist'
16import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' 12import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
17import { getServerActor } from '../../../helpers/utils' 13import { getServerActor } from '../../../helpers/utils'
14import {
15 MAccountActor,
16 MActor,
17 MActorLight,
18 MChannelActor,
19 MVideoAP,
20 MVideoAPWithoutCaption,
21 MVideoPlaylistFull,
22 MVideoRedundancyVideo
23} from '../../../typings/models'
24
25async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, t: Transaction, overrodeByActor?: MActor) {
26 const video = videoArg as MVideoAP
18 27
19async function sendUpdateVideo (video: VideoModel, t: Transaction, overrodeByActor?: ActorModel) {
20 if (video.privacy === VideoPrivacy.PRIVATE) return undefined 28 if (video.privacy === VideoPrivacy.PRIVATE) return undefined
21 29
22 logger.info('Creating job to update video %s.', video.url) 30 logger.info('Creating job to update video %s.', video.url)
@@ -41,7 +49,7 @@ async function sendUpdateVideo (video: VideoModel, t: Transaction, overrodeByAct
41 return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t) 49 return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t)
42} 50}
43 51
44async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelModel, t: Transaction) { 52async function sendUpdateActor (accountOrChannel: MAccountActor | MChannelActor, t: Transaction) {
45 const byActor = accountOrChannel.Actor 53 const byActor = accountOrChannel.Actor
46 54
47 logger.info('Creating job to update actor %s.', byActor.url) 55 logger.info('Creating job to update actor %s.', byActor.url)
@@ -51,7 +59,7 @@ async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelMod
51 const audience = getAudience(byActor) 59 const audience = getAudience(byActor)
52 const updateActivity = buildUpdateActivity(url, byActor, accountOrChannelObject, audience) 60 const updateActivity = buildUpdateActivity(url, byActor, accountOrChannelObject, audience)
53 61
54 let actorsInvolved: ActorModel[] 62 let actorsInvolved: MActor[]
55 if (accountOrChannel instanceof AccountModel) { 63 if (accountOrChannel instanceof AccountModel) {
56 // Actors that shared my videos are involved too 64 // Actors that shared my videos are involved too
57 actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, t) 65 actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, t)
@@ -65,7 +73,7 @@ async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelMod
65 return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t) 73 return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t)
66} 74}
67 75
68async function sendUpdateCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel) { 76async function sendUpdateCacheFile (byActor: MActorLight, redundancyModel: MVideoRedundancyVideo) {
69 logger.info('Creating job to update cache file %s.', redundancyModel.url) 77 logger.info('Creating job to update cache file %s.', redundancyModel.url)
70 78
71 const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.getVideo().id) 79 const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.getVideo().id)
@@ -80,7 +88,7 @@ async function sendUpdateCacheFile (byActor: ActorModel, redundancyModel: VideoR
80 return sendVideoRelatedActivity(activityBuilder, { byActor, video }) 88 return sendVideoRelatedActivity(activityBuilder, { byActor, video })
81} 89}
82 90
83async function sendUpdateVideoPlaylist (videoPlaylist: VideoPlaylistModel, t: Transaction) { 91async function sendUpdateVideoPlaylist (videoPlaylist: MVideoPlaylistFull, t: Transaction) {
84 if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined 92 if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined
85 93
86 const byActor = videoPlaylist.OwnerAccount.Actor 94 const byActor = videoPlaylist.OwnerAccount.Actor
@@ -113,7 +121,7 @@ export {
113 121
114// --------------------------------------------------------------------------- 122// ---------------------------------------------------------------------------
115 123
116function buildUpdateActivity (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityUpdate { 124function buildUpdateActivity (url: string, byActor: MActorLight, object: any, audience?: ActivityAudience): ActivityUpdate {
117 if (!audience) audience = getAudience(byActor) 125 if (!audience) audience = getAudience(byActor)
118 126
119 return audiencify( 127 return audiencify(
@@ -121,8 +129,7 @@ function buildUpdateActivity (url: string, byActor: ActorModel, object: any, aud
121 type: 'Update' as 'Update', 129 type: 'Update' as 'Update',
122 id: url, 130 id: url,
123 actor: byActor.url, 131 actor: byActor.url,
124 object: audiencify(object, audience 132 object: audiencify(object, audience)
125 )
126 }, 133 },
127 audience 134 audience
128 ) 135 )
diff --git a/server/lib/activitypub/send/send-view.ts b/server/lib/activitypub/send/send-view.ts
index 8ad126be0..8809417f9 100644
--- a/server/lib/activitypub/send/send-view.ts
+++ b/server/lib/activitypub/send/send-view.ts
@@ -1,13 +1,13 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub' 2import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub'
3import { ActorModel } from '../../../models/activitypub/actor' 3import { ActorModel } from '../../../models/activitypub/actor'
4import { VideoModel } from '../../../models/video/video'
5import { getVideoLikeActivityPubUrl } from '../url' 4import { getVideoLikeActivityPubUrl } from '../url'
6import { sendVideoRelatedActivity } from './utils' 5import { sendVideoRelatedActivity } from './utils'
7import { audiencify, getAudience } from '../audience' 6import { audiencify, getAudience } from '../audience'
8import { logger } from '../../../helpers/logger' 7import { logger } from '../../../helpers/logger'
8import { MActorAudience, MVideoAccountLight, MVideoUrl } from '@server/typings/models'
9 9
10async function sendView (byActor: ActorModel, video: VideoModel, t: Transaction) { 10async function sendView (byActor: ActorModel, video: MVideoAccountLight, t: Transaction) {
11 logger.info('Creating job to send view of %s.', video.url) 11 logger.info('Creating job to send view of %s.', video.url)
12 12
13 const activityBuilder = (audience: ActivityAudience) => { 13 const activityBuilder = (audience: ActivityAudience) => {
@@ -19,7 +19,7 @@ async function sendView (byActor: ActorModel, video: VideoModel, t: Transaction)
19 return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) 19 return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t })
20} 20}
21 21
22function buildViewActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityView { 22function buildViewActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityView {
23 if (!audience) audience = getAudience(byActor) 23 if (!audience) audience = getAudience(byActor)
24 24
25 return audiencify( 25 return audiencify(
diff --git a/server/lib/activitypub/send/utils.ts b/server/lib/activitypub/send/utils.ts
index 4f69afb00..8129ab32a 100644
--- a/server/lib/activitypub/send/utils.ts
+++ b/server/lib/activitypub/send/utils.ts
@@ -4,15 +4,14 @@ import { logger } from '../../../helpers/logger'
4import { ActorModel } from '../../../models/activitypub/actor' 4import { ActorModel } from '../../../models/activitypub/actor'
5import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 5import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
6import { JobQueue } from '../../job-queue' 6import { JobQueue } from '../../job-queue'
7import { VideoModel } from '../../../models/video/video'
8import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience' 7import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience'
9import { getServerActor } from '../../../helpers/utils' 8import { getServerActor } from '../../../helpers/utils'
10import { afterCommitIfTransaction } from '../../../helpers/database-utils' 9import { afterCommitIfTransaction } from '../../../helpers/database-utils'
11import { ActorFollowerException, ActorModelId, ActorModelOnly } from '../../../typings/models' 10import { MActorFollowerException, MActor, MActorId, MActorLight, MVideo, MVideoAccountLight } from '../../../typings/models'
12 11
13async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { 12async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: {
14 byActor: ActorModelOnly, 13 byActor: MActorLight,
15 video: VideoModel, 14 video: MVideoAccountLight,
16 transaction?: Transaction 15 transaction?: Transaction
17}) { 16}) {
18 const { byActor, video, transaction } = options 17 const { byActor, video, transaction } = options
@@ -41,8 +40,8 @@ async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAud
41async function forwardVideoRelatedActivity ( 40async function forwardVideoRelatedActivity (
42 activity: Activity, 41 activity: Activity,
43 t: Transaction, 42 t: Transaction,
44 followersException: ActorFollowerException[] = [], 43 followersException: MActorFollowerException[] = [],
45 video: VideoModel 44 video: MVideo
46) { 45) {
47 // Mastodon does not add our announces in audience, so we forward to them manually 46 // Mastodon does not add our announces in audience, so we forward to them manually
48 const additionalActors = await getActorsInvolvedInVideo(video, t) 47 const additionalActors = await getActorsInvolvedInVideo(video, t)
@@ -54,7 +53,7 @@ async function forwardVideoRelatedActivity (
54async function forwardActivity ( 53async function forwardActivity (
55 activity: Activity, 54 activity: Activity,
56 t: Transaction, 55 t: Transaction,
57 followersException: ActorFollowerException[] = [], 56 followersException: MActorFollowerException[] = [],
58 additionalFollowerUrls: string[] = [] 57 additionalFollowerUrls: string[] = []
59) { 58) {
60 logger.info('Forwarding activity %s.', activity.id) 59 logger.info('Forwarding activity %s.', activity.id)
@@ -88,10 +87,10 @@ async function forwardActivity (
88 87
89async function broadcastToFollowers ( 88async function broadcastToFollowers (
90 data: any, 89 data: any,
91 byActor: ActorModelId, 90 byActor: MActorId,
92 toFollowersOf: ActorModelId[], 91 toFollowersOf: MActorId[],
93 t: Transaction, 92 t: Transaction,
94 actorsException: ActorFollowerException[] = [] 93 actorsException: MActorFollowerException[] = []
95) { 94) {
96 const uris = await computeFollowerUris(toFollowersOf, actorsException, t) 95 const uris = await computeFollowerUris(toFollowersOf, actorsException, t)
97 96
@@ -100,16 +99,16 @@ async function broadcastToFollowers (
100 99
101async function broadcastToActors ( 100async function broadcastToActors (
102 data: any, 101 data: any,
103 byActor: ActorModelId, 102 byActor: MActorId,
104 toActors: ActorModelOnly[], 103 toActors: MActor[],
105 t?: Transaction, 104 t?: Transaction,
106 actorsException: ActorFollowerException[] = [] 105 actorsException: MActorFollowerException[] = []
107) { 106) {
108 const uris = await computeUris(toActors, actorsException) 107 const uris = await computeUris(toActors, actorsException)
109 return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor)) 108 return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor))
110} 109}
111 110
112function broadcastTo (uris: string[], data: any, byActor: ActorModelId) { 111function broadcastTo (uris: string[], data: any, byActor: MActorId) {
113 if (uris.length === 0) return undefined 112 if (uris.length === 0) return undefined
114 113
115 logger.debug('Creating broadcast job.', { uris }) 114 logger.debug('Creating broadcast job.', { uris })
@@ -123,7 +122,7 @@ function broadcastTo (uris: string[], data: any, byActor: ActorModelId) {
123 return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload }) 122 return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })
124} 123}
125 124
126function unicastTo (data: any, byActor: ActorModelId, toActorUrl: string) { 125function unicastTo (data: any, byActor: MActorId, toActorUrl: string) {
127 logger.debug('Creating unicast job.', { uri: toActorUrl }) 126 logger.debug('Creating unicast job.', { uri: toActorUrl })
128 127
129 const payload = { 128 const payload = {
@@ -148,7 +147,7 @@ export {
148 147
149// --------------------------------------------------------------------------- 148// ---------------------------------------------------------------------------
150 149
151async function computeFollowerUris (toFollowersOf: ActorModelId[], actorsException: ActorFollowerException[], t: Transaction) { 150async function computeFollowerUris (toFollowersOf: MActorId[], actorsException: MActorFollowerException[], t: Transaction) {
152 const toActorFollowerIds = toFollowersOf.map(a => a.id) 151 const toActorFollowerIds = toFollowersOf.map(a => a.id)
153 152
154 const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t) 153 const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t)
@@ -157,7 +156,7 @@ async function computeFollowerUris (toFollowersOf: ActorModelId[], actorsExcepti
157 return result.data.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1) 156 return result.data.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
158} 157}
159 158
160async function computeUris (toActors: ActorModelOnly[], actorsException: ActorFollowerException[] = []) { 159async function computeUris (toActors: MActor[], actorsException: MActorFollowerException[] = []) {
161 const serverActor = await getServerActor() 160 const serverActor = await getServerActor()
162 const targetUrls = toActors 161 const targetUrls = toActors
163 .filter(a => a.id !== serverActor.id) // Don't send to ourselves 162 .filter(a => a.id !== serverActor.id) // Don't send to ourselves
@@ -170,7 +169,7 @@ async function computeUris (toActors: ActorModelOnly[], actorsException: ActorFo
170 .filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1) 169 .filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
171} 170}
172 171
173async function buildSharedInboxesException (actorsException: ActorFollowerException[]) { 172async function buildSharedInboxesException (actorsException: MActorFollowerException[]) {
174 const serverActor = await getServerActor() 173 const serverActor = await getServerActor()
175 174
176 return actorsException 175 return actorsException
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts
index 7f38402b6..fdca9bed7 100644
--- a/server/lib/activitypub/share.ts
+++ b/server/lib/activitypub/share.ts
@@ -1,19 +1,18 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { VideoPrivacy } from '../../../shared/models/videos' 2import { VideoPrivacy } from '../../../shared/models/videos'
3import { getServerActor } from '../../helpers/utils' 3import { getServerActor } from '../../helpers/utils'
4import { VideoModel } from '../../models/video/video'
5import { VideoShareModel } from '../../models/video/video-share' 4import { VideoShareModel } from '../../models/video/video-share'
6import { sendUndoAnnounce, sendVideoAnnounce } from './send' 5import { sendUndoAnnounce, sendVideoAnnounce } from './send'
7import { getVideoAnnounceActivityPubUrl } from './url' 6import { getVideoAnnounceActivityPubUrl } from './url'
8import { VideoChannelModel } from '../../models/video/video-channel'
9import * as Bluebird from 'bluebird' 7import * as Bluebird from 'bluebird'
10import { doRequest } from '../../helpers/requests' 8import { doRequest } from '../../helpers/requests'
11import { getOrCreateActorAndServerAndModel } from './actor' 9import { getOrCreateActorAndServerAndModel } from './actor'
12import { logger } from '../../helpers/logger' 10import { logger } from '../../helpers/logger'
13import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' 11import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
14import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' 12import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
13import { MChannelActor, MChannelActorLight, MVideo, MVideoAccountLight, MVideoId } from '../../typings/models/video'
15 14
16async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) { 15async function shareVideoByServerAndChannel (video: MVideoAccountLight, t: Transaction) {
17 if (video.privacy === VideoPrivacy.PRIVATE) return undefined 16 if (video.privacy === VideoPrivacy.PRIVATE) return undefined
18 17
19 return Promise.all([ 18 return Promise.all([
@@ -22,7 +21,11 @@ async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction)
22 ]) 21 ])
23} 22}
24 23
25async function changeVideoChannelShare (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) { 24async function changeVideoChannelShare (
25 video: MVideoAccountLight,
26 oldVideoChannel: MChannelActorLight,
27 t: Transaction
28) {
26 logger.info('Updating video channel of video %s: %s -> %s.', video.uuid, oldVideoChannel.name, video.VideoChannel.name) 29 logger.info('Updating video channel of video %s: %s -> %s.', video.uuid, oldVideoChannel.name, video.VideoChannel.name)
27 30
28 await undoShareByVideoChannel(video, oldVideoChannel, t) 31 await undoShareByVideoChannel(video, oldVideoChannel, t)
@@ -30,7 +33,7 @@ async function changeVideoChannelShare (video: VideoModel, oldVideoChannel: Vide
30 await shareByVideoChannel(video, t) 33 await shareByVideoChannel(video, t)
31} 34}
32 35
33async function addVideoShares (shareUrls: string[], instance: VideoModel) { 36async function addVideoShares (shareUrls: string[], video: MVideoId) {
34 await Bluebird.map(shareUrls, async shareUrl => { 37 await Bluebird.map(shareUrls, async shareUrl => {
35 try { 38 try {
36 // Fetch url 39 // Fetch url
@@ -50,7 +53,7 @@ async function addVideoShares (shareUrls: string[], instance: VideoModel) {
50 53
51 const entry = { 54 const entry = {
52 actorId: actor.id, 55 actorId: actor.id,
53 videoId: instance.id, 56 videoId: video.id,
54 url: shareUrl 57 url: shareUrl
55 } 58 }
56 59
@@ -69,7 +72,7 @@ export {
69 72
70// --------------------------------------------------------------------------- 73// ---------------------------------------------------------------------------
71 74
72async function shareByServer (video: VideoModel, t: Transaction) { 75async function shareByServer (video: MVideo, t: Transaction) {
73 const serverActor = await getServerActor() 76 const serverActor = await getServerActor()
74 77
75 const serverShareUrl = getVideoAnnounceActivityPubUrl(serverActor, video) 78 const serverShareUrl = getVideoAnnounceActivityPubUrl(serverActor, video)
@@ -88,7 +91,7 @@ async function shareByServer (video: VideoModel, t: Transaction) {
88 return sendVideoAnnounce(serverActor, serverShare, video, t) 91 return sendVideoAnnounce(serverActor, serverShare, video, t)
89} 92}
90 93
91async function shareByVideoChannel (video: VideoModel, t: Transaction) { 94async function shareByVideoChannel (video: MVideoAccountLight, t: Transaction) {
92 const videoChannelShareUrl = getVideoAnnounceActivityPubUrl(video.VideoChannel.Actor, video) 95 const videoChannelShareUrl = getVideoAnnounceActivityPubUrl(video.VideoChannel.Actor, video)
93 const [ videoChannelShare ] = await VideoShareModel.findOrCreate({ 96 const [ videoChannelShare ] = await VideoShareModel.findOrCreate({
94 defaults: { 97 defaults: {
@@ -105,7 +108,7 @@ async function shareByVideoChannel (video: VideoModel, t: Transaction) {
105 return sendVideoAnnounce(video.VideoChannel.Actor, videoChannelShare, video, t) 108 return sendVideoAnnounce(video.VideoChannel.Actor, videoChannelShare, video, t)
106} 109}
107 110
108async function undoShareByVideoChannel (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) { 111async function undoShareByVideoChannel (video: MVideo, oldVideoChannel: MChannelActorLight, t: Transaction) {
109 // Load old share 112 // Load old share
110 const oldShare = await VideoShareModel.load(oldVideoChannel.actorId, video.id, t) 113 const oldShare = await VideoShareModel.load(oldVideoChannel.actorId, video.id, t)
111 if (!oldShare) return new Error('Cannot find old video channel share ' + oldVideoChannel.actorId + ' for video ' + video.id) 114 if (!oldShare) return new Error('Cannot find old video channel share ' + oldVideoChannel.actorId + ' for video ' + video.id)
diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts
index dfcb3c668..6290af34b 100644
--- a/server/lib/activitypub/url.ts
+++ b/server/lib/activitypub/url.ts
@@ -1,36 +1,42 @@
1import { WEBSERVER } from '../../initializers/constants' 1import { WEBSERVER } from '../../initializers/constants'
2import { VideoModel } from '../../models/video/video' 2import {
3import { VideoAbuseModel } from '../../models/video/video-abuse' 3 MActor,
4import { VideoCommentModel } from '../../models/video/video-comment' 4 MActorFollowActors,
5import { VideoFileModel } from '../../models/video/video-file' 5 MActorId,
6import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' 6 MActorUrl,
7import { VideoPlaylistModel } from '../../models/video/video-playlist' 7 MCommentId,
8import { ActorModelOnly, ActorModelUrl } from '../../typings/models' 8 MVideoAbuseId,
9import { ActorFollowModelLight } from '../../typings/models/actor-follow' 9 MVideoId,
10 10 MVideoUrl,
11function getVideoActivityPubUrl (video: VideoModel) { 11 MVideoUUID
12} from '../../typings/models'
13import { MVideoPlaylist, MVideoPlaylistUUID } from '../../typings/models/video/video-playlist'
14import { MVideoFileVideoUUID } from '../../typings/models/video/video-file'
15import { MStreamingPlaylist } from '../../typings/models/video/video-streaming-playlist'
16
17function getVideoActivityPubUrl (video: MVideoUUID) {
12 return WEBSERVER.URL + '/videos/watch/' + video.uuid 18 return WEBSERVER.URL + '/videos/watch/' + video.uuid
13} 19}
14 20
15function getVideoPlaylistActivityPubUrl (videoPlaylist: VideoPlaylistModel) { 21function getVideoPlaylistActivityPubUrl (videoPlaylist: MVideoPlaylist) {
16 return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid 22 return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid
17} 23}
18 24
19function getVideoPlaylistElementActivityPubUrl (videoPlaylist: VideoPlaylistModel, video: VideoModel) { 25function getVideoPlaylistElementActivityPubUrl (videoPlaylist: MVideoPlaylistUUID, video: MVideoUUID) {
20 return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid + '/' + video.uuid 26 return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid + '/' + video.uuid
21} 27}
22 28
23function getVideoCacheFileActivityPubUrl (videoFile: VideoFileModel) { 29function getVideoCacheFileActivityPubUrl (videoFile: MVideoFileVideoUUID) {
24 const suffixFPS = videoFile.fps && videoFile.fps !== -1 ? '-' + videoFile.fps : '' 30 const suffixFPS = videoFile.fps && videoFile.fps !== -1 ? '-' + videoFile.fps : ''
25 31
26 return `${WEBSERVER.URL}/redundancy/videos/${videoFile.Video.uuid}/${videoFile.resolution}${suffixFPS}` 32 return `${WEBSERVER.URL}/redundancy/videos/${videoFile.Video.uuid}/${videoFile.resolution}${suffixFPS}`
27} 33}
28 34
29function getVideoCacheStreamingPlaylistActivityPubUrl (video: VideoModel, playlist: VideoStreamingPlaylistModel) { 35function getVideoCacheStreamingPlaylistActivityPubUrl (video: MVideoUUID, playlist: MStreamingPlaylist) {
30 return `${WEBSERVER.URL}/redundancy/streaming-playlists/${playlist.getStringType()}/${video.uuid}` 36 return `${WEBSERVER.URL}/redundancy/streaming-playlists/${playlist.getStringType()}/${video.uuid}`
31} 37}
32 38
33function getVideoCommentActivityPubUrl (video: VideoModel, videoComment: VideoCommentModel) { 39function getVideoCommentActivityPubUrl (video: MVideoUUID, videoComment: MCommentId) {
34 return WEBSERVER.URL + '/videos/watch/' + video.uuid + '/comments/' + videoComment.id 40 return WEBSERVER.URL + '/videos/watch/' + video.uuid + '/comments/' + videoComment.id
35} 41}
36 42
@@ -42,54 +48,54 @@ function getAccountActivityPubUrl (accountName: string) {
42 return WEBSERVER.URL + '/accounts/' + accountName 48 return WEBSERVER.URL + '/accounts/' + accountName
43} 49}
44 50
45function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseModel) { 51function getVideoAbuseActivityPubUrl (videoAbuse: MVideoAbuseId) {
46 return WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id 52 return WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id
47} 53}
48 54
49function getVideoViewActivityPubUrl (byActor: ActorModelUrl, video: VideoModel) { 55function getVideoViewActivityPubUrl (byActor: MActorUrl, video: MVideoId) {
50 return byActor.url + '/views/videos/' + video.id + '/' + new Date().toISOString() 56 return byActor.url + '/views/videos/' + video.id + '/' + new Date().toISOString()
51} 57}
52 58
53function getVideoLikeActivityPubUrl (byActor: ActorModelUrl, video: VideoModel | { id: number }) { 59function getVideoLikeActivityPubUrl (byActor: MActorUrl, video: MVideoId) {
54 return byActor.url + '/likes/' + video.id 60 return byActor.url + '/likes/' + video.id
55} 61}
56 62
57function getVideoDislikeActivityPubUrl (byActor: ActorModelUrl, video: VideoModel | { id: number }) { 63function getVideoDislikeActivityPubUrl (byActor: MActorUrl, video: MVideoId) {
58 return byActor.url + '/dislikes/' + video.id 64 return byActor.url + '/dislikes/' + video.id
59} 65}
60 66
61function getVideoSharesActivityPubUrl (video: VideoModel) { 67function getVideoSharesActivityPubUrl (video: MVideoUrl) {
62 return video.url + '/announces' 68 return video.url + '/announces'
63} 69}
64 70
65function getVideoCommentsActivityPubUrl (video: VideoModel) { 71function getVideoCommentsActivityPubUrl (video: MVideoUrl) {
66 return video.url + '/comments' 72 return video.url + '/comments'
67} 73}
68 74
69function getVideoLikesActivityPubUrl (video: VideoModel) { 75function getVideoLikesActivityPubUrl (video: MVideoUrl) {
70 return video.url + '/likes' 76 return video.url + '/likes'
71} 77}
72 78
73function getVideoDislikesActivityPubUrl (video: VideoModel) { 79function getVideoDislikesActivityPubUrl (video: MVideoUrl) {
74 return video.url + '/dislikes' 80 return video.url + '/dislikes'
75} 81}
76 82
77function getActorFollowActivityPubUrl (follower: ActorModelOnly, following: ActorModelOnly) { 83function getActorFollowActivityPubUrl (follower: MActor, following: MActorId) {
78 return follower.url + '/follows/' + following.id 84 return follower.url + '/follows/' + following.id
79} 85}
80 86
81function getActorFollowAcceptActivityPubUrl (actorFollow: ActorFollowModelLight) { 87function getActorFollowAcceptActivityPubUrl (actorFollow: MActorFollowActors) {
82 const follower = actorFollow.ActorFollower 88 const follower = actorFollow.ActorFollower
83 const me = actorFollow.ActorFollowing 89 const me = actorFollow.ActorFollowing
84 90
85 return follower.url + '/accepts/follows/' + me.id 91 return follower.url + '/accepts/follows/' + me.id
86} 92}
87 93
88function getActorFollowRejectActivityPubUrl (follower: ActorModelOnly, following: ActorModelOnly) { 94function getActorFollowRejectActivityPubUrl (follower: MActorUrl, following: MActorId) {
89 return follower.url + '/rejects/follows/' + following.id 95 return follower.url + '/rejects/follows/' + following.id
90} 96}
91 97
92function getVideoAnnounceActivityPubUrl (byActor: ActorModelOnly, video: VideoModel) { 98function getVideoAnnounceActivityPubUrl (byActor: MActorId, video: MVideoUrl) {
93 return video.url + '/announces/' + byActor.id 99 return video.url + '/announces/' + byActor.id
94} 100}
95 101
diff --git a/server/lib/activitypub/video-comments.ts b/server/lib/activitypub/video-comments.ts
index 8d2c1ade3..375ac0aad 100644
--- a/server/lib/activitypub/video-comments.ts
+++ b/server/lib/activitypub/video-comments.ts
@@ -2,20 +2,20 @@ import { sanitizeAndCheckVideoCommentObject } from '../../helpers/custom-validat
2import { logger } from '../../helpers/logger' 2import { logger } from '../../helpers/logger'
3import { doRequest } from '../../helpers/requests' 3import { doRequest } from '../../helpers/requests'
4import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' 4import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
5import { VideoModel } from '../../models/video/video'
6import { VideoCommentModel } from '../../models/video/video-comment' 5import { VideoCommentModel } from '../../models/video/video-comment'
7import { getOrCreateActorAndServerAndModel } from './actor' 6import { getOrCreateActorAndServerAndModel } from './actor'
8import { getOrCreateVideoAndAccountAndChannel } from './videos' 7import { getOrCreateVideoAndAccountAndChannel } from './videos'
9import * as Bluebird from 'bluebird' 8import * as Bluebird from 'bluebird'
10import { checkUrlsSameHost } from '../../helpers/activitypub' 9import { checkUrlsSameHost } from '../../helpers/activitypub'
10import { MCommentOwner, MCommentOwnerVideo, MVideoAccountAllFiles } from '../../typings/models/video'
11 11
12type ResolveThreadParams = { 12type ResolveThreadParams = {
13 url: string, 13 url: string,
14 comments?: VideoCommentModel[], 14 comments?: MCommentOwner[],
15 isVideo?: boolean, 15 isVideo?: boolean,
16 commentCreated?: boolean 16 commentCreated?: boolean
17} 17}
18type ResolveThreadResult = Promise<{ video: VideoModel, comment: VideoCommentModel, commentCreated: boolean }> 18type ResolveThreadResult = Promise<{ video: MVideoAccountAllFiles, comment: MCommentOwnerVideo, commentCreated: boolean }>
19 19
20async function addVideoComments (commentUrls: string[]) { 20async function addVideoComments (commentUrls: string[]) {
21 return Bluebird.map(commentUrls, commentUrl => { 21 return Bluebird.map(commentUrls, commentUrl => {
@@ -85,9 +85,9 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) {
85 const syncParam = { likes: true, dislikes: true, shares: true, comments: false, thumbnail: true, refreshVideo: false } 85 const syncParam = { likes: true, dislikes: true, shares: true, comments: false, thumbnail: true, refreshVideo: false }
86 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: url, syncParam }) 86 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: url, syncParam })
87 87
88 let resultComment: VideoCommentModel 88 let resultComment: MCommentOwnerVideo
89 if (comments.length !== 0) { 89 if (comments.length !== 0) {
90 const firstReply = comments[ comments.length - 1 ] 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,7 +97,7 @@ 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 ] 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
@@ -107,7 +107,7 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) {
107 comments[i] = await comment.save() 107 comments[i] = await comment.save()
108 } 108 }
109 109
110 resultComment = comments[0] 110 resultComment = comments[0] as MCommentOwnerVideo
111 } 111 }
112 112
113 return { video, comment: resultComment, commentCreated } 113 return { video, comment: resultComment, commentCreated }
@@ -151,7 +151,7 @@ async function resolveParentComment (params: ResolveThreadParams) {
151 originCommentId: null, 151 originCommentId: null,
152 createdAt: new Date(body.published), 152 createdAt: new Date(body.published),
153 updatedAt: new Date(body.updated) 153 updatedAt: new Date(body.updated)
154 }) 154 }) as MCommentOwner
155 comment.Account = actor.Account 155 comment.Account = actor.Account
156 156
157 return resolveThread({ 157 return resolveThread({
diff --git a/server/lib/activitypub/video-rates.ts b/server/lib/activitypub/video-rates.ts
index cda5b2981..6bd46bb58 100644
--- a/server/lib/activitypub/video-rates.ts
+++ b/server/lib/activitypub/video-rates.ts
@@ -1,6 +1,4 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { AccountModel } from '../../models/account/account'
3import { VideoModel } from '../../models/video/video'
4import { sendLike, sendUndoDislike, sendUndoLike } from './send' 2import { sendLike, sendUndoDislike, sendUndoLike } from './send'
5import { VideoRateType } from '../../../shared/models/videos' 3import { VideoRateType } from '../../../shared/models/videos'
6import * as Bluebird from 'bluebird' 4import * as Bluebird from 'bluebird'
@@ -10,11 +8,11 @@ import { logger } from '../../helpers/logger'
10import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' 8import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
11import { doRequest } from '../../helpers/requests' 9import { doRequest } from '../../helpers/requests'
12import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' 10import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
13import { ActorModel } from '../../models/activitypub/actor'
14import { getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from './url' 11import { getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from './url'
15import { sendDislike } from './send/send-dislike' 12import { sendDislike } from './send/send-dislike'
13import { MAccountActor, MActorUrl, MVideo, MVideoAccountLight, MVideoId } from '../../typings/models'
16 14
17async function createRates (ratesUrl: string[], video: VideoModel, rate: VideoRateType) { 15async function createRates (ratesUrl: string[], video: MVideo, rate: VideoRateType) {
18 let rateCounts = 0 16 let rateCounts = 0
19 17
20 await Bluebird.map(ratesUrl, async rateUrl => { 18 await Bluebird.map(ratesUrl, async rateUrl => {
@@ -64,11 +62,13 @@ async function createRates (ratesUrl: string[], video: VideoModel, rate: VideoRa
64 return 62 return
65} 63}
66 64
67async function sendVideoRateChange (account: AccountModel, 65async function sendVideoRateChange (
68 video: VideoModel, 66 account: MAccountActor,
69 likes: number, 67 video: MVideoAccountLight,
70 dislikes: number, 68 likes: number,
71 t: Transaction) { 69 dislikes: number,
70 t: Transaction
71) {
72 const actor = account.Actor 72 const actor = account.Actor
73 73
74 // Keep the order: first we undo and then we create 74 // Keep the order: first we undo and then we create
@@ -84,8 +84,10 @@ async function sendVideoRateChange (account: AccountModel,
84 if (dislikes > 0) await sendDislike(actor, video, t) 84 if (dislikes > 0) await sendDislike(actor, video, t)
85} 85}
86 86
87function getRateUrl (rateType: VideoRateType, actor: ActorModel, video: VideoModel) { 87function getRateUrl (rateType: VideoRateType, actor: MActorUrl, video: MVideoId) {
88 return rateType === 'like' ? getVideoLikeActivityPubUrl(actor, video) : getVideoDislikeActivityPubUrl(actor, video) 88 return rateType === 'like'
89 ? getVideoLikeActivityPubUrl(actor, video)
90 : getVideoDislikeActivityPubUrl(actor, video)
89} 91}
90 92
91export { 93export {
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts
index 3a8451a32..5c10f9764 100644
--- a/server/lib/activitypub/videos.ts
+++ b/server/lib/activitypub/videos.ts
@@ -24,7 +24,6 @@ import {
24 REMOTE_SCHEME, 24 REMOTE_SCHEME,
25 STATIC_PATHS 25 STATIC_PATHS
26} from '../../initializers/constants' 26} from '../../initializers/constants'
27import { ActorModel } from '../../models/activitypub/actor'
28import { TagModel } from '../../models/video/tag' 27import { TagModel } from '../../models/video/tag'
29import { VideoModel } from '../../models/video/video' 28import { VideoModel } from '../../models/video/video'
30import { VideoFileModel } from '../../models/video/video-file' 29import { VideoFileModel } from '../../models/video/video-file'
@@ -38,7 +37,6 @@ import { JobQueue } from '../job-queue'
38import { ActivitypubHttpFetcherPayload } from '../job-queue/handlers/activitypub-http-fetcher' 37import { ActivitypubHttpFetcherPayload } from '../job-queue/handlers/activitypub-http-fetcher'
39import { createRates } from './video-rates' 38import { createRates } from './video-rates'
40import { addVideoShares, shareVideoByServerAndChannel } from './share' 39import { addVideoShares, shareVideoByServerAndChannel } from './share'
41import { AccountModel } from '../../models/account/account'
42import { fetchVideoByUrl, VideoFetchByUrlType } from '../../helpers/video' 40import { fetchVideoByUrl, VideoFetchByUrlType } from '../../helpers/video'
43import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' 41import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
44import { Notifier } from '../notifier' 42import { Notifier } from '../notifier'
@@ -49,15 +47,33 @@ import { VideoShareModel } from '../../models/video/video-share'
49import { VideoCommentModel } from '../../models/video/video-comment' 47import { VideoCommentModel } from '../../models/video/video-comment'
50import { sequelizeTypescript } from '../../initializers/database' 48import { sequelizeTypescript } from '../../initializers/database'
51import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '../thumbnail' 49import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '../thumbnail'
52import { ThumbnailModel } from '../../models/video/thumbnail'
53import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' 50import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
54import { join } from 'path' 51import { join } from 'path'
55import { FilteredModelAttributes } from '../../typings/sequelize' 52import { FilteredModelAttributes } from '../../typings/sequelize'
56import { autoBlacklistVideoIfNeeded } from '../video-blacklist' 53import { autoBlacklistVideoIfNeeded } from '../video-blacklist'
57import { ActorFollowScoreCache } from '../files-cache' 54import { ActorFollowScoreCache } from '../files-cache'
58import { AccountModelIdActor, VideoChannelModelId, VideoChannelModelIdActor } from '../../typings/models' 55import {
56 MAccountActor,
57 MChannelAccountLight,
58 MChannelDefault,
59 MChannelId,
60 MVideo,
61 MVideoAccountAllFiles,
62 MVideoAccountLight,
63 MVideoAP,
64 MVideoAPWithoutCaption,
65 MVideoFile,
66 MVideoFullLight,
67 MVideoId,
68 MVideoTag,
69 MVideoThumbnail,
70 MVideoWithAllFiles
71} from '../../typings/models'
72import { MThumbnail } from '../../typings/models/video/thumbnail'
73
74async function federateVideoIfNeeded (videoArg: MVideoAPWithoutCaption, isNewVideo: boolean, transaction?: sequelize.Transaction) {
75 const video = videoArg as MVideoAP
59 76
60async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) {
61 if ( 77 if (
62 // Check this is not a blacklisted video, or unfederated blacklisted video 78 // Check this is not a blacklisted video, or unfederated blacklisted video
63 (video.isBlacklisted() === false || (isNewVideo === false && video.VideoBlacklist.unfederated === false)) && 79 (video.isBlacklisted() === false || (isNewVideo === false && video.VideoBlacklist.unfederated === false)) &&
@@ -102,7 +118,7 @@ async function fetchRemoteVideo (videoUrl: string): Promise<{ response: request.
102 return { response, videoObject: body } 118 return { response, videoObject: body }
103} 119}
104 120
105async function fetchRemoteVideoDescription (video: VideoModel) { 121async function fetchRemoteVideoDescription (video: MVideoAccountLight) {
106 const host = video.VideoChannel.Account.Actor.Server.host 122 const host = video.VideoChannel.Account.Actor.Server.host
107 const path = video.getDescriptionAPIPath() 123 const path = video.getDescriptionAPIPath()
108 const options = { 124 const options = {
@@ -114,14 +130,14 @@ async function fetchRemoteVideoDescription (video: VideoModel) {
114 return body.description ? body.description : '' 130 return body.description ? body.description : ''
115} 131}
116 132
117function fetchRemoteVideoStaticFile (video: VideoModel, path: string, destPath: string) { 133function fetchRemoteVideoStaticFile (video: MVideoAccountLight, path: string, destPath: string) {
118 const url = buildRemoteBaseUrl(video, path) 134 const url = buildRemoteBaseUrl(video, path)
119 135
120 // We need to provide a callback, if no we could have an uncaught exception 136 // We need to provide a callback, if no we could have an uncaught exception
121 return doRequestAndSaveToFile({ uri: url }, destPath) 137 return doRequestAndSaveToFile({ uri: url }, destPath)
122} 138}
123 139
124function buildRemoteBaseUrl (video: VideoModel, path: string) { 140function buildRemoteBaseUrl (video: MVideoAccountLight, path: string) {
125 const host = video.VideoChannel.Account.Actor.Server.host 141 const host = video.VideoChannel.Account.Actor.Server.host
126 142
127 return REMOTE_SCHEME.HTTP + '://' + host + path 143 return REMOTE_SCHEME.HTTP + '://' + host + path
@@ -146,7 +162,7 @@ type SyncParam = {
146 thumbnail: boolean 162 thumbnail: boolean
147 refreshVideo?: boolean 163 refreshVideo?: boolean
148} 164}
149async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) { 165async function syncVideoExternalAttributes (video: MVideo, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) {
150 logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid) 166 logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid)
151 167
152 const jobPayloads: ActivitypubHttpFetcherPayload[] = [] 168 const jobPayloads: ActivitypubHttpFetcherPayload[] = []
@@ -194,12 +210,24 @@ async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: Vid
194 await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })) 210 await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload }))
195} 211}
196 212
213function getOrCreateVideoAndAccountAndChannel (options: {
214 videoObject: { id: string } | string,
215 syncParam?: SyncParam,
216 fetchType?: 'all',
217 allowRefresh?: boolean
218}): Promise<{ video: MVideoAccountAllFiles, created: boolean, autoBlacklisted?: boolean }>
219function getOrCreateVideoAndAccountAndChannel (options: {
220 videoObject: { id: string } | string,
221 syncParam?: SyncParam,
222 fetchType?: VideoFetchByUrlType,
223 allowRefresh?: boolean
224}): Promise<{ video: MVideoAccountAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }>
197async function getOrCreateVideoAndAccountAndChannel (options: { 225async function getOrCreateVideoAndAccountAndChannel (options: {
198 videoObject: { id: string } | string, 226 videoObject: { id: string } | string,
199 syncParam?: SyncParam, 227 syncParam?: SyncParam,
200 fetchType?: VideoFetchByUrlType, 228 fetchType?: VideoFetchByUrlType,
201 allowRefresh?: boolean // true by default 229 allowRefresh?: boolean // true by default
202}) { 230}): Promise<{ video: MVideoAccountAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }> {
203 // Default params 231 // Default params
204 const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } 232 const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false }
205 const fetchType = options.fetchType || 'all' 233 const fetchType = options.fetchType || 'all'
@@ -227,8 +255,9 @@ async function getOrCreateVideoAndAccountAndChannel (options: {
227 const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl) 255 const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl)
228 if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl) 256 if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl)
229 257
230 const channelActor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo) 258 const actor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo)
231 const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, channelActor, syncParam.thumbnail) 259 const videoChannel = actor.VideoChannel
260 const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, videoChannel, syncParam.thumbnail)
232 261
233 await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam) 262 await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam)
234 263
@@ -236,22 +265,22 @@ async function getOrCreateVideoAndAccountAndChannel (options: {
236} 265}
237 266
238async function updateVideoFromAP (options: { 267async function updateVideoFromAP (options: {
239 video: VideoModel, 268 video: MVideoAccountAllFiles,
240 videoObject: VideoTorrentObject, 269 videoObject: VideoTorrentObject,
241 account: AccountModelIdActor, 270 account: MAccountActor,
242 channel: VideoChannelModelIdActor, 271 channel: MChannelDefault,
243 overrideTo?: string[] 272 overrideTo?: string[]
244}) { 273}) {
245 const { video, videoObject, account, channel, overrideTo } = options 274 const { video, videoObject, account, channel, overrideTo } = options
246 275
247 logger.debug('Updating remote video "%s".', options.videoObject.uuid) 276 logger.debug('Updating remote video "%s".', options.videoObject.uuid, { account, channel })
248 277
249 let videoFieldsSave: any 278 let videoFieldsSave: any
250 const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE 279 const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE
251 const wasUnlistedVideo = video.privacy === VideoPrivacy.UNLISTED 280 const wasUnlistedVideo = video.privacy === VideoPrivacy.UNLISTED
252 281
253 try { 282 try {
254 let thumbnailModel: ThumbnailModel 283 let thumbnailModel: MThumbnail
255 284
256 try { 285 try {
257 thumbnailModel = await createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) 286 thumbnailModel = await createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE)
@@ -259,7 +288,7 @@ async function updateVideoFromAP (options: {
259 logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err }) 288 logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err })
260 } 289 }
261 290
262 await sequelizeTypescript.transaction(async t => { 291 const videoUpdated = await sequelizeTypescript.transaction(async t => {
263 const sequelizeOptions = { transaction: t } 292 const sequelizeOptions = { transaction: t }
264 293
265 videoFieldsSave = video.toJSON() 294 videoFieldsSave = video.toJSON()
@@ -293,21 +322,21 @@ async function updateVideoFromAP (options: {
293 video.channelId = videoData.channelId 322 video.channelId = videoData.channelId
294 video.views = videoData.views 323 video.views = videoData.views
295 324
296 await video.save(sequelizeOptions) 325 const videoUpdated = await video.save(sequelizeOptions) as MVideoFullLight
297 326
298 if (thumbnailModel) await video.addAndSaveThumbnail(thumbnailModel, t) 327 if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel, t)
299 328
300 // FIXME: use icon URL instead 329 // FIXME: use icon URL instead
301 const previewUrl = buildRemoteBaseUrl(video, join(STATIC_PATHS.PREVIEWS, video.getPreview().filename)) 330 const previewUrl = buildRemoteBaseUrl(videoUpdated, join(STATIC_PATHS.PREVIEWS, videoUpdated.getPreview().filename))
302 const previewModel = createPlaceholderThumbnail(previewUrl, video, ThumbnailType.PREVIEW, PREVIEWS_SIZE) 331 const previewModel = createPlaceholderThumbnail(previewUrl, video, ThumbnailType.PREVIEW, PREVIEWS_SIZE)
303 await video.addAndSaveThumbnail(previewModel, t) 332 await videoUpdated.addAndSaveThumbnail(previewModel, t)
304 333
305 { 334 {
306 const videoFileAttributes = videoFileActivityUrlToDBAttributes(video, videoObject) 335 const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoUpdated, videoObject)
307 const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a)) 336 const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a))
308 337
309 // Remove video files that do not exist anymore 338 // Remove video files that do not exist anymore
310 const destroyTasks = video.VideoFiles 339 const destroyTasks = videoUpdated.VideoFiles
311 .filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f))) 340 .filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f)))
312 .map(f => f.destroy(sequelizeOptions)) 341 .map(f => f.destroy(sequelizeOptions))
313 await Promise.all(destroyTasks) 342 await Promise.all(destroyTasks)
@@ -318,15 +347,15 @@ async function updateVideoFromAP (options: {
318 .then(([ file ]) => file) 347 .then(([ file ]) => file)
319 }) 348 })
320 349
321 video.VideoFiles = await Promise.all(upsertTasks) 350 videoUpdated.VideoFiles = await Promise.all(upsertTasks)
322 } 351 }
323 352
324 { 353 {
325 const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(video, videoObject, video.VideoFiles) 354 const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(videoUpdated, videoObject, videoUpdated.VideoFiles)
326 const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) 355 const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a))
327 356
328 // Remove video files that do not exist anymore 357 // Remove video files that do not exist anymore
329 const destroyTasks = video.VideoStreamingPlaylists 358 const destroyTasks = videoUpdated.VideoStreamingPlaylists
330 .filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f))) 359 .filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f)))
331 .map(f => f.destroy(sequelizeOptions)) 360 .map(f => f.destroy(sequelizeOptions))
332 await Promise.all(destroyTasks) 361 await Promise.all(destroyTasks)
@@ -337,38 +366,42 @@ async function updateVideoFromAP (options: {
337 .then(([ streamingPlaylist ]) => streamingPlaylist) 366 .then(([ streamingPlaylist ]) => streamingPlaylist)
338 }) 367 })
339 368
340 video.VideoStreamingPlaylists = await Promise.all(upsertTasks) 369 videoUpdated.VideoStreamingPlaylists = await Promise.all(upsertTasks)
341 } 370 }
342 371
343 { 372 {
344 // Update Tags 373 // Update Tags
345 const tags = videoObject.tag.map(tag => tag.name) 374 const tags = videoObject.tag.map(tag => tag.name)
346 const tagInstances = await TagModel.findOrCreateTags(tags, t) 375 const tagInstances = await TagModel.findOrCreateTags(tags, t)
347 await video.$set('Tags', tagInstances, sequelizeOptions) 376 await videoUpdated.$set('Tags', tagInstances, sequelizeOptions)
348 } 377 }
349 378
350 { 379 {
351 // Update captions 380 // Update captions
352 await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(video.id, t) 381 await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(videoUpdated.id, t)
353 382
354 const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { 383 const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => {
355 return VideoCaptionModel.insertOrReplaceLanguage(video.id, c.identifier, t) 384 return VideoCaptionModel.insertOrReplaceLanguage(videoUpdated.id, c.identifier, t)
356 }) 385 })
357 video.VideoCaptions = await Promise.all(videoCaptionsPromises) 386 await Promise.all(videoCaptionsPromises)
358 } 387 }
388
389 return videoUpdated
359 }) 390 })
360 391
361 await autoBlacklistVideoIfNeeded({ 392 await autoBlacklistVideoIfNeeded({
362 video, 393 video: videoUpdated,
363 user: undefined, 394 user: undefined,
364 isRemote: true, 395 isRemote: true,
365 isNew: false, 396 isNew: false,
366 transaction: undefined 397 transaction: undefined
367 }) 398 })
368 399
369 if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(video) // Notify our users? 400 if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated) // Notify our users?
370 401
371 logger.info('Remote video with uuid %s updated', videoObject.uuid) 402 logger.info('Remote video with uuid %s updated', videoObject.uuid)
403
404 return videoUpdated
372 } catch (err) { 405 } catch (err) {
373 if (video !== undefined && videoFieldsSave !== undefined) { 406 if (video !== undefined && videoFieldsSave !== undefined) {
374 resetSequelizeInstance(video, videoFieldsSave) 407 resetSequelizeInstance(video, videoFieldsSave)
@@ -381,15 +414,15 @@ async function updateVideoFromAP (options: {
381} 414}
382 415
383async function refreshVideoIfNeeded (options: { 416async function refreshVideoIfNeeded (options: {
384 video: VideoModel, 417 video: MVideoThumbnail,
385 fetchedType: VideoFetchByUrlType, 418 fetchedType: VideoFetchByUrlType,
386 syncParam: SyncParam 419 syncParam: SyncParam
387}): Promise<VideoModel> { 420}): Promise<MVideoThumbnail> {
388 if (!options.video.isOutdated()) return options.video 421 if (!options.video.isOutdated()) return options.video
389 422
390 // We need more attributes if the argument video was fetched with not enough joints 423 // We need more attributes if the argument video was fetched with not enough joints
391 const video = options.fetchedType === 'all' 424 const video = options.fetchedType === 'all'
392 ? options.video 425 ? options.video as MVideoAccountAllFiles
393 : await VideoModel.loadByUrlAndPopulateAccount(options.video.url) 426 : await VideoModel.loadByUrlAndPopulateAccount(options.video.url)
394 427
395 try { 428 try {
@@ -410,12 +443,11 @@ async function refreshVideoIfNeeded (options: {
410 } 443 }
411 444
412 const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) 445 const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject)
413 const account = await AccountModel.load(channelActor.VideoChannel.accountId)
414 446
415 const updateOptions = { 447 const updateOptions = {
416 video, 448 video,
417 videoObject, 449 videoObject,
418 account, 450 account: channelActor.VideoChannel.Account,
419 channel: channelActor.VideoChannel 451 channel: channelActor.VideoChannel
420 } 452 }
421 await retryTransactionWrapper(updateVideoFromAP, updateOptions) 453 await retryTransactionWrapper(updateVideoFromAP, updateOptions)
@@ -467,15 +499,15 @@ function isAPPlaylistSegmentHashesUrlObject (tag: any): tag is ActivityPlaylistS
467 return tag.name === 'sha256' && tag.type === 'Link' && urlMediaType === 'application/json' 499 return tag.name === 'sha256' && tag.type === 'Link' && urlMediaType === 'application/json'
468} 500}
469 501
470async function createVideo (videoObject: VideoTorrentObject, channelActor: ActorModel, waitThumbnail = false) { 502async function createVideo (videoObject: VideoTorrentObject, channel: MChannelAccountLight, waitThumbnail = false) {
471 logger.debug('Adding remote video %s.', videoObject.id) 503 logger.debug('Adding remote video %s.', videoObject.id)
472 504
473 const videoData = await videoActivityObjectToDBAttributes(channelActor.VideoChannel, videoObject, videoObject.to) 505 const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, videoObject.to)
474 const video = VideoModel.build(videoData) 506 const video = VideoModel.build(videoData) as MVideoThumbnail
475 507
476 const promiseThumbnail = createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) 508 const promiseThumbnail = createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE)
477 509
478 let thumbnailModel: ThumbnailModel 510 let thumbnailModel: MThumbnail
479 if (waitThumbnail === true) { 511 if (waitThumbnail === true) {
480 thumbnailModel = await promiseThumbnail 512 thumbnailModel = await promiseThumbnail
481 } 513 }
@@ -483,8 +515,8 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor
483 const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => { 515 const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => {
484 const sequelizeOptions = { transaction: t } 516 const sequelizeOptions = { transaction: t }
485 517
486 const videoCreated = await video.save(sequelizeOptions) 518 const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight
487 videoCreated.VideoChannel = channelActor.VideoChannel 519 videoCreated.VideoChannel = channel
488 520
489 if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) 521 if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t)
490 522
@@ -517,15 +549,14 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor
517 const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { 549 const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => {
518 return VideoCaptionModel.insertOrReplaceLanguage(videoCreated.id, c.identifier, t) 550 return VideoCaptionModel.insertOrReplaceLanguage(videoCreated.id, c.identifier, t)
519 }) 551 })
520 const captions = await Promise.all(videoCaptionsPromises) 552 await Promise.all(videoCaptionsPromises)
521 553
522 video.VideoFiles = videoFiles 554 videoCreated.VideoFiles = videoFiles
523 video.VideoStreamingPlaylists = streamingPlaylists 555 videoCreated.VideoStreamingPlaylists = streamingPlaylists
524 video.Tags = tagInstances 556 videoCreated.Tags = tagInstances
525 video.VideoCaptions = captions
526 557
527 const autoBlacklisted = await autoBlacklistVideoIfNeeded({ 558 const autoBlacklisted = await autoBlacklistVideoIfNeeded({
528 video, 559 video: videoCreated,
529 user: undefined, 560 user: undefined,
530 isRemote: true, 561 isRemote: true,
531 isNew: true, 562 isNew: true,
@@ -548,11 +579,7 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor
548 return { autoBlacklisted, videoCreated } 579 return { autoBlacklisted, videoCreated }
549} 580}
550 581
551async function videoActivityObjectToDBAttributes ( 582async function videoActivityObjectToDBAttributes (videoChannel: MChannelId, videoObject: VideoTorrentObject, to: string[] = []) {
552 videoChannel: VideoChannelModelId,
553 videoObject: VideoTorrentObject,
554 to: string[] = []
555) {
556 const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED 583 const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED
557 const duration = videoObject.duration.replace(/[^\d]+/, '') 584 const duration = videoObject.duration.replace(/[^\d]+/, '')
558 585
@@ -603,7 +630,7 @@ async function videoActivityObjectToDBAttributes (
603 } 630 }
604} 631}
605 632
606function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: VideoTorrentObject) { 633function videoFileActivityUrlToDBAttributes (video: MVideo, videoObject: VideoTorrentObject) {
607 const fileUrls = videoObject.url.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[] 634 const fileUrls = videoObject.url.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[]
608 635
609 if (fileUrls.length === 0) { 636 if (fileUrls.length === 0) {
@@ -641,7 +668,7 @@ function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: Vid
641 return attributes 668 return attributes
642} 669}
643 670
644function streamingPlaylistActivityUrlToDBAttributes (video: VideoModel, videoObject: VideoTorrentObject, videoFiles: VideoFileModel[]) { 671function streamingPlaylistActivityUrlToDBAttributes (video: MVideoId, videoObject: VideoTorrentObject, videoFiles: MVideoFile[]) {
645 const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[] 672 const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[]
646 if (playlistUrls.length === 0) return [] 673 if (playlistUrls.length === 0) return []
647 674
diff --git a/server/lib/avatar.ts b/server/lib/avatar.ts
index 1b38e6cb5..9005b3e22 100644
--- a/server/lib/avatar.ts
+++ b/server/lib/avatar.ts
@@ -3,8 +3,6 @@ import { sendUpdateActor } from './activitypub/send'
3import { AVATARS_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants' 3import { AVATARS_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants'
4import { updateActorAvatarInstance } from './activitypub' 4import { updateActorAvatarInstance } from './activitypub'
5import { processImage } from '../helpers/image-utils' 5import { processImage } from '../helpers/image-utils'
6import { AccountModel } from '../models/account/account'
7import { VideoChannelModel } from '../models/video/video-channel'
8import { extname, join } from 'path' 6import { extname, join } from 'path'
9import { retryTransactionWrapper } from '../helpers/database-utils' 7import { retryTransactionWrapper } from '../helpers/database-utils'
10import * as uuidv4 from 'uuid/v4' 8import * as uuidv4 from 'uuid/v4'
@@ -13,8 +11,12 @@ import { sequelizeTypescript } from '../initializers/database'
13import * as LRUCache from 'lru-cache' 11import * as LRUCache from 'lru-cache'
14import { queue } from 'async' 12import { queue } from 'async'
15import { downloadImage } from '../helpers/requests' 13import { downloadImage } from '../helpers/requests'
14import { MAccountActorDefault, MChannelActorDefault } from '../typings/models'
16 15
17async function updateActorAvatarFile (avatarPhysicalFile: Express.Multer.File, accountOrChannel: AccountModel | VideoChannelModel) { 16async function updateActorAvatarFile (
17 avatarPhysicalFile: Express.Multer.File,
18 accountOrChannel: MAccountActorDefault | MChannelActorDefault
19) {
18 const extension = extname(avatarPhysicalFile.filename) 20 const extension = extname(avatarPhysicalFile.filename)
19 const avatarName = uuidv4() + extension 21 const avatarName = uuidv4() + extension
20 const destination = join(CONFIG.STORAGE.AVATARS_DIR, avatarName) 22 const destination = join(CONFIG.STORAGE.AVATARS_DIR, avatarName)
diff --git a/server/lib/blocklist.ts b/server/lib/blocklist.ts
index 1633e500c..28c69b46e 100644
--- a/server/lib/blocklist.ts
+++ b/server/lib/blocklist.ts
@@ -1,6 +1,7 @@
1import { sequelizeTypescript } from '../initializers' 1import { sequelizeTypescript } from '../initializers'
2import { AccountBlocklistModel } from '../models/account/account-blocklist' 2import { AccountBlocklistModel } from '../models/account/account-blocklist'
3import { ServerBlocklistModel } from '../models/server/server-blocklist' 3import { ServerBlocklistModel } from '../models/server/server-blocklist'
4import { MAccountBlocklist, MServerBlocklist } from '@server/typings/models'
4 5
5function addAccountInBlocklist (byAccountId: number, targetAccountId: number) { 6function addAccountInBlocklist (byAccountId: number, targetAccountId: number) {
6 return sequelizeTypescript.transaction(async t => { 7 return sequelizeTypescript.transaction(async t => {
@@ -20,13 +21,13 @@ function addServerInBlocklist (byAccountId: number, targetServerId: number) {
20 }) 21 })
21} 22}
22 23
23function removeAccountFromBlocklist (accountBlock: AccountBlocklistModel) { 24function removeAccountFromBlocklist (accountBlock: MAccountBlocklist) {
24 return sequelizeTypescript.transaction(async t => { 25 return sequelizeTypescript.transaction(async t => {
25 return accountBlock.destroy({ transaction: t }) 26 return accountBlock.destroy({ transaction: t })
26 }) 27 })
27} 28}
28 29
29function removeServerFromBlocklist (serverBlock: ServerBlocklistModel) { 30function removeServerFromBlocklist (serverBlock: MServerBlocklist) {
30 return sequelizeTypescript.transaction(async t => { 31 return sequelizeTypescript.transaction(async t => {
31 return serverBlock.destroy({ transaction: t }) 32 return serverBlock.destroy({ transaction: t })
32 }) 33 })
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts
index 8841dd2ac..a33aef26d 100644
--- a/server/lib/client-html.ts
+++ b/server/lib/client-html.ts
@@ -13,6 +13,7 @@ import { VideoChannelModel } from '../models/video/video-channel'
13import * as Bluebird from 'bluebird' 13import * as Bluebird from 'bluebird'
14import { CONFIG } from '../initializers/config' 14import { CONFIG } from '../initializers/config'
15import { logger } from '../helpers/logger' 15import { logger } from '../helpers/logger'
16import { MAccountActor, MChannelActor, MVideo } from '../typings/models'
16 17
17export class ClientHtml { 18export class ClientHtml {
18 19
@@ -65,7 +66,7 @@ export class ClientHtml {
65 } 66 }
66 67
67 private static async getAccountOrChannelHTMLPage ( 68 private static async getAccountOrChannelHTMLPage (
68 loader: () => Bluebird<AccountModel | VideoChannelModel>, 69 loader: () => Bluebird<MAccountActor | MChannelActor>,
69 req: express.Request, 70 req: express.Request,
70 res: express.Response 71 res: express.Response
71 ) { 72 ) {
@@ -157,7 +158,7 @@ export class ClientHtml {
157 return htmlStringPage.replace('</head>', linkTag + '</head>') 158 return htmlStringPage.replace('</head>', linkTag + '</head>')
158 } 159 }
159 160
160 private static addVideoOpenGraphAndOEmbedTags (htmlStringPage: string, video: VideoModel) { 161 private static addVideoOpenGraphAndOEmbedTags (htmlStringPage: string, video: MVideo) {
161 const previewUrl = WEBSERVER.URL + video.getPreviewStaticPath() 162 const previewUrl = WEBSERVER.URL + video.getPreviewStaticPath()
162 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() 163 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
163 164
@@ -236,7 +237,7 @@ export class ClientHtml {
236 return this.addOpenGraphAndOEmbedTags(htmlStringPage, tagsString) 237 return this.addOpenGraphAndOEmbedTags(htmlStringPage, tagsString)
237 } 238 }
238 239
239 private static addAccountOrChannelMetaTags (htmlStringPage: string, entity: AccountModel | VideoChannelModel) { 240 private static addAccountOrChannelMetaTags (htmlStringPage: string, entity: MAccountActor | MChannelActor) {
240 // SEO, use origin account or channel URL 241 // SEO, use origin account or channel URL
241 const metaTags = `<link rel="canonical" href="${entity.Actor.url}" />` 242 const metaTags = `<link rel="canonical" href="${entity.Actor.url}" />`
242 243
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts
index 73c2bcb1b..fe57a3e4c 100644
--- a/server/lib/emailer.ts
+++ b/server/lib/emailer.ts
@@ -3,16 +3,14 @@ import { isTestInstance } from '../helpers/core-utils'
3import { bunyanLogger, logger } from '../helpers/logger' 3import { bunyanLogger, logger } from '../helpers/logger'
4import { CONFIG } from '../initializers/config' 4import { CONFIG } from '../initializers/config'
5import { UserModel } from '../models/account/user' 5import { UserModel } from '../models/account/user'
6import { VideoModel } from '../models/video/video'
7import { JobQueue } from './job-queue' 6import { JobQueue } from './job-queue'
8import { EmailPayload } from './job-queue/handlers/email' 7import { EmailPayload } from './job-queue/handlers/email'
9import { readFileSync } from 'fs-extra' 8import { readFileSync } from 'fs-extra'
10import { VideoCommentModel } from '../models/video/video-comment'
11import { VideoAbuseModel } from '../models/video/video-abuse'
12import { VideoBlacklistModel } from '../models/video/video-blacklist' 9import { VideoBlacklistModel } from '../models/video/video-blacklist'
13import { VideoImportModel } from '../models/video/video-import'
14import { ActorFollowModel } from '../models/activitypub/actor-follow'
15import { WEBSERVER } from '../initializers/constants' 10import { WEBSERVER } from '../initializers/constants'
11import { MCommentOwnerVideo, MVideo, MVideoAbuseVideo, MVideoAccountLight, MVideoBlacklistVideo } from '../typings/models/video'
12import { MActorFollowActors, MActorFollowFull, MUser } from '../typings/models'
13import { MVideoImport, MVideoImportVideo } from '@server/typings/models/video/video-import'
16 14
17type SendEmailOptions = { 15type SendEmailOptions = {
18 to: string[] 16 to: string[]
@@ -90,7 +88,7 @@ class Emailer {
90 } 88 }
91 } 89 }
92 90
93 addNewVideoFromSubscriberNotification (to: string[], video: VideoModel) { 91 addNewVideoFromSubscriberNotification (to: string[], video: MVideoAccountLight) {
94 const channelName = video.VideoChannel.getDisplayName() 92 const channelName = video.VideoChannel.getDisplayName()
95 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() 93 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
96 94
@@ -111,7 +109,7 @@ class Emailer {
111 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 109 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
112 } 110 }
113 111
114 addNewFollowNotification (to: string[], actorFollow: ActorFollowModel, followType: 'account' | 'channel') { 112 addNewFollowNotification (to: string[], actorFollow: MActorFollowFull, followType: 'account' | 'channel') {
115 const followerName = actorFollow.ActorFollower.Account.getDisplayName() 113 const followerName = actorFollow.ActorFollower.Account.getDisplayName()
116 const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName() 114 const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName()
117 115
@@ -130,7 +128,7 @@ class Emailer {
130 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 128 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
131 } 129 }
132 130
133 addNewInstanceFollowerNotification (to: string[], actorFollow: ActorFollowModel) { 131 addNewInstanceFollowerNotification (to: string[], actorFollow: MActorFollowActors) {
134 const awaitingApproval = actorFollow.state === 'pending' ? ' awaiting manual approval.' : '' 132 const awaitingApproval = actorFollow.state === 'pending' ? ' awaiting manual approval.' : ''
135 133
136 const text = `Hi dear admin,\n\n` + 134 const text = `Hi dear admin,\n\n` +
@@ -148,7 +146,7 @@ class Emailer {
148 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 146 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
149 } 147 }
150 148
151 myVideoPublishedNotification (to: string[], video: VideoModel) { 149 myVideoPublishedNotification (to: string[], video: MVideo) {
152 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() 150 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
153 151
154 const text = `Hi dear user,\n\n` + 152 const text = `Hi dear user,\n\n` +
@@ -168,7 +166,7 @@ class Emailer {
168 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 166 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
169 } 167 }
170 168
171 myVideoImportSuccessNotification (to: string[], videoImport: VideoImportModel) { 169 myVideoImportSuccessNotification (to: string[], videoImport: MVideoImportVideo) {
172 const videoUrl = WEBSERVER.URL + videoImport.Video.getWatchStaticPath() 170 const videoUrl = WEBSERVER.URL + videoImport.Video.getWatchStaticPath()
173 171
174 const text = `Hi dear user,\n\n` + 172 const text = `Hi dear user,\n\n` +
@@ -188,7 +186,7 @@ class Emailer {
188 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 186 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
189 } 187 }
190 188
191 myVideoImportErrorNotification (to: string[], videoImport: VideoImportModel) { 189 myVideoImportErrorNotification (to: string[], videoImport: MVideoImport) {
192 const importUrl = WEBSERVER.URL + '/my-account/video-imports' 190 const importUrl = WEBSERVER.URL + '/my-account/video-imports'
193 191
194 const text = `Hi dear user,\n\n` + 192 const text = `Hi dear user,\n\n` +
@@ -208,7 +206,7 @@ class Emailer {
208 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 206 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
209 } 207 }
210 208
211 addNewCommentOnMyVideoNotification (to: string[], comment: VideoCommentModel) { 209 addNewCommentOnMyVideoNotification (to: string[], comment: MCommentOwnerVideo) {
212 const accountName = comment.Account.getDisplayName() 210 const accountName = comment.Account.getDisplayName()
213 const video = comment.Video 211 const video = comment.Video
214 const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() 212 const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath()
@@ -230,7 +228,7 @@ class Emailer {
230 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 228 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
231 } 229 }
232 230
233 addNewCommentMentionNotification (to: string[], comment: VideoCommentModel) { 231 addNewCommentMentionNotification (to: string[], comment: MCommentOwnerVideo) {
234 const accountName = comment.Account.getDisplayName() 232 const accountName = comment.Account.getDisplayName()
235 const video = comment.Video 233 const video = comment.Video
236 const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() 234 const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath()
@@ -252,7 +250,7 @@ class Emailer {
252 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 250 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
253 } 251 }
254 252
255 addVideoAbuseModeratorsNotification (to: string[], videoAbuse: VideoAbuseModel) { 253 addVideoAbuseModeratorsNotification (to: string[], videoAbuse: MVideoAbuseVideo) {
256 const videoUrl = WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath() 254 const videoUrl = WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath()
257 255
258 const text = `Hi,\n\n` + 256 const text = `Hi,\n\n` +
@@ -269,7 +267,7 @@ class Emailer {
269 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 267 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
270 } 268 }
271 269
272 addVideoAutoBlacklistModeratorsNotification (to: string[], video: VideoModel) { 270 addVideoAutoBlacklistModeratorsNotification (to: string[], video: MVideo) {
273 const VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list' 271 const VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list'
274 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() 272 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
275 273
@@ -292,7 +290,7 @@ class Emailer {
292 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 290 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
293 } 291 }
294 292
295 addNewUserRegistrationNotification (to: string[], user: UserModel) { 293 addNewUserRegistrationNotification (to: string[], user: MUser) {
296 const text = `Hi,\n\n` + 294 const text = `Hi,\n\n` +
297 `User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` + 295 `User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` +
298 `Cheers,\n` + 296 `Cheers,\n` +
@@ -307,7 +305,7 @@ class Emailer {
307 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 305 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
308 } 306 }
309 307
310 addVideoBlacklistNotification (to: string[], videoBlacklist: VideoBlacklistModel) { 308 addVideoBlacklistNotification (to: string[], videoBlacklist: MVideoBlacklistVideo) {
311 const videoName = videoBlacklist.Video.name 309 const videoName = videoBlacklist.Video.name
312 const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() 310 const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath()
313 311
@@ -329,7 +327,7 @@ class Emailer {
329 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 327 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
330 } 328 }
331 329
332 addVideoUnblacklistNotification (to: string[], video: VideoModel) { 330 addVideoUnblacklistNotification (to: string[], video: MVideo) {
333 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() 331 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
334 332
335 const text = 'Hi,\n\n' + 333 const text = 'Hi,\n\n' +
@@ -381,7 +379,7 @@ class Emailer {
381 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 379 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
382 } 380 }
383 381
384 addUserBlockJob (user: UserModel, blocked: boolean, reason?: string) { 382 addUserBlockJob (user: MUser, blocked: boolean, reason?: string) {
385 const reasonString = reason ? ` for the following reason: ${reason}` : '' 383 const reasonString = reason ? ` for the following reason: ${reason}` : ''
386 const blockedWord = blocked ? 'blocked' : 'unblocked' 384 const blockedWord = blocked ? 'blocked' : 'unblocked'
387 const blockedString = `Your account ${user.username} on ${WEBSERVER.HOST} has been ${blockedWord}${reasonString}.` 385 const blockedString = `Your account ${user.username} on ${WEBSERVER.HOST} has been ${blockedWord}${reasonString}.`
diff --git a/server/lib/hls.ts b/server/lib/hls.ts
index 98da4dcd8..05136c21c 100644
--- a/server/lib/hls.ts
+++ b/server/lib/hls.ts
@@ -1,4 +1,3 @@
1import { VideoModel } from '../models/video/video'
2import { basename, dirname, join } from 'path' 1import { basename, dirname, join } from 'path'
3import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from '../initializers/constants' 2import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from '../initializers/constants'
4import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, writeFile } from 'fs-extra' 3import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, writeFile } from 'fs-extra'
@@ -12,6 +11,7 @@ import { flatten, uniq } from 'lodash'
12import { VideoFileModel } from '../models/video/video-file' 11import { VideoFileModel } from '../models/video/video-file'
13import { CONFIG } from '../initializers/config' 12import { CONFIG } from '../initializers/config'
14import { sequelizeTypescript } from '../initializers/database' 13import { sequelizeTypescript } from '../initializers/database'
14import { MVideoWithFile } from '@server/typings/models'
15 15
16async function updateStreamingPlaylistsInfohashesIfNeeded () { 16async function updateStreamingPlaylistsInfohashesIfNeeded () {
17 const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion() 17 const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion()
@@ -28,7 +28,7 @@ async function updateStreamingPlaylistsInfohashesIfNeeded () {
28 } 28 }
29} 29}
30 30
31async function updateMasterHLSPlaylist (video: VideoModel) { 31async function updateMasterHLSPlaylist (video: MVideoWithFile) {
32 const directory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) 32 const directory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)
33 const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ] 33 const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ]
34 const masterPlaylistPath = join(directory, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename()) 34 const masterPlaylistPath = join(directory, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename())
@@ -55,7 +55,7 @@ async function updateMasterHLSPlaylist (video: VideoModel) {
55 await writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n') 55 await writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n')
56} 56}
57 57
58async function updateSha256Segments (video: VideoModel) { 58async function updateSha256Segments (video: MVideoWithFile) {
59 const json: { [filename: string]: { [range: string]: string } } = {} 59 const json: { [filename: string]: { [range: string]: string } } = {}
60 60
61 const playlistDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) 61 const playlistDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)
diff --git a/server/lib/job-queue/handlers/activitypub-follow.ts b/server/lib/job-queue/handlers/activitypub-follow.ts
index 4ae66cd01..741b1ffde 100644
--- a/server/lib/job-queue/handlers/activitypub-follow.ts
+++ b/server/lib/job-queue/handlers/activitypub-follow.ts
@@ -10,6 +10,7 @@ import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
10import { ActorModel } from '../../../models/activitypub/actor' 10import { ActorModel } from '../../../models/activitypub/actor'
11import { Notifier } from '../../notifier' 11import { Notifier } from '../../notifier'
12import { sequelizeTypescript } from '../../../initializers/database' 12import { sequelizeTypescript } from '../../../initializers/database'
13import { MActorFollowFull, MActorFull } from '../../../typings/models'
13 14
14export type ActivitypubFollowPayload = { 15export type ActivitypubFollowPayload = {
15 followerActorId: number 16 followerActorId: number
@@ -23,13 +24,13 @@ async function processActivityPubFollow (job: Bull.Job) {
23 24
24 logger.info('Processing ActivityPub follow in job %d.', job.id) 25 logger.info('Processing ActivityPub follow in job %d.', job.id)
25 26
26 let targetActor: ActorModel 27 let targetActor: MActorFull
27 if (!host || host === WEBSERVER.HOST) { 28 if (!host || host === WEBSERVER.HOST) {
28 targetActor = await ActorModel.loadLocalByName(payload.name) 29 targetActor = await ActorModel.loadLocalByName(payload.name)
29 } else { 30 } else {
30 const sanitizedHost = sanitizeHost(host, REMOTE_SCHEME.HTTP) 31 const sanitizedHost = sanitizeHost(host, REMOTE_SCHEME.HTTP)
31 const actorUrl = await loadActorUrlOrGetFromWebfinger(payload.name + '@' + sanitizedHost) 32 const actorUrl = await loadActorUrlOrGetFromWebfinger(payload.name + '@' + sanitizedHost)
32 targetActor = await getOrCreateActorAndServerAndModel(actorUrl) 33 targetActor = await getOrCreateActorAndServerAndModel(actorUrl, 'all')
33 } 34 }
34 35
35 const fromActor = await ActorModel.load(payload.followerActorId) 36 const fromActor = await ActorModel.load(payload.followerActorId)
@@ -44,7 +45,7 @@ export {
44 45
45// --------------------------------------------------------------------------- 46// ---------------------------------------------------------------------------
46 47
47async function follow (fromActor: ActorModel, targetActor: ActorModel) { 48async function follow (fromActor: MActorFull, targetActor: MActorFull) {
48 if (fromActor.id === targetActor.id) { 49 if (fromActor.id === targetActor.id) {
49 throw new Error('Follower is the same than target actor.') 50 throw new Error('Follower is the same than target actor.')
50 } 51 }
@@ -53,7 +54,7 @@ async function follow (fromActor: ActorModel, targetActor: ActorModel) {
53 const state = !fromActor.serverId && !targetActor.serverId ? 'accepted' : 'pending' 54 const state = !fromActor.serverId && !targetActor.serverId ? 'accepted' : 'pending'
54 55
55 const actorFollow = await sequelizeTypescript.transaction(async t => { 56 const actorFollow = await sequelizeTypescript.transaction(async t => {
56 const [ actorFollow ] = await ActorFollowModel.findOrCreate({ 57 const [ actorFollow ] = await ActorFollowModel.findOrCreate<MActorFollowFull>({
57 where: { 58 where: {
58 actorId: fromActor.id, 59 actorId: fromActor.id,
59 targetActorId: targetActor.id 60 targetActorId: targetActor.id
diff --git a/server/lib/job-queue/handlers/activitypub-http-fetcher.ts b/server/lib/job-queue/handlers/activitypub-http-fetcher.ts
index c3f59dc77..0182c5169 100644
--- a/server/lib/job-queue/handlers/activitypub-http-fetcher.ts
+++ b/server/lib/job-queue/handlers/activitypub-http-fetcher.ts
@@ -11,6 +11,7 @@ import { AccountModel } from '../../../models/account/account'
11import { AccountVideoRateModel } from '../../../models/account/account-video-rate' 11import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
12import { VideoShareModel } from '../../../models/video/video-share' 12import { VideoShareModel } from '../../../models/video/video-share'
13import { VideoCommentModel } from '../../../models/video/video-comment' 13import { VideoCommentModel } from '../../../models/video/video-comment'
14import { MAccountDefault, MVideoFullLight } from '../../../typings/models'
14 15
15type FetchType = 'activity' | 'video-likes' | 'video-dislikes' | 'video-shares' | 'video-comments' | 'account-playlists' 16type FetchType = 'activity' | 'video-likes' | 'video-dislikes' | 'video-shares' | 'video-comments' | 'account-playlists'
16 17
@@ -26,10 +27,10 @@ async function processActivityPubHttpFetcher (job: Bull.Job) {
26 27
27 const payload = job.data as ActivitypubHttpFetcherPayload 28 const payload = job.data as ActivitypubHttpFetcherPayload
28 29
29 let video: VideoModel 30 let video: MVideoFullLight
30 if (payload.videoId) video = await VideoModel.loadAndPopulateAccountAndServerAndTags(payload.videoId) 31 if (payload.videoId) video = await VideoModel.loadAndPopulateAccountAndServerAndTags(payload.videoId)
31 32
32 let account: AccountModel 33 let account: MAccountDefault
33 if (payload.accountId) account = await AccountModel.load(payload.accountId) 34 if (payload.accountId) account = await AccountModel.load(payload.accountId)
34 35
35 const fetcherType: { [ id in FetchType ]: (items: any[]) => Promise<any> } = { 36 const fetcherType: { [ id in FetchType ]: (items: any[]) => Promise<any> } = {
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 cdee1f6fd..d3bde6e6a 100644
--- a/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts
+++ b/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts
@@ -3,6 +3,7 @@ import { getServerActor } from '../../../../helpers/utils'
3import { ActorModel } from '../../../../models/activitypub/actor' 3import { ActorModel } from '../../../../models/activitypub/actor'
4import { sha256 } from '../../../../helpers/core-utils' 4import { sha256 } from '../../../../helpers/core-utils'
5import { HTTP_SIGNATURE } from '../../../../initializers/constants' 5import { HTTP_SIGNATURE } from '../../../../initializers/constants'
6import { MActor } from '../../../../typings/models'
6 7
7type Payload = { body: any, signatureActorId?: number } 8type Payload = { body: any, signatureActorId?: number }
8 9
@@ -19,7 +20,8 @@ async function computeBody (payload: Payload) {
19} 20}
20 21
21async function buildSignedRequestOptions (payload: Payload) { 22async function buildSignedRequestOptions (payload: Payload) {
22 let actor: ActorModel | null 23 let actor: MActor | null
24
23 if (payload.signatureActorId) { 25 if (payload.signatureActorId) {
24 actor = await ActorModel.load(payload.signatureActorId) 26 actor = await ActorModel.load(payload.signatureActorId)
25 if (!actor) throw new Error('Unknown signature actor id.') 27 if (!actor) throw new Error('Unknown signature actor id.')
diff --git a/server/lib/job-queue/handlers/video-file-import.ts b/server/lib/job-queue/handlers/video-file-import.ts
index 8cacb0ef3..5c5b7dccb 100644
--- a/server/lib/job-queue/handlers/video-file-import.ts
+++ b/server/lib/job-queue/handlers/video-file-import.ts
@@ -6,6 +6,7 @@ import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg
6import { copy, stat } from 'fs-extra' 6import { copy, stat } from 'fs-extra'
7import { VideoFileModel } from '../../../models/video/video-file' 7import { VideoFileModel } from '../../../models/video/video-file'
8import { extname } from 'path' 8import { extname } from 'path'
9import { MVideoFile, MVideoWithFile } from '@server/typings/models'
9 10
10export type VideoFileImportPayload = { 11export type VideoFileImportPayload = {
11 videoUUID: string, 12 videoUUID: string,
@@ -37,7 +38,7 @@ export {
37 38
38// --------------------------------------------------------------------------- 39// ---------------------------------------------------------------------------
39 40
40async function updateVideoFile (video: VideoModel, inputFilePath: string) { 41async function updateVideoFile (video: MVideoWithFile, inputFilePath: string) {
41 const { videoFileResolution } = await getVideoFileResolution(inputFilePath) 42 const { videoFileResolution } = await getVideoFileResolution(inputFilePath)
42 const { size } = await stat(inputFilePath) 43 const { size } = await stat(inputFilePath)
43 const fps = await getVideoFileFPS(inputFilePath) 44 const fps = await getVideoFileFPS(inputFilePath)
@@ -48,7 +49,7 @@ async function updateVideoFile (video: VideoModel, inputFilePath: string) {
48 size, 49 size,
49 fps, 50 fps,
50 videoId: video.id 51 videoId: video.id
51 }) 52 }) as MVideoFile
52 53
53 const currentVideoFile = video.VideoFiles.find(videoFile => videoFile.resolution === updatedVideoFile.resolution) 54 const currentVideoFile = video.VideoFiles.find(videoFile => videoFile.resolution === updatedVideoFile.resolution)
54 55
@@ -60,9 +61,9 @@ async function updateVideoFile (video: VideoModel, inputFilePath: string) {
60 video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile) 61 video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile)
61 62
62 // Update the database 63 // Update the database
63 currentVideoFile.set('extname', updatedVideoFile.extname) 64 currentVideoFile.extname = updatedVideoFile.extname
64 currentVideoFile.set('size', updatedVideoFile.size) 65 currentVideoFile.size = updatedVideoFile.size
65 currentVideoFile.set('fps', updatedVideoFile.fps) 66 currentVideoFile.fps = updatedVideoFile.fps
66 67
67 updatedVideoFile = currentVideoFile 68 updatedVideoFile = currentVideoFile
68 } 69 }
diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts
index 13b741180..f9dda79f8 100644
--- a/server/lib/job-queue/handlers/video-import.ts
+++ b/server/lib/job-queue/handlers/video-import.ts
@@ -17,9 +17,10 @@ import { move, remove, stat } from 'fs-extra'
17import { Notifier } from '../../notifier' 17import { Notifier } from '../../notifier'
18import { CONFIG } from '../../../initializers/config' 18import { CONFIG } from '../../../initializers/config'
19import { sequelizeTypescript } from '../../../initializers/database' 19import { sequelizeTypescript } from '../../../initializers/database'
20import { ThumbnailModel } from '../../../models/video/thumbnail'
21import { createVideoMiniatureFromUrl, generateVideoMiniature } from '../../thumbnail' 20import { createVideoMiniatureFromUrl, generateVideoMiniature } from '../../thumbnail'
22import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' 21import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
22import { MThumbnail } from '../../../typings/models/video/thumbnail'
23import { MVideoImportDefault, MVideoImportDefaultFiles, MVideoImportVideo } from '@server/typings/models/video/video-import'
23 24
24type VideoImportYoutubeDLPayload = { 25type VideoImportYoutubeDLPayload = {
25 type: 'youtube-dl' 26 type: 'youtube-dl'
@@ -110,11 +111,13 @@ type ProcessFileOptions = {
110 generateThumbnail: boolean 111 generateThumbnail: boolean
111 generatePreview: boolean 112 generatePreview: boolean
112} 113}
113async function processFile (downloader: () => Promise<string>, videoImport: VideoImportModel, options: ProcessFileOptions) { 114async function processFile (downloader: () => Promise<string>, videoImportArg: MVideoImportDefault, options: ProcessFileOptions) {
114 let tempVideoPath: string 115 let tempVideoPath: string
115 let videoDestFile: string 116 let videoDestFile: string
116 let videoFile: VideoFileModel 117 let videoFile: VideoFileModel
117 118
119 const videoImport = videoImportArg as MVideoImportDefaultFiles
120
118 try { 121 try {
119 // Download video from youtubeDL 122 // Download video from youtubeDL
120 tempVideoPath = await downloader() 123 tempVideoPath = await downloader()
@@ -148,7 +151,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
148 tempVideoPath = null // This path is not used anymore 151 tempVideoPath = null // This path is not used anymore
149 152
150 // Process thumbnail 153 // Process thumbnail
151 let thumbnailModel: ThumbnailModel 154 let thumbnailModel: MThumbnail
152 if (options.downloadThumbnail && options.thumbnailUrl) { 155 if (options.downloadThumbnail && options.thumbnailUrl) {
153 thumbnailModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.MINIATURE) 156 thumbnailModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.MINIATURE)
154 } else if (options.generateThumbnail || options.downloadThumbnail) { 157 } else if (options.generateThumbnail || options.downloadThumbnail) {
@@ -156,7 +159,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
156 } 159 }
157 160
158 // Process preview 161 // Process preview
159 let previewModel: ThumbnailModel 162 let previewModel: MThumbnail
160 if (options.downloadPreview && options.thumbnailUrl) { 163 if (options.downloadPreview && options.thumbnailUrl) {
161 previewModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.PREVIEW) 164 previewModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.PREVIEW)
162 } else if (options.generatePreview || options.downloadPreview) { 165 } else if (options.generatePreview || options.downloadPreview) {
@@ -166,14 +169,15 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
166 // Create torrent 169 // Create torrent
167 await videoImport.Video.createTorrentAndSetInfoHash(videoFile) 170 await videoImport.Video.createTorrentAndSetInfoHash(videoFile)
168 171
169 const videoImportUpdated: VideoImportModel = await sequelizeTypescript.transaction(async t => { 172 const { videoImportUpdated, video } = await sequelizeTypescript.transaction(async t => {
173 const videoImportToUpdate = videoImport as MVideoImportVideo
174
170 // Refresh video 175 // Refresh video
171 const video = await VideoModel.load(videoImport.videoId, t) 176 const video = await VideoModel.load(videoImportToUpdate.videoId, t)
172 if (!video) throw new Error('Video linked to import ' + videoImport.videoId + ' does not exist anymore.') 177 if (!video) throw new Error('Video linked to import ' + videoImportToUpdate.videoId + ' does not exist anymore.')
173 videoImport.Video = video
174 178
175 const videoFileCreated = await videoFile.save({ transaction: t }) 179 const videoFileCreated = await videoFile.save({ transaction: t })
176 video.VideoFiles = [ videoFileCreated ] 180 videoImportToUpdate.Video = Object.assign(video, { VideoFiles: [ videoFileCreated ] })
177 181
178 // Update video DB object 182 // Update video DB object
179 video.duration = duration 183 video.duration = duration
@@ -188,25 +192,25 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
188 await federateVideoIfNeeded(videoForFederation, true, t) 192 await federateVideoIfNeeded(videoForFederation, true, t)
189 193
190 // Update video import object 194 // Update video import object
191 videoImport.state = VideoImportState.SUCCESS 195 videoImportToUpdate.state = VideoImportState.SUCCESS
192 const videoImportUpdated = await videoImport.save({ transaction: t }) 196 const videoImportUpdated = await videoImportToUpdate.save({ transaction: t }) as MVideoImportVideo
197 videoImportUpdated.Video = video
193 198
194 logger.info('Video %s imported.', video.uuid) 199 logger.info('Video %s imported.', video.uuid)
195 200
196 videoImportUpdated.Video = videoForFederation 201 return { videoImportUpdated, video: videoForFederation }
197 return videoImportUpdated
198 }) 202 })
199 203
200 Notifier.Instance.notifyOnFinishedVideoImport(videoImportUpdated, true) 204 Notifier.Instance.notifyOnFinishedVideoImport(videoImportUpdated, true)
201 205
202 if (videoImportUpdated.Video.isBlacklisted()) { 206 if (video.isBlacklisted()) {
203 Notifier.Instance.notifyOnVideoAutoBlacklist(videoImportUpdated.Video) 207 Notifier.Instance.notifyOnVideoAutoBlacklist(video)
204 } else { 208 } else {
205 Notifier.Instance.notifyOnNewVideoIfNeeded(videoImportUpdated.Video) 209 Notifier.Instance.notifyOnNewVideoIfNeeded(video)
206 } 210 }
207 211
208 // Create transcoding jobs? 212 // Create transcoding jobs?
209 if (videoImportUpdated.Video.state === VideoState.TO_TRANSCODE) { 213 if (video.state === VideoState.TO_TRANSCODE) {
210 // Put uuid because we don't have id auto incremented for now 214 // Put uuid because we don't have id auto incremented for now
211 const dataInput = { 215 const dataInput = {
212 type: 'optimize' as 'optimize', 216 type: 'optimize' as 'optimize',
diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts
index 981daf9a1..2ebe15bcb 100644
--- a/server/lib/job-queue/handlers/video-transcoding.ts
+++ b/server/lib/job-queue/handlers/video-transcoding.ts
@@ -11,6 +11,7 @@ import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils'
11import { generateHlsPlaylist, optimizeVideofile, transcodeOriginalVideofile, mergeAudioVideofile } from '../../video-transcoding' 11import { generateHlsPlaylist, optimizeVideofile, transcodeOriginalVideofile, mergeAudioVideofile } from '../../video-transcoding'
12import { Notifier } from '../../notifier' 12import { Notifier } from '../../notifier'
13import { CONFIG } from '../../../initializers/config' 13import { CONFIG } from '../../../initializers/config'
14import { MVideoUUID, MVideoWithFile } from '@server/typings/models'
14 15
15interface BaseTranscodingPayload { 16interface BaseTranscodingPayload {
16 videoUUID: string 17 videoUUID: string
@@ -73,7 +74,7 @@ async function processVideoTranscoding (job: Bull.Job) {
73 return video 74 return video
74} 75}
75 76
76async function onHlsPlaylistGenerationSuccess (video: VideoModel) { 77async function onHlsPlaylistGenerationSuccess (video: MVideoUUID) {
77 if (video === undefined) return undefined 78 if (video === undefined) return undefined
78 79
79 await sequelizeTypescript.transaction(async t => { 80 await sequelizeTypescript.transaction(async t => {
@@ -87,7 +88,7 @@ async function onHlsPlaylistGenerationSuccess (video: VideoModel) {
87 }) 88 })
88} 89}
89 90
90async function publishNewResolutionIfNeeded (video: VideoModel, payload?: NewResolutionTranscodingPayload | MergeAudioTranscodingPayload) { 91async function publishNewResolutionIfNeeded (video: MVideoUUID, payload?: NewResolutionTranscodingPayload | MergeAudioTranscodingPayload) {
91 const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => { 92 const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => {
92 // Maybe the video changed in database, refresh it 93 // Maybe the video changed in database, refresh it
93 let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t) 94 let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t)
@@ -119,7 +120,7 @@ async function publishNewResolutionIfNeeded (video: VideoModel, payload?: NewRes
119 await createHlsJobIfEnabled(payload) 120 await createHlsJobIfEnabled(payload)
120} 121}
121 122
122async function onVideoFileOptimizerSuccess (videoArg: VideoModel, payload: OptimizeTranscodingPayload) { 123async function onVideoFileOptimizerSuccess (videoArg: MVideoWithFile, payload: OptimizeTranscodingPayload) {
123 if (videoArg === undefined) return undefined 124 if (videoArg === undefined) return undefined
124 125
125 // Outside the transaction (IO on disk) 126 // Outside the transaction (IO on disk)
diff --git a/server/lib/notifier.ts b/server/lib/notifier.ts
index a7dfb0979..f01101b8e 100644
--- a/server/lib/notifier.ts
+++ b/server/lib/notifier.ts
@@ -8,13 +8,23 @@ import { UserModel } from '../models/account/user'
8import { PeerTubeSocket } from './peertube-socket' 8import { PeerTubeSocket } from './peertube-socket'
9import { CONFIG } from '../initializers/config' 9import { CONFIG } from '../initializers/config'
10import { VideoPrivacy, VideoState } from '../../shared/models/videos' 10import { VideoPrivacy, VideoState } from '../../shared/models/videos'
11import { VideoAbuseModel } from '../models/video/video-abuse'
12import { VideoBlacklistModel } from '../models/video/video-blacklist' 11import { VideoBlacklistModel } from '../models/video/video-blacklist'
13import * as Bluebird from 'bluebird' 12import * as Bluebird from 'bluebird'
14import { VideoImportModel } from '../models/video/video-import' 13import { VideoImportModel } from '../models/video/video-import'
15import { AccountBlocklistModel } from '../models/account/account-blocklist' 14import { AccountBlocklistModel } from '../models/account/account-blocklist'
15import {
16 MCommentOwnerVideo,
17 MVideo,
18 MVideoAbuseVideo,
19 MVideoAccountLight,
20 MVideoBlacklistVideo,
21 MVideoFullLight
22} from '../typings/models/video'
23import { MUser, MUserAccount, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/typings/models/user'
24import { MActorFollowActors, MActorFollowFull } from '../typings/models'
16import { ActorFollowModel } from '../models/activitypub/actor-follow' 25import { ActorFollowModel } from '../models/activitypub/actor-follow'
17import { AccountModel } from '../models/account/account' 26import { MVideoImportVideo } from '@server/typings/models/video/video-import'
27import { AccountModel } from '@server/models/account/account'
18 28
19class Notifier { 29class Notifier {
20 30
@@ -22,7 +32,7 @@ class Notifier {
22 32
23 private constructor () {} 33 private constructor () {}
24 34
25 notifyOnNewVideoIfNeeded (video: VideoModel): void { 35 notifyOnNewVideoIfNeeded (video: MVideoAccountLight): void {
26 // Only notify on public and published videos which are not blacklisted 36 // Only notify on public and published videos which are not blacklisted
27 if (video.privacy !== VideoPrivacy.PUBLIC || video.state !== VideoState.PUBLISHED || video.isBlacklisted()) return 37 if (video.privacy !== VideoPrivacy.PUBLIC || video.state !== VideoState.PUBLISHED || video.isBlacklisted()) return
28 38
@@ -30,7 +40,7 @@ class Notifier {
30 .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err })) 40 .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err }))
31 } 41 }
32 42
33 notifyOnVideoPublishedAfterTranscoding (video: VideoModel): void { 43 notifyOnVideoPublishedAfterTranscoding (video: MVideoFullLight): void {
34 // don't notify if didn't wait for transcoding or video is still blacklisted/waiting for scheduled update 44 // don't notify if didn't wait for transcoding or video is still blacklisted/waiting for scheduled update
35 if (!video.waitTranscoding || video.VideoBlacklist || video.ScheduleVideoUpdate) return 45 if (!video.waitTranscoding || video.VideoBlacklist || video.ScheduleVideoUpdate) return
36 46
@@ -38,7 +48,7 @@ class Notifier {
38 .catch(err => logger.error('Cannot notify owner that its video %s has been published after transcoding.', video.url, { err })) 48 .catch(err => logger.error('Cannot notify owner that its video %s has been published after transcoding.', video.url, { err }))
39 } 49 }
40 50
41 notifyOnVideoPublishedAfterScheduledUpdate (video: VideoModel): void { 51 notifyOnVideoPublishedAfterScheduledUpdate (video: MVideoFullLight): void {
42 // don't notify if video is still blacklisted or waiting for transcoding 52 // don't notify if video is still blacklisted or waiting for transcoding
43 if (video.VideoBlacklist || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return 53 if (video.VideoBlacklist || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return
44 54
@@ -46,7 +56,7 @@ class Notifier {
46 .catch(err => logger.error('Cannot notify owner that its video %s has been published after scheduled update.', video.url, { err })) 56 .catch(err => logger.error('Cannot notify owner that its video %s has been published after scheduled update.', video.url, { err }))
47 } 57 }
48 58
49 notifyOnVideoPublishedAfterRemovedFromAutoBlacklist (video: VideoModel): void { 59 notifyOnVideoPublishedAfterRemovedFromAutoBlacklist (video: MVideoFullLight): void {
50 // don't notify if video is still waiting for transcoding or scheduled update 60 // don't notify if video is still waiting for transcoding or scheduled update
51 if (video.ScheduleVideoUpdate || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return 61 if (video.ScheduleVideoUpdate || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return
52 62
@@ -54,7 +64,7 @@ class Notifier {
54 .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 64 .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
55 } 65 }
56 66
57 notifyOnNewComment (comment: VideoCommentModel): void { 67 notifyOnNewComment (comment: MCommentOwnerVideo): void {
58 this.notifyVideoOwnerOfNewComment(comment) 68 this.notifyVideoOwnerOfNewComment(comment)
59 .catch(err => logger.error('Cannot notify video owner of new comment %s.', comment.url, { err })) 69 .catch(err => logger.error('Cannot notify video owner of new comment %s.', comment.url, { err }))
60 70
@@ -62,37 +72,37 @@ class Notifier {
62 .catch(err => logger.error('Cannot notify mentions of comment %s.', comment.url, { err })) 72 .catch(err => logger.error('Cannot notify mentions of comment %s.', comment.url, { err }))
63 } 73 }
64 74
65 notifyOnNewVideoAbuse (videoAbuse: VideoAbuseModel): void { 75 notifyOnNewVideoAbuse (videoAbuse: MVideoAbuseVideo): void {
66 this.notifyModeratorsOfNewVideoAbuse(videoAbuse) 76 this.notifyModeratorsOfNewVideoAbuse(videoAbuse)
67 .catch(err => logger.error('Cannot notify of new video abuse of video %s.', videoAbuse.Video.url, { err })) 77 .catch(err => logger.error('Cannot notify of new video abuse of video %s.', videoAbuse.Video.url, { err }))
68 } 78 }
69 79
70 notifyOnVideoAutoBlacklist (video: VideoModel): void { 80 notifyOnVideoAutoBlacklist (video: MVideo): void {
71 this.notifyModeratorsOfVideoAutoBlacklist(video) 81 this.notifyModeratorsOfVideoAutoBlacklist(video)
72 .catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', video.url, { err })) 82 .catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', video.url, { err }))
73 } 83 }
74 84
75 notifyOnVideoBlacklist (videoBlacklist: VideoBlacklistModel): void { 85 notifyOnVideoBlacklist (videoBlacklist: MVideoBlacklistVideo): void {
76 this.notifyVideoOwnerOfBlacklist(videoBlacklist) 86 this.notifyVideoOwnerOfBlacklist(videoBlacklist)
77 .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err })) 87 .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err }))
78 } 88 }
79 89
80 notifyOnVideoUnblacklist (video: VideoModel): void { 90 notifyOnVideoUnblacklist (video: MVideo): void {
81 this.notifyVideoOwnerOfUnblacklist(video) 91 this.notifyVideoOwnerOfUnblacklist(video)
82 .catch(err => logger.error('Cannot notify video owner of unblacklist of %s.', video.url, { err })) 92 .catch(err => logger.error('Cannot notify video owner of unblacklist of %s.', video.url, { err }))
83 } 93 }
84 94
85 notifyOnFinishedVideoImport (videoImport: VideoImportModel, success: boolean): void { 95 notifyOnFinishedVideoImport (videoImport: MVideoImportVideo, success: boolean): void {
86 this.notifyOwnerVideoImportIsFinished(videoImport, success) 96 this.notifyOwnerVideoImportIsFinished(videoImport, success)
87 .catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err })) 97 .catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err }))
88 } 98 }
89 99
90 notifyOnNewUserRegistration (user: UserModel): void { 100 notifyOnNewUserRegistration (user: MUserAccount): void {
91 this.notifyModeratorsOfNewUserRegistration(user) 101 this.notifyModeratorsOfNewUserRegistration(user)
92 .catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err })) 102 .catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err }))
93 } 103 }
94 104
95 notifyOfNewUserFollow (actorFollow: ActorFollowModel): void { 105 notifyOfNewUserFollow (actorFollow: MActorFollowFull): void {
96 this.notifyUserOfNewActorFollow(actorFollow) 106 this.notifyUserOfNewActorFollow(actorFollow)
97 .catch(err => { 107 .catch(err => {
98 logger.error( 108 logger.error(
@@ -104,14 +114,14 @@ class Notifier {
104 }) 114 })
105 } 115 }
106 116
107 notifyOfNewInstanceFollow (actorFollow: ActorFollowModel): void { 117 notifyOfNewInstanceFollow (actorFollow: MActorFollowActors): void {
108 this.notifyAdminsOfNewInstanceFollow(actorFollow) 118 this.notifyAdminsOfNewInstanceFollow(actorFollow)
109 .catch(err => { 119 .catch(err => {
110 logger.error('Cannot notify administrators of new follower %s.', actorFollow.ActorFollower.url, { err }) 120 logger.error('Cannot notify administrators of new follower %s.', actorFollow.ActorFollower.url, { err })
111 }) 121 })
112 } 122 }
113 123
114 private async notifySubscribersOfNewVideo (video: VideoModel) { 124 private async notifySubscribersOfNewVideo (video: MVideoAccountLight) {
115 // List all followers that are users 125 // List all followers that are users
116 const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId) 126 const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId)
117 127
@@ -127,7 +137,7 @@ class Notifier {
127 userId: user.id, 137 userId: user.id,
128 videoId: video.id 138 videoId: video.id
129 }) 139 })
130 notification.Video = video 140 notification.Video = video as VideoModel
131 141
132 return notification 142 return notification
133 } 143 }
@@ -139,7 +149,7 @@ class Notifier {
139 return this.notify({ users, settingGetter, notificationCreator, emailSender }) 149 return this.notify({ users, settingGetter, notificationCreator, emailSender })
140 } 150 }
141 151
142 private async notifyVideoOwnerOfNewComment (comment: VideoCommentModel) { 152 private async notifyVideoOwnerOfNewComment (comment: MCommentOwnerVideo) {
143 if (comment.Video.isOwned() === false) return 153 if (comment.Video.isOwned() === false) return
144 154
145 const user = await UserModel.loadByVideoId(comment.videoId) 155 const user = await UserModel.loadByVideoId(comment.videoId)
@@ -162,7 +172,7 @@ class Notifier {
162 userId: user.id, 172 userId: user.id,
163 commentId: comment.id 173 commentId: comment.id
164 }) 174 })
165 notification.Comment = comment 175 notification.Comment = comment as VideoCommentModel
166 176
167 return notification 177 return notification
168 } 178 }
@@ -174,7 +184,7 @@ class Notifier {
174 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) 184 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
175 } 185 }
176 186
177 private async notifyOfCommentMention (comment: VideoCommentModel) { 187 private async notifyOfCommentMention (comment: MCommentOwnerVideo) {
178 const extractedUsernames = comment.extractMentions() 188 const extractedUsernames = comment.extractMentions()
179 logger.debug( 189 logger.debug(
180 'Extracted %d username from comment %s.', extractedUsernames.length, comment.url, 190 'Extracted %d username from comment %s.', extractedUsernames.length, comment.url,
@@ -209,7 +219,7 @@ class Notifier {
209 userId: user.id, 219 userId: user.id,
210 commentId: comment.id 220 commentId: comment.id
211 }) 221 })
212 notification.Comment = comment 222 notification.Comment = comment as VideoCommentModel
213 223
214 return notification 224 return notification
215 } 225 }
@@ -221,7 +231,7 @@ class Notifier {
221 return this.notify({ users, settingGetter, notificationCreator, emailSender }) 231 return this.notify({ users, settingGetter, notificationCreator, emailSender })
222 } 232 }
223 233
224 private async notifyUserOfNewActorFollow (actorFollow: ActorFollowModel) { 234 private async notifyUserOfNewActorFollow (actorFollow: MActorFollowFull) {
225 if (actorFollow.ActorFollowing.isOwned() === false) return 235 if (actorFollow.ActorFollowing.isOwned() === false) return
226 236
227 // Account follows one of our account? 237 // Account follows one of our account?
@@ -236,9 +246,6 @@ class Notifier {
236 246
237 if (!user) return 247 if (!user) return
238 248
239 if (!actorFollow.ActorFollower.Account || !actorFollow.ActorFollower.Account.name) {
240 actorFollow.ActorFollower.Account = await actorFollow.ActorFollower.$get('Account') as AccountModel
241 }
242 const followerAccount = actorFollow.ActorFollower.Account 249 const followerAccount = actorFollow.ActorFollower.Account
243 250
244 const accountMuted = await AccountBlocklistModel.isAccountMutedBy(user.Account.id, followerAccount.id) 251 const accountMuted = await AccountBlocklistModel.isAccountMutedBy(user.Account.id, followerAccount.id)
@@ -256,7 +263,7 @@ class Notifier {
256 userId: user.id, 263 userId: user.id,
257 actorFollowId: actorFollow.id 264 actorFollowId: actorFollow.id
258 }) 265 })
259 notification.ActorFollow = actorFollow 266 notification.ActorFollow = actorFollow as ActorFollowModel
260 267
261 return notification 268 return notification
262 } 269 }
@@ -268,7 +275,7 @@ class Notifier {
268 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) 275 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
269 } 276 }
270 277
271 private async notifyAdminsOfNewInstanceFollow (actorFollow: ActorFollowModel) { 278 private async notifyAdminsOfNewInstanceFollow (actorFollow: MActorFollowActors) {
272 const admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW) 279 const admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW)
273 280
274 logger.info('Notifying %d administrators of new instance follower: %s.', admins.length, actorFollow.ActorFollower.url) 281 logger.info('Notifying %d administrators of new instance follower: %s.', admins.length, actorFollow.ActorFollower.url)
@@ -283,7 +290,7 @@ class Notifier {
283 userId: user.id, 290 userId: user.id,
284 actorFollowId: actorFollow.id 291 actorFollowId: actorFollow.id
285 }) 292 })
286 notification.ActorFollow = actorFollow 293 notification.ActorFollow = actorFollow as ActorFollowModel
287 294
288 return notification 295 return notification
289 } 296 }
@@ -295,7 +302,7 @@ class Notifier {
295 return this.notify({ users: admins, settingGetter, notificationCreator, emailSender }) 302 return this.notify({ users: admins, settingGetter, notificationCreator, emailSender })
296 } 303 }
297 304
298 private async notifyModeratorsOfNewVideoAbuse (videoAbuse: VideoAbuseModel) { 305 private async notifyModeratorsOfNewVideoAbuse (videoAbuse: MVideoAbuseVideo) {
299 const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_ABUSES) 306 const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_ABUSES)
300 if (moderators.length === 0) return 307 if (moderators.length === 0) return
301 308
@@ -306,7 +313,7 @@ class Notifier {
306 } 313 }
307 314
308 async function notificationCreator (user: UserModel) { 315 async function notificationCreator (user: UserModel) {
309 const notification = await UserNotificationModel.create({ 316 const notification: UserNotificationModelForApi = await UserNotificationModel.create({
310 type: UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS, 317 type: UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS,
311 userId: user.id, 318 userId: user.id,
312 videoAbuseId: videoAbuse.id 319 videoAbuseId: videoAbuse.id
@@ -323,7 +330,7 @@ class Notifier {
323 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) 330 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender })
324 } 331 }
325 332
326 private async notifyModeratorsOfVideoAutoBlacklist (video: VideoModel) { 333 private async notifyModeratorsOfVideoAutoBlacklist (video: MVideo) {
327 const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST) 334 const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST)
328 if (moderators.length === 0) return 335 if (moderators.length === 0) return
329 336
@@ -339,7 +346,7 @@ class Notifier {
339 userId: user.id, 346 userId: user.id,
340 videoId: video.id 347 videoId: video.id
341 }) 348 })
342 notification.Video = video 349 notification.Video = video as VideoModel
343 350
344 return notification 351 return notification
345 } 352 }
@@ -351,7 +358,7 @@ class Notifier {
351 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) 358 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender })
352 } 359 }
353 360
354 private async notifyVideoOwnerOfBlacklist (videoBlacklist: VideoBlacklistModel) { 361 private async notifyVideoOwnerOfBlacklist (videoBlacklist: MVideoBlacklistVideo) {
355 const user = await UserModel.loadByVideoId(videoBlacklist.videoId) 362 const user = await UserModel.loadByVideoId(videoBlacklist.videoId)
356 if (!user) return 363 if (!user) return
357 364
@@ -367,7 +374,7 @@ class Notifier {
367 userId: user.id, 374 userId: user.id,
368 videoBlacklistId: videoBlacklist.id 375 videoBlacklistId: videoBlacklist.id
369 }) 376 })
370 notification.VideoBlacklist = videoBlacklist 377 notification.VideoBlacklist = videoBlacklist as VideoBlacklistModel
371 378
372 return notification 379 return notification
373 } 380 }
@@ -379,7 +386,7 @@ class Notifier {
379 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) 386 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
380 } 387 }
381 388
382 private async notifyVideoOwnerOfUnblacklist (video: VideoModel) { 389 private async notifyVideoOwnerOfUnblacklist (video: MVideo) {
383 const user = await UserModel.loadByVideoId(video.id) 390 const user = await UserModel.loadByVideoId(video.id)
384 if (!user) return 391 if (!user) return
385 392
@@ -395,7 +402,7 @@ class Notifier {
395 userId: user.id, 402 userId: user.id,
396 videoId: video.id 403 videoId: video.id
397 }) 404 })
398 notification.Video = video 405 notification.Video = video as VideoModel
399 406
400 return notification 407 return notification
401 } 408 }
@@ -407,7 +414,7 @@ class Notifier {
407 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) 414 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
408 } 415 }
409 416
410 private async notifyOwnedVideoHasBeenPublished (video: VideoModel) { 417 private async notifyOwnedVideoHasBeenPublished (video: MVideoFullLight) {
411 const user = await UserModel.loadByVideoId(video.id) 418 const user = await UserModel.loadByVideoId(video.id)
412 if (!user) return 419 if (!user) return
413 420
@@ -423,7 +430,7 @@ class Notifier {
423 userId: user.id, 430 userId: user.id,
424 videoId: video.id 431 videoId: video.id
425 }) 432 })
426 notification.Video = video 433 notification.Video = video as VideoModel
427 434
428 return notification 435 return notification
429 } 436 }
@@ -435,7 +442,7 @@ class Notifier {
435 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) 442 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
436 } 443 }
437 444
438 private async notifyOwnerVideoImportIsFinished (videoImport: VideoImportModel, success: boolean) { 445 private async notifyOwnerVideoImportIsFinished (videoImport: MVideoImportVideo, success: boolean) {
439 const user = await UserModel.loadByVideoImportId(videoImport.id) 446 const user = await UserModel.loadByVideoImportId(videoImport.id)
440 if (!user) return 447 if (!user) return
441 448
@@ -451,7 +458,7 @@ class Notifier {
451 userId: user.id, 458 userId: user.id,
452 videoImportId: videoImport.id 459 videoImportId: videoImport.id
453 }) 460 })
454 notification.VideoImport = videoImport 461 notification.VideoImport = videoImport as VideoImportModel
455 462
456 return notification 463 return notification
457 } 464 }
@@ -465,13 +472,13 @@ class Notifier {
465 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) 472 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
466 } 473 }
467 474
468 private async notifyModeratorsOfNewUserRegistration (registeredUser: UserModel) { 475 private async notifyModeratorsOfNewUserRegistration (registeredUser: MUserAccount) {
469 const moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS) 476 const moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS)
470 if (moderators.length === 0) return 477 if (moderators.length === 0) return
471 478
472 logger.info( 479 logger.info(
473 'Notifying %s moderators of new user registration of %s.', 480 'Notifying %s moderators of new user registration of %s.',
474 moderators.length, registeredUser.Account.Actor.preferredUsername 481 moderators.length, registeredUser.username
475 ) 482 )
476 483
477 function settingGetter (user: UserModel) { 484 function settingGetter (user: UserModel) {
@@ -484,7 +491,7 @@ class Notifier {
484 userId: user.id, 491 userId: user.id,
485 accountId: registeredUser.Account.id 492 accountId: registeredUser.Account.id
486 }) 493 })
487 notification.Account = registeredUser.Account 494 notification.Account = registeredUser.Account as AccountModel
488 495
489 return notification 496 return notification
490 } 497 }
@@ -497,10 +504,10 @@ class Notifier {
497 } 504 }
498 505
499 private async notify (options: { 506 private async notify (options: {
500 users: UserModel[], 507 users: MUserWithNotificationSetting[],
501 notificationCreator: (user: UserModel) => Promise<UserNotificationModel>, 508 notificationCreator: (user: MUserWithNotificationSetting) => Promise<UserNotificationModelForApi>,
502 emailSender: (emails: string[]) => Promise<any> | Bluebird<any>, 509 emailSender: (emails: string[]) => Promise<any> | Bluebird<any>,
503 settingGetter: (user: UserModel) => UserNotificationSettingValue 510 settingGetter: (user: MUserWithNotificationSetting) => UserNotificationSettingValue
504 }) { 511 }) {
505 const emails: string[] = [] 512 const emails: string[] = []
506 513
@@ -521,7 +528,7 @@ class Notifier {
521 } 528 }
522 } 529 }
523 530
524 private isEmailEnabled (user: UserModel, value: UserNotificationSettingValue) { 531 private isEmailEnabled (user: MUser, value: UserNotificationSettingValue) {
525 if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION === true && user.emailVerified === false) return false 532 if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION === true && user.emailVerified === false) return false
526 533
527 return value & UserNotificationSettingValue.EMAIL 534 return value & UserNotificationSettingValue.EMAIL
diff --git a/server/lib/oauth-model.ts b/server/lib/oauth-model.ts
index a1153e88a..086856f41 100644
--- a/server/lib/oauth-model.ts
+++ b/server/lib/oauth-model.ts
@@ -8,10 +8,11 @@ import { LRU_CACHE } from '../initializers/constants'
8import { Transaction } from 'sequelize' 8import { Transaction } from 'sequelize'
9import { CONFIG } from '../initializers/config' 9import { CONFIG } from '../initializers/config'
10import * as LRUCache from 'lru-cache' 10import * as LRUCache from 'lru-cache'
11import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token'
11 12
12type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date } 13type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date }
13 14
14const accessTokenCache = new LRUCache<string, OAuthTokenModel>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE }) 15const accessTokenCache = new LRUCache<string, MOAuthTokenUser>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE })
15const userHavingToken = new LRUCache<number, string>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE }) 16const userHavingToken = new LRUCache<number, string>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE })
16 17
17// --------------------------------------------------------------------------- 18// ---------------------------------------------------------------------------
diff --git a/server/lib/peertube-socket.ts b/server/lib/peertube-socket.ts
index eb84ecd4b..1c7b09175 100644
--- a/server/lib/peertube-socket.ts
+++ b/server/lib/peertube-socket.ts
@@ -1,8 +1,8 @@
1import * as SocketIO from 'socket.io' 1import * as SocketIO from 'socket.io'
2import { authenticateSocket } from '../middlewares' 2import { authenticateSocket } from '../middlewares'
3import { UserNotificationModel } from '../models/account/user-notification'
4import { logger } from '../helpers/logger' 3import { logger } from '../helpers/logger'
5import { Server } from 'http' 4import { Server } from 'http'
5import { UserNotificationModelForApi } from '@server/typings/models/user'
6 6
7class PeerTubeSocket { 7class PeerTubeSocket {
8 8
@@ -32,7 +32,7 @@ class PeerTubeSocket {
32 }) 32 })
33 } 33 }
34 34
35 sendNotification (userId: number, notification: UserNotificationModel) { 35 sendNotification (userId: number, notification: UserNotificationModelForApi) {
36 const socket = this.userNotificationSockets[userId] 36 const socket = this.userNotificationSockets[userId]
37 37
38 if (!socket) return 38 if (!socket) return
diff --git a/server/lib/redundancy.ts b/server/lib/redundancy.ts
index 04d3ded8f..1b4ecd7c0 100644
--- a/server/lib/redundancy.ts
+++ b/server/lib/redundancy.ts
@@ -2,8 +2,9 @@ import { VideoRedundancyModel } from '../models/redundancy/video-redundancy'
2import { sendUndoCacheFile } from './activitypub/send' 2import { sendUndoCacheFile } from './activitypub/send'
3import { Transaction } from 'sequelize' 3import { Transaction } from 'sequelize'
4import { getServerActor } from '../helpers/utils' 4import { getServerActor } from '../helpers/utils'
5import { MVideoRedundancyVideo } from '@server/typings/models'
5 6
6async function removeVideoRedundancy (videoRedundancy: VideoRedundancyModel, t?: Transaction) { 7async function removeVideoRedundancy (videoRedundancy: MVideoRedundancyVideo, t?: Transaction) {
7 const serverActor = await getServerActor() 8 const serverActor = await getServerActor()
8 9
9 // Local cache, send undo to remote instances 10 // Local cache, send undo to remote instances
diff --git a/server/lib/schedulers/videos-redundancy-scheduler.ts b/server/lib/schedulers/videos-redundancy-scheduler.ts
index 04f601bfb..de8fc075b 100644
--- a/server/lib/schedulers/videos-redundancy-scheduler.ts
+++ b/server/lib/schedulers/videos-redundancy-scheduler.ts
@@ -3,7 +3,6 @@ import { HLS_REDUNDANCY_DIRECTORY, REDUNDANCY, VIDEO_IMPORT_TIMEOUT, WEBSERVER }
3import { logger } from '../../helpers/logger' 3import { logger } from '../../helpers/logger'
4import { VideosRedundancy } from '../../../shared/models/redundancy' 4import { VideosRedundancy } from '../../../shared/models/redundancy'
5import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' 5import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
6import { VideoFileModel } from '../../models/video/video-file'
7import { downloadWebTorrentVideo } from '../../helpers/webtorrent' 6import { downloadWebTorrentVideo } from '../../helpers/webtorrent'
8import { join } from 'path' 7import { join } from 'path'
9import { move } from 'fs-extra' 8import { move } from 'fs-extra'
@@ -12,16 +11,29 @@ import { sendCreateCacheFile, sendUpdateCacheFile } from '../activitypub/send'
12import { getVideoCacheFileActivityPubUrl, getVideoCacheStreamingPlaylistActivityPubUrl } from '../activitypub/url' 11import { getVideoCacheFileActivityPubUrl, getVideoCacheStreamingPlaylistActivityPubUrl } from '../activitypub/url'
13import { removeVideoRedundancy } from '../redundancy' 12import { removeVideoRedundancy } from '../redundancy'
14import { getOrCreateVideoAndAccountAndChannel } from '../activitypub' 13import { getOrCreateVideoAndAccountAndChannel } from '../activitypub'
15import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist'
16import { VideoModel } from '../../models/video/video'
17import { downloadPlaylistSegments } from '../hls' 14import { downloadPlaylistSegments } from '../hls'
18import { CONFIG } from '../../initializers/config' 15import { CONFIG } from '../../initializers/config'
16import {
17 MStreamingPlaylist,
18 MStreamingPlaylistVideo,
19 MVideoAccountLight,
20 MVideoFile,
21 MVideoFileVideo,
22 MVideoRedundancyFileVideo,
23 MVideoRedundancyStreamingPlaylistVideo,
24 MVideoRedundancyVideo,
25 MVideoWithAllFiles
26} from '@server/typings/models'
19 27
20type CandidateToDuplicate = { 28type CandidateToDuplicate = {
21 redundancy: VideosRedundancy, 29 redundancy: VideosRedundancy,
22 video: VideoModel, 30 video: MVideoWithAllFiles,
23 files: VideoFileModel[], 31 files: MVideoFile[],
24 streamingPlaylists: VideoStreamingPlaylistModel[] 32 streamingPlaylists: MStreamingPlaylist[]
33}
34
35function isMVideoRedundancyFileVideo (o: MVideoRedundancyVideo): o is MVideoRedundancyFileVideo {
36 return !!(o as MVideoRedundancyFileVideo).VideoFile
25} 37}
26 38
27export class VideosRedundancyScheduler extends AbstractScheduler { 39export class VideosRedundancyScheduler extends AbstractScheduler {
@@ -102,7 +114,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
102 } 114 }
103 } 115 }
104 116
105 private async extendsRedundancy (redundancyModel: VideoRedundancyModel) { 117 private async extendsRedundancy (redundancyModel: MVideoRedundancyVideo) {
106 const redundancy = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES.find(s => s.strategy === redundancyModel.strategy) 118 const redundancy = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES.find(s => s.strategy === redundancyModel.strategy)
107 // Redundancy strategy disabled, remove our redundancy instead of extending expiration 119 // Redundancy strategy disabled, remove our redundancy instead of extending expiration
108 if (!redundancy) { 120 if (!redundancy) {
@@ -172,7 +184,8 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
172 } 184 }
173 } 185 }
174 186
175 private async createVideoFileRedundancy (redundancy: VideosRedundancy, video: VideoModel, file: VideoFileModel) { 187 private async createVideoFileRedundancy (redundancy: VideosRedundancy, video: MVideoAccountLight, fileArg: MVideoFile) {
188 const file = fileArg as MVideoFileVideo
176 file.Video = video 189 file.Video = video
177 190
178 const serverActor = await getServerActor() 191 const serverActor = await getServerActor()
@@ -187,7 +200,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
187 const destPath = join(CONFIG.STORAGE.REDUNDANCY_DIR, video.getVideoFilename(file)) 200 const destPath = join(CONFIG.STORAGE.REDUNDANCY_DIR, video.getVideoFilename(file))
188 await move(tmpPath, destPath) 201 await move(tmpPath, destPath)
189 202
190 const createdModel = await VideoRedundancyModel.create({ 203 const createdModel: MVideoRedundancyFileVideo = await VideoRedundancyModel.create({
191 expiresOn: this.buildNewExpiration(redundancy.minLifetime), 204 expiresOn: this.buildNewExpiration(redundancy.minLifetime),
192 url: getVideoCacheFileActivityPubUrl(file), 205 url: getVideoCacheFileActivityPubUrl(file),
193 fileUrl: video.getVideoRedundancyUrl(file, WEBSERVER.URL), 206 fileUrl: video.getVideoRedundancyUrl(file, WEBSERVER.URL),
@@ -203,7 +216,12 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
203 logger.info('Duplicated %s - %d -> %s.', video.url, file.resolution, createdModel.url) 216 logger.info('Duplicated %s - %d -> %s.', video.url, file.resolution, createdModel.url)
204 } 217 }
205 218
206 private async createStreamingPlaylistRedundancy (redundancy: VideosRedundancy, video: VideoModel, playlist: VideoStreamingPlaylistModel) { 219 private async createStreamingPlaylistRedundancy (
220 redundancy: VideosRedundancy,
221 video: MVideoAccountLight,
222 playlistArg: MStreamingPlaylist
223 ) {
224 const playlist = playlistArg as MStreamingPlaylistVideo
207 playlist.Video = video 225 playlist.Video = video
208 226
209 const serverActor = await getServerActor() 227 const serverActor = await getServerActor()
@@ -213,7 +231,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
213 const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid) 231 const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid)
214 await downloadPlaylistSegments(playlist.playlistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT) 232 await downloadPlaylistSegments(playlist.playlistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT)
215 233
216 const createdModel = await VideoRedundancyModel.create({ 234 const createdModel: MVideoRedundancyStreamingPlaylistVideo = await VideoRedundancyModel.create({
217 expiresOn: this.buildNewExpiration(redundancy.minLifetime), 235 expiresOn: this.buildNewExpiration(redundancy.minLifetime),
218 url: getVideoCacheStreamingPlaylistActivityPubUrl(video, playlist), 236 url: getVideoCacheStreamingPlaylistActivityPubUrl(video, playlist),
219 fileUrl: playlist.getVideoRedundancyUrl(WEBSERVER.URL), 237 fileUrl: playlist.getVideoRedundancyUrl(WEBSERVER.URL),
@@ -229,7 +247,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
229 logger.info('Duplicated playlist %s -> %s.', playlist.playlistUrl, createdModel.url) 247 logger.info('Duplicated playlist %s -> %s.', playlist.playlistUrl, createdModel.url)
230 } 248 }
231 249
232 private async extendsExpirationOf (redundancy: VideoRedundancyModel, expiresAfterMs: number) { 250 private async extendsExpirationOf (redundancy: MVideoRedundancyVideo, expiresAfterMs: number) {
233 logger.info('Extending expiration of %s.', redundancy.url) 251 logger.info('Extending expiration of %s.', redundancy.url)
234 252
235 const serverActor = await getServerActor() 253 const serverActor = await getServerActor()
@@ -243,7 +261,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
243 private async purgeCacheIfNeeded (candidateToDuplicate: CandidateToDuplicate) { 261 private async purgeCacheIfNeeded (candidateToDuplicate: CandidateToDuplicate) {
244 while (this.isTooHeavy(candidateToDuplicate)) { 262 while (this.isTooHeavy(candidateToDuplicate)) {
245 const redundancy = candidateToDuplicate.redundancy 263 const redundancy = candidateToDuplicate.redundancy
246 const toDelete = await VideoRedundancyModel.loadOldestLocalThatAlreadyExpired(redundancy.strategy, redundancy.minLifetime) 264 const toDelete = await VideoRedundancyModel.loadOldestLocalExpired(redundancy.strategy, redundancy.minLifetime)
247 if (!toDelete) return 265 if (!toDelete) return
248 266
249 await removeVideoRedundancy(toDelete) 267 await removeVideoRedundancy(toDelete)
@@ -263,14 +281,14 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
263 return new Date(Date.now() + expiresAfterMs) 281 return new Date(Date.now() + expiresAfterMs)
264 } 282 }
265 283
266 private buildEntryLogId (object: VideoRedundancyModel) { 284 private buildEntryLogId (object: MVideoRedundancyFileVideo | MVideoRedundancyStreamingPlaylistVideo) {
267 if (object.VideoFile) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}` 285 if (isMVideoRedundancyFileVideo(object)) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}`
268 286
269 return `${object.VideoStreamingPlaylist.playlistUrl}` 287 return `${object.VideoStreamingPlaylist.playlistUrl}`
270 } 288 }
271 289
272 private getTotalFileSizes (files: VideoFileModel[], playlists: VideoStreamingPlaylistModel[]) { 290 private getTotalFileSizes (files: MVideoFile[], playlists: MStreamingPlaylist[]) {
273 const fileReducer = (previous: number, current: VideoFileModel) => previous + current.size 291 const fileReducer = (previous: number, current: MVideoFile) => previous + current.size
274 292
275 const totalSize = files.reduce(fileReducer, 0) 293 const totalSize = files.reduce(fileReducer, 0)
276 if (playlists.length === 0) return totalSize 294 if (playlists.length === 0) return totalSize
diff --git a/server/lib/thumbnail.ts b/server/lib/thumbnail.ts
index a59773f5a..84791955e 100644
--- a/server/lib/thumbnail.ts
+++ b/server/lib/thumbnail.ts
@@ -1,20 +1,20 @@
1import { VideoFileModel } from '../models/video/video-file'
2import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils' 1import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils'
3import { CONFIG } from '../initializers/config' 2import { CONFIG } from '../initializers/config'
4import { PREVIEWS_SIZE, THUMBNAILS_SIZE, ASSETS_PATH } from '../initializers/constants' 3import { ASSETS_PATH, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '../initializers/constants'
5import { VideoModel } from '../models/video/video'
6import { ThumbnailModel } from '../models/video/thumbnail' 4import { ThumbnailModel } from '../models/video/thumbnail'
7import { ThumbnailType } from '../../shared/models/videos/thumbnail.type' 5import { ThumbnailType } from '../../shared/models/videos/thumbnail.type'
8import { processImage } from '../helpers/image-utils' 6import { processImage } from '../helpers/image-utils'
9import { join } from 'path' 7import { join } from 'path'
10import { downloadImage } from '../helpers/requests' 8import { downloadImage } from '../helpers/requests'
11import { VideoPlaylistModel } from '../models/video/video-playlist' 9import { MVideoPlaylistThumbnail } from '../typings/models/video/video-playlist'
10import { MVideoFile, MVideoThumbnail } from '../typings/models'
11import { MThumbnail } from '../typings/models/video/thumbnail'
12 12
13type ImageSize = { height: number, width: number } 13type ImageSize = { height: number, width: number }
14 14
15function createPlaylistMiniatureFromExisting ( 15function createPlaylistMiniatureFromExisting (
16 inputPath: string, 16 inputPath: string,
17 playlist: VideoPlaylistModel, 17 playlist: MVideoPlaylistThumbnail,
18 automaticallyGenerated: boolean, 18 automaticallyGenerated: boolean,
19 keepOriginal = false, 19 keepOriginal = false,
20 size?: ImageSize 20 size?: ImageSize
@@ -26,7 +26,7 @@ function createPlaylistMiniatureFromExisting (
26 return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail }) 26 return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail })
27} 27}
28 28
29function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: VideoPlaylistModel, size?: ImageSize) { 29function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: MVideoPlaylistThumbnail, size?: ImageSize) {
30 const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromPlaylist(playlist, size) 30 const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromPlaylist(playlist, size)
31 const type = ThumbnailType.MINIATURE 31 const type = ThumbnailType.MINIATURE
32 32
@@ -34,7 +34,7 @@ function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: VideoPlaylis
34 return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl }) 34 return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl })
35} 35}
36 36
37function createVideoMiniatureFromUrl (fileUrl: string, video: VideoModel, type: ThumbnailType, size?: ImageSize) { 37function createVideoMiniatureFromUrl (fileUrl: string, video: MVideoThumbnail, type: ThumbnailType, size?: ImageSize) {
38 const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size) 38 const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size)
39 const thumbnailCreator = () => downloadImage(fileUrl, basePath, filename, { width, height }) 39 const thumbnailCreator = () => downloadImage(fileUrl, basePath, filename, { width, height })
40 40
@@ -43,7 +43,7 @@ function createVideoMiniatureFromUrl (fileUrl: string, video: VideoModel, type:
43 43
44function createVideoMiniatureFromExisting ( 44function createVideoMiniatureFromExisting (
45 inputPath: string, 45 inputPath: string,
46 video: VideoModel, 46 video: MVideoThumbnail,
47 type: ThumbnailType, 47 type: ThumbnailType,
48 automaticallyGenerated: boolean, 48 automaticallyGenerated: boolean,
49 size?: ImageSize 49 size?: ImageSize
@@ -54,7 +54,7 @@ function createVideoMiniatureFromExisting (
54 return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail }) 54 return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail })
55} 55}
56 56
57function generateVideoMiniature (video: VideoModel, videoFile: VideoFileModel, type: ThumbnailType) { 57function generateVideoMiniature (video: MVideoThumbnail, videoFile: MVideoFile, type: ThumbnailType) {
58 const input = video.getVideoFilePath(videoFile) 58 const input = video.getVideoFilePath(videoFile)
59 59
60 const { filename, basePath, height, width, existingThumbnail, outputPath } = buildMetadataFromVideo(video, type) 60 const { filename, basePath, height, width, existingThumbnail, outputPath } = buildMetadataFromVideo(video, type)
@@ -65,7 +65,7 @@ function generateVideoMiniature (video: VideoModel, videoFile: VideoFileModel, t
65 return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated: true, existingThumbnail }) 65 return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated: true, existingThumbnail })
66} 66}
67 67
68function createPlaceholderThumbnail (fileUrl: string, video: VideoModel, type: ThumbnailType, size: ImageSize) { 68function createPlaceholderThumbnail (fileUrl: string, video: MVideoThumbnail, type: ThumbnailType, size: ImageSize) {
69 const { filename, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size) 69 const { filename, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size)
70 70
71 const thumbnail = existingThumbnail ? existingThumbnail : new ThumbnailModel() 71 const thumbnail = existingThumbnail ? existingThumbnail : new ThumbnailModel()
@@ -90,7 +90,7 @@ export {
90 createPlaylistMiniatureFromExisting 90 createPlaylistMiniatureFromExisting
91} 91}
92 92
93function buildMetadataFromPlaylist (playlist: VideoPlaylistModel, size: ImageSize) { 93function buildMetadataFromPlaylist (playlist: MVideoPlaylistThumbnail, size: ImageSize) {
94 const filename = playlist.generateThumbnailName() 94 const filename = playlist.generateThumbnailName()
95 const basePath = CONFIG.STORAGE.THUMBNAILS_DIR 95 const basePath = CONFIG.STORAGE.THUMBNAILS_DIR
96 96
@@ -104,7 +104,7 @@ function buildMetadataFromPlaylist (playlist: VideoPlaylistModel, size: ImageSiz
104 } 104 }
105} 105}
106 106
107function buildMetadataFromVideo (video: VideoModel, type: ThumbnailType, size?: ImageSize) { 107function buildMetadataFromVideo (video: MVideoThumbnail, type: ThumbnailType, size?: ImageSize) {
108 const existingThumbnail = Array.isArray(video.Thumbnails) 108 const existingThumbnail = Array.isArray(video.Thumbnails)
109 ? video.Thumbnails.find(t => t.type === type) 109 ? video.Thumbnails.find(t => t.type === type)
110 : undefined 110 : undefined
@@ -148,7 +148,7 @@ async function createThumbnailFromFunction (parameters: {
148 type: ThumbnailType, 148 type: ThumbnailType,
149 automaticallyGenerated?: boolean, 149 automaticallyGenerated?: boolean,
150 fileUrl?: string, 150 fileUrl?: string,
151 existingThumbnail?: ThumbnailModel 151 existingThumbnail?: MThumbnail
152}) { 152}) {
153 const { thumbnailCreator, filename, width, height, type, existingThumbnail, automaticallyGenerated = null, fileUrl = null } = parameters 153 const { thumbnailCreator, filename, width, height, type, existingThumbnail, automaticallyGenerated = null, fileUrl = null } = parameters
154 154
diff --git a/server/lib/user.ts b/server/lib/user.ts
index 0e4007770..266974cac 100644
--- a/server/lib/user.ts
+++ b/server/lib/user.ts
@@ -5,7 +5,6 @@ import { AccountModel } from '../models/account/account'
5import { UserModel } from '../models/account/user' 5import { UserModel } from '../models/account/user'
6import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub' 6import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub'
7import { createVideoChannel } from './video-channel' 7import { createVideoChannel } from './video-channel'
8import { VideoChannelModel } from '../models/video/video-channel'
9import { ActorModel } from '../models/activitypub/actor' 8import { ActorModel } from '../models/activitypub/actor'
10import { UserNotificationSettingModel } from '../models/account/user-notification-setting' 9import { UserNotificationSettingModel } from '../models/account/user-notification-setting'
11import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users' 10import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users'
@@ -14,14 +13,17 @@ import { sequelizeTypescript } from '../initializers/database'
14import { Transaction } from 'sequelize/types' 13import { Transaction } from 'sequelize/types'
15import { Redis } from './redis' 14import { Redis } from './redis'
16import { Emailer } from './emailer' 15import { Emailer } from './emailer'
16import { MAccountActor, MActor, MChannelActor } from '../typings/models'
17import { MUser, MUserId, MUserNotifSettingAccount } from '../typings/models/user'
17 18
18type ChannelNames = { name: string, displayName: string } 19type ChannelNames = { name: string, displayName: string }
20
19async function createUserAccountAndChannelAndPlaylist (parameters: { 21async function createUserAccountAndChannelAndPlaylist (parameters: {
20 userToCreate: UserModel, 22 userToCreate: UserModel,
21 userDisplayName?: string, 23 userDisplayName?: string,
22 channelNames?: ChannelNames, 24 channelNames?: ChannelNames,
23 validateUser?: boolean 25 validateUser?: boolean
24}) { 26}): Promise<{ user: MUserNotifSettingAccount, account: MAccountActor, videoChannel: MChannelActor }> {
25 const { userToCreate, userDisplayName, channelNames, validateUser = true } = parameters 27 const { userToCreate, userDisplayName, channelNames, validateUser = true } = parameters
26 28
27 const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => { 29 const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => {
@@ -30,7 +32,7 @@ async function createUserAccountAndChannelAndPlaylist (parameters: {
30 validate: validateUser 32 validate: validateUser
31 } 33 }
32 34
33 const userCreated = await userToCreate.save(userOptions) 35 const userCreated: MUserNotifSettingAccount = await userToCreate.save(userOptions)
34 userCreated.NotificationSetting = await createDefaultUserNotificationSettings(userCreated, t) 36 userCreated.NotificationSetting = await createDefaultUserNotificationSettings(userCreated, t)
35 37
36 const accountCreated = await createLocalAccountWithoutKeys({ 38 const accountCreated = await createLocalAccountWithoutKeys({
@@ -50,15 +52,15 @@ async function createUserAccountAndChannelAndPlaylist (parameters: {
50 return { user: userCreated, account: accountCreated, videoChannel, videoPlaylist } 52 return { user: userCreated, account: accountCreated, videoChannel, videoPlaylist }
51 }) 53 })
52 54
53 const [ accountKeys, channelKeys ] = await Promise.all([ 55 const [ accountActorWithKeys, channelActorWithKeys ] = await Promise.all([
54 setAsyncActorKeys(account.Actor), 56 setAsyncActorKeys(account.Actor),
55 setAsyncActorKeys(videoChannel.Actor) 57 setAsyncActorKeys(videoChannel.Actor)
56 ]) 58 ])
57 59
58 account.Actor = accountKeys 60 account.Actor = accountActorWithKeys
59 videoChannel.Actor = channelKeys 61 videoChannel.Actor = channelActorWithKeys
60 62
61 return { user, account, videoChannel } as { user: UserModel, account: AccountModel, videoChannel: VideoChannelModel } 63 return { user, account, videoChannel }
62} 64}
63 65
64async function createLocalAccountWithoutKeys (parameters: { 66async function createLocalAccountWithoutKeys (parameters: {
@@ -73,7 +75,7 @@ async function createLocalAccountWithoutKeys (parameters: {
73 const url = getAccountActivityPubUrl(name) 75 const url = getAccountActivityPubUrl(name)
74 76
75 const actorInstance = buildActorInstance(type, url, name) 77 const actorInstance = buildActorInstance(type, url, name)
76 const actorInstanceCreated = await actorInstance.save({ transaction: t }) 78 const actorInstanceCreated: MActor = await actorInstance.save({ transaction: t })
77 79
78 const accountInstance = new AccountModel({ 80 const accountInstance = new AccountModel({
79 name: displayName || name, 81 name: displayName || name,
@@ -82,7 +84,7 @@ async function createLocalAccountWithoutKeys (parameters: {
82 actorId: actorInstanceCreated.id 84 actorId: actorInstanceCreated.id
83 }) 85 })
84 86
85 const accountInstanceCreated = await accountInstance.save({ transaction: t }) 87 const accountInstanceCreated: MAccountActor = await accountInstance.save({ transaction: t })
86 accountInstanceCreated.Actor = actorInstanceCreated 88 accountInstanceCreated.Actor = actorInstanceCreated
87 89
88 return accountInstanceCreated 90 return accountInstanceCreated
@@ -102,7 +104,7 @@ async function createApplicationActor (applicationId: number) {
102 return accountCreated 104 return accountCreated
103} 105}
104 106
105async function sendVerifyUserEmail (user: UserModel, isPendingEmail = false) { 107async function sendVerifyUserEmail (user: MUser, isPendingEmail = false) {
106 const verificationString = await Redis.Instance.setVerifyEmailVerificationString(user.id) 108 const verificationString = await Redis.Instance.setVerifyEmailVerificationString(user.id)
107 let url = WEBSERVER.URL + '/verify-account/email?userId=' + user.id + '&verificationString=' + verificationString 109 let url = WEBSERVER.URL + '/verify-account/email?userId=' + user.id + '&verificationString=' + verificationString
108 110
@@ -124,7 +126,7 @@ export {
124 126
125// --------------------------------------------------------------------------- 127// ---------------------------------------------------------------------------
126 128
127function createDefaultUserNotificationSettings (user: UserModel, t: Transaction | undefined) { 129function createDefaultUserNotificationSettings (user: MUserId, t: Transaction | undefined) {
128 const values: UserNotificationSetting & { userId: number } = { 130 const values: UserNotificationSetting & { userId: number } = {
129 userId: user.id, 131 userId: user.id,
130 newVideoFromSubscription: UserNotificationSettingValue.WEB, 132 newVideoFromSubscription: UserNotificationSettingValue.WEB,
@@ -143,7 +145,7 @@ function createDefaultUserNotificationSettings (user: UserModel, t: Transaction
143 return UserNotificationSettingModel.create(values, { transaction: t }) 145 return UserNotificationSettingModel.create(values, { transaction: t })
144} 146}
145 147
146async function buildChannelAttributes (user: UserModel, channelNames?: ChannelNames) { 148async function buildChannelAttributes (user: MUser, channelNames?: ChannelNames) {
147 if (channelNames) return channelNames 149 if (channelNames) return channelNames
148 150
149 let channelName = user.username + '_channel' 151 let channelName = user.username + '_channel'
diff --git a/server/lib/video-blacklist.ts b/server/lib/video-blacklist.ts
index bdaecd8e2..a0fc26e84 100644
--- a/server/lib/video-blacklist.ts
+++ b/server/lib/video-blacklist.ts
@@ -2,16 +2,15 @@ import { Transaction } from 'sequelize'
2import { CONFIG } from '../initializers/config' 2import { CONFIG } from '../initializers/config'
3import { UserRight, VideoBlacklistType } from '../../shared/models' 3import { UserRight, VideoBlacklistType } from '../../shared/models'
4import { VideoBlacklistModel } from '../models/video/video-blacklist' 4import { VideoBlacklistModel } from '../models/video/video-blacklist'
5import { UserModel } from '../models/account/user'
6import { VideoModel } from '../models/video/video'
7import { logger } from '../helpers/logger' 5import { logger } from '../helpers/logger'
8import { UserAdminFlag } from '../../shared/models/users/user-flag.model' 6import { UserAdminFlag } from '../../shared/models/users/user-flag.model'
9import { Hooks } from './plugins/hooks' 7import { Hooks } from './plugins/hooks'
10import { Notifier } from './notifier' 8import { Notifier } from './notifier'
9import { MUser, MVideoBlacklist, MVideoWithBlacklistLight } from '@server/typings/models'
11 10
12async function autoBlacklistVideoIfNeeded (parameters: { 11async function autoBlacklistVideoIfNeeded (parameters: {
13 video: VideoModel, 12 video: MVideoWithBlacklistLight,
14 user?: UserModel, 13 user?: MUser,
15 isRemote: boolean, 14 isRemote: boolean,
16 isNew: boolean, 15 isNew: boolean,
17 notify?: boolean, 16 notify?: boolean,
@@ -32,7 +31,7 @@ async function autoBlacklistVideoIfNeeded (parameters: {
32 reason: 'Auto-blacklisted. Moderator review required.', 31 reason: 'Auto-blacklisted. Moderator review required.',
33 type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED 32 type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED
34 } 33 }
35 const [ videoBlacklist ] = await VideoBlacklistModel.findOrCreate({ 34 const [ videoBlacklist ] = await VideoBlacklistModel.findOrCreate<MVideoBlacklist>({
36 where: { 35 where: {
37 videoId: video.id 36 videoId: video.id
38 }, 37 },
@@ -49,10 +48,10 @@ async function autoBlacklistVideoIfNeeded (parameters: {
49} 48}
50 49
51async function autoBlacklistNeeded (parameters: { 50async function autoBlacklistNeeded (parameters: {
52 video: VideoModel, 51 video: MVideoWithBlacklistLight,
53 isRemote: boolean, 52 isRemote: boolean,
54 isNew: boolean, 53 isNew: boolean,
55 user?: UserModel 54 user?: MUser
56}) { 55}) {
57 const { user, video, isRemote, isNew } = parameters 56 const { user, video, isRemote, isNew } = parameters
58 57
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts
index ee0482c36..ee8eb6568 100644
--- a/server/lib/video-channel.ts
+++ b/server/lib/video-channel.ts
@@ -1,12 +1,19 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as uuidv4 from 'uuid/v4' 2import * as uuidv4 from 'uuid/v4'
3import { VideoChannelCreate } from '../../shared/models' 3import { VideoChannelCreate } from '../../shared/models'
4import { AccountModel } from '../models/account/account'
5import { VideoChannelModel } from '../models/video/video-channel' 4import { VideoChannelModel } from '../models/video/video-channel'
6import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub' 5import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub'
7import { VideoModel } from '../models/video/video' 6import { VideoModel } from '../models/video/video'
7import { MAccountId, MChannelActor, MChannelId } from '../typings/models'
8 8
9async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) { 9type CustomVideoChannelModelAccount <T extends MAccountId> = MChannelActor &
10 { Account?: T }
11
12async function createVideoChannel <T extends MAccountId> (
13 videoChannelInfo: VideoChannelCreate,
14 account: T,
15 t: Sequelize.Transaction
16): Promise<CustomVideoChannelModelAccount<T>> {
10 const uuid = uuidv4() 17 const uuid = uuidv4()
11 const url = getVideoChannelActivityPubUrl(videoChannelInfo.name) 18 const url = getVideoChannelActivityPubUrl(videoChannelInfo.name)
12 const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid) 19 const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid)
@@ -21,10 +28,10 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
21 actorId: actorInstanceCreated.id 28 actorId: actorInstanceCreated.id
22 } 29 }
23 30
24 const videoChannel = VideoChannelModel.build(videoChannelData) 31 const videoChannel = new VideoChannelModel(videoChannelData)
25 32
26 const options = { transaction: t } 33 const options = { transaction: t }
27 const videoChannelCreated = await videoChannel.save(options) 34 const videoChannelCreated: CustomVideoChannelModelAccount<T> = await videoChannel.save(options) as MChannelActor
28 35
29 // Do not forget to add Account/Actor information to the created video channel 36 // Do not forget to add Account/Actor information to the created video channel
30 videoChannelCreated.Account = account 37 videoChannelCreated.Account = account
@@ -34,7 +41,7 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
34 return videoChannelCreated 41 return videoChannelCreated
35} 42}
36 43
37async function federateAllVideosOfChannel (videoChannel: VideoChannelModel) { 44async function federateAllVideosOfChannel (videoChannel: MChannelId) {
38 const videoIds = await VideoModel.getAllIdsFromChannel(videoChannel) 45 const videoIds = await VideoModel.getAllIdsFromChannel(videoChannel)
39 46
40 for (const videoId of videoIds) { 47 for (const videoId of videoIds) {
diff --git a/server/lib/video-comment.ts b/server/lib/video-comment.ts
index 449aa74cb..bb811bd2c 100644
--- a/server/lib/video-comment.ts
+++ b/server/lib/video-comment.ts
@@ -1,17 +1,16 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { ResultList } from '../../shared/models' 2import { ResultList } from '../../shared/models'
3import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model' 3import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model'
4import { AccountModel } from '../models/account/account'
5import { VideoModel } from '../models/video/video'
6import { VideoCommentModel } from '../models/video/video-comment' 4import { VideoCommentModel } from '../models/video/video-comment'
7import { getVideoCommentActivityPubUrl } from './activitypub' 5import { getVideoCommentActivityPubUrl } from './activitypub'
8import { sendCreateVideoComment } from './activitypub/send' 6import { sendCreateVideoComment } from './activitypub/send'
7import { MAccountDefault, MComment, MCommentOwnerVideoReply, MVideoFullLight } from '../typings/models'
9 8
10async function createVideoComment (obj: { 9async function createVideoComment (obj: {
11 text: string, 10 text: string,
12 inReplyToComment: VideoCommentModel | null, 11 inReplyToComment: MComment | null,
13 video: VideoModel 12 video: MVideoFullLight,
14 account: AccountModel 13 account: MAccountDefault
15}, t: Sequelize.Transaction) { 14}, t: Sequelize.Transaction) {
16 let originCommentId: number | null = null 15 let originCommentId: number | null = null
17 let inReplyToCommentId: number | null = null 16 let inReplyToCommentId: number | null = null
@@ -32,7 +31,7 @@ async function createVideoComment (obj: {
32 31
33 comment.url = getVideoCommentActivityPubUrl(obj.video, comment) 32 comment.url = getVideoCommentActivityPubUrl(obj.video, comment)
34 33
35 const savedComment = await comment.save({ transaction: t }) 34 const savedComment: MCommentOwnerVideoReply = await comment.save({ transaction: t })
36 savedComment.InReplyToVideoComment = obj.inReplyToComment 35 savedComment.InReplyToVideoComment = obj.inReplyToComment
37 savedComment.Video = obj.video 36 savedComment.Video = obj.video
38 savedComment.Account = obj.account 37 savedComment.Account = obj.account
diff --git a/server/lib/video-playlist.ts b/server/lib/video-playlist.ts
index 6e214e60f..29b70cfda 100644
--- a/server/lib/video-playlist.ts
+++ b/server/lib/video-playlist.ts
@@ -1,12 +1,13 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { AccountModel } from '../models/account/account'
3import { VideoPlaylistModel } from '../models/video/video-playlist' 2import { VideoPlaylistModel } from '../models/video/video-playlist'
4import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model' 3import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model'
5import { getVideoPlaylistActivityPubUrl } from './activitypub' 4import { getVideoPlaylistActivityPubUrl } from './activitypub'
6import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model' 5import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model'
6import { MAccount } from '../typings/models'
7import { MVideoPlaylistOwner } from '../typings/models/video/video-playlist'
7 8
8async function createWatchLaterPlaylist (account: AccountModel, t: Sequelize.Transaction) { 9async function createWatchLaterPlaylist (account: MAccount, t: Sequelize.Transaction) {
9 const videoPlaylist = new VideoPlaylistModel({ 10 const videoPlaylist: MVideoPlaylistOwner = new VideoPlaylistModel({
10 name: 'Watch later', 11 name: 'Watch later',
11 privacy: VideoPlaylistPrivacy.PRIVATE, 12 privacy: VideoPlaylistPrivacy.PRIVATE,
12 type: VideoPlaylistType.WATCH_LATER, 13 type: VideoPlaylistType.WATCH_LATER,
diff --git a/server/lib/video-transcoding.ts b/server/lib/video-transcoding.ts
index ba6b29163..a204c0c63 100644
--- a/server/lib/video-transcoding.ts
+++ b/server/lib/video-transcoding.ts
@@ -5,16 +5,16 @@ import { ensureDir, move, remove, stat } from 'fs-extra'
5import { logger } from '../helpers/logger' 5import { logger } from '../helpers/logger'
6import { VideoResolution } from '../../shared/models/videos' 6import { VideoResolution } from '../../shared/models/videos'
7import { VideoFileModel } from '../models/video/video-file' 7import { VideoFileModel } from '../models/video/video-file'
8import { VideoModel } from '../models/video/video'
9import { updateMasterHLSPlaylist, updateSha256Segments } from './hls' 8import { updateMasterHLSPlaylist, updateSha256Segments } from './hls'
10import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' 9import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
11import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' 10import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type'
12import { CONFIG } from '../initializers/config' 11import { CONFIG } from '../initializers/config'
12import { MVideoFile, MVideoWithFile, MVideoWithFileThumbnail } from '@server/typings/models'
13 13
14/** 14/**
15 * Optimize the original video file and replace it. The resolution is not changed. 15 * Optimize the original video file and replace it. The resolution is not changed.
16 */ 16 */
17async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFileModel) { 17async function optimizeVideofile (video: MVideoWithFile, inputVideoFileArg?: MVideoFile) {
18 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR 18 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
19 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR 19 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
20 const newExtname = '.mp4' 20 const newExtname = '.mp4'
@@ -57,7 +57,7 @@ async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFi
57/** 57/**
58 * Transcode the original video file to a lower resolution. 58 * Transcode the original video file to a lower resolution.
59 */ 59 */
60async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoResolution, isPortrait: boolean) { 60async function transcodeOriginalVideofile (video: MVideoWithFile, resolution: VideoResolution, isPortrait: boolean) {
61 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR 61 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
62 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR 62 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
63 const extname = '.mp4' 63 const extname = '.mp4'
@@ -87,7 +87,7 @@ async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoR
87 return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath) 87 return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath)
88} 88}
89 89
90async function mergeAudioVideofile (video: VideoModel, resolution: VideoResolution) { 90async function mergeAudioVideofile (video: MVideoWithFileThumbnail, resolution: VideoResolution) {
91 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR 91 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
92 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR 92 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
93 const newExtname = '.mp4' 93 const newExtname = '.mp4'
@@ -117,7 +117,7 @@ async function mergeAudioVideofile (video: VideoModel, resolution: VideoResoluti
117 return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) 117 return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath)
118} 118}
119 119
120async function generateHlsPlaylist (video: VideoModel, resolution: VideoResolution, isPortraitMode: boolean) { 120async function generateHlsPlaylist (video: MVideoWithFile, resolution: VideoResolution, isPortraitMode: boolean) {
121 const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) 121 const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)
122 await ensureDir(join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)) 122 await ensureDir(join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid))
123 123
@@ -165,14 +165,14 @@ export {
165 165
166// --------------------------------------------------------------------------- 166// ---------------------------------------------------------------------------
167 167
168async function onVideoFileTranscoding (video: VideoModel, videoFile: VideoFileModel, transcodingPath: string, outputPath: string) { 168async function onVideoFileTranscoding (video: MVideoWithFile, videoFile: MVideoFile, transcodingPath: string, outputPath: string) {
169 const stats = await stat(transcodingPath) 169 const stats = await stat(transcodingPath)
170 const fps = await getVideoFileFPS(transcodingPath) 170 const fps = await getVideoFileFPS(transcodingPath)
171 171
172 await move(transcodingPath, outputPath) 172 await move(transcodingPath, outputPath)
173 173
174 videoFile.set('size', stats.size) 174 videoFile.size = stats.size
175 videoFile.set('fps', fps) 175 videoFile.fps = fps
176 176
177 await video.createTorrentAndSetInfoHash(videoFile) 177 await video.createTorrentAndSetInfoHash(videoFile)
178 178
diff --git a/server/middlewares/validators/follows.ts b/server/middlewares/validators/follows.ts
index c3d772297..788735663 100644
--- a/server/middlewares/validators/follows.ts
+++ b/server/middlewares/validators/follows.ts
@@ -10,6 +10,7 @@ import { areValidationErrors } from './utils'
10import { ActorModel } from '../../models/activitypub/actor' 10import { ActorModel } from '../../models/activitypub/actor'
11import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' 11import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
12import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' 12import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
13import { MActorFollowActorsDefault } from '@server/typings/models'
13 14
14const followValidator = [ 15const followValidator = [
15 body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'), 16 body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'),
@@ -65,7 +66,7 @@ const getFollowerValidator = [
65 66
66 if (areValidationErrors(req, res)) return 67 if (areValidationErrors(req, res)) return
67 68
68 let follow: ActorFollowModel 69 let follow: MActorFollowActorsDefault
69 try { 70 try {
70 const actorUrl = await loadActorUrlOrGetFromWebfinger(req.params.nameWithHost) 71 const actorUrl = await loadActorUrlOrGetFromWebfinger(req.params.nameWithHost)
71 const actor = await ActorModel.loadByUrl(actorUrl) 72 const actor = await ActorModel.loadByUrl(actorUrl)
diff --git a/server/middlewares/validators/redundancy.ts b/server/middlewares/validators/redundancy.ts
index 1fdac0e4e..e65d3b8d3 100644
--- a/server/middlewares/validators/redundancy.ts
+++ b/server/middlewares/validators/redundancy.ts
@@ -24,7 +24,7 @@ const videoFileRedundancyGetValidator = [
24 if (areValidationErrors(req, res)) return 24 if (areValidationErrors(req, res)) return
25 if (!await doesVideoExist(req.params.videoId, res)) return 25 if (!await doesVideoExist(req.params.videoId, res)) return
26 26
27 const video = res.locals.video 27 const video = res.locals.videoAll
28 const videoFile = video.VideoFiles.find(f => { 28 const videoFile = video.VideoFiles.find(f => {
29 return f.resolution === req.params.resolution && (!req.params.fps || f.fps === req.params.fps) 29 return f.resolution === req.params.resolution && (!req.params.fps || f.fps === req.params.fps)
30 }) 30 })
@@ -50,7 +50,7 @@ const videoPlaylistRedundancyGetValidator = [
50 if (areValidationErrors(req, res)) return 50 if (areValidationErrors(req, res)) return
51 if (!await doesVideoExist(req.params.videoId, res)) return 51 if (!await doesVideoExist(req.params.videoId, res)) return
52 52
53 const video = res.locals.video 53 const video = res.locals.videoAll
54 const videoStreamingPlaylist = video.VideoStreamingPlaylists.find(p => p === req.params.streamingPlaylistType) 54 const videoStreamingPlaylist = video.VideoStreamingPlaylists.find(p => p === req.params.streamingPlaylistType)
55 55
56 if (!videoStreamingPlaylist) return res.status(404).json({ error: 'Video playlist not found.' }) 56 if (!videoStreamingPlaylist) return res.status(404).json({ error: 'Video playlist not found.' })
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts
index 16d297047..40dd0f0e9 100644
--- a/server/middlewares/validators/users.ts
+++ b/server/middlewares/validators/users.ts
@@ -2,7 +2,7 @@ import * as Bluebird from 'bluebird'
2import * as express from 'express' 2import * as express from 'express'
3import { body, param } from 'express-validator' 3import { body, param } from 'express-validator'
4import { omit } from 'lodash' 4import { omit } from 'lodash'
5import { isIdOrUUIDValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' 5import { isIdOrUUIDValid, toBooleanOrNull } from '../../helpers/custom-validators/misc'
6import { 6import {
7 isUserAdminFlagsValid, 7 isUserAdminFlagsValid,
8 isUserAutoPlayVideoValid, 8 isUserAutoPlayVideoValid,
@@ -31,6 +31,7 @@ import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
31import { isThemeRegistered } from '../../lib/plugins/theme-utils' 31import { isThemeRegistered } from '../../lib/plugins/theme-utils'
32import { doesVideoExist } from '../../helpers/middlewares' 32import { doesVideoExist } from '../../helpers/middlewares'
33import { UserRole } from '../../../shared/models/users' 33import { UserRole } from '../../../shared/models/users'
34import { MUserDefault } from '@server/typings/models'
34 35
35const usersAddValidator = [ 36const usersAddValidator = [
36 body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'), 37 body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'),
@@ -457,7 +458,7 @@ async function checkUserNameOrEmailDoesNotAlreadyExist (username: string, email:
457 return true 458 return true
458} 459}
459 460
460async function checkUserExist (finder: () => Bluebird<UserModel>, res: express.Response, abortResponse = true) { 461async function checkUserExist (finder: () => Bluebird<MUserDefault>, res: express.Response, abortResponse = true) {
461 const user = await finder() 462 const user = await finder()
462 463
463 if (!user) { 464 if (!user) {
diff --git a/server/middlewares/validators/videos/video-abuses.ts b/server/middlewares/validators/videos/video-abuses.ts
index e27d91bb1..a4aef4024 100644
--- a/server/middlewares/validators/videos/video-abuses.ts
+++ b/server/middlewares/validators/videos/video-abuses.ts
@@ -33,7 +33,7 @@ const videoAbuseGetValidator = [
33 33
34 if (areValidationErrors(req, res)) return 34 if (areValidationErrors(req, res)) return
35 if (!await doesVideoExist(req.params.videoId, res)) return 35 if (!await doesVideoExist(req.params.videoId, res)) return
36 if (!await doesVideoAbuseExist(req.params.id, res.locals.video.id, res)) return 36 if (!await doesVideoAbuseExist(req.params.id, res.locals.videoAll.id, res)) return
37 37
38 return next() 38 return next()
39 } 39 }
@@ -54,7 +54,7 @@ const videoAbuseUpdateValidator = [
54 54
55 if (areValidationErrors(req, res)) return 55 if (areValidationErrors(req, res)) return
56 if (!await doesVideoExist(req.params.videoId, res)) return 56 if (!await doesVideoExist(req.params.videoId, res)) return
57 if (!await doesVideoAbuseExist(req.params.id, res.locals.video.id, res)) return 57 if (!await doesVideoAbuseExist(req.params.id, res.locals.videoAll.id, res)) return
58 58
59 return next() 59 return next()
60 } 60 }
diff --git a/server/middlewares/validators/videos/video-blacklist.ts b/server/middlewares/validators/videos/video-blacklist.ts
index 3e8c5b30c..5440e57e7 100644
--- a/server/middlewares/validators/videos/video-blacklist.ts
+++ b/server/middlewares/validators/videos/video-blacklist.ts
@@ -14,7 +14,7 @@ const videosBlacklistRemoveValidator = [
14 14
15 if (areValidationErrors(req, res)) return 15 if (areValidationErrors(req, res)) return
16 if (!await doesVideoExist(req.params.videoId, res)) return 16 if (!await doesVideoExist(req.params.videoId, res)) return
17 if (!await doesVideoBlacklistExist(res.locals.video.id, res)) return 17 if (!await doesVideoBlacklistExist(res.locals.videoAll.id, res)) return
18 18
19 return next() 19 return next()
20 } 20 }
@@ -36,7 +36,7 @@ const videosBlacklistAddValidator = [
36 if (areValidationErrors(req, res)) return 36 if (areValidationErrors(req, res)) return
37 if (!await doesVideoExist(req.params.videoId, res)) return 37 if (!await doesVideoExist(req.params.videoId, res)) return
38 38
39 const video = res.locals.video 39 const video = res.locals.videoAll
40 if (req.body.unfederate === true && video.remote === true) { 40 if (req.body.unfederate === true && video.remote === true) {
41 return res 41 return res
42 .status(409) 42 .status(409)
@@ -59,7 +59,7 @@ const videosBlacklistUpdateValidator = [
59 59
60 if (areValidationErrors(req, res)) return 60 if (areValidationErrors(req, res)) return
61 if (!await doesVideoExist(req.params.videoId, res)) return 61 if (!await doesVideoExist(req.params.videoId, res)) return
62 if (!await doesVideoBlacklistExist(res.locals.video.id, res)) return 62 if (!await doesVideoBlacklistExist(res.locals.videoAll.id, res)) return
63 63
64 return next() 64 return next()
65 } 65 }
diff --git a/server/middlewares/validators/videos/video-captions.ts b/server/middlewares/validators/videos/video-captions.ts
index f5610222a..2fb1da5ce 100644
--- a/server/middlewares/validators/videos/video-captions.ts
+++ b/server/middlewares/validators/videos/video-captions.ts
@@ -26,7 +26,7 @@ const addVideoCaptionValidator = [
26 26
27 // Check if the user who did the request is able to update the video 27 // Check if the user who did the request is able to update the video
28 const user = res.locals.oauth.token.User 28 const user = res.locals.oauth.token.User
29 if (!checkUserCanManageVideo(user, res.locals.video, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req) 29 if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req)
30 30
31 return next() 31 return next()
32 } 32 }
@@ -41,11 +41,11 @@ const deleteVideoCaptionValidator = [
41 41
42 if (areValidationErrors(req, res)) return 42 if (areValidationErrors(req, res)) return
43 if (!await doesVideoExist(req.params.videoId, res)) return 43 if (!await doesVideoExist(req.params.videoId, res)) return
44 if (!await doesVideoCaptionExist(res.locals.video, req.params.captionLanguage, res)) return 44 if (!await doesVideoCaptionExist(res.locals.videoAll, req.params.captionLanguage, res)) return
45 45
46 // Check if the user who did the request is able to update the video 46 // Check if the user who did the request is able to update the video
47 const user = res.locals.oauth.token.User 47 const user = res.locals.oauth.token.User
48 if (!checkUserCanManageVideo(user, res.locals.video, UserRight.UPDATE_ANY_VIDEO, res)) return 48 if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.UPDATE_ANY_VIDEO, res)) return
49 49
50 return next() 50 return next()
51 } 51 }
diff --git a/server/middlewares/validators/videos/video-channels.ts b/server/middlewares/validators/videos/video-channels.ts
index 3ee5064fc..a0df03f7e 100644
--- a/server/middlewares/validators/videos/video-channels.ts
+++ b/server/middlewares/validators/videos/video-channels.ts
@@ -7,13 +7,14 @@ import {
7 isVideoChannelSupportValid 7 isVideoChannelSupportValid
8} from '../../../helpers/custom-validators/video-channels' 8} from '../../../helpers/custom-validators/video-channels'
9import { logger } from '../../../helpers/logger' 9import { logger } from '../../../helpers/logger'
10import { UserModel } from '../../../models/account/user'
11import { VideoChannelModel } from '../../../models/video/video-channel' 10import { VideoChannelModel } from '../../../models/video/video-channel'
12import { areValidationErrors } from '../utils' 11import { areValidationErrors } from '../utils'
13import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor' 12import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor'
14import { ActorModel } from '../../../models/activitypub/actor' 13import { ActorModel } from '../../../models/activitypub/actor'
15import { isBooleanValid } from '../../../helpers/custom-validators/misc' 14import { isBooleanValid } from '../../../helpers/custom-validators/misc'
16import { doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../../../helpers/middlewares' 15import { doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../../../helpers/middlewares'
16import { MChannelActorAccountDefault } from '../../../typings/models/video'
17import { MUser } from '@server/typings/models'
17 18
18const videoChannelsAddValidator = [ 19const videoChannelsAddValidator = [
19 body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), 20 body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'),
@@ -131,7 +132,7 @@ export {
131 132
132// --------------------------------------------------------------------------- 133// ---------------------------------------------------------------------------
133 134
134function checkUserCanDeleteVideoChannel (user: UserModel, videoChannel: VideoChannelModel, res: express.Response) { 135function checkUserCanDeleteVideoChannel (user: MUser, videoChannel: MChannelActorAccountDefault, res: express.Response) {
135 if (videoChannel.Actor.isOwned() === false) { 136 if (videoChannel.Actor.isOwned() === false) {
136 res.status(403) 137 res.status(403)
137 .json({ error: 'Cannot remove video channel of another server.' }) 138 .json({ error: 'Cannot remove video channel of another server.' })
diff --git a/server/middlewares/validators/videos/video-comments.ts b/server/middlewares/validators/videos/video-comments.ts
index 83a0c24b0..8adbb02ba 100644
--- a/server/middlewares/validators/videos/video-comments.ts
+++ b/server/middlewares/validators/videos/video-comments.ts
@@ -4,13 +4,13 @@ import { UserRight } from '../../../../shared'
4import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc' 4import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc'
5import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments' 5import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments'
6import { logger } from '../../../helpers/logger' 6import { logger } from '../../../helpers/logger'
7import { UserModel } from '../../../models/account/user'
8import { VideoModel } from '../../../models/video/video'
9import { VideoCommentModel } from '../../../models/video/video-comment' 7import { VideoCommentModel } from '../../../models/video/video-comment'
10import { areValidationErrors } from '../utils' 8import { areValidationErrors } from '../utils'
11import { Hooks } from '../../../lib/plugins/hooks' 9import { Hooks } from '../../../lib/plugins/hooks'
12import { isLocalVideoThreadAccepted, isLocalVideoCommentReplyAccepted, AcceptResult } from '../../../lib/moderation' 10import { AcceptResult, isLocalVideoCommentReplyAccepted, isLocalVideoThreadAccepted } from '../../../lib/moderation'
13import { doesVideoExist } from '../../../helpers/middlewares' 11import { doesVideoExist } from '../../../helpers/middlewares'
12import { MCommentOwner, MVideo, MVideoFullLight, MVideoId } from '../../../typings/models/video'
13import { MUser } from '@server/typings/models'
14 14
15const listVideoCommentThreadsValidator = [ 15const listVideoCommentThreadsValidator = [
16 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), 16 param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
@@ -34,7 +34,7 @@ const listVideoThreadCommentsValidator = [
34 34
35 if (areValidationErrors(req, res)) return 35 if (areValidationErrors(req, res)) return
36 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return 36 if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return
37 if (!await doesVideoCommentThreadExist(req.params.threadId, res.locals.video, res)) return 37 if (!await doesVideoCommentThreadExist(req.params.threadId, res.locals.onlyVideo, res)) return
38 38
39 return next() 39 return next()
40 } 40 }
@@ -49,8 +49,8 @@ const addVideoCommentThreadValidator = [
49 49
50 if (areValidationErrors(req, res)) return 50 if (areValidationErrors(req, res)) return
51 if (!await doesVideoExist(req.params.videoId, res)) return 51 if (!await doesVideoExist(req.params.videoId, res)) return
52 if (!isVideoCommentsEnabled(res.locals.video, res)) return 52 if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return
53 if (!await isVideoCommentAccepted(req, res, false)) return 53 if (!await isVideoCommentAccepted(req, res, res.locals.videoAll,false)) return
54 54
55 return next() 55 return next()
56 } 56 }
@@ -66,9 +66,9 @@ const addVideoCommentReplyValidator = [
66 66
67 if (areValidationErrors(req, res)) return 67 if (areValidationErrors(req, res)) return
68 if (!await doesVideoExist(req.params.videoId, res)) return 68 if (!await doesVideoExist(req.params.videoId, res)) return
69 if (!isVideoCommentsEnabled(res.locals.video, res)) return 69 if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return
70 if (!await doesVideoCommentExist(req.params.commentId, res.locals.video, res)) return 70 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return
71 if (!await isVideoCommentAccepted(req, res, true)) return 71 if (!await isVideoCommentAccepted(req, res, res.locals.videoAll, true)) return
72 72
73 return next() 73 return next()
74 } 74 }
@@ -83,7 +83,7 @@ const videoCommentGetValidator = [
83 83
84 if (areValidationErrors(req, res)) return 84 if (areValidationErrors(req, res)) return
85 if (!await doesVideoExist(req.params.videoId, res, 'id')) return 85 if (!await doesVideoExist(req.params.videoId, res, 'id')) return
86 if (!await doesVideoCommentExist(req.params.commentId, res.locals.video, res)) return 86 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoId, res)) return
87 87
88 return next() 88 return next()
89 } 89 }
@@ -98,10 +98,10 @@ const removeVideoCommentValidator = [
98 98
99 if (areValidationErrors(req, res)) return 99 if (areValidationErrors(req, res)) return
100 if (!await doesVideoExist(req.params.videoId, res)) return 100 if (!await doesVideoExist(req.params.videoId, res)) return
101 if (!await doesVideoCommentExist(req.params.commentId, res.locals.video, res)) return 101 if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return
102 102
103 // Check if the user who did the request is able to delete the video 103 // Check if the user who did the request is able to delete the video
104 if (!checkUserCanDeleteVideoComment(res.locals.oauth.token.User, res.locals.videoComment, res)) return 104 if (!checkUserCanDeleteVideoComment(res.locals.oauth.token.User, res.locals.videoCommentFull, res)) return
105 105
106 return next() 106 return next()
107 } 107 }
@@ -120,7 +120,7 @@ export {
120 120
121// --------------------------------------------------------------------------- 121// ---------------------------------------------------------------------------
122 122
123async function doesVideoCommentThreadExist (id: number, video: VideoModel, res: express.Response) { 123async function doesVideoCommentThreadExist (id: number, video: MVideoId, res: express.Response) {
124 const videoComment = await VideoCommentModel.loadById(id) 124 const videoComment = await VideoCommentModel.loadById(id)
125 125
126 if (!videoComment) { 126 if (!videoComment) {
@@ -151,7 +151,7 @@ async function doesVideoCommentThreadExist (id: number, video: VideoModel, res:
151 return true 151 return true
152} 152}
153 153
154async function doesVideoCommentExist (id: number, video: VideoModel, res: express.Response) { 154async function doesVideoCommentExist (id: number, video: MVideoId, res: express.Response) {
155 const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id) 155 const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id)
156 156
157 if (!videoComment) { 157 if (!videoComment) {
@@ -170,11 +170,11 @@ async function doesVideoCommentExist (id: number, video: VideoModel, res: expres
170 return false 170 return false
171 } 171 }
172 172
173 res.locals.videoComment = videoComment 173 res.locals.videoCommentFull = videoComment
174 return true 174 return true
175} 175}
176 176
177function isVideoCommentsEnabled (video: VideoModel, res: express.Response) { 177function isVideoCommentsEnabled (video: MVideo, res: express.Response) {
178 if (video.commentsEnabled !== true) { 178 if (video.commentsEnabled !== true) {
179 res.status(409) 179 res.status(409)
180 .json({ error: 'Video comments are disabled for this video.' }) 180 .json({ error: 'Video comments are disabled for this video.' })
@@ -186,7 +186,7 @@ function isVideoCommentsEnabled (video: VideoModel, res: express.Response) {
186 return true 186 return true
187} 187}
188 188
189function checkUserCanDeleteVideoComment (user: UserModel, videoComment: VideoCommentModel, res: express.Response) { 189function checkUserCanDeleteVideoComment (user: MUser, videoComment: MCommentOwner, res: express.Response) {
190 const account = videoComment.Account 190 const account = videoComment.Account
191 if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) === false && account.userId !== user.id) { 191 if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) === false && account.userId !== user.id) {
192 res.status(403) 192 res.status(403)
@@ -198,9 +198,9 @@ function checkUserCanDeleteVideoComment (user: UserModel, videoComment: VideoCom
198 return true 198 return true
199} 199}
200 200
201async function isVideoCommentAccepted (req: express.Request, res: express.Response, isReply: boolean) { 201async function isVideoCommentAccepted (req: express.Request, res: express.Response, video: MVideoFullLight, isReply: boolean) {
202 const acceptParameters = { 202 const acceptParameters = {
203 video: res.locals.video, 203 video,
204 commentBody: req.body, 204 commentBody: req.body,
205 user: res.locals.oauth.token.User 205 user: res.locals.oauth.token.User
206 } 206 }
@@ -208,7 +208,7 @@ async function isVideoCommentAccepted (req: express.Request, res: express.Respon
208 let acceptedResult: AcceptResult 208 let acceptedResult: AcceptResult
209 209
210 if (isReply) { 210 if (isReply) {
211 const acceptReplyParameters = Object.assign(acceptParameters, { parentComment: res.locals.videoComment }) 211 const acceptReplyParameters = Object.assign(acceptParameters, { parentComment: res.locals.videoCommentFull })
212 212
213 acceptedResult = await Hooks.wrapFun( 213 acceptedResult = await Hooks.wrapFun(
214 isLocalVideoCommentReplyAccepted, 214 isLocalVideoCommentReplyAccepted,
diff --git a/server/middlewares/validators/videos/video-playlists.ts b/server/middlewares/validators/videos/video-playlists.ts
index 5823795be..ca36d419a 100644
--- a/server/middlewares/validators/videos/video-playlists.ts
+++ b/server/middlewares/validators/videos/video-playlists.ts
@@ -2,7 +2,6 @@ import * as express from 'express'
2import { body, param, query, ValidationChain } from 'express-validator' 2import { body, param, query, ValidationChain } from 'express-validator'
3import { UserRight, VideoPlaylistCreate, VideoPlaylistUpdate } from '../../../../shared' 3import { UserRight, VideoPlaylistCreate, VideoPlaylistUpdate } from '../../../../shared'
4import { logger } from '../../../helpers/logger' 4import { logger } from '../../../helpers/logger'
5import { UserModel } from '../../../models/account/user'
6import { areValidationErrors } from '../utils' 5import { areValidationErrors } from '../utils'
7import { isVideoImage } from '../../../helpers/custom-validators/videos' 6import { isVideoImage } from '../../../helpers/custom-validators/videos'
8import { CONSTRAINTS_FIELDS } from '../../../initializers/constants' 7import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
@@ -22,13 +21,14 @@ import {
22 isVideoPlaylistTimestampValid, 21 isVideoPlaylistTimestampValid,
23 isVideoPlaylistTypeValid 22 isVideoPlaylistTypeValid
24} from '../../../helpers/custom-validators/video-playlists' 23} from '../../../helpers/custom-validators/video-playlists'
25import { VideoPlaylistModel } from '../../../models/video/video-playlist'
26import { cleanUpReqFiles } from '../../../helpers/express-utils' 24import { cleanUpReqFiles } from '../../../helpers/express-utils'
27import { VideoPlaylistElementModel } from '../../../models/video/video-playlist-element' 25import { VideoPlaylistElementModel } from '../../../models/video/video-playlist-element'
28import { authenticatePromiseIfNeeded } from '../../oauth' 26import { authenticatePromiseIfNeeded } from '../../oauth'
29import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' 27import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
30import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model' 28import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
31import { doesVideoChannelIdExist, doesVideoExist, doesVideoPlaylistExist } from '../../../helpers/middlewares' 29import { doesVideoChannelIdExist, doesVideoExist, doesVideoPlaylistExist, VideoPlaylistFetchType } from '../../../helpers/middlewares'
30import { MVideoPlaylist } from '../../../typings/models/video/video-playlist'
31import { MUserAccountId } from '@server/typings/models'
32 32
33const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([ 33const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([
34 body('displayName') 34 body('displayName')
@@ -67,9 +67,9 @@ const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([
67 67
68 if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return cleanUpReqFiles(req) 68 if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return cleanUpReqFiles(req)
69 69
70 const videoPlaylist = res.locals.videoPlaylist 70 const videoPlaylist = getPlaylist(res)
71 71
72 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) { 72 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) {
73 return cleanUpReqFiles(req) 73 return cleanUpReqFiles(req)
74 } 74 }
75 75
@@ -110,13 +110,13 @@ const videoPlaylistsDeleteValidator = [
110 110
111 if (!await doesVideoPlaylistExist(req.params.playlistId, res)) return 111 if (!await doesVideoPlaylistExist(req.params.playlistId, res)) return
112 112
113 const videoPlaylist = res.locals.videoPlaylist 113 const videoPlaylist = getPlaylist(res)
114 if (videoPlaylist.type === VideoPlaylistType.WATCH_LATER) { 114 if (videoPlaylist.type === VideoPlaylistType.WATCH_LATER) {
115 return res.status(400) 115 return res.status(400)
116 .json({ error: 'Cannot delete a watch later playlist.' }) 116 .json({ error: 'Cannot delete a watch later playlist.' })
117 } 117 }
118 118
119 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) { 119 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) {
120 return 120 return
121 } 121 }
122 122
@@ -124,45 +124,47 @@ const videoPlaylistsDeleteValidator = [
124 } 124 }
125] 125]
126 126
127const videoPlaylistsGetValidator = [ 127const videoPlaylistsGetValidator = (fetchType: VideoPlaylistFetchType) => {
128 param('playlistId') 128 return [
129 .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), 129 param('playlistId')
130 .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'),
130 131
131 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 132 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
132 logger.debug('Checking videoPlaylistsGetValidator parameters', { parameters: req.params }) 133 logger.debug('Checking videoPlaylistsGetValidator parameters', { parameters: req.params })
133 134
134 if (areValidationErrors(req, res)) return 135 if (areValidationErrors(req, res)) return
135 136
136 if (!await doesVideoPlaylistExist(req.params.playlistId, res)) return 137 if (!await doesVideoPlaylistExist(req.params.playlistId, res, fetchType)) return
137 138
138 const videoPlaylist = res.locals.videoPlaylist 139 const videoPlaylist = res.locals.videoPlaylistFull || res.locals.videoPlaylistSummary
139 140
140 // Video is unlisted, check we used the uuid to fetch it 141 // Video is unlisted, check we used the uuid to fetch it
141 if (videoPlaylist.privacy === VideoPlaylistPrivacy.UNLISTED) { 142 if (videoPlaylist.privacy === VideoPlaylistPrivacy.UNLISTED) {
142 if (isUUIDValid(req.params.playlistId)) return next() 143 if (isUUIDValid(req.params.playlistId)) return next()
143 144
144 return res.status(404).end() 145 return res.status(404).end()
145 } 146 }
146 147
147 if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) { 148 if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) {
148 await authenticatePromiseIfNeeded(req, res) 149 await authenticatePromiseIfNeeded(req, res)
149 150
150 const user = res.locals.oauth ? res.locals.oauth.token.User : null 151 const user = res.locals.oauth ? res.locals.oauth.token.User : null
151 152
152 if ( 153 if (
153 !user || 154 !user ||
154 (videoPlaylist.OwnerAccount.id !== user.Account.id && !user.hasRight(UserRight.UPDATE_ANY_VIDEO_PLAYLIST)) 155 (videoPlaylist.OwnerAccount.id !== user.Account.id && !user.hasRight(UserRight.UPDATE_ANY_VIDEO_PLAYLIST))
155 ) { 156 ) {
156 return res.status(403) 157 return res.status(403)
157 .json({ error: 'Cannot get this private video playlist.' }) 158 .json({ error: 'Cannot get this private video playlist.' })
159 }
160
161 return next()
158 } 162 }
159 163
160 return next() 164 return next()
161 } 165 }
162 166 ]
163 return next() 167}
164 }
165]
166 168
167const videoPlaylistsAddVideoValidator = [ 169const videoPlaylistsAddVideoValidator = [
168 param('playlistId') 170 param('playlistId')
@@ -184,8 +186,8 @@ const videoPlaylistsAddVideoValidator = [
184 if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return 186 if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return
185 if (!await doesVideoExist(req.body.videoId, res, 'only-video')) return 187 if (!await doesVideoExist(req.body.videoId, res, 'only-video')) return
186 188
187 const videoPlaylist = res.locals.videoPlaylist 189 const videoPlaylist = getPlaylist(res)
188 const video = res.locals.video 190 const video = res.locals.onlyVideo
189 191
190 const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndVideo(videoPlaylist.id, video.id) 192 const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndVideo(videoPlaylist.id, video.id)
191 if (videoPlaylistElement) { 193 if (videoPlaylistElement) {
@@ -196,7 +198,7 @@ const videoPlaylistsAddVideoValidator = [
196 return 198 return
197 } 199 }
198 200
199 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) { 201 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) {
200 return 202 return
201 } 203 }
202 204
@@ -223,7 +225,7 @@ const videoPlaylistsUpdateOrRemoveVideoValidator = [
223 225
224 if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return 226 if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return
225 227
226 const videoPlaylist = res.locals.videoPlaylist 228 const videoPlaylist = getPlaylist(res)
227 229
228 const videoPlaylistElement = await VideoPlaylistElementModel.loadById(req.params.playlistElementId) 230 const videoPlaylistElement = await VideoPlaylistElementModel.loadById(req.params.playlistElementId)
229 if (!videoPlaylistElement) { 231 if (!videoPlaylistElement) {
@@ -289,7 +291,7 @@ const videoPlaylistsReorderVideosValidator = [
289 291
290 if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return 292 if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return
291 293
292 const videoPlaylist = res.locals.videoPlaylist 294 const videoPlaylist = getPlaylist(res)
293 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) return 295 if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) return
294 296
295 const nextPosition = await VideoPlaylistElementModel.getNextPositionOf(videoPlaylist.id) 297 const nextPosition = await VideoPlaylistElementModel.getNextPositionOf(videoPlaylist.id)
@@ -388,7 +390,7 @@ function getCommonPlaylistEditAttributes () {
388 ] as (ValidationChain | express.Handler)[] 390 ] as (ValidationChain | express.Handler)[]
389} 391}
390 392
391function checkUserCanManageVideoPlaylist (user: UserModel, videoPlaylist: VideoPlaylistModel, right: UserRight, res: express.Response) { 393function checkUserCanManageVideoPlaylist (user: MUserAccountId, videoPlaylist: MVideoPlaylist, right: UserRight, res: express.Response) {
392 if (videoPlaylist.isOwned() === false) { 394 if (videoPlaylist.isOwned() === false) {
393 res.status(403) 395 res.status(403)
394 .json({ error: 'Cannot manage video playlist of another server.' }) 396 .json({ error: 'Cannot manage video playlist of another server.' })
@@ -410,3 +412,7 @@ function checkUserCanManageVideoPlaylist (user: UserModel, videoPlaylist: VideoP
410 412
411 return true 413 return true
412} 414}
415
416function getPlaylist (res: express.Response) {
417 return res.locals.videoPlaylistFull || res.locals.videoPlaylistSummary
418}
diff --git a/server/middlewares/validators/videos/video-shares.ts b/server/middlewares/validators/videos/video-shares.ts
index ace62be5c..20fc96243 100644
--- a/server/middlewares/validators/videos/video-shares.ts
+++ b/server/middlewares/validators/videos/video-shares.ts
@@ -16,7 +16,7 @@ const videosShareValidator = [
16 if (areValidationErrors(req, res)) return 16 if (areValidationErrors(req, res)) return
17 if (!await doesVideoExist(req.params.id, res)) return 17 if (!await doesVideoExist(req.params.id, res)) return
18 18
19 const video = res.locals.video 19 const video = res.locals.videoAll
20 20
21 const share = await VideoShareModel.load(req.params.actorId, video.id) 21 const share = await VideoShareModel.load(req.params.actorId, video.id)
22 if (!share) { 22 if (!share) {
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts
index af06f3c62..a194d14b3 100644
--- a/server/middlewares/validators/videos/videos.ts
+++ b/server/middlewares/validators/videos/videos.ts
@@ -37,13 +37,14 @@ import { VideoModel } from '../../../models/video/video'
37import { checkUserCanTerminateOwnershipChange, doesChangeVideoOwnershipExist } from '../../../helpers/custom-validators/video-ownership' 37import { checkUserCanTerminateOwnershipChange, doesChangeVideoOwnershipExist } from '../../../helpers/custom-validators/video-ownership'
38import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/video-change-ownership-accept.model' 38import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/video-change-ownership-accept.model'
39import { AccountModel } from '../../../models/account/account' 39import { AccountModel } from '../../../models/account/account'
40import { VideoFetchType } from '../../../helpers/video'
41import { isNSFWQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search' 40import { isNSFWQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search'
42import { getServerActor } from '../../../helpers/utils' 41import { getServerActor } from '../../../helpers/utils'
43import { CONFIG } from '../../../initializers/config' 42import { CONFIG } from '../../../initializers/config'
44import { isLocalVideoAccepted } from '../../../lib/moderation' 43import { isLocalVideoAccepted } from '../../../lib/moderation'
45import { Hooks } from '../../../lib/plugins/hooks' 44import { Hooks } from '../../../lib/plugins/hooks'
46import { checkUserCanManageVideo, doesVideoChannelOfAccountExist, doesVideoExist } from '../../../helpers/middlewares' 45import { checkUserCanManageVideo, doesVideoChannelOfAccountExist, doesVideoExist } from '../../../helpers/middlewares'
46import { MVideoFullLight } from '@server/typings/models'
47import { getVideo } from '../../../helpers/video'
47 48
48const videosAddValidator = getCommonVideoEditAttributes().concat([ 49const videosAddValidator = getCommonVideoEditAttributes().concat([
49 body('videofile') 50 body('videofile')
@@ -113,7 +114,7 @@ const videosUpdateValidator = getCommonVideoEditAttributes().concat([
113 114
114 // Check if the user who did the request is able to update the video 115 // Check if the user who did the request is able to update the video
115 const user = res.locals.oauth.token.User 116 const user = res.locals.oauth.token.User
116 if (!checkUserCanManageVideo(user, res.locals.video, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req) 117 if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req)
117 118
118 if (req.body.channelId && !await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) 119 if (req.body.channelId && !await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req)
119 120
@@ -122,7 +123,7 @@ const videosUpdateValidator = getCommonVideoEditAttributes().concat([
122]) 123])
123 124
124async function checkVideoFollowConstraints (req: express.Request, res: express.Response, next: express.NextFunction) { 125async function checkVideoFollowConstraints (req: express.Request, res: express.Response, next: express.NextFunction) {
125 const video = res.locals.video 126 const video = getVideo(res)
126 127
127 // Anybody can watch local videos 128 // Anybody can watch local videos
128 if (video.isOwned() === true) return next() 129 if (video.isOwned() === true) return next()
@@ -146,7 +147,7 @@ async function checkVideoFollowConstraints (req: express.Request, res: express.R
146 }) 147 })
147} 148}
148 149
149const videosCustomGetValidator = (fetchType: VideoFetchType) => { 150const videosCustomGetValidator = (fetchType: 'all' | 'only-video' | 'only-video-with-rights') => {
150 return [ 151 return [
151 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 152 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
152 153
@@ -156,10 +157,11 @@ const videosCustomGetValidator = (fetchType: VideoFetchType) => {
156 if (areValidationErrors(req, res)) return 157 if (areValidationErrors(req, res)) return
157 if (!await doesVideoExist(req.params.id, res, fetchType)) return 158 if (!await doesVideoExist(req.params.id, res, fetchType)) return
158 159
159 const video = res.locals.video 160 const video = getVideo(res)
161 const videoAll = video as MVideoFullLight
160 162
161 // Video private or blacklisted 163 // Video private or blacklisted
162 if (video.privacy === VideoPrivacy.PRIVATE || video.VideoBlacklist) { 164 if (video.privacy === VideoPrivacy.PRIVATE || videoAll.VideoBlacklist) {
163 await authenticatePromiseIfNeeded(req, res) 165 await authenticatePromiseIfNeeded(req, res)
164 166
165 const user = res.locals.oauth ? res.locals.oauth.token.User : null 167 const user = res.locals.oauth ? res.locals.oauth.token.User : null
@@ -167,7 +169,7 @@ const videosCustomGetValidator = (fetchType: VideoFetchType) => {
167 // Only the owner or a user that have blacklist rights can see the video 169 // Only the owner or a user that have blacklist rights can see the video
168 if ( 170 if (
169 !user || 171 !user ||
170 (video.VideoChannel.Account.userId !== user.id && !user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)) 172 (videoAll.VideoChannel && videoAll.VideoChannel.Account.userId !== user.id && !user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST))
171 ) { 173 ) {
172 return res.status(403) 174 return res.status(403)
173 .json({ error: 'Cannot get this private or blacklisted video.' }) 175 .json({ error: 'Cannot get this private or blacklisted video.' })
@@ -202,7 +204,7 @@ const videosRemoveValidator = [
202 if (!await doesVideoExist(req.params.id, res)) return 204 if (!await doesVideoExist(req.params.id, res)) return
203 205
204 // Check if the user who did the request is able to delete the video 206 // Check if the user who did the request is able to delete the video
205 if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.video, UserRight.REMOVE_ANY_VIDEO, res)) return 207 if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.videoAll, UserRight.REMOVE_ANY_VIDEO, res)) return
206 208
207 return next() 209 return next()
208 } 210 }
@@ -218,7 +220,7 @@ const videosChangeOwnershipValidator = [
218 if (!await doesVideoExist(req.params.videoId, res)) return 220 if (!await doesVideoExist(req.params.videoId, res)) return
219 221
220 // Check if the user who did the request is able to change the ownership of the video 222 // Check if the user who did the request is able to change the ownership of the video
221 if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.video, UserRight.CHANGE_VIDEO_OWNERSHIP, res)) return 223 if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.videoAll, UserRight.CHANGE_VIDEO_OWNERSHIP, res)) return
222 224
223 const nextOwner = await AccountModel.loadLocalByName(req.body.username) 225 const nextOwner = await AccountModel.loadLocalByName(req.body.username)
224 if (!nextOwner) { 226 if (!nextOwner) {
diff --git a/server/middlewares/validators/webfinger.ts b/server/middlewares/validators/webfinger.ts
index d7cfe17f0..d50e6527f 100644
--- a/server/middlewares/validators/webfinger.ts
+++ b/server/middlewares/validators/webfinger.ts
@@ -18,6 +18,7 @@ const webfingerValidator = [
18 const nameWithHost = getHostWithPort(req.query.resource.substr(5)) 18 const nameWithHost = getHostWithPort(req.query.resource.substr(5))
19 const [ name ] = nameWithHost.split('@') 19 const [ name ] = nameWithHost.split('@')
20 20
21 // FIXME: we don't need the full actor
21 const actor = await ActorModel.loadLocalByName(name) 22 const actor = await ActorModel.loadLocalByName(name)
22 if (!actor) { 23 if (!actor) {
23 return res.status(404) 24 return res.status(404)
@@ -25,7 +26,7 @@ const webfingerValidator = [
25 .end() 26 .end()
26 } 27 }
27 28
28 res.locals.actor = actor 29 res.locals.actorFull = actor
29 return next() 30 return next()
30 } 31 }
31] 32]
diff --git a/server/models/account/account-blocklist.ts b/server/models/account/account-blocklist.ts
index d5746ad76..bb5371395 100644
--- a/server/models/account/account-blocklist.ts
+++ b/server/models/account/account-blocklist.ts
@@ -3,6 +3,8 @@ import { AccountModel } from './account'
3import { getSort } from '../utils' 3import { getSort } from '../utils'
4import { AccountBlock } from '../../../shared/models/blocklist' 4import { AccountBlock } from '../../../shared/models/blocklist'
5import { Op } from 'sequelize' 5import { Op } from 'sequelize'
6import * as Bluebird from 'bluebird'
7import { MAccountBlocklist, MAccountBlocklistAccounts } from '@server/typings/models'
6 8
7enum ScopeNames { 9enum ScopeNames {
8 WITH_ACCOUNTS = 'WITH_ACCOUNTS' 10 WITH_ACCOUNTS = 'WITH_ACCOUNTS'
@@ -103,7 +105,7 @@ export class AccountBlocklistModel extends Model<AccountBlocklistModel> {
103 }) 105 })
104 } 106 }
105 107
106 static loadByAccountAndTarget (accountId: number, targetAccountId: number) { 108 static loadByAccountAndTarget (accountId: number, targetAccountId: number): Bluebird<MAccountBlocklist> {
107 const query = { 109 const query = {
108 where: { 110 where: {
109 accountId, 111 accountId,
@@ -126,7 +128,7 @@ export class AccountBlocklistModel extends Model<AccountBlocklistModel> {
126 128
127 return AccountBlocklistModel 129 return AccountBlocklistModel
128 .scope([ ScopeNames.WITH_ACCOUNTS ]) 130 .scope([ ScopeNames.WITH_ACCOUNTS ])
129 .findAndCountAll(query) 131 .findAndCountAll<MAccountBlocklistAccounts>(query)
130 .then(({ rows, count }) => { 132 .then(({ rows, count }) => {
131 return { total: count, data: rows } 133 return { total: count, data: rows }
132 }) 134 })
diff --git a/server/models/account/account-video-rate.ts b/server/models/account/account-video-rate.ts
index 4bd8114cf..8b62dd05f 100644
--- a/server/models/account/account-video-rate.ts
+++ b/server/models/account/account-video-rate.ts
@@ -10,6 +10,8 @@ import { buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils'
10import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 10import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
11import { AccountVideoRate } from '../../../shared' 11import { AccountVideoRate } from '../../../shared'
12import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel' 12import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel'
13import * as Bluebird from 'bluebird'
14import { MAccountVideoRate, MAccountVideoRateAccountUrl, MAccountVideoRateAccountVideo } from '@server/typings/models/video/video-rate'
13 15
14/* 16/*
15 Account rates per video. 17 Account rates per video.
@@ -77,7 +79,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
77 }) 79 })
78 Account: AccountModel 80 Account: AccountModel
79 81
80 static load (accountId: number, videoId: number, transaction?: Transaction) { 82 static load (accountId: number, videoId: number, transaction?: Transaction): Bluebird<MAccountVideoRate> {
81 const options: FindOptions = { 83 const options: FindOptions = {
82 where: { 84 where: {
83 accountId, 85 accountId,
@@ -89,7 +91,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
89 return AccountVideoRateModel.findOne(options) 91 return AccountVideoRateModel.findOne(options)
90 } 92 }
91 93
92 static loadByAccountAndVideoOrUrl (accountId: number, videoId: number, url: string, transaction?: Transaction) { 94 static loadByAccountAndVideoOrUrl (accountId: number, videoId: number, url: string, t?: Transaction): Bluebird<MAccountVideoRate> {
93 const options: FindOptions = { 95 const options: FindOptions = {
94 where: { 96 where: {
95 [ Op.or]: [ 97 [ Op.or]: [
@@ -103,7 +105,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
103 ] 105 ]
104 } 106 }
105 } 107 }
106 if (transaction) options.transaction = transaction 108 if (t) options.transaction = t
107 109
108 return AccountVideoRateModel.findOne(options) 110 return AccountVideoRateModel.findOne(options)
109 } 111 }
@@ -140,7 +142,12 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
140 return AccountVideoRateModel.findAndCountAll(query) 142 return AccountVideoRateModel.findAndCountAll(query)
141 } 143 }
142 144
143 static loadLocalAndPopulateVideo (rateType: VideoRateType, accountName: string, videoId: number, transaction?: Transaction) { 145 static loadLocalAndPopulateVideo (
146 rateType: VideoRateType,
147 accountName: string,
148 videoId: number,
149 t?: Transaction
150 ): Bluebird<MAccountVideoRateAccountVideo> {
144 const options: FindOptions = { 151 const options: FindOptions = {
145 where: { 152 where: {
146 videoId, 153 videoId,
@@ -152,7 +159,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
152 required: true, 159 required: true,
153 include: [ 160 include: [
154 { 161 {
155 attributes: [ 'id', 'url', 'preferredUsername' ], 162 attributes: [ 'id', 'url', 'followersUrl', 'preferredUsername' ],
156 model: ActorModel.unscoped(), 163 model: ActorModel.unscoped(),
157 required: true, 164 required: true,
158 where: { 165 where: {
@@ -167,7 +174,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
167 } 174 }
168 ] 175 ]
169 } 176 }
170 if (transaction) options.transaction = transaction 177 if (t) options.transaction = t
171 178
172 return AccountVideoRateModel.findOne(options) 179 return AccountVideoRateModel.findOne(options)
173 } 180 }
@@ -208,7 +215,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
208 ] 215 ]
209 } 216 }
210 217
211 return AccountVideoRateModel.findAndCountAll(query) 218 return AccountVideoRateModel.findAndCountAll<MAccountVideoRateAccountUrl>(query)
212 } 219 }
213 220
214 static cleanOldRatesOf (videoId: number, type: VideoRateType, beforeUpdatedAt: Date) { 221 static cleanOldRatesOf (videoId: number, type: VideoRateType, beforeUpdatedAt: Date) {
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index 4dc412301..4cc731075 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -3,7 +3,8 @@ import {
3 BeforeDestroy, 3 BeforeDestroy,
4 BelongsTo, 4 BelongsTo,
5 Column, 5 Column,
6 CreatedAt, DataType, 6 CreatedAt,
7 DataType,
7 Default, 8 Default,
8 DefaultScope, 9 DefaultScope,
9 ForeignKey, 10 ForeignKey,
@@ -31,6 +32,8 @@ import { FindOptions, IncludeOptions, Op, Transaction, WhereOptions } from 'sequ
31import { AccountBlocklistModel } from './account-blocklist' 32import { AccountBlocklistModel } from './account-blocklist'
32import { ServerBlocklistModel } from '../server/server-blocklist' 33import { ServerBlocklistModel } from '../server/server-blocklist'
33import { ActorFollowModel } from '../activitypub/actor-follow' 34import { ActorFollowModel } from '../activitypub/actor-follow'
35import { MAccountActor, MAccountDefault } from '../../typings/models'
36import * as Bluebird from 'bluebird'
34 37
35export enum ScopeNames { 38export enum ScopeNames {
36 SUMMARY = 'SUMMARY' 39 SUMMARY = 'SUMMARY'
@@ -229,11 +232,11 @@ export class AccountModel extends Model<AccountModel> {
229 return undefined 232 return undefined
230 } 233 }
231 234
232 static load (id: number, transaction?: Transaction) { 235 static load (id: number, transaction?: Transaction): Bluebird<MAccountDefault> {
233 return AccountModel.findByPk(id, { transaction }) 236 return AccountModel.findByPk(id, { transaction })
234 } 237 }
235 238
236 static loadByNameWithHost (nameWithHost: string) { 239 static loadByNameWithHost (nameWithHost: string): Bluebird<MAccountDefault> {
237 const [ accountName, host ] = nameWithHost.split('@') 240 const [ accountName, host ] = nameWithHost.split('@')
238 241
239 if (!host || host === WEBSERVER.HOST) return AccountModel.loadLocalByName(accountName) 242 if (!host || host === WEBSERVER.HOST) return AccountModel.loadLocalByName(accountName)
@@ -241,7 +244,7 @@ export class AccountModel extends Model<AccountModel> {
241 return AccountModel.loadByNameAndHost(accountName, host) 244 return AccountModel.loadByNameAndHost(accountName, host)
242 } 245 }
243 246
244 static loadLocalByName (name: string) { 247 static loadLocalByName (name: string): Bluebird<MAccountDefault> {
245 const query = { 248 const query = {
246 where: { 249 where: {
247 [ Op.or ]: [ 250 [ Op.or ]: [
@@ -271,7 +274,7 @@ export class AccountModel extends Model<AccountModel> {
271 return AccountModel.findOne(query) 274 return AccountModel.findOne(query)
272 } 275 }
273 276
274 static loadByNameAndHost (name: string, host: string) { 277 static loadByNameAndHost (name: string, host: string): Bluebird<MAccountDefault> {
275 const query = { 278 const query = {
276 include: [ 279 include: [
277 { 280 {
@@ -296,7 +299,7 @@ export class AccountModel extends Model<AccountModel> {
296 return AccountModel.findOne(query) 299 return AccountModel.findOne(query)
297 } 300 }
298 301
299 static loadByUrl (url: string, transaction?: Transaction) { 302 static loadByUrl (url: string, transaction?: Transaction): Bluebird<MAccountDefault> {
300 const query = { 303 const query = {
301 include: [ 304 include: [
302 { 305 {
@@ -329,7 +332,7 @@ export class AccountModel extends Model<AccountModel> {
329 }) 332 })
330 } 333 }
331 334
332 static listLocalsForSitemap (sort: string) { 335 static listLocalsForSitemap (sort: string): Bluebird<MAccountActor[]> {
333 const query = { 336 const query = {
334 attributes: [ ], 337 attributes: [ ],
335 offset: 0, 338 offset: 0,
diff --git a/server/models/account/user-notification.ts b/server/models/account/user-notification.ts
index f38cd7e78..9b13a8376 100644
--- a/server/models/account/user-notification.ts
+++ b/server/models/account/user-notification.ts
@@ -16,6 +16,7 @@ import { ActorModel } from '../activitypub/actor'
16import { ActorFollowModel } from '../activitypub/actor-follow' 16import { ActorFollowModel } from '../activitypub/actor-follow'
17import { AvatarModel } from '../avatar/avatar' 17import { AvatarModel } from '../avatar/avatar'
18import { ServerModel } from '../server/server' 18import { ServerModel } from '../server/server'
19import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/typings/models/user'
19 20
20enum ScopeNames { 21enum ScopeNames {
21 WITH_ALL = 'WITH_ALL' 22 WITH_ALL = 'WITH_ALL'
@@ -371,7 +372,7 @@ export class UserNotificationModel extends Model<UserNotificationModel> {
371 return UserNotificationModel.update({ read: true }, query) 372 return UserNotificationModel.update({ read: true }, query)
372 } 373 }
373 374
374 toFormattedJSON (): UserNotification { 375 toFormattedJSON (this: UserNotificationModelForApi): UserNotification {
375 const video = this.Video 376 const video = this.Video
376 ? Object.assign(this.formatVideo(this.Video),{ channel: this.formatActor(this.Video.VideoChannel) }) 377 ? Object.assign(this.formatVideo(this.Video),{ channel: this.formatActor(this.Video.VideoChannel) })
377 : undefined 378 : undefined
@@ -436,7 +437,7 @@ export class UserNotificationModel extends Model<UserNotificationModel> {
436 } 437 }
437 } 438 }
438 439
439 private formatVideo (video: VideoModel) { 440 formatVideo (this: UserNotificationModelForApi, video: UserNotificationIncludes.VideoInclude) {
440 return { 441 return {
441 id: video.id, 442 id: video.id,
442 uuid: video.uuid, 443 uuid: video.uuid,
@@ -444,7 +445,10 @@ export class UserNotificationModel extends Model<UserNotificationModel> {
444 } 445 }
445 } 446 }
446 447
447 private formatActor (accountOrChannel: AccountModel | VideoChannelModel) { 448 formatActor (
449 this: UserNotificationModelForApi,
450 accountOrChannel: UserNotificationIncludes.AccountIncludeActor | UserNotificationIncludes.VideoChannelIncludeActor
451 ) {
448 const avatar = accountOrChannel.Actor.Avatar 452 const avatar = accountOrChannel.Actor.Avatar
449 ? { path: accountOrChannel.Actor.Avatar.getStaticPath() } 453 ? { path: accountOrChannel.Actor.Avatar.getStaticPath() }
450 : undefined 454 : undefined
diff --git a/server/models/account/user-video-history.ts b/server/models/account/user-video-history.ts
index a862fc45f..3fe4c8db1 100644
--- a/server/models/account/user-video-history.ts
+++ b/server/models/account/user-video-history.ts
@@ -1,7 +1,8 @@
1import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, IsInt, Model, Table, UpdatedAt } from 'sequelize-typescript' 1import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, IsInt, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { VideoModel } from '../video/video' 2import { VideoModel } from '../video/video'
3import { UserModel } from './user' 3import { UserModel } from './user'
4import { Transaction, Op, DestroyOptions } from 'sequelize' 4import { DestroyOptions, Op, Transaction } from 'sequelize'
5import { MUserAccountId, MUserId } from '@server/typings/models'
5 6
6@Table({ 7@Table({
7 tableName: 'userVideoHistory', 8 tableName: 'userVideoHistory',
@@ -54,7 +55,7 @@ export class UserVideoHistoryModel extends Model<UserVideoHistoryModel> {
54 }) 55 })
55 User: UserModel 56 User: UserModel
56 57
57 static listForApi (user: UserModel, start: number, count: number) { 58 static listForApi (user: MUserAccountId, start: number, count: number) {
58 return VideoModel.listForApi({ 59 return VideoModel.listForApi({
59 start, 60 start,
60 count, 61 count,
@@ -67,7 +68,7 @@ export class UserVideoHistoryModel extends Model<UserVideoHistoryModel> {
67 }) 68 })
68 } 69 }
69 70
70 static removeUserHistoryBefore (user: UserModel, beforeDate: string, t: Transaction) { 71 static removeUserHistoryBefore (user: MUserId, beforeDate: string, t: Transaction) {
71 const query: DestroyOptions = { 72 const query: DestroyOptions = {
72 where: { 73 where: {
73 userId: user.id 74 userId: user.id
diff --git a/server/models/account/user.ts b/server/models/account/user.ts
index 0041bf577..24b1626e7 100644
--- a/server/models/account/user.ts
+++ b/server/models/account/user.ts
@@ -54,6 +54,8 @@ import { VideoImportModel } from '../video/video-import'
54import { UserAdminFlag } from '../../../shared/models/users/user-flag.model' 54import { UserAdminFlag } from '../../../shared/models/users/user-flag.model'
55import { isThemeNameValid } from '../../helpers/custom-validators/plugins' 55import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
56import { getThemeOrDefault } from '../../lib/plugins/theme-utils' 56import { getThemeOrDefault } from '../../lib/plugins/theme-utils'
57import * as Bluebird from 'bluebird'
58import { MUserChannel, MUserDefault, MUserId, MUserWithNotificationSetting } from '@server/typings/models'
57 59
58enum ScopeNames { 60enum ScopeNames {
59 WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL' 61 WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL'
@@ -303,7 +305,7 @@ export class UserModel extends Model<UserModel> {
303 }) 305 })
304 } 306 }
305 307
306 static listWithRight (right: UserRight) { 308 static listWithRight (right: UserRight): Bluebird<MUserDefault[]> {
307 const roles = Object.keys(USER_ROLE_LABELS) 309 const roles = Object.keys(USER_ROLE_LABELS)
308 .map(k => parseInt(k, 10) as UserRole) 310 .map(k => parseInt(k, 10) as UserRole)
309 .filter(role => hasUserRight(role, right)) 311 .filter(role => hasUserRight(role, right))
@@ -319,7 +321,7 @@ export class UserModel extends Model<UserModel> {
319 return UserModel.findAll(query) 321 return UserModel.findAll(query)
320 } 322 }
321 323
322 static listUserSubscribersOf (actorId: number) { 324 static listUserSubscribersOf (actorId: number): Bluebird<MUserWithNotificationSetting[]> {
323 const query = { 325 const query = {
324 include: [ 326 include: [
325 { 327 {
@@ -358,7 +360,7 @@ export class UserModel extends Model<UserModel> {
358 return UserModel.unscoped().findAll(query) 360 return UserModel.unscoped().findAll(query)
359 } 361 }
360 362
361 static listByUsernames (usernames: string[]) { 363 static listByUsernames (usernames: string[]): Bluebird<MUserDefault[]> {
362 const query = { 364 const query = {
363 where: { 365 where: {
364 username: usernames 366 username: usernames
@@ -368,11 +370,11 @@ export class UserModel extends Model<UserModel> {
368 return UserModel.findAll(query) 370 return UserModel.findAll(query)
369 } 371 }
370 372
371 static loadById (id: number) { 373 static loadById (id: number): Bluebird<MUserDefault> {
372 return UserModel.findByPk(id) 374 return UserModel.findByPk(id)
373 } 375 }
374 376
375 static loadByUsername (username: string) { 377 static loadByUsername (username: string): Bluebird<MUserDefault> {
376 const query = { 378 const query = {
377 where: { 379 where: {
378 username: { [ Op.iLike ]: username } 380 username: { [ Op.iLike ]: username }
@@ -382,7 +384,7 @@ export class UserModel extends Model<UserModel> {
382 return UserModel.findOne(query) 384 return UserModel.findOne(query)
383 } 385 }
384 386
385 static loadByUsernameAndPopulateChannels (username: string) { 387 static loadByUsernameAndPopulateChannels (username: string): Bluebird<MUserChannel> {
386 const query = { 388 const query = {
387 where: { 389 where: {
388 username: { [ Op.iLike ]: username } 390 username: { [ Op.iLike ]: username }
@@ -392,7 +394,7 @@ export class UserModel extends Model<UserModel> {
392 return UserModel.scope(ScopeNames.WITH_VIDEO_CHANNEL).findOne(query) 394 return UserModel.scope(ScopeNames.WITH_VIDEO_CHANNEL).findOne(query)
393 } 395 }
394 396
395 static loadByEmail (email: string) { 397 static loadByEmail (email: string): Bluebird<MUserDefault> {
396 const query = { 398 const query = {
397 where: { 399 where: {
398 email 400 email
@@ -402,7 +404,7 @@ export class UserModel extends Model<UserModel> {
402 return UserModel.findOne(query) 404 return UserModel.findOne(query)
403 } 405 }
404 406
405 static loadByUsernameOrEmail (username: string, email?: string) { 407 static loadByUsernameOrEmail (username: string, email?: string): Bluebird<MUserDefault> {
406 if (!email) email = username 408 if (!email) email = username
407 409
408 const query = { 410 const query = {
@@ -414,7 +416,7 @@ export class UserModel extends Model<UserModel> {
414 return UserModel.findOne(query) 416 return UserModel.findOne(query)
415 } 417 }
416 418
417 static loadByVideoId (videoId: number) { 419 static loadByVideoId (videoId: number): Bluebird<MUserDefault> {
418 const query = { 420 const query = {
419 include: [ 421 include: [
420 { 422 {
@@ -445,7 +447,7 @@ export class UserModel extends Model<UserModel> {
445 return UserModel.findOne(query) 447 return UserModel.findOne(query)
446 } 448 }
447 449
448 static loadByVideoImportId (videoImportId: number) { 450 static loadByVideoImportId (videoImportId: number): Bluebird<MUserDefault> {
449 const query = { 451 const query = {
450 include: [ 452 include: [
451 { 453 {
@@ -462,7 +464,7 @@ export class UserModel extends Model<UserModel> {
462 return UserModel.findOne(query) 464 return UserModel.findOne(query)
463 } 465 }
464 466
465 static loadByChannelActorId (videoChannelActorId: number) { 467 static loadByChannelActorId (videoChannelActorId: number): Bluebird<MUserDefault> {
466 const query = { 468 const query = {
467 include: [ 469 include: [
468 { 470 {
@@ -486,7 +488,7 @@ export class UserModel extends Model<UserModel> {
486 return UserModel.findOne(query) 488 return UserModel.findOne(query)
487 } 489 }
488 490
489 static loadByAccountActorId (accountActorId: number) { 491 static loadByAccountActorId (accountActorId: number): Bluebird<MUserDefault> {
490 const query = { 492 const query = {
491 include: [ 493 include: [
492 { 494 {
@@ -503,7 +505,7 @@ export class UserModel extends Model<UserModel> {
503 return UserModel.findOne(query) 505 return UserModel.findOne(query)
504 } 506 }
505 507
506 static getOriginalVideoFileTotalFromUser (user: UserModel) { 508 static getOriginalVideoFileTotalFromUser (user: MUserId) {
507 // Don't use sequelize because we need to use a sub query 509 // Don't use sequelize because we need to use a sub query
508 const query = UserModel.generateUserQuotaBaseSQL() 510 const query = UserModel.generateUserQuotaBaseSQL()
509 511
@@ -511,7 +513,7 @@ export class UserModel extends Model<UserModel> {
511 } 513 }
512 514
513 // Returns cumulative size of all video files uploaded in the last 24 hours. 515 // Returns cumulative size of all video files uploaded in the last 24 hours.
514 static getOriginalVideoFileTotalDailyFromUser (user: UserModel) { 516 static getOriginalVideoFileTotalDailyFromUser (user: MUserId) {
515 // Don't use sequelize because we need to use a sub query 517 // Don't use sequelize because we need to use a sub query
516 const query = UserModel.generateUserQuotaBaseSQL('"video"."createdAt" > now() - interval \'24 hours\'') 518 const query = UserModel.generateUserQuotaBaseSQL('"video"."createdAt" > now() - interval \'24 hours\'')
517 519
diff --git a/server/models/activitypub/actor-follow.ts b/server/models/activitypub/actor-follow.ts
index 51b09e09b..8ef770cd4 100644
--- a/server/models/activitypub/actor-follow.ts
+++ b/server/models/activitypub/actor-follow.ts
@@ -27,7 +27,13 @@ import { createSafeIn, getSort } from '../utils'
27import { ActorModel, unusedActorAttributesForAPI } from './actor' 27import { ActorModel, unusedActorAttributesForAPI } from './actor'
28import { VideoChannelModel } from '../video/video-channel' 28import { VideoChannelModel } from '../video/video-channel'
29import { AccountModel } from '../account/account' 29import { AccountModel } from '../account/account'
30import { IncludeOptions, Op, Transaction, QueryTypes } from 'sequelize' 30import { IncludeOptions, Op, QueryTypes, Transaction } from 'sequelize'
31import {
32 MActorFollowActorsDefault,
33 MActorFollowActorsDefaultSubscription,
34 MActorFollowFollowingHost,
35 MActorFollowSubscriptions
36} from '@server/typings/models'
31 37
32@Table({ 38@Table({
33 tableName: 'actorFollow', 39 tableName: 'actorFollow',
@@ -143,7 +149,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
143 if (numberOfActorFollowsRemoved) logger.info('Removed bad %d actor follows.', numberOfActorFollowsRemoved) 149 if (numberOfActorFollowsRemoved) logger.info('Removed bad %d actor follows.', numberOfActorFollowsRemoved)
144 } 150 }
145 151
146 static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Transaction) { 152 static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Transaction): Bluebird<MActorFollowActorsDefault> {
147 const query = { 153 const query = {
148 where: { 154 where: {
149 actorId, 155 actorId,
@@ -167,7 +173,12 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
167 return ActorFollowModel.findOne(query) 173 return ActorFollowModel.findOne(query)
168 } 174 }
169 175
170 static loadByActorAndTargetNameAndHostForAPI (actorId: number, targetName: string, targetHost: string, t?: Transaction) { 176 static loadByActorAndTargetNameAndHostForAPI (
177 actorId: number,
178 targetName: string,
179 targetHost: string,
180 t?: Transaction
181 ): Bluebird<MActorFollowActorsDefaultSubscription> {
171 const actorFollowingPartInclude: IncludeOptions = { 182 const actorFollowingPartInclude: IncludeOptions = {
172 model: ActorModel, 183 model: ActorModel,
173 required: true, 184 required: true,
@@ -220,7 +231,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
220 }) 231 })
221 } 232 }
222 233
223 static listSubscribedIn (actorId: number, targets: { name: string, host?: string }[]) { 234 static listSubscribedIn (actorId: number, targets: { name: string, host?: string }[]): Bluebird<MActorFollowFollowingHost[]> {
224 const whereTab = targets 235 const whereTab = targets
225 .map(t => { 236 .map(t => {
226 if (t.host) { 237 if (t.host) {
@@ -314,7 +325,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
314 ] 325 ]
315 } 326 }
316 327
317 return ActorFollowModel.findAndCountAll(query) 328 return ActorFollowModel.findAndCountAll<MActorFollowActorsDefault>(query)
318 .then(({ rows, count }) => { 329 .then(({ rows, count }) => {
319 return { 330 return {
320 data: rows, 331 data: rows,
@@ -357,7 +368,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
357 ] 368 ]
358 } 369 }
359 370
360 return ActorFollowModel.findAndCountAll(query) 371 return ActorFollowModel.findAndCountAll<MActorFollowActorsDefault>(query)
361 .then(({ rows, count }) => { 372 .then(({ rows, count }) => {
362 return { 373 return {
363 data: rows, 374 data: rows,
@@ -414,7 +425,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
414 ] 425 ]
415 } 426 }
416 427
417 return ActorFollowModel.findAndCountAll(query) 428 return ActorFollowModel.findAndCountAll<MActorFollowSubscriptions>(query)
418 .then(({ rows, count }) => { 429 .then(({ rows, count }) => {
419 return { 430 return {
420 data: rows.map(r => r.ActorFollowing.VideoChannel), 431 data: rows.map(r => r.ActorFollowing.VideoChannel),
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts
index 9cc53f78a..2312127b4 100644
--- a/server/models/activitypub/actor.ts
+++ b/server/models/activitypub/actor.ts
@@ -36,6 +36,8 @@ import { isOutdated, throwIfNotValid } from '../utils'
36import { VideoChannelModel } from '../video/video-channel' 36import { VideoChannelModel } from '../video/video-channel'
37import { ActorFollowModel } from './actor-follow' 37import { ActorFollowModel } from './actor-follow'
38import { VideoModel } from '../video/video' 38import { VideoModel } from '../video/video'
39import { MActor, MActorAccountChannelId, MActorFull } from '../../typings/models'
40import * as Bluebird from 'bluebird'
39 41
40enum ScopeNames { 42enum ScopeNames {
41 FULL = 'FULL' 43 FULL = 'FULL'
@@ -252,11 +254,15 @@ export class ActorModel extends Model<ActorModel> {
252 }) 254 })
253 VideoChannel: VideoChannelModel 255 VideoChannel: VideoChannelModel
254 256
255 static load (id: number) { 257 static load (id: number): Bluebird<MActor> {
256 return ActorModel.unscoped().findByPk(id) 258 return ActorModel.unscoped().findByPk(id)
257 } 259 }
258 260
259 static loadAccountActorByVideoId (videoId: number, transaction: Sequelize.Transaction) { 261 static loadFull (id: number): Bluebird<MActorFull> {
262 return ActorModel.scope(ScopeNames.FULL).findByPk(id)
263 }
264
265 static loadFromAccountByVideoId (videoId: number, transaction: Sequelize.Transaction): Bluebird<MActor> {
260 const query = { 266 const query = {
261 include: [ 267 include: [
262 { 268 {
@@ -300,7 +306,7 @@ export class ActorModel extends Model<ActorModel> {
300 .then(a => !!a) 306 .then(a => !!a)
301 } 307 }
302 308
303 static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction) { 309 static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction): Bluebird<MActorFull[]> {
304 const query = { 310 const query = {
305 where: { 311 where: {
306 followersUrl: { 312 followersUrl: {
@@ -313,7 +319,7 @@ export class ActorModel extends Model<ActorModel> {
313 return ActorModel.scope(ScopeNames.FULL).findAll(query) 319 return ActorModel.scope(ScopeNames.FULL).findAll(query)
314 } 320 }
315 321
316 static loadLocalByName (preferredUsername: string, transaction?: Sequelize.Transaction) { 322 static loadLocalByName (preferredUsername: string, transaction?: Sequelize.Transaction): Bluebird<MActorFull> {
317 const query = { 323 const query = {
318 where: { 324 where: {
319 preferredUsername, 325 preferredUsername,
@@ -325,7 +331,7 @@ export class ActorModel extends Model<ActorModel> {
325 return ActorModel.scope(ScopeNames.FULL).findOne(query) 331 return ActorModel.scope(ScopeNames.FULL).findOne(query)
326 } 332 }
327 333
328 static loadByNameAndHost (preferredUsername: string, host: string) { 334 static loadByNameAndHost (preferredUsername: string, host: string): Bluebird<MActorFull> {
329 const query = { 335 const query = {
330 where: { 336 where: {
331 preferredUsername 337 preferredUsername
@@ -344,7 +350,7 @@ export class ActorModel extends Model<ActorModel> {
344 return ActorModel.scope(ScopeNames.FULL).findOne(query) 350 return ActorModel.scope(ScopeNames.FULL).findOne(query)
345 } 351 }
346 352
347 static loadByUrl (url: string, transaction?: Sequelize.Transaction) { 353 static loadByUrl (url: string, transaction?: Sequelize.Transaction): Bluebird<MActorAccountChannelId> {
348 const query = { 354 const query = {
349 where: { 355 where: {
350 url 356 url
@@ -367,7 +373,7 @@ export class ActorModel extends Model<ActorModel> {
367 return ActorModel.unscoped().findOne(query) 373 return ActorModel.unscoped().findOne(query)
368 } 374 }
369 375
370 static loadByUrlAndPopulateAccountAndChannel (url: string, transaction?: Sequelize.Transaction) { 376 static loadByUrlAndPopulateAccountAndChannel (url: string, transaction?: Sequelize.Transaction): Bluebird<MActorFull> {
371 const query = { 377 const query = {
372 where: { 378 where: {
373 url 379 url
diff --git a/server/models/oauth/oauth-token.ts b/server/models/oauth/oauth-token.ts
index 903d551df..b680be237 100644
--- a/server/models/oauth/oauth-token.ts
+++ b/server/models/oauth/oauth-token.ts
@@ -18,6 +18,8 @@ import { Transaction } from 'sequelize'
18import { AccountModel } from '../account/account' 18import { AccountModel } from '../account/account'
19import { ActorModel } from '../activitypub/actor' 19import { ActorModel } from '../activitypub/actor'
20import { clearCacheByToken } from '../../lib/oauth-model' 20import { clearCacheByToken } from '../../lib/oauth-model'
21import * as Bluebird from 'bluebird'
22import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token'
21 23
22export type OAuthTokenInfo = { 24export type OAuthTokenInfo = {
23 refreshToken: string 25 refreshToken: string
@@ -160,7 +162,7 @@ export class OAuthTokenModel extends Model<OAuthTokenModel> {
160 }) 162 })
161 } 163 }
162 164
163 static getByTokenAndPopulateUser (bearerToken: string) { 165 static getByTokenAndPopulateUser (bearerToken: string): Bluebird<MOAuthTokenUser> {
164 const query = { 166 const query = {
165 where: { 167 where: {
166 accessToken: bearerToken 168 accessToken: bearerToken
@@ -170,13 +172,13 @@ export class OAuthTokenModel extends Model<OAuthTokenModel> {
170 return OAuthTokenModel.scope(ScopeNames.WITH_USER) 172 return OAuthTokenModel.scope(ScopeNames.WITH_USER)
171 .findOne(query) 173 .findOne(query)
172 .then(token => { 174 .then(token => {
173 if (token) token[ 'user' ] = token.User 175 if (!token) return null
174 176
175 return token 177 return Object.assign(token, { user: token.User })
176 }) 178 })
177 } 179 }
178 180
179 static getByRefreshTokenAndPopulateUser (refreshToken: string) { 181 static getByRefreshTokenAndPopulateUser (refreshToken: string): Bluebird<MOAuthTokenUser> {
180 const query = { 182 const query = {
181 where: { 183 where: {
182 refreshToken: refreshToken 184 refreshToken: refreshToken
@@ -186,12 +188,9 @@ export class OAuthTokenModel extends Model<OAuthTokenModel> {
186 return OAuthTokenModel.scope(ScopeNames.WITH_USER) 188 return OAuthTokenModel.scope(ScopeNames.WITH_USER)
187 .findOne(query) 189 .findOne(query)
188 .then(token => { 190 .then(token => {
189 if (token) { 191 if (!token) return new OAuthTokenModel()
190 token['user'] = token.User 192
191 return token 193 return Object.assign(token, { user: token.User })
192 } else {
193 return new OAuthTokenModel()
194 }
195 }) 194 })
196 } 195 }
197 196
diff --git a/server/models/redundancy/video-redundancy.ts b/server/models/redundancy/video-redundancy.ts
index 3df1c4f9c..1c216b300 100644
--- a/server/models/redundancy/video-redundancy.ts
+++ b/server/models/redundancy/video-redundancy.ts
@@ -30,6 +30,7 @@ import * as Bluebird from 'bluebird'
30import { col, FindOptions, fn, literal, Op, Transaction } from 'sequelize' 30import { col, FindOptions, fn, literal, Op, Transaction } from 'sequelize'
31import { VideoStreamingPlaylistModel } from '../video/video-streaming-playlist' 31import { VideoStreamingPlaylistModel } from '../video/video-streaming-playlist'
32import { CONFIG } from '../../initializers/config' 32import { CONFIG } from '../../initializers/config'
33import { MVideoRedundancy, MVideoRedundancyVideo } from '@server/typings/models'
33 34
34export enum ScopeNames { 35export enum ScopeNames {
35 WITH_VIDEO = 'WITH_VIDEO' 36 WITH_VIDEO = 'WITH_VIDEO'
@@ -166,7 +167,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
166 return undefined 167 return undefined
167 } 168 }
168 169
169 static async loadLocalByFileId (videoFileId: number) { 170 static async loadLocalByFileId (videoFileId: number): Promise<MVideoRedundancyVideo> {
170 const actor = await getServerActor() 171 const actor = await getServerActor()
171 172
172 const query = { 173 const query = {
@@ -179,7 +180,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
179 return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query) 180 return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query)
180 } 181 }
181 182
182 static async loadLocalByStreamingPlaylistId (videoStreamingPlaylistId: number) { 183 static async loadLocalByStreamingPlaylistId (videoStreamingPlaylistId: number): Promise<MVideoRedundancyVideo> {
183 const actor = await getServerActor() 184 const actor = await getServerActor()
184 185
185 const query = { 186 const query = {
@@ -192,7 +193,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
192 return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query) 193 return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query)
193 } 194 }
194 195
195 static loadByUrl (url: string, transaction?: Transaction) { 196 static loadByUrl (url: string, transaction?: Transaction): Bluebird<MVideoRedundancy> {
196 const query = { 197 const query = {
197 where: { 198 where: {
198 url 199 url
@@ -306,7 +307,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
306 return VideoRedundancyModel.getVideoSample(VideoModel.unscoped().findAll(query)) 307 return VideoRedundancyModel.getVideoSample(VideoModel.unscoped().findAll(query))
307 } 308 }
308 309
309 static async loadOldestLocalThatAlreadyExpired (strategy: VideoRedundancyStrategy, expiresAfterMs: number) { 310 static async loadOldestLocalExpired (strategy: VideoRedundancyStrategy, expiresAfterMs: number): Promise<MVideoRedundancyVideo> {
310 const expiredDate = new Date() 311 const expiredDate = new Date()
311 expiredDate.setMilliseconds(expiredDate.getMilliseconds() - expiresAfterMs) 312 expiredDate.setMilliseconds(expiredDate.getMilliseconds() - expiresAfterMs)
312 313
diff --git a/server/models/server/plugin.ts b/server/models/server/plugin.ts
index a15f9a7e2..debd25ea1 100644
--- a/server/models/server/plugin.ts
+++ b/server/models/server/plugin.ts
@@ -11,6 +11,8 @@ import { PluginType } from '../../../shared/models/plugins/plugin.type'
11import { PeerTubePlugin } from '../../../shared/models/plugins/peertube-plugin.model' 11import { PeerTubePlugin } from '../../../shared/models/plugins/peertube-plugin.model'
12import { FindAndCountOptions, json } from 'sequelize' 12import { FindAndCountOptions, json } from 'sequelize'
13import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model' 13import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model'
14import * as Bluebird from 'bluebird'
15import { MPlugin } from '@server/typings/models'
14 16
15@DefaultScope(() => ({ 17@DefaultScope(() => ({
16 attributes: { 18 attributes: {
@@ -85,7 +87,7 @@ export class PluginModel extends Model<PluginModel> {
85 @UpdatedAt 87 @UpdatedAt
86 updatedAt: Date 88 updatedAt: Date
87 89
88 static listEnabledPluginsAndThemes () { 90 static listEnabledPluginsAndThemes (): Bluebird<MPlugin[]> {
89 const query = { 91 const query = {
90 where: { 92 where: {
91 enabled: true, 93 enabled: true,
@@ -96,7 +98,7 @@ export class PluginModel extends Model<PluginModel> {
96 return PluginModel.findAll(query) 98 return PluginModel.findAll(query)
97 } 99 }
98 100
99 static loadByNpmName (npmName: string) { 101 static loadByNpmName (npmName: string): Bluebird<MPlugin> {
100 const name = this.normalizePluginName(npmName) 102 const name = this.normalizePluginName(npmName)
101 const type = this.getTypeFromNpmName(npmName) 103 const type = this.getTypeFromNpmName(npmName)
102 104
@@ -206,13 +208,13 @@ export class PluginModel extends Model<PluginModel> {
206 if (options.pluginType) query.where['type'] = options.pluginType 208 if (options.pluginType) query.where['type'] = options.pluginType
207 209
208 return PluginModel 210 return PluginModel
209 .findAndCountAll(query) 211 .findAndCountAll<MPlugin>(query)
210 .then(({ rows, count }) => { 212 .then(({ rows, count }) => {
211 return { total: count, data: rows } 213 return { total: count, data: rows }
212 }) 214 })
213 } 215 }
214 216
215 static listInstalled () { 217 static listInstalled (): Bluebird<MPlugin[]> {
216 const query = { 218 const query = {
217 where: { 219 where: {
218 uninstalled: false 220 uninstalled: false
diff --git a/server/models/server/server-blocklist.ts b/server/models/server/server-blocklist.ts
index 5138b0f76..e4db93dfc 100644
--- a/server/models/server/server-blocklist.ts
+++ b/server/models/server/server-blocklist.ts
@@ -3,6 +3,8 @@ import { AccountModel } from '../account/account'
3import { ServerModel } from './server' 3import { ServerModel } from './server'
4import { ServerBlock } from '../../../shared/models/blocklist' 4import { ServerBlock } from '../../../shared/models/blocklist'
5import { getSort } from '../utils' 5import { getSort } from '../utils'
6import * as Bluebird from 'bluebird'
7import { MServerBlocklist, MServerBlocklistAccountServer } from '@server/typings/models'
6 8
7enum ScopeNames { 9enum ScopeNames {
8 WITH_ACCOUNT = 'WITH_ACCOUNT', 10 WITH_ACCOUNT = 'WITH_ACCOUNT',
@@ -73,7 +75,7 @@ export class ServerBlocklistModel extends Model<ServerBlocklistModel> {
73 }) 75 })
74 BlockedServer: ServerModel 76 BlockedServer: ServerModel
75 77
76 static loadByAccountAndHost (accountId: number, host: string) { 78 static loadByAccountAndHost (accountId: number, host: string): Bluebird<MServerBlocklist> {
77 const query = { 79 const query = {
78 where: { 80 where: {
79 accountId 81 accountId
@@ -104,7 +106,7 @@ export class ServerBlocklistModel extends Model<ServerBlocklistModel> {
104 106
105 return ServerBlocklistModel 107 return ServerBlocklistModel
106 .scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_SERVER ]) 108 .scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_SERVER ])
107 .findAndCountAll(query) 109 .findAndCountAll<MServerBlocklistAccountServer>(query)
108 .then(({ rows, count }) => { 110 .then(({ rows, count }) => {
109 return { total: count, data: rows } 111 return { total: count, data: rows }
110 }) 112 })
diff --git a/server/models/server/server.ts b/server/models/server/server.ts
index 1d211f1e0..b0bdd2b0b 100644
--- a/server/models/server/server.ts
+++ b/server/models/server/server.ts
@@ -2,8 +2,9 @@ import { AllowNull, Column, CreatedAt, Default, HasMany, Is, Model, Table, Updat
2import { isHostValid } from '../../helpers/custom-validators/servers' 2import { isHostValid } from '../../helpers/custom-validators/servers'
3import { ActorModel } from '../activitypub/actor' 3import { ActorModel } from '../activitypub/actor'
4import { throwIfNotValid } from '../utils' 4import { throwIfNotValid } from '../utils'
5import { AccountBlocklistModel } from '../account/account-blocklist'
6import { ServerBlocklistModel } from './server-blocklist' 5import { ServerBlocklistModel } from './server-blocklist'
6import * as Bluebird from 'bluebird'
7import { MServer } from '@server/typings/models/server'
7 8
8@Table({ 9@Table({
9 tableName: 'server', 10 tableName: 'server',
@@ -50,7 +51,7 @@ export class ServerModel extends Model<ServerModel> {
50 }) 51 })
51 BlockedByAccounts: ServerBlocklistModel[] 52 BlockedByAccounts: ServerBlocklistModel[]
52 53
53 static loadByHost (host: string) { 54 static loadByHost (host: string): Bluebird<MServer> {
54 const query = { 55 const query = {
55 where: { 56 where: {
56 host 57 host
diff --git a/server/models/video/video-abuse.ts b/server/models/video/video-abuse.ts
index 1ac7919b3..af7b40d11 100644
--- a/server/models/video/video-abuse.ts
+++ b/server/models/video/video-abuse.ts
@@ -11,6 +11,8 @@ import { getSort, throwIfNotValid } from '../utils'
11import { VideoModel } from './video' 11import { VideoModel } from './video'
12import { VideoAbuseState } from '../../../shared' 12import { VideoAbuseState } from '../../../shared'
13import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants' 13import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants'
14import { MVideoAbuse, MVideoAbuseAccountVideo, MVideoAbuseVideo } from '../../typings/models'
15import * as Bluebird from 'bluebird'
14 16
15@Table({ 17@Table({
16 tableName: 'videoAbuse', 18 tableName: 'videoAbuse',
@@ -73,7 +75,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
73 }) 75 })
74 Video: VideoModel 76 Video: VideoModel
75 77
76 static loadByIdAndVideoId (id: number, videoId: number) { 78 static loadByIdAndVideoId (id: number, videoId: number): Bluebird<MVideoAbuse> {
77 const query = { 79 const query = {
78 where: { 80 where: {
79 id, 81 id,
@@ -106,7 +108,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
106 }) 108 })
107 } 109 }
108 110
109 toFormattedJSON (): VideoAbuse { 111 toFormattedJSON (this: MVideoAbuseAccountVideo): VideoAbuse {
110 return { 112 return {
111 id: this.id, 113 id: this.id,
112 reason: this.reason, 114 reason: this.reason,
@@ -125,7 +127,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
125 } 127 }
126 } 128 }
127 129
128 toActivityPubObject (): VideoAbuseObject { 130 toActivityPubObject (this: MVideoAbuseVideo): VideoAbuseObject {
129 return { 131 return {
130 type: 'Flag' as 'Flag', 132 type: 'Flag' as 'Flag',
131 content: this.reason, 133 content: this.reason,
diff --git a/server/models/video/video-blacklist.ts b/server/models/video/video-blacklist.ts
index 22d949da0..5a0cac94a 100644
--- a/server/models/video/video-blacklist.ts
+++ b/server/models/video/video-blacklist.ts
@@ -1,12 +1,14 @@
1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { getSortOnModel, SortType, throwIfNotValid } from '../utils' 2import { getSortOnModel, SortType, throwIfNotValid } from '../utils'
3import { ScopeNames as VideoModelScopeNames, VideoModel } from './video' 3import { VideoModel } from './video'
4import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from './video-channel' 4import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from './video-channel'
5import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist' 5import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist'
6import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos' 6import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos'
7import { CONSTRAINTS_FIELDS } from '../../initializers/constants' 7import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
8import { FindOptions } from 'sequelize' 8import { FindOptions } from 'sequelize'
9import { ThumbnailModel } from './thumbnail' 9import { ThumbnailModel } from './thumbnail'
10import * as Bluebird from 'bluebird'
11import { MVideoBlacklist } from '@server/typings/models'
10 12
11@Table({ 13@Table({
12 tableName: 'videoBlacklist', 14 tableName: 'videoBlacklist',
@@ -99,7 +101,7 @@ export class VideoBlacklistModel extends Model<VideoBlacklistModel> {
99 }) 101 })
100 } 102 }
101 103
102 static loadByVideoId (id: number) { 104 static loadByVideoId (id: number): Bluebird<MVideoBlacklist> {
103 const query = { 105 const query = {
104 where: { 106 where: {
105 videoId: id 107 videoId: id
diff --git a/server/models/video/video-caption.ts b/server/models/video/video-caption.ts
index a01565851..9ce350d12 100644
--- a/server/models/video/video-caption.ts
+++ b/server/models/video/video-caption.ts
@@ -21,6 +21,8 @@ import { join } from 'path'
21import { logger } from '../../helpers/logger' 21import { logger } from '../../helpers/logger'
22import { remove } from 'fs-extra' 22import { remove } from 'fs-extra'
23import { CONFIG } from '../../initializers/config' 23import { CONFIG } from '../../initializers/config'
24import * as Bluebird from 'bluebird'
25import { MVideoCaptionVideo } from '@server/typings/models'
24 26
25export enum ScopeNames { 27export enum ScopeNames {
26 WITH_VIDEO_UUID_AND_REMOTE = 'WITH_VIDEO_UUID_AND_REMOTE' 28 WITH_VIDEO_UUID_AND_REMOTE = 'WITH_VIDEO_UUID_AND_REMOTE'
@@ -30,7 +32,7 @@ export enum ScopeNames {
30 [ScopeNames.WITH_VIDEO_UUID_AND_REMOTE]: { 32 [ScopeNames.WITH_VIDEO_UUID_AND_REMOTE]: {
31 include: [ 33 include: [
32 { 34 {
33 attributes: [ 'uuid', 'remote' ], 35 attributes: [ 'id', 'uuid', 'remote' ],
34 model: VideoModel.unscoped(), 36 model: VideoModel.unscoped(),
35 required: true 37 required: true
36 } 38 }
@@ -93,7 +95,7 @@ export class VideoCaptionModel extends Model<VideoCaptionModel> {
93 return undefined 95 return undefined
94 } 96 }
95 97
96 static loadByVideoIdAndLanguage (videoId: string | number, language: string) { 98 static loadByVideoIdAndLanguage (videoId: string | number, language: string): Bluebird<MVideoCaptionVideo> {
97 const videoInclude = { 99 const videoInclude = {
98 model: VideoModel.unscoped(), 100 model: VideoModel.unscoped(),
99 attributes: [ 'id', 'remote', 'uuid' ], 101 attributes: [ 'id', 'remote', 'uuid' ],
@@ -122,7 +124,7 @@ export class VideoCaptionModel extends Model<VideoCaptionModel> {
122 .then(([ caption ]) => caption) 124 .then(([ caption ]) => caption)
123 } 125 }
124 126
125 static listVideoCaptions (videoId: number) { 127 static listVideoCaptions (videoId: number): Bluebird<MVideoCaptionVideo[]> {
126 const query = { 128 const query = {
127 order: [ [ 'language', 'ASC' ] ] as OrderItem[], 129 order: [ [ 'language', 'ASC' ] ] as OrderItem[],
128 where: { 130 where: {
diff --git a/server/models/video/video-change-ownership.ts b/server/models/video/video-change-ownership.ts
index b545a2f8c..2d0ff48fb 100644
--- a/server/models/video/video-change-ownership.ts
+++ b/server/models/video/video-change-ownership.ts
@@ -3,6 +3,8 @@ import { AccountModel } from '../account/account'
3import { ScopeNames as VideoScopeNames, VideoModel } from './video' 3import { ScopeNames as VideoScopeNames, VideoModel } from './video'
4import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '../../../shared/models/videos' 4import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '../../../shared/models/videos'
5import { getSort } from '../utils' 5import { getSort } from '../utils'
6import { MVideoChangeOwnershipFull } from '@server/typings/models/video/video-change-ownership'
7import * as Bluebird from 'bluebird'
6 8
7enum ScopeNames { 9enum ScopeNames {
8 WITH_ACCOUNTS = 'WITH_ACCOUNTS', 10 WITH_ACCOUNTS = 'WITH_ACCOUNTS',
@@ -108,11 +110,11 @@ export class VideoChangeOwnershipModel extends Model<VideoChangeOwnershipModel>
108 110
109 return Promise.all([ 111 return Promise.all([
110 VideoChangeOwnershipModel.scope(ScopeNames.WITH_ACCOUNTS).count(query), 112 VideoChangeOwnershipModel.scope(ScopeNames.WITH_ACCOUNTS).count(query),
111 VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ]).findAll(query) 113 VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ]).findAll<MVideoChangeOwnershipFull>(query)
112 ]).then(([ count, rows ]) => ({ total: count, data: rows })) 114 ]).then(([ count, rows ]) => ({ total: count, data: rows }))
113 } 115 }
114 116
115 static load (id: number) { 117 static load (id: number): Bluebird<MVideoChangeOwnershipFull> {
116 return VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ]) 118 return VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ])
117 .findByPk(id) 119 .findByPk(id)
118 } 120 }
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index 6241a75a3..79b9e7d2b 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -33,6 +33,13 @@ import { ServerModel } from '../server/server'
33import { FindOptions, ModelIndexesOptions, Op } from 'sequelize' 33import { FindOptions, ModelIndexesOptions, Op } from 'sequelize'
34import { AvatarModel } from '../avatar/avatar' 34import { AvatarModel } from '../avatar/avatar'
35import { VideoPlaylistModel } from './video-playlist' 35import { VideoPlaylistModel } from './video-playlist'
36import * as Bluebird from 'bluebird'
37import {
38 MChannelAccountDefault,
39 MChannelActor,
40 MChannelActorAccountDefault,
41 MChannelActorAccountDefaultVideos
42} from '../../typings/models/video'
36 43
37// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation 44// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation
38const indexes: ModelIndexesOptions[] = [ 45const indexes: ModelIndexesOptions[] = [
@@ -47,7 +54,7 @@ const indexes: ModelIndexesOptions[] = [
47] 54]
48 55
49export enum ScopeNames { 56export enum ScopeNames {
50 AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', 57 FOR_API = 'FOR_API',
51 WITH_ACCOUNT = 'WITH_ACCOUNT', 58 WITH_ACCOUNT = 'WITH_ACCOUNT',
52 WITH_ACTOR = 'WITH_ACTOR', 59 WITH_ACTOR = 'WITH_ACTOR',
53 WITH_VIDEOS = 'WITH_VIDEOS', 60 WITH_VIDEOS = 'WITH_VIDEOS',
@@ -74,10 +81,10 @@ export type SummaryOptions = {
74@Scopes(() => ({ 81@Scopes(() => ({
75 [ScopeNames.SUMMARY]: (options: SummaryOptions = {}) => { 82 [ScopeNames.SUMMARY]: (options: SummaryOptions = {}) => {
76 const base: FindOptions = { 83 const base: FindOptions = {
77 attributes: [ 'name', 'description', 'id', 'actorId' ], 84 attributes: [ 'id', 'name', 'description', 'actorId' ],
78 include: [ 85 include: [
79 { 86 {
80 attributes: [ 'preferredUsername', 'url', 'serverId', 'avatarId' ], 87 attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ],
81 model: ActorModel.unscoped(), 88 model: ActorModel.unscoped(),
82 required: true, 89 required: true,
83 include: [ 90 include: [
@@ -106,7 +113,7 @@ export type SummaryOptions = {
106 113
107 return base 114 return base
108 }, 115 },
109 [ScopeNames.AVAILABLE_FOR_LIST]: (options: AvailableForListOptions) => { 116 [ScopeNames.FOR_API]: (options: AvailableForListOptions) => {
110 // Only list local channels OR channels that are on an instance followed by actorId 117 // Only list local channels OR channels that are on an instance followed by actorId
111 const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId) 118 const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId)
112 119
@@ -268,7 +275,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
268 } 275 }
269 276
270 const scopes = { 277 const scopes = {
271 method: [ ScopeNames.AVAILABLE_FOR_LIST, { actorId } as AvailableForListOptions ] 278 method: [ ScopeNames.FOR_API, { actorId } as AvailableForListOptions ]
272 } 279 }
273 return VideoChannelModel 280 return VideoChannelModel
274 .scope(scopes) 281 .scope(scopes)
@@ -278,7 +285,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
278 }) 285 })
279 } 286 }
280 287
281 static listLocalsForSitemap (sort: string) { 288 static listLocalsForSitemap (sort: string): Bluebird<MChannelActor[]> {
282 const query = { 289 const query = {
283 attributes: [ ], 290 attributes: [ ],
284 offset: 0, 291 offset: 0,
@@ -331,7 +338,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
331 } 338 }
332 339
333 const scopes = { 340 const scopes = {
334 method: [ ScopeNames.AVAILABLE_FOR_LIST, { actorId: options.actorId } as AvailableForListOptions ] 341 method: [ ScopeNames.FOR_API, { actorId: options.actorId } as AvailableForListOptions ]
335 } 342 }
336 return VideoChannelModel 343 return VideoChannelModel
337 .scope(scopes) 344 .scope(scopes)
@@ -369,13 +376,13 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
369 }) 376 })
370 } 377 }
371 378
372 static loadByIdAndPopulateAccount (id: number) { 379 static loadByIdAndPopulateAccount (id: number): Bluebird<MChannelActorAccountDefault> {
373 return VideoChannelModel.unscoped() 380 return VideoChannelModel.unscoped()
374 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) 381 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
375 .findByPk(id) 382 .findByPk(id)
376 } 383 }
377 384
378 static loadByIdAndAccount (id: number, accountId: number) { 385 static loadByIdAndAccount (id: number, accountId: number): Bluebird<MChannelActorAccountDefault> {
379 const query = { 386 const query = {
380 where: { 387 where: {
381 id, 388 id,
@@ -388,13 +395,13 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
388 .findOne(query) 395 .findOne(query)
389 } 396 }
390 397
391 static loadAndPopulateAccount (id: number) { 398 static loadAndPopulateAccount (id: number): Bluebird<MChannelActorAccountDefault> {
392 return VideoChannelModel.unscoped() 399 return VideoChannelModel.unscoped()
393 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) 400 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
394 .findByPk(id) 401 .findByPk(id)
395 } 402 }
396 403
397 static loadByUrlAndPopulateAccount (url: string) { 404 static loadByUrlAndPopulateAccount (url: string): Bluebird<MChannelAccountDefault> {
398 const query = { 405 const query = {
399 include: [ 406 include: [
400 { 407 {
@@ -420,7 +427,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
420 return VideoChannelModel.loadByNameAndHostAndPopulateAccount(name, host) 427 return VideoChannelModel.loadByNameAndHostAndPopulateAccount(name, host)
421 } 428 }
422 429
423 static loadLocalByNameAndPopulateAccount (name: string) { 430 static loadLocalByNameAndPopulateAccount (name: string): Bluebird<MChannelActorAccountDefault> {
424 const query = { 431 const query = {
425 include: [ 432 include: [
426 { 433 {
@@ -439,7 +446,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
439 .findOne(query) 446 .findOne(query)
440 } 447 }
441 448
442 static loadByNameAndHostAndPopulateAccount (name: string, host: string) { 449 static loadByNameAndHostAndPopulateAccount (name: string, host: string): Bluebird<MChannelActorAccountDefault> {
443 const query = { 450 const query = {
444 include: [ 451 include: [
445 { 452 {
@@ -464,7 +471,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
464 .findOne(query) 471 .findOne(query)
465 } 472 }
466 473
467 static loadAndPopulateAccountAndVideos (id: number) { 474 static loadAndPopulateAccountAndVideos (id: number): Bluebird<MChannelActorAccountDefaultVideos> {
468 const options = { 475 const options = {
469 include: [ 476 include: [
470 VideoModel 477 VideoModel
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index 58b75510d..c88dac1c1 100644
--- a/server/models/video/video-comment.ts
+++ b/server/models/video/video-comment.ts
@@ -1,36 +1,30 @@
1import { 1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
2 AllowNull,
3 BeforeDestroy,
4 BelongsTo,
5 Column,
6 CreatedAt,
7 DataType,
8 ForeignKey,
9 Is,
10 Model,
11 Scopes,
12 Table,
13 UpdatedAt
14} from 'sequelize-typescript'
15import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects' 2import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects'
16import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object' 3import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object'
17import { VideoComment } from '../../../shared/models/videos/video-comment.model' 4import { VideoComment } from '../../../shared/models/videos/video-comment.model'
18import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 5import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
19import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants' 6import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants'
20import { sendDeleteVideoComment } from '../../lib/activitypub/send'
21import { AccountModel } from '../account/account' 7import { AccountModel } from '../account/account'
22import { ActorModel } from '../activitypub/actor' 8import { ActorModel } from '../activitypub/actor'
23import { AvatarModel } from '../avatar/avatar'
24import { ServerModel } from '../server/server'
25import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils' 9import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils'
26import { VideoModel } from './video' 10import { VideoModel } from './video'
27import { VideoChannelModel } from './video-channel' 11import { VideoChannelModel } from './video-channel'
28import { getServerActor } from '../../helpers/utils' 12import { getServerActor } from '../../helpers/utils'
29import { UserModel } from '../account/user'
30import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor' 13import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor'
31import { regexpCapture } from '../../helpers/regexp' 14import { regexpCapture } from '../../helpers/regexp'
32import { uniq } from 'lodash' 15import { uniq } from 'lodash'
33import { FindOptions, literal, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize' 16import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize'
17import * as Bluebird from 'bluebird'
18import {
19 MComment,
20 MCommentId,
21 MCommentOwner,
22 MCommentOwnerReplyVideoLight,
23 MCommentOwnerVideo,
24 MCommentOwnerVideoFeed,
25 MCommentOwnerVideoReply
26} from '../../typings/models/video'
27import { MUserAccountId } from '@server/typings/models'
34 28
35enum ScopeNames { 29enum ScopeNames {
36 WITH_ACCOUNT = 'WITH_ACCOUNT', 30 WITH_ACCOUNT = 'WITH_ACCOUNT',
@@ -68,22 +62,7 @@ enum ScopeNames {
68 [ScopeNames.WITH_ACCOUNT]: { 62 [ScopeNames.WITH_ACCOUNT]: {
69 include: [ 63 include: [
70 { 64 {
71 model: AccountModel, 65 model: AccountModel
72 include: [
73 {
74 model: ActorModel,
75 include: [
76 {
77 model: ServerModel,
78 required: false
79 },
80 {
81 model: AvatarModel,
82 required: false
83 }
84 ]
85 }
86 ]
87 } 66 }
88 ] 67 ]
89 }, 68 },
@@ -102,22 +81,12 @@ enum ScopeNames {
102 required: true, 81 required: true,
103 include: [ 82 include: [
104 { 83 {
105 model: VideoChannelModel.unscoped(), 84 model: VideoChannelModel,
106 required: true, 85 required: true,
107 include: [ 86 include: [
108 { 87 {
109 model: ActorModel,
110 required: true
111 },
112 {
113 model: AccountModel, 88 model: AccountModel,
114 required: true, 89 required: true
115 include: [
116 {
117 model: ActorModel,
118 required: true
119 }
120 ]
121 } 90 }
122 ] 91 ]
123 } 92 }
@@ -212,7 +181,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
212 }) 181 })
213 Account: AccountModel 182 Account: AccountModel
214 183
215 static loadById (id: number, t?: Transaction) { 184 static loadById (id: number, t?: Transaction): Bluebird<MComment> {
216 const query: FindOptions = { 185 const query: FindOptions = {
217 where: { 186 where: {
218 id 187 id
@@ -224,7 +193,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
224 return VideoCommentModel.findOne(query) 193 return VideoCommentModel.findOne(query)
225 } 194 }
226 195
227 static loadByIdAndPopulateVideoAndAccountAndReply (id: number, t?: Transaction) { 196 static loadByIdAndPopulateVideoAndAccountAndReply (id: number, t?: Transaction): Bluebird<MCommentOwnerVideoReply> {
228 const query: FindOptions = { 197 const query: FindOptions = {
229 where: { 198 where: {
230 id 199 id
@@ -238,7 +207,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
238 .findOne(query) 207 .findOne(query)
239 } 208 }
240 209
241 static loadByUrlAndPopulateAccountAndVideo (url: string, t?: Transaction) { 210 static loadByUrlAndPopulateAccountAndVideo (url: string, t?: Transaction): Bluebird<MCommentOwnerVideo> {
242 const query: FindOptions = { 211 const query: FindOptions = {
243 where: { 212 where: {
244 url 213 url
@@ -250,7 +219,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
250 return VideoCommentModel.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEO ]).findOne(query) 219 return VideoCommentModel.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEO ]).findOne(query)
251 } 220 }
252 221
253 static loadByUrlAndPopulateReplyAndVideoUrlAndAccount (url: string, t?: Transaction) { 222 static loadByUrlAndPopulateReplyAndVideoUrlAndAccount (url: string, t?: Transaction): Bluebird<MCommentOwnerReplyVideoLight> {
254 const query: FindOptions = { 223 const query: FindOptions = {
255 where: { 224 where: {
256 url 225 url
@@ -273,7 +242,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
273 start: number, 242 start: number,
274 count: number, 243 count: number,
275 sort: string, 244 sort: string,
276 user?: UserModel 245 user?: MUserAccountId
277 }) { 246 }) {
278 const { videoId, start, count, sort, user } = parameters 247 const { videoId, start, count, sort, user } = parameters
279 248
@@ -314,7 +283,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
314 static async listThreadCommentsForApi (parameters: { 283 static async listThreadCommentsForApi (parameters: {
315 videoId: number, 284 videoId: number,
316 threadId: number, 285 threadId: number,
317 user?: UserModel 286 user?: MUserAccountId
318 }) { 287 }) {
319 const { videoId, threadId, user } = parameters 288 const { videoId, threadId, user } = parameters
320 289
@@ -353,7 +322,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
353 }) 322 })
354 } 323 }
355 324
356 static listThreadParentComments (comment: VideoCommentModel, t: Transaction, order: 'ASC' | 'DESC' = 'ASC') { 325 static listThreadParentComments (comment: MCommentId, t: Transaction, order: 'ASC' | 'DESC' = 'ASC'): Bluebird<MCommentOwner[]> {
357 const query = { 326 const query = {
358 order: [ [ 'createdAt', order ] ] as Order, 327 order: [ [ 'createdAt', order ] ] as Order,
359 where: { 328 where: {
@@ -389,10 +358,10 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
389 transaction: t 358 transaction: t
390 } 359 }
391 360
392 return VideoCommentModel.findAndCountAll(query) 361 return VideoCommentModel.findAndCountAll<MComment>(query)
393 } 362 }
394 363
395 static listForFeed (start: number, count: number, videoId?: number) { 364 static listForFeed (start: number, count: number, videoId?: number): Bluebird<MCommentOwnerVideoFeed[]> {
396 const query = { 365 const query = {
397 order: [ [ 'createdAt', 'DESC' ] ] as Order, 366 order: [ [ 'createdAt', 'DESC' ] ] as Order,
398 offset: start, 367 offset: start,
@@ -521,7 +490,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
521 } as VideoComment 490 } as VideoComment
522 } 491 }
523 492
524 toActivityPubObject (threadParentComments: VideoCommentModel[]): VideoCommentObject { 493 toActivityPubObject (threadParentComments: MCommentOwner[]): VideoCommentObject {
525 let inReplyTo: string 494 let inReplyTo: string
526 // New thread, so in AS we reply to the video 495 // New thread, so in AS we reply to the video
527 if (this.inReplyToCommentId === null) { 496 if (this.inReplyToCommentId === null) {
diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts
index 05c490759..6304f741c 100644
--- a/server/models/video/video-file.ts
+++ b/server/models/video/video-file.ts
@@ -25,6 +25,7 @@ import { VideoRedundancyModel } from '../redundancy/video-redundancy'
25import { VideoStreamingPlaylistModel } from './video-streaming-playlist' 25import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
26import { FindOptions, QueryTypes, Transaction } from 'sequelize' 26import { FindOptions, QueryTypes, Transaction } from 'sequelize'
27import { MIMETYPES } from '../../initializers/constants' 27import { MIMETYPES } from '../../initializers/constants'
28import { MVideoFile } from '@server/typings/models'
28 29
29@Table({ 30@Table({
30 tableName: 'videoFile', 31 tableName: 'videoFile',
@@ -166,7 +167,7 @@ export class VideoFileModel extends Model<VideoFileModel> {
166 return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname] 167 return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname]
167 } 168 }
168 169
169 hasSameUniqueKeysThan (other: VideoFileModel) { 170 hasSameUniqueKeysThan (other: MVideoFile) {
170 return this.fps === other.fps && 171 return this.fps === other.fps &&
171 this.resolution === other.resolution && 172 this.resolution === other.resolution &&
172 this.videoId === other.videoId 173 this.videoId === other.videoId
diff --git a/server/models/video/video-format-utils.ts b/server/models/video/video-format-utils.ts
index 284539def..4e7eb5f0c 100644
--- a/server/models/video/video-format-utils.ts
+++ b/server/models/video/video-format-utils.ts
@@ -1,6 +1,5 @@
1import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos' 1import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos'
2import { VideoModel } from './video' 2import { VideoModel } from './video'
3import { VideoFileModel } from './video-file'
4import { 3import {
5 ActivityPlaylistInfohashesObject, 4 ActivityPlaylistInfohashesObject,
6 ActivityPlaylistSegmentHashesObject, 5 ActivityPlaylistSegmentHashesObject,
@@ -17,7 +16,9 @@ import {
17} from '../../lib/activitypub' 16} from '../../lib/activitypub'
18import { isArray } from '../../helpers/custom-validators/misc' 17import { isArray } from '../../helpers/custom-validators/misc'
19import { VideoStreamingPlaylist } from '../../../shared/models/videos/video-streaming-playlist.model' 18import { VideoStreamingPlaylist } from '../../../shared/models/videos/video-streaming-playlist.model'
20import { VideoStreamingPlaylistModel } from './video-streaming-playlist' 19import { MVideo, MVideoAP, MVideoDetails } from '../../typings/models'
20import { MStreamingPlaylistRedundancies } from '../../typings/models/video/video-streaming-playlist'
21import { MVideoFileRedundanciesOpt } from '../../typings/models/video/video-file'
21 22
22export type VideoFormattingJSONOptions = { 23export type VideoFormattingJSONOptions = {
23 completeDescription?: boolean 24 completeDescription?: boolean
@@ -102,7 +103,7 @@ function videoModelToFormattedJSON (video: VideoModel, options?: VideoFormatting
102 return videoObject 103 return videoObject
103} 104}
104 105
105function videoModelToFormattedDetailsJSON (video: VideoModel): VideoDetails { 106function videoModelToFormattedDetailsJSON (video: MVideoDetails): VideoDetails {
106 const formattedJson = video.toFormattedJSON({ 107 const formattedJson = video.toFormattedJSON({
107 additionalAttributes: { 108 additionalAttributes: {
108 scheduledUpdate: true, 109 scheduledUpdate: true,
@@ -114,7 +115,7 @@ function videoModelToFormattedDetailsJSON (video: VideoModel): VideoDetails {
114 115
115 const tags = video.Tags ? video.Tags.map(t => t.name) : [] 116 const tags = video.Tags ? video.Tags.map(t => t.name) : []
116 117
117 const streamingPlaylists = streamingPlaylistsModelToFormattedJSON(video, video.VideoStreamingPlaylists) 118 const streamingPlaylists = streamingPlaylistsModelToFormattedJSON(video.VideoStreamingPlaylists)
118 119
119 const detailsJson = { 120 const detailsJson = {
120 support: video.support, 121 support: video.support,
@@ -142,7 +143,7 @@ function videoModelToFormattedDetailsJSON (video: VideoModel): VideoDetails {
142 return Object.assign(formattedJson, detailsJson) 143 return Object.assign(formattedJson, detailsJson)
143} 144}
144 145
145function streamingPlaylistsModelToFormattedJSON (video: VideoModel, playlists: VideoStreamingPlaylistModel[]): VideoStreamingPlaylist[] { 146function streamingPlaylistsModelToFormattedJSON (playlists: MStreamingPlaylistRedundancies[]): VideoStreamingPlaylist[] {
146 if (isArray(playlists) === false) return [] 147 if (isArray(playlists) === false) return []
147 148
148 return playlists 149 return playlists
@@ -161,7 +162,7 @@ function streamingPlaylistsModelToFormattedJSON (video: VideoModel, playlists: V
161 }) 162 })
162} 163}
163 164
164function videoFilesModelToFormattedJSON (video: VideoModel, videoFiles: VideoFileModel[]): VideoFile[] { 165function videoFilesModelToFormattedJSON (video: MVideo, videoFiles: MVideoFileRedundanciesOpt[]): VideoFile[] {
165 const { baseUrlHttp, baseUrlWs } = video.getBaseUrls() 166 const { baseUrlHttp, baseUrlWs } = video.getBaseUrls()
166 167
167 return videoFiles 168 return videoFiles
@@ -189,7 +190,7 @@ function videoFilesModelToFormattedJSON (video: VideoModel, videoFiles: VideoFil
189 }) 190 })
190} 191}
191 192
192function videoModelToActivityPubObject (video: VideoModel): VideoTorrentObject { 193function videoModelToActivityPubObject (video: MVideoAP): VideoTorrentObject {
193 const { baseUrlHttp, baseUrlWs } = video.getBaseUrls() 194 const { baseUrlHttp, baseUrlWs } = video.getBaseUrls()
194 if (!video.Tags) video.Tags = [] 195 if (!video.Tags) video.Tags = []
195 196
diff --git a/server/models/video/video-import.ts b/server/models/video/video-import.ts
index 480a671c8..f596eea9d 100644
--- a/server/models/video/video-import.ts
+++ b/server/models/video/video-import.ts
@@ -20,6 +20,8 @@ import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../help
20import { VideoImport, VideoImportState } from '../../../shared' 20import { VideoImport, VideoImportState } from '../../../shared'
21import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos' 21import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos'
22import { UserModel } from '../account/user' 22import { UserModel } from '../account/user'
23import * as Bluebird from 'bluebird'
24import { MVideoImportDefault } from '@server/typings/models/video/video-import'
23 25
24@DefaultScope(() => ({ 26@DefaultScope(() => ({
25 include: [ 27 include: [
@@ -28,7 +30,11 @@ import { UserModel } from '../account/user'
28 required: true 30 required: true
29 }, 31 },
30 { 32 {
31 model: VideoModel.scope([ VideoModelScopeNames.WITH_ACCOUNT_DETAILS, VideoModelScopeNames.WITH_TAGS]), 33 model: VideoModel.scope([
34 VideoModelScopeNames.WITH_ACCOUNT_DETAILS,
35 VideoModelScopeNames.WITH_TAGS,
36 VideoModelScopeNames.WITH_THUMBNAILS
37 ]),
32 required: false 38 required: false
33 } 39 }
34 ] 40 ]
@@ -114,7 +120,7 @@ export class VideoImportModel extends Model<VideoImportModel> {
114 return undefined 120 return undefined
115 } 121 }
116 122
117 static loadAndPopulateVideo (id: number) { 123 static loadAndPopulateVideo (id: number): Bluebird<MVideoImportDefault> {
118 return VideoImportModel.findByPk(id) 124 return VideoImportModel.findByPk(id)
119 } 125 }
120 126
@@ -135,7 +141,7 @@ export class VideoImportModel extends Model<VideoImportModel> {
135 } 141 }
136 } 142 }
137 143
138 return VideoImportModel.findAndCountAll(query) 144 return VideoImportModel.findAndCountAll<MVideoImportDefault>(query)
139 .then(({ rows, count }) => { 145 .then(({ rows, count }) => {
140 return { 146 return {
141 data: rows, 147 data: rows,
diff --git a/server/models/video/video-playlist-element.ts b/server/models/video/video-playlist-element.ts
index dd7653533..901113161 100644
--- a/server/models/video/video-playlist-element.ts
+++ b/server/models/video/video-playlist-element.ts
@@ -25,6 +25,9 @@ import { UserModel } from '../account/user'
25import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../shared/models/videos/playlist/video-playlist-element.model' 25import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../shared/models/videos/playlist/video-playlist-element.model'
26import { AccountModel } from '../account/account' 26import { AccountModel } from '../account/account'
27import { VideoPrivacy } from '../../../shared/models/videos' 27import { VideoPrivacy } from '../../../shared/models/videos'
28import * as Bluebird from 'bluebird'
29import { MVideoPlaylistAP, MVideoPlaylistElement, MVideoPlaylistVideoThumbnail } from '@server/typings/models/video/video-playlist-element'
30import { MUserAccountId } from '@server/typings/models'
28 31
29@Table({ 32@Table({
30 tableName: 'videoPlaylistElement', 33 tableName: 'videoPlaylistElement',
@@ -116,7 +119,7 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
116 count: number, 119 count: number,
117 videoPlaylistId: number, 120 videoPlaylistId: number,
118 serverAccount: AccountModel, 121 serverAccount: AccountModel,
119 user?: UserModel 122 user?: MUserAccountId
120 }) { 123 }) {
121 const accountIds = [ options.serverAccount.id ] 124 const accountIds = [ options.serverAccount.id ]
122 const videoScope: (ScopeOptions | string)[] = [ 125 const videoScope: (ScopeOptions | string)[] = [
@@ -162,7 +165,7 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
162 ]).then(([ total, data ]) => ({ total, data })) 165 ]).then(([ total, data ]) => ({ total, data }))
163 } 166 }
164 167
165 static loadByPlaylistAndVideo (videoPlaylistId: number, videoId: number) { 168 static loadByPlaylistAndVideo (videoPlaylistId: number, videoId: number): Bluebird<MVideoPlaylistElement> {
166 const query = { 169 const query = {
167 where: { 170 where: {
168 videoPlaylistId, 171 videoPlaylistId,
@@ -173,11 +176,11 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
173 return VideoPlaylistElementModel.findOne(query) 176 return VideoPlaylistElementModel.findOne(query)
174 } 177 }
175 178
176 static loadById (playlistElementId: number) { 179 static loadById (playlistElementId: number): Bluebird<MVideoPlaylistElement> {
177 return VideoPlaylistElementModel.findByPk(playlistElementId) 180 return VideoPlaylistElementModel.findByPk(playlistElementId)
178 } 181 }
179 182
180 static loadByPlaylistAndVideoForAP (playlistId: number | string, videoId: number | string) { 183 static loadByPlaylistAndVideoForAP (playlistId: number | string, videoId: number | string): Bluebird<MVideoPlaylistAP> {
181 const playlistWhere = validator.isUUID('' + playlistId) ? { uuid: playlistId } : { id: playlistId } 184 const playlistWhere = validator.isUUID('' + playlistId) ? { uuid: playlistId } : { id: playlistId }
182 const videoWhere = validator.isUUID('' + videoId) ? { uuid: videoId } : { id: videoId } 185 const videoWhere = validator.isUUID('' + videoId) ? { uuid: videoId } : { id: videoId }
183 186
@@ -218,7 +221,7 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel>
218 }) 221 })
219 } 222 }
220 223
221 static loadFirstElementWithVideoThumbnail (videoPlaylistId: number) { 224 static loadFirstElementWithVideoThumbnail (videoPlaylistId: number): Bluebird<MVideoPlaylistVideoThumbnail> {
222 const query = { 225 const query = {
223 order: getSort('position'), 226 order: getSort('position'),
224 where: { 227 where: {
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts
index c8e97c491..9f1d03ac5 100644
--- a/server/models/video/video-playlist.ts
+++ b/server/models/video/video-playlist.ts
@@ -43,6 +43,14 @@ import { VideoPlaylistType } from '../../../shared/models/videos/playlist/video-
43import { ThumbnailModel } from './thumbnail' 43import { ThumbnailModel } from './thumbnail'
44import { ActivityIconObject } from '../../../shared/models/activitypub/objects' 44import { ActivityIconObject } from '../../../shared/models/activitypub/objects'
45import { FindOptions, literal, Op, ScopeOptions, Transaction, WhereOptions } from 'sequelize' 45import { FindOptions, literal, Op, ScopeOptions, Transaction, WhereOptions } from 'sequelize'
46import * as Bluebird from 'bluebird'
47import {
48 MVideoPlaylistAccountThumbnail,
49 MVideoPlaylistFull,
50 MVideoPlaylistFullSummary,
51 MVideoPlaylistIdWithElements
52} from '../../typings/models/video/video-playlist'
53import { MThumbnail } from '../../typings/models/video/thumbnail'
46 54
47enum ScopeNames { 55enum ScopeNames {
48 AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', 56 AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST',
@@ -332,7 +340,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
332 }) 340 })
333 } 341 }
334 342
335 static listPlaylistIdsOf (accountId: number, videoIds: number[]) { 343 static listPlaylistIdsOf (accountId: number, videoIds: number[]): Bluebird<MVideoPlaylistIdWithElements[]> {
336 const query = { 344 const query = {
337 attributes: [ 'id' ], 345 attributes: [ 'id' ],
338 where: { 346 where: {
@@ -368,7 +376,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
368 .then(e => !!e) 376 .then(e => !!e)
369 } 377 }
370 378
371 static loadWithAccountAndChannelSummary (id: number | string, transaction: Transaction) { 379 static loadWithAccountAndChannelSummary (id: number | string, transaction: Transaction): Bluebird<MVideoPlaylistFullSummary> {
372 const where = buildWhereIdOrUUID(id) 380 const where = buildWhereIdOrUUID(id)
373 381
374 const query = { 382 const query = {
@@ -381,7 +389,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
381 .findOne(query) 389 .findOne(query)
382 } 390 }
383 391
384 static loadWithAccountAndChannel (id: number | string, transaction: Transaction) { 392 static loadWithAccountAndChannel (id: number | string, transaction: Transaction): Bluebird<MVideoPlaylistFull> {
385 const where = buildWhereIdOrUUID(id) 393 const where = buildWhereIdOrUUID(id)
386 394
387 const query = { 395 const query = {
@@ -394,7 +402,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
394 .findOne(query) 402 .findOne(query)
395 } 403 }
396 404
397 static loadByUrlAndPopulateAccount (url: string) { 405 static loadByUrlAndPopulateAccount (url: string): Bluebird<MVideoPlaylistAccountThumbnail> {
398 const query = { 406 const query = {
399 where: { 407 where: {
400 url 408 url
@@ -423,7 +431,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
423 return VideoPlaylistModel.update({ privacy: VideoPlaylistPrivacy.PRIVATE, videoChannelId: null }, query) 431 return VideoPlaylistModel.update({ privacy: VideoPlaylistPrivacy.PRIVATE, videoChannelId: null }, query)
424 } 432 }
425 433
426 async setAndSaveThumbnail (thumbnail: ThumbnailModel, t: Transaction) { 434 async setAndSaveThumbnail (thumbnail: MThumbnail, t: Transaction) {
427 thumbnail.videoPlaylistId = this.id 435 thumbnail.videoPlaylistId = this.id
428 436
429 this.Thumbnail = await thumbnail.save({ transaction: t }) 437 this.Thumbnail = await thumbnail.save({ transaction: t })
diff --git a/server/models/video/video-share.ts b/server/models/video/video-share.ts
index d8ed64557..9019b401a 100644
--- a/server/models/video/video-share.ts
+++ b/server/models/video/video-share.ts
@@ -8,6 +8,8 @@ import { buildLocalActorIdsIn, throwIfNotValid } from '../utils'
8import { VideoModel } from './video' 8import { VideoModel } from './video'
9import { VideoChannelModel } from './video-channel' 9import { VideoChannelModel } from './video-channel'
10import { Op, Transaction } from 'sequelize' 10import { Op, Transaction } from 'sequelize'
11import { MVideoShareActor, MVideoShareFull } from '../../typings/models/video'
12import { MActorDefault } from '../../typings/models'
11 13
12enum ScopeNames { 14enum ScopeNames {
13 FULL = 'FULL', 15 FULL = 'FULL',
@@ -88,7 +90,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
88 }) 90 })
89 Video: VideoModel 91 Video: VideoModel
90 92
91 static load (actorId: number, videoId: number, t?: Transaction) { 93 static load (actorId: number, videoId: number, t?: Transaction): Bluebird<MVideoShareActor> {
92 return VideoShareModel.scope(ScopeNames.WITH_ACTOR).findOne({ 94 return VideoShareModel.scope(ScopeNames.WITH_ACTOR).findOne({
93 where: { 95 where: {
94 actorId, 96 actorId,
@@ -98,7 +100,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
98 }) 100 })
99 } 101 }
100 102
101 static loadByUrl (url: string, t: Transaction) { 103 static loadByUrl (url: string, t: Transaction): Bluebird<MVideoShareFull> {
102 return VideoShareModel.scope(ScopeNames.FULL).findOne({ 104 return VideoShareModel.scope(ScopeNames.FULL).findOne({
103 where: { 105 where: {
104 url 106 url
@@ -107,7 +109,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
107 }) 109 })
108 } 110 }
109 111
110 static loadActorsByShare (videoId: number, t: Transaction) { 112 static loadActorsByShare (videoId: number, t: Transaction): Bluebird<MActorDefault[]> {
111 const query = { 113 const query = {
112 where: { 114 where: {
113 videoId 115 videoId
@@ -122,10 +124,10 @@ export class VideoShareModel extends Model<VideoShareModel> {
122 } 124 }
123 125
124 return VideoShareModel.scope(ScopeNames.FULL).findAll(query) 126 return VideoShareModel.scope(ScopeNames.FULL).findAll(query)
125 .then(res => res.map(r => r.Actor)) 127 .then((res: MVideoShareFull[]) => res.map(r => r.Actor))
126 } 128 }
127 129
128 static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Transaction): Bluebird<ActorModel[]> { 130 static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Transaction): Bluebird<MActorDefault[]> {
129 const query = { 131 const query = {
130 attributes: [], 132 attributes: [],
131 include: [ 133 include: [
@@ -163,7 +165,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
163 .then(res => res.map(r => r.Actor)) 165 .then(res => res.map(r => r.Actor))
164 } 166 }
165 167
166 static loadActorsByVideoChannel (videoChannelId: number, t: Transaction): Bluebird<ActorModel[]> { 168 static loadActorsByVideoChannel (videoChannelId: number, t: Transaction): Bluebird<MActorDefault[]> {
167 const query = { 169 const query = {
168 attributes: [], 170 attributes: [],
169 include: [ 171 include: [
diff --git a/server/models/video/video-streaming-playlist.ts b/server/models/video/video-streaming-playlist.ts
index 31dc82c54..0ea90d28c 100644
--- a/server/models/video/video-streaming-playlist.ts
+++ b/server/models/video/video-streaming-playlist.ts
@@ -1,16 +1,16 @@
1import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, HasMany, Is, Model, Table, UpdatedAt, DataType } from 'sequelize-typescript' 1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' 2import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos'
3import { throwIfNotValid } from '../utils' 3import { throwIfNotValid } from '../utils'
4import { VideoModel } from './video' 4import { VideoModel } from './video'
5import { VideoRedundancyModel } from '../redundancy/video-redundancy' 5import { VideoRedundancyModel } from '../redundancy/video-redundancy'
6import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' 6import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
7import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 7import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
8import { CONSTRAINTS_FIELDS, STATIC_PATHS, P2P_MEDIA_LOADER_PEER_VERSION } from '../../initializers/constants' 8import { CONSTRAINTS_FIELDS, P2P_MEDIA_LOADER_PEER_VERSION, STATIC_PATHS } from '../../initializers/constants'
9import { VideoFileModel } from './video-file'
10import { join } from 'path' 9import { join } from 'path'
11import { sha1 } from '../../helpers/core-utils' 10import { sha1 } from '../../helpers/core-utils'
12import { isArrayOf } from '../../helpers/custom-validators/misc' 11import { isArrayOf } from '../../helpers/custom-validators/misc'
13import { QueryTypes, Op } from 'sequelize' 12import { Op, QueryTypes } from 'sequelize'
13import { MStreamingPlaylist, MVideoFile } from '@server/typings/models'
14 14
15@Table({ 15@Table({
16 tableName: 'videoStreamingPlaylist', 16 tableName: 'videoStreamingPlaylist',
@@ -91,7 +91,7 @@ export class VideoStreamingPlaylistModel extends Model<VideoStreamingPlaylistMod
91 .then(results => results.length === 1) 91 .then(results => results.length === 1)
92 } 92 }
93 93
94 static buildP2PMediaLoaderInfoHashes (playlistUrl: string, videoFiles: VideoFileModel[]) { 94 static buildP2PMediaLoaderInfoHashes (playlistUrl: string, videoFiles: MVideoFile[]) {
95 const hashes: string[] = [] 95 const hashes: string[] = []
96 96
97 // https://github.com/Novage/p2p-media-loader/blob/master/p2p-media-loader-core/lib/p2p-media-manager.ts#L115 97 // https://github.com/Novage/p2p-media-loader/blob/master/p2p-media-loader-core/lib/p2p-media-manager.ts#L115
@@ -165,7 +165,7 @@ export class VideoStreamingPlaylistModel extends Model<VideoStreamingPlaylistMod
165 return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getStringType() + '/' + this.Video.uuid 165 return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getStringType() + '/' + this.Video.uuid
166 } 166 }
167 167
168 hasSameUniqueKeysThan (other: VideoStreamingPlaylistModel) { 168 hasSameUniqueKeysThan (other: MStreamingPlaylist) {
169 return this.type === other.type && 169 return this.type === other.type &&
170 this.videoId === other.videoId 170 this.videoId === other.videoId
171 } 171 }
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index b59df397d..7b1f0bc31 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -36,7 +36,7 @@ import {
36 Table, 36 Table,
37 UpdatedAt 37 UpdatedAt
38} from 'sequelize-typescript' 38} from 'sequelize-typescript'
39import { UserRight, VideoPrivacy, VideoResolution, VideoState } from '../../../shared' 39import { UserRight, VideoPrivacy, VideoState } from '../../../shared'
40import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' 40import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
41import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos' 41import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos'
42import { VideoFilter } from '../../../shared/models/videos/video-query.type' 42import { VideoFilter } from '../../../shared/models/videos/video-query.type'
@@ -111,7 +111,6 @@ import {
111 videoModelToFormattedJSON 111 videoModelToFormattedJSON
112} from './video-format-utils' 112} from './video-format-utils'
113import { UserVideoHistoryModel } from '../account/user-video-history' 113import { UserVideoHistoryModel } from '../account/user-video-history'
114import { UserModel } from '../account/user'
115import { VideoImportModel } from './video-import' 114import { VideoImportModel } from './video-import'
116import { VideoStreamingPlaylistModel } from './video-streaming-playlist' 115import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
117import { VideoPlaylistElementModel } from './video-playlist-element' 116import { VideoPlaylistElementModel } from './video-playlist-element'
@@ -120,6 +119,24 @@ import { ThumbnailModel } from './thumbnail'
120import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' 119import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
121import { createTorrentPromise } from '../../helpers/webtorrent' 120import { createTorrentPromise } from '../../helpers/webtorrent'
122import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' 121import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
122import {
123 MChannel,
124 MChannelActorAccountDefault,
125 MChannelId,
126 MUserAccountId,
127 MUserId,
128 MVideoAccountAllFiles,
129 MVideoAccountLight,
130 MVideoDetails,
131 MVideoFullLight,
132 MVideoIdThumbnail,
133 MVideoThumbnail,
134 MVideoWithAllFiles,
135 MVideoWithBlacklistThumbnailScheduled,
136 MVideoWithRights
137} from '../../typings/models'
138import { MVideoFile, MVideoFileRedundanciesOpt } from '../../typings/models/video/video-file'
139import { MThumbnail } from '../../typings/models/video/thumbnail'
123 140
124// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation 141// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation
125const indexes: (ModelIndexesOptions & { where?: WhereOptions })[] = [ 142const indexes: (ModelIndexesOptions & { where?: WhereOptions })[] = [
@@ -232,8 +249,8 @@ export type AvailableForListIDsOptions = {
232 videoPlaylistId?: number 249 videoPlaylistId?: number
233 250
234 trendingDays?: number 251 trendingDays?: number
235 user?: UserModel, 252 user?: MUserAccountId
236 historyOfUser?: UserModel 253 historyOfUser?: MUserId
237 254
238 baseWhere?: WhereOptions[] 255 baseWhere?: WhereOptions[]
239} 256}
@@ -634,7 +651,7 @@ export type AvailableForListIDsOptions = {
634 [ ScopeNames.WITH_BLACKLISTED ]: { 651 [ ScopeNames.WITH_BLACKLISTED ]: {
635 include: [ 652 include: [
636 { 653 {
637 attributes: [ 'id', 'reason' ], 654 attributes: [ 'id', 'reason', 'unfederated' ],
638 model: VideoBlacklistModel, 655 model: VideoBlacklistModel,
639 required: false 656 required: false
640 } 657 }
@@ -989,18 +1006,16 @@ export class VideoModel extends Model<VideoModel> {
989 VideoCaptions: VideoCaptionModel[] 1006 VideoCaptions: VideoCaptionModel[]
990 1007
991 @BeforeDestroy 1008 @BeforeDestroy
992 static async sendDelete (instance: VideoModel, options) { 1009 static async sendDelete (instance: MVideoAccountLight, options) {
993 if (instance.isOwned()) { 1010 if (instance.isOwned()) {
994 if (!instance.VideoChannel) { 1011 if (!instance.VideoChannel) {
995 instance.VideoChannel = await instance.$get('VideoChannel', { 1012 instance.VideoChannel = await instance.$get('VideoChannel', {
996 include: [ 1013 include: [
997 { 1014 ActorModel,
998 model: AccountModel, 1015 AccountModel
999 include: [ ActorModel ]
1000 }
1001 ], 1016 ],
1002 transaction: options.transaction 1017 transaction: options.transaction
1003 }) as VideoChannelModel 1018 }) as MChannelActorAccountDefault
1004 } 1019 }
1005 1020
1006 return sendDeleteVideo(instance, options.transaction) 1021 return sendDeleteVideo(instance, options.transaction)
@@ -1039,7 +1054,7 @@ export class VideoModel extends Model<VideoModel> {
1039 return undefined 1054 return undefined
1040 } 1055 }
1041 1056
1042 static listLocal () { 1057 static listLocal (): Bluebird<MVideoWithAllFiles[]> {
1043 const query = { 1058 const query = {
1044 where: { 1059 where: {
1045 remote: false 1060 remote: false
@@ -1159,7 +1174,7 @@ export class VideoModel extends Model<VideoModel> {
1159 }) 1174 })
1160 } 1175 }
1161 1176
1162 static listUserVideosForApi (accountId: number, start: number, count: number, sort: string, withFiles = false) { 1177 static listUserVideosForApi (accountId: number, start: number, count: number, sort: string) {
1163 function buildBaseQuery (): FindOptions { 1178 function buildBaseQuery (): FindOptions {
1164 return { 1179 return {
1165 offset: start, 1180 offset: start,
@@ -1192,19 +1207,12 @@ export class VideoModel extends Model<VideoModel> {
1192 ScopeNames.WITH_THUMBNAILS 1207 ScopeNames.WITH_THUMBNAILS
1193 ] 1208 ]
1194 1209
1195 if (withFiles === true) {
1196 findQuery.include.push({
1197 model: VideoFileModel.unscoped(),
1198 required: true
1199 })
1200 }
1201
1202 return Promise.all([ 1210 return Promise.all([
1203 VideoModel.count(countQuery), 1211 VideoModel.count(countQuery),
1204 VideoModel.scope(findScopes).findAll(findQuery) 1212 VideoModel.scope(findScopes).findAll(findQuery)
1205 ]).then(([ count, rows ]) => { 1213 ]).then(([ count, rows ]) => {
1206 return { 1214 return {
1207 data: rows, 1215 data: rows as MVideoWithBlacklistThumbnailScheduled[],
1208 total: count 1216 total: count
1209 } 1217 }
1210 }) 1218 })
@@ -1228,8 +1236,8 @@ export class VideoModel extends Model<VideoModel> {
1228 followerActorId?: number 1236 followerActorId?: number
1229 videoPlaylistId?: number, 1237 videoPlaylistId?: number,
1230 trendingDays?: number, 1238 trendingDays?: number,
1231 user?: UserModel, 1239 user?: MUserAccountId,
1232 historyOfUser?: UserModel 1240 historyOfUser?: MUserId
1233 }, countVideos = true) { 1241 }, countVideos = true) {
1234 if (options.filter && options.filter === 'all-local' && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) { 1242 if (options.filter && options.filter === 'all-local' && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) {
1235 throw new Error('Try to filter all-local but no user has not the see all videos right') 1243 throw new Error('Try to filter all-local but no user has not the see all videos right')
@@ -1294,7 +1302,7 @@ export class VideoModel extends Model<VideoModel> {
1294 tagsAllOf?: string[] 1302 tagsAllOf?: string[]
1295 durationMin?: number // seconds 1303 durationMin?: number // seconds
1296 durationMax?: number // seconds 1304 durationMax?: number // seconds
1297 user?: UserModel, 1305 user?: MUserAccountId,
1298 filter?: VideoFilter 1306 filter?: VideoFilter
1299 }) { 1307 }) {
1300 const whereAnd = [] 1308 const whereAnd = []
@@ -1387,7 +1395,7 @@ export class VideoModel extends Model<VideoModel> {
1387 return VideoModel.getAvailableForApi(query, queryOptions) 1395 return VideoModel.getAvailableForApi(query, queryOptions)
1388 } 1396 }
1389 1397
1390 static load (id: number | string, t?: Transaction) { 1398 static load (id: number | string, t?: Transaction): Bluebird<MVideoThumbnail> {
1391 const where = buildWhereIdOrUUID(id) 1399 const where = buildWhereIdOrUUID(id)
1392 const options = { 1400 const options = {
1393 where, 1401 where,
@@ -1397,7 +1405,7 @@ export class VideoModel extends Model<VideoModel> {
1397 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) 1405 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options)
1398 } 1406 }
1399 1407
1400 static loadWithRights (id: number | string, t?: Transaction) { 1408 static loadWithRights (id: number | string, t?: Transaction): Bluebird<MVideoWithRights> {
1401 const where = buildWhereIdOrUUID(id) 1409 const where = buildWhereIdOrUUID(id)
1402 const options = { 1410 const options = {
1403 where, 1411 where,
@@ -1411,7 +1419,7 @@ export class VideoModel extends Model<VideoModel> {
1411 ]).findOne(options) 1419 ]).findOne(options)
1412 } 1420 }
1413 1421
1414 static loadOnlyId (id: number | string, t?: Transaction) { 1422 static loadOnlyId (id: number | string, t?: Transaction): Bluebird<MVideoIdThumbnail> {
1415 const where = buildWhereIdOrUUID(id) 1423 const where = buildWhereIdOrUUID(id)
1416 1424
1417 const options = { 1425 const options = {
@@ -1423,7 +1431,7 @@ export class VideoModel extends Model<VideoModel> {
1423 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) 1431 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options)
1424 } 1432 }
1425 1433
1426 static loadWithFiles (id: number | string, t?: Transaction, logging?: boolean) { 1434 static loadWithFiles (id: number | string, t?: Transaction, logging?: boolean): Bluebird<MVideoWithAllFiles> {
1427 const where = buildWhereIdOrUUID(id) 1435 const where = buildWhereIdOrUUID(id)
1428 1436
1429 const query = { 1437 const query = {
@@ -1439,7 +1447,7 @@ export class VideoModel extends Model<VideoModel> {
1439 ]).findOne(query) 1447 ]).findOne(query)
1440 } 1448 }
1441 1449
1442 static loadByUUID (uuid: string) { 1450 static loadByUUID (uuid: string): Bluebird<MVideoThumbnail> {
1443 const options = { 1451 const options = {
1444 where: { 1452 where: {
1445 uuid 1453 uuid
@@ -1449,7 +1457,7 @@ export class VideoModel extends Model<VideoModel> {
1449 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) 1457 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options)
1450 } 1458 }
1451 1459
1452 static loadByUrl (url: string, transaction?: Transaction) { 1460 static loadByUrl (url: string, transaction?: Transaction): Bluebird<MVideoThumbnail> {
1453 const query: FindOptions = { 1461 const query: FindOptions = {
1454 where: { 1462 where: {
1455 url 1463 url
@@ -1460,7 +1468,7 @@ export class VideoModel extends Model<VideoModel> {
1460 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(query) 1468 return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(query)
1461 } 1469 }
1462 1470
1463 static loadByUrlAndPopulateAccount (url: string, transaction?: Transaction) { 1471 static loadByUrlAndPopulateAccount (url: string, transaction?: Transaction): Bluebird<MVideoAccountAllFiles> {
1464 const query: FindOptions = { 1472 const query: FindOptions = {
1465 where: { 1473 where: {
1466 url 1474 url
@@ -1472,11 +1480,12 @@ export class VideoModel extends Model<VideoModel> {
1472 ScopeNames.WITH_ACCOUNT_DETAILS, 1480 ScopeNames.WITH_ACCOUNT_DETAILS,
1473 ScopeNames.WITH_FILES, 1481 ScopeNames.WITH_FILES,
1474 ScopeNames.WITH_STREAMING_PLAYLISTS, 1482 ScopeNames.WITH_STREAMING_PLAYLISTS,
1475 ScopeNames.WITH_THUMBNAILS 1483 ScopeNames.WITH_THUMBNAILS,
1484 ScopeNames.WITH_BLACKLISTED
1476 ]).findOne(query) 1485 ]).findOne(query)
1477 } 1486 }
1478 1487
1479 static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Transaction, userId?: number) { 1488 static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Transaction, userId?: number): Bluebird<MVideoFullLight> {
1480 const where = buildWhereIdOrUUID(id) 1489 const where = buildWhereIdOrUUID(id)
1481 1490
1482 const options = { 1491 const options = {
@@ -1508,7 +1517,7 @@ export class VideoModel extends Model<VideoModel> {
1508 id: number | string, 1517 id: number | string,
1509 t?: Transaction, 1518 t?: Transaction,
1510 userId?: number 1519 userId?: number
1511 }) { 1520 }): Bluebird<MVideoDetails> {
1512 const { id, t, userId } = parameters 1521 const { id, t, userId } = parameters
1513 const where = buildWhereIdOrUUID(id) 1522 const where = buildWhereIdOrUUID(id)
1514 1523
@@ -1586,7 +1595,7 @@ export class VideoModel extends Model<VideoModel> {
1586 .then(results => results.length === 1) 1595 .then(results => results.length === 1)
1587 } 1596 }
1588 1597
1589 static bulkUpdateSupportField (videoChannel: VideoChannelModel, t: Transaction) { 1598 static bulkUpdateSupportField (videoChannel: MChannel, t: Transaction) {
1590 const options = { 1599 const options = {
1591 where: { 1600 where: {
1592 channelId: videoChannel.id 1601 channelId: videoChannel.id
@@ -1597,7 +1606,7 @@ export class VideoModel extends Model<VideoModel> {
1597 return VideoModel.update({ support: videoChannel.support }, options) 1606 return VideoModel.update({ support: videoChannel.support }, options)
1598 } 1607 }
1599 1608
1600 static getAllIdsFromChannel (videoChannel: VideoChannelModel) { 1609 static getAllIdsFromChannel (videoChannel: MChannelId): Bluebird<number[]> {
1601 const query = { 1610 const query = {
1602 attributes: [ 'id' ], 1611 attributes: [ 'id' ],
1603 where: { 1612 where: {
@@ -1769,7 +1778,7 @@ export class VideoModel extends Model<VideoModel> {
1769 return this.VideoFiles.find(f => f.resolution === resolution) 1778 return this.VideoFiles.find(f => f.resolution === resolution)
1770 } 1779 }
1771 1780
1772 async addAndSaveThumbnail (thumbnail: ThumbnailModel, transaction: Transaction) { 1781 async addAndSaveThumbnail (thumbnail: MThumbnail, transaction: Transaction) {
1773 thumbnail.videoId = this.id 1782 thumbnail.videoId = this.id
1774 1783
1775 const savedThumbnail = await thumbnail.save({ transaction }) 1784 const savedThumbnail = await thumbnail.save({ transaction })
@@ -1782,7 +1791,7 @@ export class VideoModel extends Model<VideoModel> {
1782 this.Thumbnails.push(savedThumbnail) 1791 this.Thumbnails.push(savedThumbnail)
1783 } 1792 }
1784 1793
1785 getVideoFilename (videoFile: VideoFileModel) { 1794 getVideoFilename (videoFile: MVideoFile) {
1786 return this.uuid + '-' + videoFile.resolution + videoFile.extname 1795 return this.uuid + '-' + videoFile.resolution + videoFile.extname
1787 } 1796 }
1788 1797
@@ -1806,7 +1815,7 @@ export class VideoModel extends Model<VideoModel> {
1806 return this.Thumbnails.find(t => t.type === ThumbnailType.PREVIEW) 1815 return this.Thumbnails.find(t => t.type === ThumbnailType.PREVIEW)
1807 } 1816 }
1808 1817
1809 getTorrentFileName (videoFile: VideoFileModel) { 1818 getTorrentFileName (videoFile: MVideoFile) {
1810 const extension = '.torrent' 1819 const extension = '.torrent'
1811 return this.uuid + '-' + videoFile.resolution + extension 1820 return this.uuid + '-' + videoFile.resolution + extension
1812 } 1821 }
@@ -1815,15 +1824,15 @@ export class VideoModel extends Model<VideoModel> {
1815 return this.remote === false 1824 return this.remote === false
1816 } 1825 }
1817 1826
1818 getTorrentFilePath (videoFile: VideoFileModel) { 1827 getTorrentFilePath (videoFile: MVideoFile) {
1819 return join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile)) 1828 return join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
1820 } 1829 }
1821 1830
1822 getVideoFilePath (videoFile: VideoFileModel) { 1831 getVideoFilePath (videoFile: MVideoFile) {
1823 return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile)) 1832 return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile))
1824 } 1833 }
1825 1834
1826 async createTorrentAndSetInfoHash (videoFile: VideoFileModel) { 1835 async createTorrentAndSetInfoHash (videoFile: MVideoFile) {
1827 const options = { 1836 const options = {
1828 // Keep the extname, it's used by the client to stream the file inside a web browser 1837 // Keep the extname, it's used by the client to stream the file inside a web browser
1829 name: `${this.name} ${videoFile.resolution}p${videoFile.extname}`, 1838 name: `${this.name} ${videoFile.resolution}p${videoFile.extname}`,
@@ -1908,7 +1917,7 @@ export class VideoModel extends Model<VideoModel> {
1908 return this.VideoStreamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS) 1917 return this.VideoStreamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
1909 } 1918 }
1910 1919
1911 removeFile (videoFile: VideoFileModel, isRedundancy = false) { 1920 removeFile (videoFile: MVideoFile, isRedundancy = false) {
1912 const baseDir = isRedundancy ? CONFIG.STORAGE.REDUNDANCY_DIR : CONFIG.STORAGE.VIDEOS_DIR 1921 const baseDir = isRedundancy ? CONFIG.STORAGE.REDUNDANCY_DIR : CONFIG.STORAGE.VIDEOS_DIR
1913 1922
1914 const filePath = join(baseDir, this.getVideoFilename(videoFile)) 1923 const filePath = join(baseDir, this.getVideoFilename(videoFile))
@@ -1916,7 +1925,7 @@ export class VideoModel extends Model<VideoModel> {
1916 .catch(err => logger.warn('Cannot delete file %s.', filePath, { err })) 1925 .catch(err => logger.warn('Cannot delete file %s.', filePath, { err }))
1917 } 1926 }
1918 1927
1919 removeTorrent (videoFile: VideoFileModel) { 1928 removeTorrent (videoFile: MVideoFile) {
1920 const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile)) 1929 const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
1921 return remove(torrentPath) 1930 return remove(torrentPath)
1922 .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err })) 1931 .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err }))
@@ -1957,7 +1966,7 @@ export class VideoModel extends Model<VideoModel> {
1957 return { baseUrlHttp, baseUrlWs } 1966 return { baseUrlHttp, baseUrlWs }
1958 } 1967 }
1959 1968
1960 generateMagnetUri (videoFile: VideoFileModel, baseUrlHttp: string, baseUrlWs: string) { 1969 generateMagnetUri (videoFile: MVideoFileRedundanciesOpt, baseUrlHttp: string, baseUrlWs: string) {
1961 const xs = this.getTorrentUrl(videoFile, baseUrlHttp) 1970 const xs = this.getTorrentUrl(videoFile, baseUrlHttp)
1962 const announce = this.getTrackerUrls(baseUrlHttp, baseUrlWs) 1971 const announce = this.getTrackerUrls(baseUrlHttp, baseUrlWs)
1963 let urlList = [ this.getVideoFileUrl(videoFile, baseUrlHttp) ] 1972 let urlList = [ this.getVideoFileUrl(videoFile, baseUrlHttp) ]
@@ -1980,27 +1989,27 @@ export class VideoModel extends Model<VideoModel> {
1980 return [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ] 1989 return [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ]
1981 } 1990 }
1982 1991
1983 getTorrentUrl (videoFile: VideoFileModel, baseUrlHttp: string) { 1992 getTorrentUrl (videoFile: MVideoFile, baseUrlHttp: string) {
1984 return baseUrlHttp + STATIC_PATHS.TORRENTS + this.getTorrentFileName(videoFile) 1993 return baseUrlHttp + STATIC_PATHS.TORRENTS + this.getTorrentFileName(videoFile)
1985 } 1994 }
1986 1995
1987 getTorrentDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) { 1996 getTorrentDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) {
1988 return baseUrlHttp + STATIC_DOWNLOAD_PATHS.TORRENTS + this.getTorrentFileName(videoFile) 1997 return baseUrlHttp + STATIC_DOWNLOAD_PATHS.TORRENTS + this.getTorrentFileName(videoFile)
1989 } 1998 }
1990 1999
1991 getVideoFileUrl (videoFile: VideoFileModel, baseUrlHttp: string) { 2000 getVideoFileUrl (videoFile: MVideoFile, baseUrlHttp: string) {
1992 return baseUrlHttp + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile) 2001 return baseUrlHttp + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile)
1993 } 2002 }
1994 2003
1995 getVideoRedundancyUrl (videoFile: VideoFileModel, baseUrlHttp: string) { 2004 getVideoRedundancyUrl (videoFile: MVideoFile, baseUrlHttp: string) {
1996 return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getVideoFilename(videoFile) 2005 return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getVideoFilename(videoFile)
1997 } 2006 }
1998 2007
1999 getVideoFileDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) { 2008 getVideoFileDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) {
2000 return baseUrlHttp + STATIC_DOWNLOAD_PATHS.VIDEOS + this.getVideoFilename(videoFile) 2009 return baseUrlHttp + STATIC_DOWNLOAD_PATHS.VIDEOS + this.getVideoFilename(videoFile)
2001 } 2010 }
2002 2011
2003 getBandwidthBits (videoFile: VideoFileModel) { 2012 getBandwidthBits (videoFile: MVideoFile) {
2004 return Math.ceil((videoFile.size * 8) / this.duration) 2013 return Math.ceil((videoFile.size * 8) / this.duration)
2005 } 2014 }
2006} 2015}
diff --git a/server/typings/activitypub-processor.model.ts b/server/typings/activitypub-processor.model.ts
index 37b2859de..7ed3a65b1 100644
--- a/server/typings/activitypub-processor.model.ts
+++ b/server/typings/activitypub-processor.model.ts
@@ -1,10 +1,9 @@
1import { Activity } from '../../shared/models/activitypub' 1import { Activity } from '../../shared/models/activitypub'
2import { ActorModel } from '../models/activitypub/actor' 2import { MActorDefault, MActorSignature } from './models'
3import { SignatureActorModel } from './models'
4 3
5export type APProcessorOptions<T extends Activity> = { 4export type APProcessorOptions<T extends Activity> = {
6 activity: T 5 activity: T
7 byActor: SignatureActorModel 6 byActor: MActorSignature
8 inboxActor?: ActorModel 7 inboxActor?: MActorDefault
9 fromFetch?: boolean 8 fromFetch?: boolean
10} 9}
diff --git a/server/typings/express.ts b/server/typings/express.ts
index f7da55ab0..260091461 100644
--- a/server/typings/express.ts
+++ b/server/typings/express.ts
@@ -1,89 +1,102 @@
1import { VideoChannelModel } from '../models/video/video-channel'
2import { VideoPlaylistModel } from '../models/video/video-playlist'
3import { VideoPlaylistElementModel } from '../models/video/video-playlist-element'
4import { UserModel } from '../models/account/user'
5import { VideoModel } from '../models/video/video'
6import { AccountModel } from '../models/account/account'
7import { VideoChangeOwnershipModel } from '../models/video/video-change-ownership'
8import { ActorModel } from '../models/activitypub/actor'
9import { VideoCommentModel } from '../models/video/video-comment'
10import { VideoShareModel } from '../models/video/video-share'
11import { AccountVideoRateModel } from '../models/account/account-video-rate'
12import { ActorFollowModel } from '../models/activitypub/actor-follow'
13import { ServerModel } from '../models/server/server'
14import { VideoFileModel } from '../models/video/video-file'
15import { VideoRedundancyModel } from '../models/redundancy/video-redundancy'
16import { ServerBlocklistModel } from '../models/server/server-blocklist'
17import { AccountBlocklistModel } from '../models/account/account-blocklist'
18import { VideoImportModel } from '../models/video/video-import'
19import { VideoAbuseModel } from '../models/video/video-abuse'
20import { VideoBlacklistModel } from '../models/video/video-blacklist'
21import { VideoCaptionModel } from '../models/video/video-caption'
22import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
23import { RegisteredPlugin } from '../lib/plugins/plugin-manager' 1import { RegisteredPlugin } from '../lib/plugins/plugin-manager'
24import { PluginModel } from '../models/server/plugin' 2import {
25import { SignatureActorModel } from './models' 3 MAccountDefault,
4 MActorAccountChannelId,
5 MActorFollowActorsDefault,
6 MActorFollowActorsDefaultSubscription,
7 MActorFull,
8 MChannelActorAccountDefault,
9 MComment,
10 MCommentOwnerVideoReply,
11 MUserDefault,
12 MVideoAbuse,
13 MVideoBlacklist,
14 MVideoCaptionVideo,
15 MVideoFullLight,
16 MVideoIdThumbnail,
17 MVideoRedundancyVideo,
18 MVideoShareActor,
19 MVideoThumbnail,
20 MVideoWithRights
21} from './models'
22import { MVideoPlaylistFull, MVideoPlaylistFullSummary } from './models/video/video-playlist'
23import { MVideoImportDefault } from '@server/typings/models/video/video-import'
24import { MAccountBlocklist, MStreamingPlaylist, MVideoFile } from '@server/typings/models'
25import { MVideoPlaylistElement } from '@server/typings/models/video/video-playlist-element'
26import { MAccountVideoRateAccountVideo } from '@server/typings/models/video/video-rate'
27import { MVideoChangeOwnershipFull } from './models/video/video-change-ownership'
28import { MPlugin, MServer } from '@server/typings/models/server'
29import { MServerBlocklist } from './models/server/server-blocklist'
30import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token'
26 31
27declare module 'express' { 32declare module 'express' {
28 33
29 interface Response { 34 interface Response {
35
30 locals: { 36 locals: {
31 video?: VideoModel 37 videoAll?: MVideoFullLight
32 videoShare?: VideoShareModel 38 onlyVideo?: MVideoThumbnail
33 videoFile?: VideoFileModel 39 onlyVideoWithRights?: MVideoWithRights
40 videoId?: MVideoIdThumbnail
41
42 videoShare?: MVideoShareActor
43
44 videoFile?: MVideoFile
45
46 videoImport?: MVideoImportDefault
47
48 videoBlacklist?: MVideoBlacklist
49
50 videoCaption?: MVideoCaptionVideo
51
52 videoAbuse?: MVideoAbuse
34 53
35 videoImport?: VideoImportModel 54 videoStreamingPlaylist?: MStreamingPlaylist
36 55
37 videoBlacklist?: VideoBlacklistModel 56 videoChannel?: MChannelActorAccountDefault
38 57
39 videoCaption?: VideoCaptionModel 58 videoPlaylistFull?: MVideoPlaylistFull
59 videoPlaylistSummary?: MVideoPlaylistFullSummary
40 60
41 videoAbuse?: VideoAbuseModel 61 videoPlaylistElement?: MVideoPlaylistElement
42 62
43 videoStreamingPlaylist?: VideoStreamingPlaylistModel 63 accountVideoRate?: MAccountVideoRateAccountVideo
44 64
45 videoChannel?: VideoChannelModel 65 videoCommentFull?: MCommentOwnerVideoReply
66 videoCommentThread?: MComment
46 67
47 videoPlaylist?: VideoPlaylistModel 68 follow?: MActorFollowActorsDefault
48 videoPlaylistElement?: VideoPlaylistElementModel 69 subscription?: MActorFollowActorsDefaultSubscription
49 70
50 accountVideoRate?: AccountVideoRateModel 71 nextOwner?: MAccountDefault
72 videoChangeOwnership?: MVideoChangeOwnershipFull
51 73
52 videoComment?: VideoCommentModel 74 account?: MAccountDefault
53 videoCommentThread?: VideoCommentModel
54 75
55 follow?: ActorFollowModel 76 actorFull?: MActorFull
56 subscription?: ActorFollowModel
57 77
58 nextOwner?: AccountModel 78 user?: MUserDefault
59 videoChangeOwnership?: VideoChangeOwnershipModel
60 account?: AccountModel
61 actor?: ActorModel
62 user?: UserModel
63 79
64 server?: ServerModel 80 server?: MServer
65 81
66 videoRedundancy?: VideoRedundancyModel 82 videoRedundancy?: MVideoRedundancyVideo
67 83
68 accountBlock?: AccountBlocklistModel 84 accountBlock?: MAccountBlocklist
69 serverBlock?: ServerBlocklistModel 85 serverBlock?: MServerBlocklist
70 86
71 oauth?: { 87 oauth?: {
72 token: { 88 token: MOAuthTokenUser
73 User: UserModel
74 user: UserModel
75 }
76 } 89 }
77 90
78 signature?: { 91 signature?: {
79 actor: SignatureActorModel 92 actor: MActorAccountChannelId
80 } 93 }
81 94
82 authenticated?: boolean 95 authenticated?: boolean
83 96
84 registeredPlugin?: RegisteredPlugin 97 registeredPlugin?: RegisteredPlugin
85 98
86 plugin?: PluginModel 99 plugin?: MPlugin
87 } 100 }
88 } 101 }
89} 102}
diff --git a/server/typings/models/account/account-blocklist.ts b/server/typings/models/account/account-blocklist.ts
new file mode 100644
index 000000000..6d1771de8
--- /dev/null
+++ b/server/typings/models/account/account-blocklist.ts
@@ -0,0 +1,11 @@
1import { AccountBlocklistModel } from '../../../models/account/account-blocklist'
2import { PickWith } from '../../utils'
3import { MAccountDefault } from './account'
4
5export type MAccountBlocklist = Omit<AccountBlocklistModel, 'ByAccount' | 'BlockedAccount'>
6
7export type MAccountBlocklistId = Pick<AccountBlocklistModel, 'id'>
8
9export type MAccountBlocklistAccounts = MAccountBlocklist &
10 PickWith<AccountBlocklistModel, 'ByAccount', MAccountDefault> &
11 PickWith<AccountBlocklistModel, 'BlockedAccount', MAccountDefault>
diff --git a/server/typings/models/account/account.ts b/server/typings/models/account/account.ts
new file mode 100644
index 000000000..f3646d510
--- /dev/null
+++ b/server/typings/models/account/account.ts
@@ -0,0 +1,56 @@
1import { AccountModel } from '../../../models/account/account'
2import {
3 MActor,
4 MActorAccountChannelId,
5 MActorAPI,
6 MActorAudience,
7 MActorDefault,
8 MActorDefaultLight, MActorId,
9 MActorServer,
10 MActorSummary,
11 MActorUrl
12} from './actor'
13import { PickWith } from '../../utils'
14import { MAccountBlocklistId } from './account-blocklist'
15import { MChannelDefault } from '@server/typings/models'
16
17export type MAccountId = Pick<AccountModel, 'id'>
18export type MAccountIdActor = MAccountId &
19 PickWith<AccountModel, 'Actor', MActorAccountChannelId>
20export type MAccountIdActorId = MAccountId &
21 PickWith<AccountModel, 'Actor', MActorId>
22
23export type MAccount = Omit<AccountModel, 'Actor' | 'User' | 'Application' | 'VideoChannels' | 'VideoPlaylists' |
24 'VideoComments' | 'BlockedAccounts'>
25
26// Default scope
27export type MAccountDefault = MAccount &
28 PickWith<AccountModel, 'Actor', MActorDefault>
29
30export type MAccountDefaultChannelDefault = MAccountDefault &
31 PickWith<AccountModel, 'VideoChannels', MChannelDefault[]>
32
33export type MAccountLight = MAccount &
34 PickWith<AccountModel, 'Actor', MActorDefaultLight>
35
36export type MAccountUserId = Pick<MAccount, 'userId'>
37
38export type MAccountActor = MAccount &
39 PickWith<AccountModel, 'Actor', MActor>
40export type MAccountServer = MAccountActor &
41 PickWith<AccountModel, 'Actor', MActorServer>
42
43export type MAccountActorDefault = MAccount &
44 PickWith<AccountModel, 'Actor', MActorDefault>
45
46export type MAccountSummary = Pick<MAccount, 'id' | 'name'> &
47 PickWith<AccountModel, 'Actor', MActorSummary>
48
49export type MAccountBlocks = MAccountSummary &
50 PickWith<AccountModel, 'BlockedAccounts', MAccountBlocklistId[]>
51
52export type MAccountAPI = MAccountDefault &
53 PickWith<AccountModel, 'Actor', MActorAPI>
54
55export type MAccountUrl = PickWith<AccountModel, 'Actor', MActorUrl>
56export type MAccountAudience = PickWith<AccountModel, 'Actor', MActorAudience>
diff --git a/server/typings/models/account/actor-follow.ts b/server/typings/models/account/actor-follow.ts
new file mode 100644
index 000000000..96c53d857
--- /dev/null
+++ b/server/typings/models/account/actor-follow.ts
@@ -0,0 +1,27 @@
1import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
2import { MActor, MActorAccountChannel, MActorChannel, MActorChannelAccount, MActorDefault, MActorHost, MActorUsername } from './actor'
3import { PickWith } from '../../utils'
4
5export type MActorFollow = Omit<ActorFollowModel, 'ActorFollower' | 'ActorFollowing'>
6
7export type MActorFollowActors = MActorFollow &
8 PickWith<ActorFollowModel, 'ActorFollower', MActor> &
9 PickWith<ActorFollowModel, 'ActorFollowing', MActor>
10
11export type MActorFollowActorsDefault = MActorFollow &
12 PickWith<ActorFollowModel, 'ActorFollower', MActorDefault> &
13 PickWith<ActorFollowModel, 'ActorFollowing', MActorDefault>
14
15export type MActorFollowActorsDefaultSubscription = MActorFollow &
16 PickWith<ActorFollowModel, 'ActorFollower', MActorDefault> &
17 PickWith<ActorFollowModel, 'ActorFollowing', MActorDefault & MActorChannel>
18
19export type MActorFollowFull = MActorFollow &
20 PickWith<ActorFollowModel, 'ActorFollower', MActorAccountChannel> &
21 PickWith<ActorFollowModel, 'ActorFollowing', MActorAccountChannel>
22
23export type MActorFollowFollowingHost = MActorFollow &
24 PickWith<ActorFollowModel, 'ActorFollowing', MActorUsername & MActorHost>
25
26export type MActorFollowSubscriptions = MActorFollow &
27 PickWith<ActorFollowModel, 'ActorFollowing', MActorChannelAccount>
diff --git a/server/typings/models/account/actor.ts b/server/typings/models/account/actor.ts
new file mode 100644
index 000000000..f3e752a98
--- /dev/null
+++ b/server/typings/models/account/actor.ts
@@ -0,0 +1,74 @@
1import { ActorModel } from '../../../models/activitypub/actor'
2import { PickWith } from '../../utils'
3import { MAccount, MAccountActorDefault, MAccountId, MAccountIdActor } from './account'
4import { MServerHost, MServerHostBlocks, MServer } from '../server'
5import { MAvatar } from './avatar'
6import { MChannel, MChannelAccountActor, MChannelActorAccountDefault, MChannelId, MChannelIdActor } from '../video'
7
8export type MActor = Omit<ActorModel, 'Account' | 'VideoChannel' | 'ActorFollowing' | 'Avatar' | 'ActorFollowers' | 'Server'>
9
10export type MActorUrl = Pick<MActor, 'url'>
11export type MActorId = Pick<MActor, 'id'>
12export type MActorUsername = Pick<MActor, 'preferredUsername'>
13export type MActorHost = PickWith<ActorModel, 'Server', MServerHost>
14
15export type MActorFollowersUrl = Pick<MActor, 'followersUrl'>
16export type MActorAudience = MActorUrl & MActorFollowersUrl
17
18export type MActorLight = Omit<MActor, 'privateKey' | 'privateKey'>
19
20export type MActorDefaultLight = MActorLight &
21 MActorHost &
22 PickWith<ActorModel, 'Avatar', MAvatar>
23
24export type MActorAccountId = MActor &
25 PickWith<ActorModel, 'Account', MAccountId>
26export type MActorAccountIdActor = MActor &
27 PickWith<ActorModel, 'Account', MAccountIdActor>
28
29export type MActorChannelId = MActor &
30 PickWith<ActorModel, 'VideoChannel', MChannelId>
31export type MActorChannelIdActor = MActor &
32 PickWith<ActorModel, 'VideoChannel', MChannelIdActor>
33
34export type MActorAccountChannelId = MActorAccountId & MActorChannelId
35export type MActorAccountChannelIdActor = MActorAccountIdActor & MActorChannelIdActor
36
37export type MActorAccount = MActor &
38 PickWith<ActorModel, 'Account', MAccount>
39
40export type MActorChannel = MActor &
41 PickWith<ActorModel, 'VideoChannel', MChannel>
42
43export type MActorAccountChannel = MActorAccount & MActorChannel
44
45export type MActorChannelAccount = MActor &
46 PickWith<ActorModel, 'VideoChannel', MChannelAccountActor>
47
48export type MActorServer = MActor &
49 PickWith<ActorModel, 'Server', MServer>
50
51export type MActorDefault = MActorServer &
52 PickWith<ActorModel, 'Avatar', MAvatar>
53
54export type MActorFull = MActorDefault &
55 PickWith<ActorModel, 'Account', MAccount> &
56 PickWith<ActorModel, 'VideoChannel', MChannelAccountActor>
57
58export type MActorFullActor = MActorDefault &
59 PickWith<ActorModel, 'Account', MAccountActorDefault> &
60 PickWith<ActorModel, 'VideoChannel', MChannelActorAccountDefault>
61
62export type MActorSummary = Pick<MActor, 'id' | 'preferredUsername' | 'url' | 'serverId' | 'avatarId'> &
63 MActorHost &
64 PickWith<ActorModel, 'Avatar', MAvatar>
65
66export type MActorSummaryBlocks = Omit<MActorSummary, 'Server'> &
67 PickWith<ActorModel, 'Server', MServerHostBlocks>
68
69export type MActorFollowerException = Pick<ActorModel, 'sharedInboxUrl' | 'inboxUrl'>
70
71export type MActorAPI = Omit<MActorDefault, 'publicKey' | 'privateKey' | 'inboxUrl' | 'outboxUrl' | 'sharedInboxUrl' |
72 'followersUrl' | 'followingUrl' | 'url' | 'createdAt' | 'updatedAt'>
73
74export type MActorSignature = MActorAccountChannelId
diff --git a/server/typings/models/account/avatar.ts b/server/typings/models/account/avatar.ts
new file mode 100644
index 000000000..257c48bfc
--- /dev/null
+++ b/server/typings/models/account/avatar.ts
@@ -0,0 +1,3 @@
1import { AvatarModel } from '../../../models/avatar/avatar'
2
3export type MAvatar = AvatarModel
diff --git a/server/typings/models/account/index.d.ts b/server/typings/models/account/index.d.ts
new file mode 100644
index 000000000..513c09c40
--- /dev/null
+++ b/server/typings/models/account/index.d.ts
@@ -0,0 +1,5 @@
1export * from './account'
2export * from './account-blocklist'
3export * from './actor'
4export * from './actor-follow'
5export * from './avatar'
diff --git a/server/typings/models/actor-follow.ts b/server/typings/models/actor-follow.ts
deleted file mode 100644
index 952ef877b..000000000
--- a/server/typings/models/actor-follow.ts
+++ /dev/null
@@ -1,8 +0,0 @@
1import { ActorFollowModel } from '../../models/activitypub/actor-follow'
2import { ActorModelOnly } from './actor'
3
4export type ActorFollowModelOnly = Omit<ActorFollowModel, 'ActorFollower' | 'ActorFollowing'>
5export type ActorFollowModelLight = ActorFollowModelOnly & {
6 ActorFollower: ActorModelOnly
7 ActorFollowing: ActorModelOnly
8}
diff --git a/server/typings/models/actor.ts b/server/typings/models/actor.ts
deleted file mode 100644
index 2656c7b66..000000000
--- a/server/typings/models/actor.ts
+++ /dev/null
@@ -1,22 +0,0 @@
1import { ActorModel } from '../../models/activitypub/actor'
2import { VideoChannelModel } from '../../models/video/video-channel'
3import { AccountModel } from '../../models/account/account'
4import { FunctionProperties } from '../utils'
5
6export type VideoChannelModelId = FunctionProperties<VideoChannelModel>
7export type AccountModelId = FunctionProperties<AccountModel> | Pick<AccountModel, 'id'>
8
9export type VideoChannelModelIdActor = VideoChannelModelId & Pick<VideoChannelModel, 'Actor'>
10export type AccountModelIdActor = AccountModelId & Pick<AccountModel, 'Actor'>
11
12export type ActorModelUrl = Pick<ActorModel, 'url'>
13export type ActorModelOnly = Omit<ActorModel, 'Account' | 'VideoChannel' | 'ActorFollowing' | 'Avatar' | 'ActorFollowers' | 'Server'>
14export type ActorModelId = Pick<ActorModelOnly, 'id'>
15
16export type SignatureActorModel = ActorModelOnly & {
17 VideoChannel: VideoChannelModelIdActor
18
19 Account: AccountModelIdActor
20}
21
22export type ActorFollowerException = Pick<ActorModel, 'sharedInboxUrl' | 'inboxUrl'>
diff --git a/server/typings/models/index.d.ts b/server/typings/models/index.d.ts
index c90656965..39e82e4a8 100644
--- a/server/typings/models/index.d.ts
+++ b/server/typings/models/index.d.ts
@@ -1 +1,4 @@
1export * from './actor' 1export * from './account'
2export * from './server'
3export * from './user'
4export * from './video'
diff --git a/server/typings/models/oauth/oauth-client.ts b/server/typings/models/oauth/oauth-client.ts
new file mode 100644
index 000000000..904a07863
--- /dev/null
+++ b/server/typings/models/oauth/oauth-client.ts
@@ -0,0 +1,3 @@
1import { OAuthClientModel } from '@server/models/oauth/oauth-client'
2
3export type MOAuthClient = Omit<OAuthClientModel, 'OAuthTokens'>
diff --git a/server/typings/models/oauth/oauth-token.ts b/server/typings/models/oauth/oauth-token.ts
new file mode 100644
index 000000000..105ea3df3
--- /dev/null
+++ b/server/typings/models/oauth/oauth-token.ts
@@ -0,0 +1,9 @@
1import { OAuthTokenModel } from '@server/models/oauth/oauth-token'
2import { PickWith } from '@server/typings/utils'
3import { MUserAccountUrl } from '@server/typings/models'
4
5export type MOAuthToken = Omit<OAuthTokenModel, 'User' | 'OAuthClients'>
6
7export type MOAuthTokenUser = MOAuthToken &
8 PickWith<OAuthTokenModel, 'User', MUserAccountUrl> &
9 { user?: MUserAccountUrl }
diff --git a/server/typings/models/server/index.d.ts b/server/typings/models/server/index.d.ts
new file mode 100644
index 000000000..c853795ad
--- /dev/null
+++ b/server/typings/models/server/index.d.ts
@@ -0,0 +1,3 @@
1export * from './plugin'
2export * from './server'
3export * from './server-blocklist'
diff --git a/server/typings/models/server/plugin.ts b/server/typings/models/server/plugin.ts
new file mode 100644
index 000000000..b1e2e149d
--- /dev/null
+++ b/server/typings/models/server/plugin.ts
@@ -0,0 +1,3 @@
1import { PluginModel } from '@server/models/server/plugin'
2
3export type MPlugin = PluginModel
diff --git a/server/typings/models/server/server-blocklist.ts b/server/typings/models/server/server-blocklist.ts
new file mode 100644
index 000000000..38065f382
--- /dev/null
+++ b/server/typings/models/server/server-blocklist.ts
@@ -0,0 +1,9 @@
1import { ServerBlocklistModel } from '@server/models/server/server-blocklist'
2import { PickWith } from '@server/typings/utils'
3import { MAccountDefault, MServer } from '@server/typings/models'
4
5export type MServerBlocklist = Omit<ServerBlocklistModel, 'ByAccount' | 'BlockedServer'>
6
7export type MServerBlocklistAccountServer = MServerBlocklist &
8 PickWith<ServerBlocklistModel, 'ByAccount', MAccountDefault> &
9 PickWith<ServerBlocklistModel, 'BlockedServer', MServer>
diff --git a/server/typings/models/server/server.ts b/server/typings/models/server/server.ts
new file mode 100644
index 000000000..6be7bf9bb
--- /dev/null
+++ b/server/typings/models/server/server.ts
@@ -0,0 +1,10 @@
1import { ServerModel } from '../../../models/server/server'
2import { PickWith } from '../../utils'
3import { MAccountBlocklistId } from '../account'
4
5export type MServer = Omit<ServerModel, 'Actors' | 'BlockedByAccounts'>
6
7export type MServerHost = Pick<MServer, 'host'>
8
9export type MServerHostBlocks = MServerHost &
10 PickWith<ServerModel, 'BlockedByAccounts', MAccountBlocklistId[]>
diff --git a/server/typings/models/user/index.d.ts b/server/typings/models/user/index.d.ts
new file mode 100644
index 000000000..e3353d0b1
--- /dev/null
+++ b/server/typings/models/user/index.d.ts
@@ -0,0 +1,3 @@
1export * from './user'
2export * from './user-notification'
3export * from './user-video-history'
diff --git a/server/typings/models/user/user-notification-setting.ts b/server/typings/models/user/user-notification-setting.ts
new file mode 100644
index 000000000..585d30a66
--- /dev/null
+++ b/server/typings/models/user/user-notification-setting.ts
@@ -0,0 +1,3 @@
1import { UserNotificationSettingModel } from '@server/models/account/user-notification-setting'
2
3export type MNotificationSetting = Omit<UserNotificationSettingModel, 'User'>
diff --git a/server/typings/models/user/user-notification.ts b/server/typings/models/user/user-notification.ts
new file mode 100644
index 000000000..b872c5dc5
--- /dev/null
+++ b/server/typings/models/user/user-notification.ts
@@ -0,0 +1,69 @@
1import { UserNotificationModel } from '../../../models/account/user-notification'
2import { PickWith } from '../../utils'
3import { VideoModel } from '../../../models/video/video'
4import { ActorModel } from '../../../models/activitypub/actor'
5import { ServerModel } from '../../../models/server/server'
6import { AvatarModel } from '../../../models/avatar/avatar'
7import { VideoChannelModel } from '../../../models/video/video-channel'
8import { AccountModel } from '../../../models/account/account'
9import { VideoCommentModel } from '../../../models/video/video-comment'
10import { VideoAbuseModel } from '../../../models/video/video-abuse'
11import { VideoBlacklistModel } from '../../../models/video/video-blacklist'
12import { VideoImportModel } from '../../../models/video/video-import'
13import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
14
15export namespace UserNotificationIncludes {
16 export type VideoInclude = Pick<VideoModel, 'id' | 'uuid' | 'name'>
17 export type VideoIncludeChannel = VideoInclude &
18 PickWith<VideoModel, 'VideoChannel', VideoChannelIncludeActor>
19
20 export type ActorInclude = Pick<ActorModel, 'preferredUsername' | 'getHost'> &
21 PickWith<ActorModel, 'Avatar', Pick<AvatarModel, 'filename' | 'getStaticPath'>> &
22 PickWith<ActorModel, 'Server', Pick<ServerModel, 'host'>>
23
24 export type VideoChannelInclude = Pick<VideoChannelModel, 'id' | 'name' | 'getDisplayName'>
25 export type VideoChannelIncludeActor = VideoChannelInclude &
26 PickWith<VideoChannelModel, 'Actor', ActorInclude>
27
28 export type AccountInclude = Pick<AccountModel, 'id' | 'name' | 'getDisplayName'>
29 export type AccountIncludeActor = AccountInclude &
30 PickWith<AccountModel, 'Actor', ActorInclude>
31
32 export type VideoCommentInclude = Pick<VideoCommentModel, 'id' | 'originCommentId' | 'getThreadId'> &
33 PickWith<VideoCommentModel, 'Account', AccountIncludeActor> &
34 PickWith<VideoCommentModel, 'Video', VideoInclude>
35
36 export type VideoAbuseInclude = Pick<VideoAbuseModel, 'id'> &
37 PickWith<VideoAbuseModel, 'Video', VideoInclude>
38
39 export type VideoBlacklistInclude = Pick<VideoBlacklistModel, 'id'> &
40 PickWith<VideoAbuseModel, 'Video', VideoInclude>
41
42 export type VideoImportInclude = Pick<VideoImportModel, 'id' | 'magnetUri' | 'targetUrl' | 'torrentName'> &
43 PickWith<VideoImportModel, 'Video', VideoInclude>
44
45 export type ActorFollower = Pick<ActorModel, 'preferredUsername' | 'getHost'> &
46 PickWith<ActorModel, 'Account', AccountInclude> &
47 PickWith<ActorModel, 'Avatar', Pick<AvatarModel, 'filename' | 'getStaticPath'>> &
48 PickWith<ActorModel, 'Server', Pick<ServerModel, 'host'>>
49
50 export type ActorFollowing = Pick<ActorModel, 'preferredUsername'> &
51 PickWith<ActorModel, 'VideoChannel', VideoChannelInclude> &
52 PickWith<ActorModel, 'Account', AccountInclude>
53
54 export type ActorFollowInclude = Pick<ActorFollowModel, 'id' | 'state'> &
55 PickWith<ActorFollowModel, 'ActorFollower', ActorFollower> &
56 PickWith<ActorFollowModel, 'ActorFollowing', ActorFollowing>
57}
58
59export type UserNotificationModelOnly = Omit<UserNotificationModel, 'User' | 'Video' | 'Comment' | 'VideoAbuse' | 'VideoBlacklist' |
60 'VideoImport' | 'Account' | 'ActorFollow'>
61
62export type UserNotificationModelForApi = UserNotificationModelOnly &
63 PickWith<UserNotificationModel, 'Video', UserNotificationIncludes.VideoIncludeChannel> &
64 PickWith<UserNotificationModel, 'Comment', UserNotificationIncludes.VideoCommentInclude> &
65 PickWith<UserNotificationModel, 'VideoAbuse', UserNotificationIncludes.VideoAbuseInclude> &
66 PickWith<UserNotificationModel, 'VideoBlacklist', UserNotificationIncludes.VideoBlacklistInclude> &
67 PickWith<UserNotificationModel, 'VideoImport', UserNotificationIncludes.VideoImportInclude> &
68 PickWith<UserNotificationModel, 'ActorFollow', UserNotificationIncludes.ActorFollowInclude> &
69 PickWith<UserNotificationModel, 'Account', UserNotificationIncludes.AccountIncludeActor>
diff --git a/server/typings/models/user/user-video-history.ts b/server/typings/models/user/user-video-history.ts
new file mode 100644
index 000000000..62673ab1b
--- /dev/null
+++ b/server/typings/models/user/user-video-history.ts
@@ -0,0 +1,5 @@
1import { UserVideoHistoryModel } from '../../../models/account/user-video-history'
2
3export type MUserVideoHistory = Omit<UserVideoHistoryModel, 'Video' | 'User'>
4
5export type MUserVideoHistoryTime = Pick<MUserVideoHistory, 'currentTime'>
diff --git a/server/typings/models/user/user.ts b/server/typings/models/user/user.ts
new file mode 100644
index 000000000..b91eed8d9
--- /dev/null
+++ b/server/typings/models/user/user.ts
@@ -0,0 +1,32 @@
1import { UserModel } from '../../../models/account/user'
2import { PickWith } from '../../utils'
3import { MAccount, MAccountDefault, MAccountDefaultChannelDefault, MAccountId, MAccountIdActorId, MAccountUrl } from '../account'
4import { MNotificationSetting } from './user-notification-setting'
5
6export type MUser = Omit<UserModel, 'Account' | 'NotificationSetting' | 'VideoImports' | 'OAuthTokens'>
7
8export type MUserId = Pick<UserModel, 'id'>
9
10export type MUserWithNotificationSetting = MUser &
11 PickWith<UserModel, 'NotificationSetting', MNotificationSetting>
12
13export type MUserAccountDefault = MUser &
14 PickWith<UserModel, 'Account', MAccountDefault>
15
16export type MUserAccount = MUser &
17 PickWith<UserModel, 'Account', MAccount>
18
19export type MUserAccountId = MUser &
20 PickWith<UserModel, 'Account', MAccountId>
21
22export type MUserNotifSettingAccount = MUserWithNotificationSetting & MUserAccount
23
24export type MUserDefault = MUser &
25 MUserWithNotificationSetting &
26 MUserAccountDefault
27
28export type MUserChannel = MUserWithNotificationSetting &
29 PickWith<UserModel, 'Account', MAccountDefaultChannelDefault>
30
31export type MUserAccountUrl = MUser &
32 PickWith<UserModel, 'Account', MAccountUrl & MAccountIdActorId>
diff --git a/server/typings/models/video-share.ts b/server/typings/models/video-share.ts
deleted file mode 100644
index 1406749d2..000000000
--- a/server/typings/models/video-share.ts
+++ /dev/null
@@ -1,3 +0,0 @@
1import { VideoShareModel } from '../../models/video/video-share'
2
3export type VideoShareModelOnly = Omit<VideoShareModel, 'Actor' | 'Video'>
diff --git a/server/typings/models/video/index.d.ts b/server/typings/models/video/index.d.ts
new file mode 100644
index 000000000..528e9d274
--- /dev/null
+++ b/server/typings/models/video/index.d.ts
@@ -0,0 +1,14 @@
1export * from './schedule-video-update'
2export * from './tag'
3export * from './thumbnail'
4export * from './video'
5export * from './video-abuse'
6export * from './video-blacklist'
7export * from './video-caption'
8export * from './video-channels'
9export * from './video-comment'
10export * from './video-file'
11export * from './video-playlist'
12export * from './video-redundancy'
13export * from './video-share'
14export * from './video-streaming-playlist'
diff --git a/server/typings/models/video/schedule-video-update.ts b/server/typings/models/video/schedule-video-update.ts
new file mode 100644
index 000000000..069705536
--- /dev/null
+++ b/server/typings/models/video/schedule-video-update.ts
@@ -0,0 +1,3 @@
1import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update'
2
3export type MScheduleVideoUpdate = Omit<ScheduleVideoUpdateModel, 'Video'>
diff --git a/server/typings/models/video/tag.ts b/server/typings/models/video/tag.ts
new file mode 100644
index 000000000..64a68873e
--- /dev/null
+++ b/server/typings/models/video/tag.ts
@@ -0,0 +1,3 @@
1import { TagModel } from '../../../models/video/tag'
2
3export type MTag = Omit<TagModel, 'Videos'>
diff --git a/server/typings/models/video/thumbnail.ts b/server/typings/models/video/thumbnail.ts
new file mode 100644
index 000000000..c03ba55ac
--- /dev/null
+++ b/server/typings/models/video/thumbnail.ts
@@ -0,0 +1,3 @@
1import { ThumbnailModel } from '../../../models/video/thumbnail'
2
3export type MThumbnail = Omit<ThumbnailModel, 'Video' | 'VideoPlaylist'>
diff --git a/server/typings/models/video/video-abuse.ts b/server/typings/models/video/video-abuse.ts
new file mode 100644
index 000000000..1667ae55a
--- /dev/null
+++ b/server/typings/models/video/video-abuse.ts
@@ -0,0 +1,15 @@
1import { VideoAbuseModel } from '../../../models/video/video-abuse'
2import { PickWith } from '../../utils'
3import { MVideo } from './video'
4import { MAccountDefault } from '../account'
5
6export type MVideoAbuse = Omit<VideoAbuseModel, 'Account' | 'Video' | 'toActivityPubObject'>
7
8export type MVideoAbuseId = Pick<VideoAbuseModel, 'id'>
9
10export type MVideoAbuseVideo = MVideoAbuse &
11 Pick<VideoAbuseModel, 'toActivityPubObject'> &
12 PickWith<VideoAbuseModel, 'Video', MVideo>
13
14export type MVideoAbuseAccountVideo = MVideoAbuseVideo &
15 PickWith<VideoAbuseModel, 'Account', MAccountDefault>
diff --git a/server/typings/models/video/video-blacklist.ts b/server/typings/models/video/video-blacklist.ts
new file mode 100644
index 000000000..9242b357d
--- /dev/null
+++ b/server/typings/models/video/video-blacklist.ts
@@ -0,0 +1,11 @@
1import { VideoBlacklistModel } from '../../../models/video/video-blacklist'
2import { PickWith } from '@server/typings/utils'
3import { MVideo } from '@server/typings/models'
4
5export type MVideoBlacklist = Omit<VideoBlacklistModel, 'Video'>
6
7export type MVideoBlacklistLight = Pick<MVideoBlacklist, 'id' | 'reason' | 'unfederated'>
8export type MVideoBlacklistUnfederated = Pick<MVideoBlacklist, 'unfederated'>
9
10export type MVideoBlacklistVideo = MVideoBlacklist &
11 PickWith<VideoBlacklistModel, 'Video', MVideo>
diff --git a/server/typings/models/video/video-caption.ts b/server/typings/models/video/video-caption.ts
new file mode 100644
index 000000000..16d8b7392
--- /dev/null
+++ b/server/typings/models/video/video-caption.ts
@@ -0,0 +1,10 @@
1import { VideoCaptionModel } from '../../../models/video/video-caption'
2import { PickWith } from '@server/typings/utils'
3import { VideoModel } from '@server/models/video/video'
4
5export type MVideoCaption = Omit<VideoCaptionModel, 'Video'>
6
7export type MVideoCaptionLanguage = Pick<MVideoCaption, 'language'>
8
9export type MVideoCaptionVideo = MVideoCaption &
10 PickWith<VideoCaptionModel, 'Video', Pick<VideoModel, 'id' | 'remote' | 'uuid'>>
diff --git a/server/typings/models/video/video-change-ownership.ts b/server/typings/models/video/video-change-ownership.ts
new file mode 100644
index 000000000..718515e2d
--- /dev/null
+++ b/server/typings/models/video/video-change-ownership.ts
@@ -0,0 +1,10 @@
1import { VideoChangeOwnershipModel } from '@server/models/video/video-change-ownership'
2import { PickWith } from '@server/typings/utils'
3import { MAccountDefault, MVideoWithFileThumbnail } from '@server/typings/models'
4
5export type MVideoChangeOwnership = Omit<VideoChangeOwnershipModel, 'Initiator' | 'NextOwner' | 'Video'>
6
7export type MVideoChangeOwnershipFull = MVideoChangeOwnership &
8 PickWith<VideoChangeOwnershipModel, 'Initiator', MAccountDefault> &
9 PickWith<VideoChangeOwnershipModel, 'NextOwner', MAccountDefault> &
10 PickWith<VideoChangeOwnershipModel, 'Video', MVideoWithFileThumbnail>
diff --git a/server/typings/models/video/video-channels.ts b/server/typings/models/video/video-channels.ts
new file mode 100644
index 000000000..e10bd6842
--- /dev/null
+++ b/server/typings/models/video/video-channels.ts
@@ -0,0 +1,70 @@
1import { FunctionProperties, PickWith } from '../../utils'
2import { VideoChannelModel } from '../../../models/video/video-channel'
3import {
4 MAccountActor,
5 MAccountAPI,
6 MAccountBlocks,
7 MAccountDefault,
8 MAccountLight,
9 MAccountUserId,
10 MActor,
11 MActorAccountChannelId,
12 MActorAPI,
13 MActorDefault,
14 MActorDefaultLight, MActorLight,
15 MActorSummary
16} from '../account'
17import { MVideo } from './video'
18
19export type MChannelId = FunctionProperties<VideoChannelModel>
20export type MChannelIdActor = MChannelId &
21 PickWith<VideoChannelModel, 'Actor', MActorAccountChannelId>
22
23export type MChannel = Omit<VideoChannelModel, 'Actor' | 'Account' | 'Videos' | 'VideoPlaylists'>
24
25export type MChannelUserId = Pick<MChannel, 'accountId'> &
26 PickWith<VideoChannelModel, 'Account', MAccountUserId>
27
28// Default scope
29export type MChannelDefault = MChannel &
30 PickWith<VideoChannelModel, 'Actor', MActorDefault>
31
32export type MChannelLight = MChannel &
33 PickWith<VideoChannelModel, 'Actor', MActorDefaultLight>
34
35export type MChannelAccountLight = MChannel &
36 PickWith<VideoChannelModel, 'Actor', MActorDefaultLight> &
37 PickWith<VideoChannelModel, 'Account', MAccountLight>
38
39export type MChannelSummary = Pick<MChannel, 'id' | 'name' | 'description' | 'actorId'> &
40 PickWith<VideoChannelModel, 'Actor', MActorSummary>
41
42export type MChannelSummaryAccount = MChannelSummary &
43 PickWith<VideoChannelModel, 'Account', MAccountBlocks>
44
45export type MChannelAPI = MChannel &
46 PickWith<VideoChannelModel, 'Actor', MActorAPI> &
47 PickWith<VideoChannelModel, 'Account', MAccountAPI>
48
49export type MChannelAccountActor = MChannel &
50 PickWith<VideoChannelModel, 'Account', MAccountActor>
51export type MChannelAccountDefault = MChannelActor &
52 PickWith<VideoChannelModel, 'Account', MAccountDefault>
53
54export type MChannelVideos = MChannel &
55 PickWith<VideoChannelModel, 'Videos', MVideo[]>
56
57export type MChannelActor = MChannel &
58 PickWith<VideoChannelModel, 'Actor', MActor>
59export type MChannelActorLight = MChannel &
60 PickWith<VideoChannelModel, 'Actor', MActorLight>
61export type MChannelActorDefault = MChannel &
62 PickWith<VideoChannelModel, 'Actor', MActorDefault>
63
64export type MChannelActorAccountActor = MChannelAccountActor & MChannelActor
65
66export type MChannelActorAccountDefault = MChannel &
67 PickWith<VideoChannelModel, 'Actor', MActorDefault> &
68 PickWith<VideoChannelModel, 'Account', MAccountDefault>
69
70export type MChannelActorAccountDefaultVideos = MChannelActorAccountDefault & MChannelVideos
diff --git a/server/typings/models/video/video-comment.ts b/server/typings/models/video/video-comment.ts
new file mode 100644
index 000000000..675613804
--- /dev/null
+++ b/server/typings/models/video/video-comment.ts
@@ -0,0 +1,29 @@
1import { VideoCommentModel } from '../../../models/video/video-comment'
2import { PickWith } from '../../utils'
3import { MAccountDefault } from '../account'
4import { MVideoAccountDefault, MVideoAccountLight, MVideoFeed, MVideoIdUrl } from './video'
5
6export type MComment = Omit<VideoCommentModel, 'OriginVideoComment' | 'InReplyToVideoComment' | 'Video' | 'Account'>
7export type MCommentId = Pick<MComment, 'id'>
8
9export type MCommentAPI = MComment & { totalReplies: number }
10
11export type MCommentOwner = MComment &
12 PickWith<VideoCommentModel, 'Account', MAccountDefault>
13
14export type MCommentVideo = MComment &
15 PickWith<VideoCommentModel, 'Video', MVideoAccountLight>
16
17export type MCommentReply = MComment &
18 PickWith<VideoCommentModel, 'InReplyToVideoComment', MComment>
19
20export type MCommentOwnerReply = MCommentOwner & MCommentReply
21export type MCommentOwnerVideo = MCommentOwner & MCommentVideo
22export type MCommentReplyVideo = MCommentReply & MCommentVideo
23export type MCommentOwnerVideoReply = MCommentOwnerVideo & MCommentReply
24
25export type MCommentOwnerReplyVideoLight = MCommentOwnerReply &
26 PickWith<VideoCommentModel, 'Video', MVideoIdUrl>
27
28export type MCommentOwnerVideoFeed = MCommentOwner &
29 PickWith<VideoCommentModel, 'Video', MVideoFeed>
diff --git a/server/typings/models/video/video-file.ts b/server/typings/models/video/video-file.ts
new file mode 100644
index 000000000..afa659d1f
--- /dev/null
+++ b/server/typings/models/video/video-file.ts
@@ -0,0 +1,15 @@
1import { VideoFileModel } from '../../../models/video/video-file'
2import { PickWith, PickWithOpt } from '../../utils'
3import { MVideo, MVideoUUID } from './video'
4import { MVideoRedundancyFileUrl } from './video-redundancy'
5
6export type MVideoFile = Omit<VideoFileModel, 'Video' | 'RedundancyVideos'>
7
8export type MVideoFileVideo = MVideoFile &
9 PickWith<VideoFileModel, 'Video', MVideo>
10
11export type MVideoFileVideoUUID = MVideoFile &
12 PickWith<VideoFileModel, 'Video', MVideoUUID>
13
14export type MVideoFileRedundanciesOpt = MVideoFile &
15 PickWithOpt<VideoFileModel, 'RedundancyVideos', MVideoRedundancyFileUrl[]>
diff --git a/server/typings/models/video/video-import.ts b/server/typings/models/video/video-import.ts
new file mode 100644
index 000000000..51be900d6
--- /dev/null
+++ b/server/typings/models/video/video-import.ts
@@ -0,0 +1,15 @@
1import { VideoImportModel } from '@server/models/video/video-import'
2import { PickWith } from '@server/typings/utils'
3import { MUser, MVideo, MVideoAccountLight, MVideoTag, MVideoThumbnail, MVideoWithFile } from '@server/typings/models'
4
5export type MVideoImport = Omit<VideoImportModel, 'User' | 'Video'>
6
7export type MVideoImportDefault = MVideoImport &
8 PickWith<VideoImportModel, 'User', MUser> &
9 PickWith<VideoImportModel, 'Video', MVideoTag & MVideoAccountLight & MVideoThumbnail>
10
11export type MVideoImportDefaultFiles = MVideoImportDefault &
12 PickWith<VideoImportModel, 'Video', MVideoTag & MVideoAccountLight & MVideoThumbnail & MVideoWithFile>
13
14export type MVideoImportVideo = MVideoImport &
15 PickWith<VideoImportModel, 'Video', MVideo>
diff --git a/server/typings/models/video/video-playlist-element.ts b/server/typings/models/video/video-playlist-element.ts
new file mode 100644
index 000000000..d1b8a18a0
--- /dev/null
+++ b/server/typings/models/video/video-playlist-element.ts
@@ -0,0 +1,15 @@
1import { VideoPlaylistElementModel } from '@server/models/video/video-playlist-element'
2import { PickWith } from '@server/typings/utils'
3import { MVideoPlaylistPrivacy, MVideoThumbnail, MVideoUrl } from '@server/typings/models'
4
5export type MVideoPlaylistElement = Omit<VideoPlaylistElementModel, 'VideoPlaylist' | 'Video'>
6export type MVideoPlaylistElementId = Pick<MVideoPlaylistElement, 'id'>
7
8export type MVideoPlaylistElementLight = Pick<MVideoPlaylistElement, 'id' | 'videoId' | 'startTimestamp' | 'stopTimestamp'>
9
10export type MVideoPlaylistVideoThumbnail = MVideoPlaylistElement &
11 PickWith<VideoPlaylistElementModel, 'Video', MVideoThumbnail>
12
13export type MVideoPlaylistAP = MVideoPlaylistElement &
14 PickWith<VideoPlaylistElementModel, 'Video', MVideoUrl> &
15 PickWith<VideoPlaylistElementModel, 'VideoPlaylist', MVideoPlaylistPrivacy>
diff --git a/server/typings/models/video/video-playlist.ts b/server/typings/models/video/video-playlist.ts
new file mode 100644
index 000000000..825b3391c
--- /dev/null
+++ b/server/typings/models/video/video-playlist.ts
@@ -0,0 +1,42 @@
1import { VideoPlaylistModel } from '../../../models/video/video-playlist'
2import { PickWith } from '../../utils'
3import { MAccount, MAccountDefault, MAccountSummary } from '../account'
4import { MThumbnail } from './thumbnail'
5import { MChannelDefault, MChannelSummary } from './video-channels'
6import { MVideoPlaylistElementLight } from '@server/typings/models/video/video-playlist-element'
7
8export type MVideoPlaylist = Omit<VideoPlaylistModel, 'OwnerAccount' | 'VideoChannel' | 'VideoPlaylistElements' | 'Thumbnail'>
9export type MVideoPlaylistId = Pick<MVideoPlaylist, 'id'>
10export type MVideoPlaylistPrivacy = Pick<MVideoPlaylist, 'privacy'>
11
12export type MVideoPlaylistWithElements = MVideoPlaylist &
13 PickWith<VideoPlaylistModel, 'VideoPlaylistElements', MVideoPlaylistElementLight[]>
14export type MVideoPlaylistIdWithElements = MVideoPlaylistId & MVideoPlaylistWithElements
15
16export type MVideoPlaylistUUID = Pick<MVideoPlaylist, 'uuid'>
17
18export type MVideoPlaylistOwner = MVideoPlaylist &
19 PickWith<VideoPlaylistModel, 'OwnerAccount', MAccount>
20
21export type MVideoPlaylistOwnerDefault = MVideoPlaylist &
22 PickWith<VideoPlaylistModel, 'OwnerAccount', MAccountDefault>
23
24export type MVideoPlaylistThumbnail = MVideoPlaylist &
25 PickWith<VideoPlaylistModel, 'Thumbnail', MThumbnail>
26
27export type MVideoPlaylistAccountThumbnail = MVideoPlaylistOwnerDefault &
28 PickWith<VideoPlaylistModel, 'Thumbnail', MThumbnail>
29
30export type MVideoPlaylistAccountChannelSummary = MVideoPlaylist &
31 PickWith<VideoPlaylistModel, 'OwnerAccount', MAccountSummary> &
32 PickWith<VideoPlaylistModel, 'VideoChannel', MChannelSummary>
33
34export type MVideoPlaylistAccountChannelDefault = MVideoPlaylist &
35 PickWith<VideoPlaylistModel, 'OwnerAccount', MAccountDefault> &
36 PickWith<VideoPlaylistModel, 'VideoChannel', MChannelDefault>
37
38export type MVideoPlaylistVideosLength = MVideoPlaylist & { videosLength: number }
39
40export type MVideoPlaylistFullSummary = MVideoPlaylistAccountChannelSummary & MVideoPlaylistThumbnail
41
42export type MVideoPlaylistFull = MVideoPlaylist & MVideoPlaylistThumbnail & MVideoPlaylistAccountChannelDefault
diff --git a/server/typings/models/video/video-rate.ts b/server/typings/models/video/video-rate.ts
new file mode 100644
index 000000000..6eefe6362
--- /dev/null
+++ b/server/typings/models/video/video-rate.ts
@@ -0,0 +1,12 @@
1import { AccountVideoRateModel } from '@server/models/account/account-video-rate'
2import { PickWith } from '@server/typings/utils'
3import { MAccountAudience, MAccountUrl, MVideo } from '..'
4
5export type MAccountVideoRate = Omit<AccountVideoRateModel, 'Video' | 'Account'>
6
7export type MAccountVideoRateAccountUrl = MAccountVideoRate &
8 PickWith<AccountVideoRateModel, 'Account', MAccountUrl>
9
10export type MAccountVideoRateAccountVideo = MAccountVideoRate &
11 PickWith<AccountVideoRateModel, 'Account', MAccountAudience> &
12 PickWith<AccountVideoRateModel, 'Video', MVideo>
diff --git a/server/typings/models/video/video-redundancy.ts b/server/typings/models/video/video-redundancy.ts
new file mode 100644
index 000000000..ec61bfb68
--- /dev/null
+++ b/server/typings/models/video/video-redundancy.ts
@@ -0,0 +1,18 @@
1import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
2import { PickWith } from '@server/typings/utils'
3import { MStreamingPlaylistVideo, MVideoFile, MVideoFileVideo } from '@server/typings/models'
4
5export type MVideoRedundancy = Omit<VideoRedundancyModel, 'VideoFile' | 'VideoStreamingPlaylist' | 'Actor'>
6
7export type MVideoRedundancyFileUrl = Pick<MVideoRedundancy, 'fileUrl'>
8
9export type MVideoRedundancyFile = MVideoRedundancy &
10 PickWith<VideoRedundancyModel, 'VideoFile', MVideoFile>
11
12export type MVideoRedundancyFileVideo = MVideoRedundancy &
13 PickWith<VideoRedundancyModel, 'VideoFile', MVideoFileVideo>
14
15export type MVideoRedundancyStreamingPlaylistVideo = MVideoRedundancy &
16 PickWith<VideoRedundancyModel, 'VideoStreamingPlaylist', MStreamingPlaylistVideo>
17
18export type MVideoRedundancyVideo = MVideoRedundancyFileVideo | MVideoRedundancyStreamingPlaylistVideo
diff --git a/server/typings/models/video/video-share.ts b/server/typings/models/video/video-share.ts
new file mode 100644
index 000000000..7e8cb8b61
--- /dev/null
+++ b/server/typings/models/video/video-share.ts
@@ -0,0 +1,12 @@
1import { VideoShareModel } from '../../../models/video/video-share'
2import { PickWith } from '../../utils'
3import { MActorDefault } from '../account'
4import { MVideo } from './video'
5
6export type MVideoShare = Omit<VideoShareModel, 'Actor' | 'Video'>
7
8export type MVideoShareActor = MVideoShare &
9 PickWith<VideoShareModel, 'Actor', MActorDefault>
10
11export type MVideoShareFull = MVideoShareActor &
12 PickWith<VideoShareModel, 'Video', MVideo>
diff --git a/server/typings/models/video/video-streaming-playlist.ts b/server/typings/models/video/video-streaming-playlist.ts
new file mode 100644
index 000000000..5b6310771
--- /dev/null
+++ b/server/typings/models/video/video-streaming-playlist.ts
@@ -0,0 +1,12 @@
1import { VideoStreamingPlaylistModel } from '../../../models/video/video-streaming-playlist'
2import { PickWith } from '../../utils'
3import { MVideoRedundancyFileUrl } from './video-redundancy'
4import { MVideo } from '@server/typings/models'
5
6export type MStreamingPlaylist = Omit<VideoStreamingPlaylistModel, 'Video' | 'RedundancyVideos'>
7
8export type MStreamingPlaylistVideo = MStreamingPlaylist &
9 PickWith<VideoStreamingPlaylistModel, 'Video', MVideo>
10
11export type MStreamingPlaylistRedundancies = MStreamingPlaylist &
12 PickWith<VideoStreamingPlaylistModel, 'RedundancyVideos', MVideoRedundancyFileUrl[]>
diff --git a/server/typings/models/video/video.ts b/server/typings/models/video/video.ts
new file mode 100644
index 000000000..0ffd0c302
--- /dev/null
+++ b/server/typings/models/video/video.ts
@@ -0,0 +1,103 @@
1import { VideoModel } from '../../../models/video/video'
2import { PickWith, PickWithOpt } from '../../utils'
3import { MChannelAccountLight, MChannelActor, MChannelActorAccountDefault, MChannelUserId } from './video-channels'
4import { MTag } from './tag'
5import { MVideoCaptionLanguage } from './video-caption'
6import { MStreamingPlaylist, MStreamingPlaylistRedundancies } from './video-streaming-playlist'
7import { MVideoFile, MVideoFileRedundanciesOpt } from './video-file'
8import { MThumbnail } from './thumbnail'
9import { MVideoBlacklistLight, MVideoBlacklistUnfederated } from './video-blacklist'
10import { MScheduleVideoUpdate } from './schedule-video-update'
11import { MUserVideoHistoryTime } from '../user/user-video-history'
12
13export type MVideo = Omit<VideoModel, 'VideoChannel' | 'Tags' | 'Thumbnails' | 'VideoPlaylistElements' | 'VideoAbuses' |
14 'VideoFiles' | 'VideoStreamingPlaylists' | 'VideoShares' | 'AccountVideoRates' | 'VideoComments' | 'VideoViews' | 'UserVideoHistories' |
15 'ScheduleVideoUpdate' | 'VideoBlacklist' | 'VideoImport' | 'VideoCaptions'>
16
17export type MVideoId = Pick<MVideo, 'id'>
18export type MVideoUrl = Pick<MVideo, 'url'>
19export type MVideoUUID = Pick<MVideo, 'uuid'>
20
21export type MVideoIdUrl = MVideoId & MVideoUrl
22export type MVideoFeed = Pick<MVideo, 'name' | 'uuid'>
23
24export type MVideoWithFile = MVideo &
25 PickWith<VideoModel, 'VideoFiles', MVideoFile[]>
26
27export type MVideoThumbnail = MVideo &
28 PickWith<VideoModel, 'Thumbnails', MThumbnail[]>
29export type MVideoIdThumbnail = MVideoThumbnail & MVideoId
30
31export type MVideoTag = MVideo &
32 PickWith<VideoModel, 'Tags', MTag[]>
33
34export type MVideoWithSchedule = MVideo &
35 PickWithOpt<VideoModel, 'ScheduleVideoUpdate', MScheduleVideoUpdate>
36
37export type MVideoWithFileThumbnail = MVideoWithFile & MVideoThumbnail
38
39export type MVideoUser = MVideo &
40 PickWith<VideoModel, 'VideoChannel', MChannelUserId>
41
42export type MVideoWithCaptions = MVideo &
43 PickWith<VideoModel, 'VideoCaptions', MVideoCaptionLanguage[]>
44
45export type MVideoWithBlacklistLight = MVideo &
46 PickWith<VideoModel, 'VideoBlacklist', MVideoBlacklistLight>
47
48export type MVideoAccountLight = MVideo &
49 PickWith<VideoModel, 'VideoChannel', MChannelAccountLight>
50
51export type MVideoWithRights = MVideoWithBlacklistLight & MVideoThumbnail & MVideoUser
52
53export type MVideoWithStreamingPlaylist = MVideo &
54 PickWith<VideoModel, 'VideoStreamingPlaylists', MStreamingPlaylist[]>
55
56export type MVideoWithAllFiles = MVideoWithFileThumbnail & MVideoWithStreamingPlaylist
57
58export type MVideoAccountAllFiles = MVideoWithAllFiles & MVideoAccountLight & MVideoWithBlacklistLight
59export type MVideoAccountAllFilesCaptions = MVideoAccountAllFiles & MVideoWithCaptions
60
61export type MVideoUserHistory = MVideo &
62 PickWith<VideoModel, 'UserVideoHistories', MUserVideoHistoryTime[]>
63
64export type MVideoWithBlacklistThumbnailScheduled = MVideoWithSchedule & MVideoWithBlacklistLight & MVideoWithFileThumbnail
65
66export type MVideoAccountDefault = MVideo &
67 PickWith<VideoModel, 'VideoChannel', MChannelActorAccountDefault>
68
69export type MVideoThumbnailAccountDefault = MVideoThumbnail &
70 PickWith<VideoModel, 'VideoChannel', MChannelActorAccountDefault>
71
72export type MVideoWithChannelActor = MVideo &
73 PickWith<VideoModel, 'VideoChannel', MChannelActor>
74
75export type MVideoFullLight = MVideoThumbnail &
76 MVideoWithBlacklistLight &
77 MVideoTag &
78 MVideoAccountLight &
79 MVideoUserHistory &
80 MVideoWithFile &
81 MVideoWithSchedule &
82 MVideoWithStreamingPlaylist &
83 MVideoUserHistory
84
85export type MVideoAP = MVideo &
86 MVideoTag &
87 MVideoAccountLight &
88 MVideoWithStreamingPlaylist &
89 MVideoWithCaptions &
90 PickWith<VideoModel, 'VideoBlacklist', MVideoBlacklistUnfederated> &
91 PickWith<VideoModel, 'VideoFiles', MVideoFileRedundanciesOpt[]>
92
93export type MVideoAPWithoutCaption = Omit<MVideoAP, 'VideoCaptions'>
94
95export type MVideoDetails = MVideo &
96 MVideoWithBlacklistLight &
97 MVideoTag &
98 MVideoAccountLight &
99 MVideoWithSchedule &
100 MVideoThumbnail &
101 MVideoUserHistory &
102 PickWith<VideoModel, 'VideoStreamingPlaylists', MStreamingPlaylistRedundancies[]> &
103 PickWith<VideoModel, 'VideoFiles', MVideoFileRedundanciesOpt[]>
diff --git a/server/typings/utils.ts b/server/typings/utils.ts
index a86b05be2..ed0fca3d1 100644
--- a/server/typings/utils.ts
+++ b/server/typings/utils.ts
@@ -1,3 +1,15 @@
1export type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T] 1export type FunctionPropertyNames<T> = {
2 [K in keyof T]: T[K] extends Function ? K : never
3}[keyof T]
2 4
3export type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>> 5export type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>
6
7export type ValueOf <T, KT extends keyof T> = T[KT]
8
9export type PickWith<T, KT extends keyof T, V> = {
10 [P in KT]: T[P] extends V ? V : never
11}
12
13export type PickWithOpt<T, KT extends keyof T, V> = {
14 [P in KT]?: T[P] extends V ? V : never
15}