From 453e83ea5d81d203ba34bc43cd5c2c750ba40568 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 15 Aug 2019 11:53:26 +0200 Subject: [PATCH] Stronger model typings --- server/controllers/activitypub/client.ts | 37 ++--- server/controllers/activitypub/inbox.ts | 5 +- server/controllers/activitypub/outbox.ts | 10 +- server/controllers/api/search.ts | 5 +- server/controllers/api/users/index.ts | 3 +- server/controllers/api/users/me.ts | 2 +- server/controllers/api/users/my-history.ts | 1 - server/controllers/api/video-channel.ts | 2 +- server/controllers/api/video-playlist.ts | 36 ++--- server/controllers/api/videos/abuse.ts | 7 +- server/controllers/api/videos/blacklist.ts | 17 ++- server/controllers/api/videos/captions.ts | 9 +- server/controllers/api/videos/comment.ts | 17 +-- server/controllers/api/videos/import.ts | 34 +++-- server/controllers/api/videos/index.ts | 15 +- server/controllers/api/videos/ownership.ts | 5 +- server/controllers/api/videos/rate.ts | 2 +- server/controllers/api/videos/watching.ts | 2 +- server/controllers/feeds.ts | 2 +- server/controllers/services.ts | 2 +- server/controllers/static.ts | 6 +- server/controllers/webfinger.ts | 2 +- server/helpers/activitypub.ts | 3 +- server/helpers/actor.ts | 9 +- server/helpers/captions-utils.ts | 6 +- .../custom-validators/video-ownership.ts | 22 +-- server/helpers/middlewares/accounts.ts | 7 +- server/helpers/middlewares/video-abuses.ts | 44 ++---- server/helpers/middlewares/video-captions.ts | 4 +- server/helpers/middlewares/video-channels.ts | 45 ++++-- server/helpers/middlewares/video-playlists.ts | 35 +++-- server/helpers/middlewares/videos.ts | 26 +++- server/helpers/peertube-crypto.ts | 9 +- server/helpers/video.ts | 29 +++- server/helpers/webfinger.ts | 3 +- server/lib/activitypub/actor.ts | 75 ++++++--- server/lib/activitypub/audience.ts | 27 ++-- server/lib/activitypub/cache-file.ts | 14 +- server/lib/activitypub/playlist.ts | 19 ++- .../lib/activitypub/process/process-accept.ts | 5 +- .../activitypub/process/process-announce.ts | 7 +- .../lib/activitypub/process/process-create.ts | 14 +- .../lib/activitypub/process/process-delete.ts | 26 ++-- .../activitypub/process/process-dislike.ts | 4 +- .../lib/activitypub/process/process-flag.ts | 6 +- .../lib/activitypub/process/process-follow.ts | 21 ++- .../lib/activitypub/process/process-like.ts | 4 +- .../lib/activitypub/process/process-reject.ts | 4 +- .../lib/activitypub/process/process-undo.ts | 12 +- .../lib/activitypub/process/process-update.ts | 12 +- .../lib/activitypub/process/process-view.ts | 6 +- server/lib/activitypub/process/process.ts | 11 +- server/lib/activitypub/send/send-accept.ts | 7 +- server/lib/activitypub/send/send-announce.ts | 15 +- server/lib/activitypub/send/send-create.ts | 32 ++-- server/lib/activitypub/send/send-delete.ts | 12 +- server/lib/activitypub/send/send-dislike.ts | 7 +- server/lib/activitypub/send/send-flag.ts | 9 +- server/lib/activitypub/send/send-follow.ts | 6 +- server/lib/activitypub/send/send-like.ts | 7 +- server/lib/activitypub/send/send-reject.ts | 7 +- server/lib/activitypub/send/send-undo.ts | 32 ++-- server/lib/activitypub/send/send-update.ts | 31 ++-- server/lib/activitypub/send/send-view.ts | 6 +- server/lib/activitypub/send/utils.ts | 35 +++-- server/lib/activitypub/share.ts | 21 +-- server/lib/activitypub/url.ts | 58 +++---- server/lib/activitypub/video-comments.ts | 16 +- server/lib/activitypub/video-rates.ts | 24 +-- server/lib/activitypub/videos.ts | 143 +++++++++++------- server/lib/avatar.ts | 8 +- server/lib/blocklist.ts | 5 +- server/lib/client-html.ts | 7 +- server/lib/emailer.ts | 36 +++-- server/lib/hls.ts | 6 +- .../job-queue/handlers/activitypub-follow.ts | 9 +- .../handlers/activitypub-http-fetcher.ts | 5 +- .../handlers/utils/activitypub-http-utils.ts | 4 +- .../job-queue/handlers/video-file-import.ts | 11 +- server/lib/job-queue/handlers/video-import.ts | 38 ++--- .../job-queue/handlers/video-transcoding.ts | 7 +- server/lib/notifier.ts | 101 +++++++------ server/lib/oauth-model.ts | 3 +- server/lib/peertube-socket.ts | 4 +- server/lib/redundancy.ts | 3 +- .../schedulers/videos-redundancy-scheduler.ts | 52 ++++--- server/lib/thumbnail.ts | 26 ++-- server/lib/user.ts | 26 ++-- server/lib/video-blacklist.ts | 13 +- server/lib/video-channel.ts | 17 ++- server/lib/video-comment.ts | 11 +- server/lib/video-playlist.ts | 7 +- server/lib/video-transcoding.ts | 16 +- server/middlewares/validators/follows.ts | 3 +- server/middlewares/validators/redundancy.ts | 4 +- server/middlewares/validators/users.ts | 5 +- .../validators/videos/video-abuses.ts | 4 +- .../validators/videos/video-blacklist.ts | 6 +- .../validators/videos/video-captions.ts | 6 +- .../validators/videos/video-channels.ts | 5 +- .../validators/videos/video-comments.ts | 40 ++--- .../validators/videos/video-playlists.ts | 84 +++++----- .../validators/videos/video-shares.ts | 2 +- .../middlewares/validators/videos/videos.ts | 20 +-- server/middlewares/validators/webfinger.ts | 3 +- server/models/account/account-blocklist.ts | 6 +- server/models/account/account-video-rate.ts | 21 ++- server/models/account/account.ts | 17 ++- server/models/account/user-notification.ts | 10 +- server/models/account/user-video-history.ts | 7 +- server/models/account/user.ts | 30 ++-- server/models/activitypub/actor-follow.ts | 25 ++- server/models/activitypub/actor.ts | 20 ++- server/models/oauth/oauth-token.ts | 19 ++- server/models/redundancy/video-redundancy.ts | 9 +- server/models/server/plugin.ts | 10 +- server/models/server/server-blocklist.ts | 6 +- server/models/server/server.ts | 5 +- server/models/video/video-abuse.ts | 8 +- server/models/video/video-blacklist.ts | 6 +- server/models/video/video-caption.ts | 8 +- server/models/video/video-change-ownership.ts | 6 +- server/models/video/video-channel.ts | 35 +++-- server/models/video/video-comment.ts | 83 ++++------ server/models/video/video-file.ts | 3 +- server/models/video/video-format-utils.ts | 15 +- server/models/video/video-import.ts | 12 +- server/models/video/video-playlist-element.ts | 13 +- server/models/video/video-playlist.ts | 18 ++- server/models/video/video-share.ts | 14 +- .../models/video/video-streaming-playlist.ts | 12 +- server/models/video/video.ts | 111 +++++++------- server/typings/activitypub-processor.model.ts | 7 +- server/typings/express.ts | 123 ++++++++------- .../models/account/account-blocklist.ts | 11 ++ server/typings/models/account/account.ts | 56 +++++++ server/typings/models/account/actor-follow.ts | 27 ++++ server/typings/models/account/actor.ts | 74 +++++++++ server/typings/models/account/avatar.ts | 3 + server/typings/models/account/index.d.ts | 5 + server/typings/models/actor-follow.ts | 8 - server/typings/models/actor.ts | 22 --- server/typings/models/index.d.ts | 5 +- server/typings/models/oauth/oauth-client.ts | 3 + server/typings/models/oauth/oauth-token.ts | 9 ++ server/typings/models/server/index.d.ts | 3 + server/typings/models/server/plugin.ts | 3 + .../typings/models/server/server-blocklist.ts | 9 ++ server/typings/models/server/server.ts | 10 ++ server/typings/models/user/index.d.ts | 3 + .../models/user/user-notification-setting.ts | 3 + .../typings/models/user/user-notification.ts | 69 +++++++++ .../typings/models/user/user-video-history.ts | 5 + server/typings/models/user/user.ts | 32 ++++ server/typings/models/video-share.ts | 3 - server/typings/models/video/index.d.ts | 14 ++ .../models/video/schedule-video-update.ts | 3 + server/typings/models/video/tag.ts | 3 + server/typings/models/video/thumbnail.ts | 3 + server/typings/models/video/video-abuse.ts | 15 ++ .../typings/models/video/video-blacklist.ts | 11 ++ server/typings/models/video/video-caption.ts | 10 ++ .../models/video/video-change-ownership.ts | 10 ++ server/typings/models/video/video-channels.ts | 70 +++++++++ server/typings/models/video/video-comment.ts | 29 ++++ server/typings/models/video/video-file.ts | 15 ++ server/typings/models/video/video-import.ts | 15 ++ .../models/video/video-playlist-element.ts | 15 ++ server/typings/models/video/video-playlist.ts | 42 +++++ server/typings/models/video/video-rate.ts | 12 ++ .../typings/models/video/video-redundancy.ts | 18 +++ server/typings/models/video/video-share.ts | 12 ++ .../models/video/video-streaming-playlist.ts | 12 ++ server/typings/models/video/video.ts | 103 +++++++++++++ server/typings/utils.ts | 14 +- tsconfig.json | 8 +- 176 files changed, 2118 insertions(+), 1133 deletions(-) create mode 100644 server/typings/models/account/account-blocklist.ts create mode 100644 server/typings/models/account/account.ts create mode 100644 server/typings/models/account/actor-follow.ts create mode 100644 server/typings/models/account/actor.ts create mode 100644 server/typings/models/account/avatar.ts create mode 100644 server/typings/models/account/index.d.ts delete mode 100644 server/typings/models/actor-follow.ts delete mode 100644 server/typings/models/actor.ts create mode 100644 server/typings/models/oauth/oauth-client.ts create mode 100644 server/typings/models/oauth/oauth-token.ts create mode 100644 server/typings/models/server/index.d.ts create mode 100644 server/typings/models/server/plugin.ts create mode 100644 server/typings/models/server/server-blocklist.ts create mode 100644 server/typings/models/server/server.ts create mode 100644 server/typings/models/user/index.d.ts create mode 100644 server/typings/models/user/user-notification-setting.ts create mode 100644 server/typings/models/user/user-notification.ts create mode 100644 server/typings/models/user/user-video-history.ts create mode 100644 server/typings/models/user/user.ts delete mode 100644 server/typings/models/video-share.ts create mode 100644 server/typings/models/video/index.d.ts create mode 100644 server/typings/models/video/schedule-video-update.ts create mode 100644 server/typings/models/video/tag.ts create mode 100644 server/typings/models/video/thumbnail.ts create mode 100644 server/typings/models/video/video-abuse.ts create mode 100644 server/typings/models/video/video-blacklist.ts create mode 100644 server/typings/models/video/video-caption.ts create mode 100644 server/typings/models/video/video-change-ownership.ts create mode 100644 server/typings/models/video/video-channels.ts create mode 100644 server/typings/models/video/video-comment.ts create mode 100644 server/typings/models/video/video-file.ts create mode 100644 server/typings/models/video/video-import.ts create mode 100644 server/typings/models/video/video-playlist-element.ts create mode 100644 server/typings/models/video/video-playlist.ts create mode 100644 server/typings/models/video/video-rate.ts create mode 100644 server/typings/models/video/video-redundancy.ts create mode 100644 server/typings/models/video/video-share.ts create mode 100644 server/typings/models/video/video-streaming-playlist.ts create mode 100644 server/typings/models/video/video.ts 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 { } from '../../middlewares' import { getAccountVideoRateValidator, videoCommentGetValidator } from '../../middlewares/validators' import { AccountModel } from '../../models/account/account' -import { ActorModel } from '../../models/activitypub/actor' import { ActorFollowModel } from '../../models/activitypub/actor-follow' import { VideoModel } from '../../models/video/video' import { VideoCommentModel } from '../../models/video/video-comment' @@ -38,6 +37,7 @@ import { buildDislikeActivity } from '../../lib/activitypub/send/send-dislike' import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists' import { VideoPlaylistModel } from '../../models/video/video-playlist' import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' +import { MAccountId, MActorId, MVideo, MVideoAPWithoutCaption } from '@server/typings/models' const activityPubClientRouter = express.Router() @@ -148,7 +148,7 @@ activityPubClientRouter.get('/redundancy/streaming-playlists/:streamingPlaylistT activityPubClientRouter.get('/video-playlists/:playlistId', executeIfActivityPub, - asyncMiddleware(videoPlaylistsGetValidator), + asyncMiddleware(videoPlaylistsGetValidator('all')), asyncMiddleware(videoPlaylistController) ) activityPubClientRouter.get('/video-playlists/:playlistId/:videoId', @@ -208,18 +208,19 @@ function getAccountVideoRate (rateType: VideoRateType) { async function videoController (req: express.Request, res: express.Response) { // We need more attributes - const video = await VideoModel.loadForGetAPI({ id: res.locals.video.id }) + const video = await VideoModel.loadForGetAPI({ id: res.locals.onlyVideoWithRights.id }) as MVideoAPWithoutCaption if (video.url.startsWith(WEBSERVER.URL) === false) return res.redirect(video.url) // We need captions to render AP object - video.VideoCaptions = await VideoCaptionModel.listVideoCaptions(video.id) + const captions = await VideoCaptionModel.listVideoCaptions(video.id) + const videoWithCaptions: MVideoAPWithoutCaption = Object.assign(video, { VideoCaptions: captions }) - const audience = getAudience(video.VideoChannel.Account.Actor, video.privacy === VideoPrivacy.PUBLIC) - const videoObject = audiencify(video.toActivityPubObject(), audience) + const audience = getAudience(videoWithCaptions.VideoChannel.Account.Actor, videoWithCaptions.privacy === VideoPrivacy.PUBLIC) + const videoObject = audiencify(videoWithCaptions.toActivityPubObject(), audience) if (req.path.endsWith('/activity')) { - const data = buildCreateActivity(video.url, video.VideoChannel.Account.Actor, videoObject, audience) + const data = buildCreateActivity(videoWithCaptions.url, video.VideoChannel.Account.Actor, videoObject, audience) return activityPubResponse(activityPubContextify(data), res) } @@ -231,13 +232,13 @@ async function videoAnnounceController (req: express.Request, res: express.Respo if (share.url.startsWith(WEBSERVER.URL) === false) return res.redirect(share.url) - const { activity } = await buildAnnounceWithVideoAudience(share.Actor, share, res.locals.video, undefined) + const { activity } = await buildAnnounceWithVideoAudience(share.Actor, share, res.locals.videoAll, undefined) return activityPubResponse(activityPubContextify(activity), res) } async function videoAnnouncesController (req: express.Request, res: express.Response) { - const video = res.locals.video + const video = res.locals.onlyVideo const handler = async (start: number, count: number) => { const result = await VideoShareModel.listAndCountByVideoId(video.id, start, count) @@ -252,21 +253,21 @@ async function videoAnnouncesController (req: express.Request, res: express.Resp } async function videoLikesController (req: express.Request, res: express.Response) { - const video = res.locals.video + const video = res.locals.onlyVideo const json = await videoRates(req, 'like', video, getVideoLikesActivityPubUrl(video)) return activityPubResponse(activityPubContextify(json), res) } async function videoDislikesController (req: express.Request, res: express.Response) { - const video = res.locals.video + const video = res.locals.onlyVideo const json = await videoRates(req, 'dislike', video, getVideoDislikesActivityPubUrl(video)) return activityPubResponse(activityPubContextify(json), res) } async function videoCommentsController (req: express.Request, res: express.Response) { - const video = res.locals.video + const video = res.locals.onlyVideo const handler = async (start: number, count: number) => { const result = await VideoCommentModel.listAndCountByVideoId(video.id, start, count) @@ -301,7 +302,7 @@ async function videoChannelFollowingController (req: express.Request, res: expre } async function videoCommentController (req: express.Request, res: express.Response) { - const videoComment = res.locals.videoComment + const videoComment = res.locals.videoCommentFull if (videoComment.url.startsWith(WEBSERVER.URL) === false) return res.redirect(videoComment.url) @@ -337,7 +338,7 @@ async function videoRedundancyController (req: express.Request, res: express.Res } async function videoPlaylistController (req: express.Request, res: express.Response) { - const playlist = res.locals.videoPlaylist + const playlist = res.locals.videoPlaylistFull // We need more attributes playlist.OwnerAccount = await AccountModel.load(playlist.ownerAccountId) @@ -358,7 +359,7 @@ async function videoPlaylistElementController (req: express.Request, res: expres // --------------------------------------------------------------------------- -async function actorFollowing (req: express.Request, actor: ActorModel) { +async function actorFollowing (req: express.Request, actor: MActorId) { const handler = (start: number, count: number) => { return ActorFollowModel.listAcceptedFollowingUrlsForApi([ actor.id ], undefined, start, count) } @@ -366,7 +367,7 @@ async function actorFollowing (req: express.Request, actor: ActorModel) { return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page) } -async function actorFollowers (req: express.Request, actor: ActorModel) { +async function actorFollowers (req: express.Request, actor: MActorId) { const handler = (start: number, count: number) => { return ActorFollowModel.listAcceptedFollowerUrlsForAP([ actor.id ], undefined, start, count) } @@ -374,7 +375,7 @@ async function actorFollowers (req: express.Request, actor: ActorModel) { return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page) } -async function actorPlaylists (req: express.Request, account: AccountModel) { +async function actorPlaylists (req: express.Request, account: MAccountId) { const handler = (start: number, count: number) => { return VideoPlaylistModel.listPublicUrlsOfForAP(account.id, start, count) } @@ -382,7 +383,7 @@ async function actorPlaylists (req: express.Request, account: AccountModel) { return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page) } -function videoRates (req: express.Request, rateType: VideoRateType, video: VideoModel, url: string) { +function videoRates (req: express.Request, rateType: VideoRateType, video: MVideo, url: string) { const handler = async (start: number, count: number) => { const result = await AccountVideoRateModel.listAndCountAccountUrlsByVideoId(rateType, video.id, start, count) 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 import { activityPubValidator } from '../../middlewares/validators/activitypub/activity' import { queue } from 'async' import { ActorModel } from '../../models/activitypub/actor' -import { SignatureActorModel } from '../../typings/models' +import { MActorDefault, MActorSignature } from '../../typings/models' const inboxRouter = express.Router() @@ -41,7 +41,8 @@ export { // --------------------------------------------------------------------------- -const inboxQueue = queue<{ activities: Activity[], signatureActor?: SignatureActorModel, inboxActor?: ActorModel }, Error>((task, cb) => { +type QueueParam = { activities: Activity[], signatureActor?: MActorSignature, inboxActor?: MActorDefault } +const inboxQueue = queue((task, cb) => { const options = { signatureActor: task.signatureActor, inboxActor: task.inboxActor } 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' import { buildAnnounceActivity, buildCreateActivity } from '../../lib/activitypub/send' import { buildAudience } from '../../lib/activitypub/audience' import { asyncMiddleware, localAccountValidator, localVideoChannelValidator } from '../../middlewares' -import { AccountModel } from '../../models/account/account' -import { ActorModel } from '../../models/activitypub/actor' import { VideoModel } from '../../models/video/video' import { activityPubResponse } from './utils' -import { VideoChannelModel } from '../../models/video/video-channel' +import { MActorLight } from '@server/typings/models' const outboxRouter = express.Router() @@ -45,14 +43,10 @@ async function outboxController (req: express.Request, res: express.Response) { return activityPubResponse(activityPubContextify(json), res) } -async function buildActivities (actor: ActorModel, start: number, count: number) { +async function buildActivities (actor: MActorLight, start: number, count: number) { const data = await VideoModel.listAllAndSharedByActorForOutbox(actor.id, start, count) const activities: Activity[] = [] - // Avoid too many SQL requests - const actors = data.data.map(v => v.VideoChannel.Account.Actor) - actors.push(actor) - for (const video of data.data) { const byActor = video.VideoChannel.Account.Actor 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 import { logger } from '../../helpers/logger' import { VideoChannelModel } from '../../models/video/video-channel' import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' +import { MChannelAccountDefault, MVideoAccountAllFiles } from '../../typings/models' const searchRouter = express.Router() @@ -84,7 +85,7 @@ async function searchVideoChannelsDB (query: VideoChannelsSearchQuery, res: expr } async function searchVideoChannelURI (search: string, isWebfingerSearch: boolean, res: express.Response) { - let videoChannel: VideoChannelModel + let videoChannel: MChannelAccountDefault let uri = search if (isWebfingerSearch) { @@ -137,7 +138,7 @@ async function searchVideosDB (query: VideosSearchQuery, res: express.Response) } async function searchVideoURI (url: string, res: express.Response) { - let video: VideoModel + let video: MVideoAccountAllFiles // Check if we can fetch a remote video with the URL 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' import { sequelizeTypescript } from '../../../initializers/database' import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' import { UserRegister } from '../../../../shared/models/users/user-register.model' +import { MUser, MUserAccountDefault } from '@server/typings/models' const auditLogger = auditLoggerFactory('users') @@ -359,7 +360,7 @@ function success (req: express.Request, res: express.Response) { res.end() } -async function changeUserBlock (res: express.Response, user: UserModel, block: boolean, reason?: string) { +async function changeUserBlock (res: express.Response, user: MUserAccountDefault, block: boolean, reason?: string) { const oldUserAuditView = new UserAuditView(user.toFormattedJSON()) 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 } async function getUserVideoRating (req: express.Request, res: express.Response) { - const videoId = res.locals.video.id + const videoId = res.locals.videoId.id const accountId = +res.locals.oauth.token.User.Account.id 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 { setDefaultPagination, userHistoryRemoveValidator } from '../../../middlewares' -import { UserModel } from '../../../models/account/user' import { getFormattedObjects } from '../../../helpers/utils' import { UserVideoHistoryModel } from '../../../models/account/user-video-history' import { 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 async function addVideoChannel (req: express.Request, res: express.Response) { const videoChannelInfo: VideoChannelCreate = req.body - const videoChannelCreated: VideoChannelModel = await sequelizeTypescript.transaction(async t => { + const videoChannelCreated = await sequelizeTypescript.transaction(async t => { const account = await AccountModel.load(res.locals.oauth.token.User.Account.id, t) 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' import { CONFIG } from '../../initializers/config' import { sequelizeTypescript } from '../../initializers/database' import { createPlaylistMiniatureFromExisting } from '../../lib/thumbnail' -import { VideoModel } from '../../models/video/video' +import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/typings/models' const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { thumbnailfile: CONFIG.STORAGE.TMP_DIR }) @@ -58,7 +58,7 @@ videoPlaylistRouter.get('/', ) videoPlaylistRouter.get('/:playlistId', - asyncMiddleware(videoPlaylistsGetValidator), + asyncMiddleware(videoPlaylistsGetValidator('summary')), getVideoPlaylist ) @@ -83,7 +83,7 @@ videoPlaylistRouter.delete('/:playlistId', ) videoPlaylistRouter.get('/:playlistId/videos', - asyncMiddleware(videoPlaylistsGetValidator), + asyncMiddleware(videoPlaylistsGetValidator('summary')), paginationValidator, setDefaultPagination, optionalAuthenticate, @@ -140,7 +140,7 @@ async function listVideoPlaylists (req: express.Request, res: express.Response) } function getVideoPlaylist (req: express.Request, res: express.Response) { - const videoPlaylist = res.locals.videoPlaylist + const videoPlaylist = res.locals.videoPlaylistSummary if (videoPlaylist.isOutdated()) { 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) { description: videoPlaylistInfo.description, privacy: videoPlaylistInfo.privacy || VideoPlaylistPrivacy.PRIVATE, ownerAccountId: user.Account.id - }) + }) as MVideoPlaylistFull videoPlaylist.url = getVideoPlaylistActivityPubUrl(videoPlaylist) // We use the UUID, so set the URL after building the object @@ -175,8 +175,8 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) { ? await createPlaylistMiniatureFromExisting(thumbnailField[0].path, videoPlaylist, false) : undefined - const videoPlaylistCreated: VideoPlaylistModel = await sequelizeTypescript.transaction(async t => { - const videoPlaylistCreated = await videoPlaylist.save({ transaction: t }) + const videoPlaylistCreated = await sequelizeTypescript.transaction(async t => { + const videoPlaylistCreated = await videoPlaylist.save({ transaction: t }) as MVideoPlaylistFull if (thumbnailModel) { thumbnailModel.automaticallyGenerated = false @@ -201,7 +201,7 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) { } async function updateVideoPlaylist (req: express.Request, res: express.Response) { - const videoPlaylistInstance = res.locals.videoPlaylist + const videoPlaylistInstance = res.locals.videoPlaylistFull const videoPlaylistFieldsSave = videoPlaylistInstance.toJSON() const videoPlaylistInfoToUpdate = req.body as VideoPlaylistUpdate @@ -275,7 +275,7 @@ async function updateVideoPlaylist (req: express.Request, res: express.Response) } async function removeVideoPlaylist (req: express.Request, res: express.Response) { - const videoPlaylistInstance = res.locals.videoPlaylist + const videoPlaylistInstance = res.locals.videoPlaylistSummary await sequelizeTypescript.transaction(async t => { await videoPlaylistInstance.destroy({ transaction: t }) @@ -290,10 +290,10 @@ async function removeVideoPlaylist (req: express.Request, res: express.Response) async function addVideoInPlaylist (req: express.Request, res: express.Response) { const body: VideoPlaylistElementCreate = req.body - const videoPlaylist = res.locals.videoPlaylist - const video = res.locals.video + const videoPlaylist = res.locals.videoPlaylistFull + const video = res.locals.onlyVideo - const playlistElement: VideoPlaylistElementModel = await sequelizeTypescript.transaction(async t => { + const playlistElement = await sequelizeTypescript.transaction(async t => { const position = await VideoPlaylistElementModel.getNextPositionOf(videoPlaylist.id, t) const playlistElement = await VideoPlaylistElementModel.create({ @@ -330,7 +330,7 @@ async function addVideoInPlaylist (req: express.Request, res: express.Response) async function updateVideoPlaylistElement (req: express.Request, res: express.Response) { const body: VideoPlaylistElementUpdate = req.body - const videoPlaylist = res.locals.videoPlaylist + const videoPlaylist = res.locals.videoPlaylistFull const videoPlaylistElement = res.locals.videoPlaylistElement const playlistElement: VideoPlaylistElementModel = await sequelizeTypescript.transaction(async t => { @@ -354,7 +354,7 @@ async function updateVideoPlaylistElement (req: express.Request, res: express.Re async function removeVideoFromPlaylist (req: express.Request, res: express.Response) { const videoPlaylistElement = res.locals.videoPlaylistElement - const videoPlaylist = res.locals.videoPlaylist + const videoPlaylist = res.locals.videoPlaylistFull const positionToDelete = videoPlaylistElement.position await sequelizeTypescript.transaction(async t => { @@ -381,7 +381,7 @@ async function removeVideoFromPlaylist (req: express.Request, res: express.Respo } async function reorderVideosPlaylist (req: express.Request, res: express.Response) { - const videoPlaylist = res.locals.videoPlaylist + const videoPlaylist = res.locals.videoPlaylistFull const body: VideoPlaylistReorder = req.body const start: number = body.startPosition @@ -434,7 +434,7 @@ async function reorderVideosPlaylist (req: express.Request, res: express.Respons } async function getVideoPlaylistVideos (req: express.Request, res: express.Response) { - const videoPlaylistInstance = res.locals.videoPlaylist + const videoPlaylistInstance = res.locals.videoPlaylistSummary const user = res.locals.oauth ? res.locals.oauth.token.User : undefined const server = await getServerActor() @@ -453,7 +453,7 @@ async function getVideoPlaylistVideos (req: express.Request, res: express.Respon return res.json(getFormattedObjects(resultList.data, resultList.total, options)) } -async function regeneratePlaylistThumbnail (videoPlaylist: VideoPlaylistModel) { +async function regeneratePlaylistThumbnail (videoPlaylist: MVideoPlaylistThumbnail) { await videoPlaylist.Thumbnail.destroy() videoPlaylist.Thumbnail = null @@ -461,7 +461,7 @@ async function regeneratePlaylistThumbnail (videoPlaylist: VideoPlaylistModel) { if (firstElement) await generateThumbnailForPlaylist(videoPlaylist, firstElement.Video) } -async function generateThumbnailForPlaylist (videoPlaylist: VideoPlaylistModel, video: VideoModel) { +async function generateThumbnailForPlaylist (videoPlaylist: MVideoPlaylistThumbnail, video: MVideoThumbnail) { logger.info('Generating default thumbnail to playlist %s.', videoPlaylist.url) 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' import { auditLoggerFactory, VideoAbuseAuditView } from '../../../helpers/audit-logger' import { Notifier } from '../../../lib/notifier' import { sendVideoAbuse } from '../../../lib/activitypub/send/send-flag' +import { MVideoAbuseAccountVideo } from '../../../typings/models/video' const auditLogger = auditLoggerFactory('abuse') const abuseVideoRouter = express.Router() @@ -94,10 +95,10 @@ async function deleteVideoAbuse (req: express.Request, res: express.Response) { } async function reportVideoAbuse (req: express.Request, res: express.Response) { - const videoInstance = res.locals.video + const videoInstance = res.locals.videoAll const body: VideoAbuseCreate = req.body - const videoAbuse: VideoAbuseModel = await sequelizeTypescript.transaction(async t => { + const videoAbuse = await sequelizeTypescript.transaction(async t => { const reporterAccount = await AccountModel.load(res.locals.oauth.token.User.Account.id, t) const abuseToCreate = { @@ -107,7 +108,7 @@ async function reportVideoAbuse (req: express.Request, res: express.Response) { state: VideoAbuseState.PENDING } - const videoAbuseInstance = await VideoAbuseModel.create(abuseToCreate, { transaction: t }) + const videoAbuseInstance: MVideoAbuseAccountVideo = await VideoAbuseModel.create(abuseToCreate, { transaction: t }) videoAbuseInstance.Video = videoInstance videoAbuseInstance.Account = reporterAccount 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 @@ import * as express from 'express' -import { VideoBlacklist, UserRight, VideoBlacklistCreate, VideoBlacklistType } from '../../../../shared' +import { UserRight, VideoBlacklistCreate, VideoBlacklistType } from '../../../../shared' import { logger } from '../../../helpers/logger' import { getFormattedObjects } from '../../../helpers/utils' import { @@ -11,15 +11,16 @@ import { setBlacklistSort, setDefaultPagination, videosBlacklistAddValidator, + videosBlacklistFiltersValidator, videosBlacklistRemoveValidator, - videosBlacklistUpdateValidator, - videosBlacklistFiltersValidator + videosBlacklistUpdateValidator } from '../../../middlewares' import { VideoBlacklistModel } from '../../../models/video/video-blacklist' import { sequelizeTypescript } from '../../../initializers' import { Notifier } from '../../../lib/notifier' import { sendDeleteVideo } from '../../../lib/activitypub/send' import { federateVideoIfNeeded } from '../../../lib/activitypub' +import { MVideoBlacklistVideo } from '@server/typings/models' const blacklistRouter = express.Router() @@ -64,7 +65,7 @@ export { // --------------------------------------------------------------------------- async function addVideoToBlacklist (req: express.Request, res: express.Response) { - const videoInstance = res.locals.video + const videoInstance = res.locals.videoAll const body: VideoBlacklistCreate = req.body const toCreate = { @@ -74,7 +75,7 @@ async function addVideoToBlacklist (req: express.Request, res: express.Response) type: VideoBlacklistType.MANUAL } - const blacklist = await VideoBlacklistModel.create(toCreate) + const blacklist: MVideoBlacklistVideo = await VideoBlacklistModel.create(toCreate) blacklist.Video = videoInstance if (body.unfederate === true) { @@ -83,7 +84,7 @@ async function addVideoToBlacklist (req: express.Request, res: express.Response) Notifier.Instance.notifyOnVideoBlacklist(blacklist) - logger.info('Video %s blacklisted.', res.locals.video.uuid) + logger.info('Video %s blacklisted.', videoInstance.uuid) return res.type('json').status(204).end() } @@ -108,7 +109,7 @@ async function listBlacklist (req: express.Request, res: express.Response) { async function removeVideoFromBlacklistController (req: express.Request, res: express.Response) { const videoBlacklist = res.locals.videoBlacklist - const video = res.locals.video + const video = res.locals.videoAll const videoBlacklistType = await sequelizeTypescript.transaction(async t => { const unfederated = videoBlacklist.unfederated @@ -135,7 +136,7 @@ async function removeVideoFromBlacklistController (req: express.Request, res: ex Notifier.Instance.notifyOnNewVideoIfNeeded(video) } - logger.info('Video %s removed from blacklist.', res.locals.video.uuid) + logger.info('Video %s removed from blacklist.', video.uuid) return res.type('json').status(204).end() } 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' import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' import { CONFIG } from '../../../initializers/config' import { sequelizeTypescript } from '../../../initializers/database' +import { MVideoCaptionVideo } from '@server/typings/models' const reqVideoCaptionAdd = createReqFiles( [ 'captionfile' ], @@ -46,19 +47,19 @@ export { // --------------------------------------------------------------------------- async function listVideoCaptions (req: express.Request, res: express.Response) { - const data = await VideoCaptionModel.listVideoCaptions(res.locals.video.id) + const data = await VideoCaptionModel.listVideoCaptions(res.locals.videoId.id) return res.json(getFormattedObjects(data, data.length)) } async function addVideoCaption (req: express.Request, res: express.Response) { const videoCaptionPhysicalFile = req.files['captionfile'][0] - const video = res.locals.video + const video = res.locals.videoAll const videoCaption = new VideoCaptionModel({ videoId: video.id, language: req.params.captionLanguage - }) + }) as MVideoCaptionVideo videoCaption.Video = video // Move physical file @@ -75,7 +76,7 @@ async function addVideoCaption (req: express.Request, res: express.Response) { } async function deleteVideoCaption (req: express.Request, res: express.Response) { - const video = res.locals.video + const video = res.locals.videoAll const videoCaption = res.locals.videoCaption 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 '../../. import { AccountModel } from '../../../models/account/account' import { Notifier } from '../../../lib/notifier' import { Hooks } from '../../../lib/plugins/hooks' -import { ActorModel } from '../../../models/activitypub/actor' -import { VideoChannelModel } from '../../../models/video/video-channel' -import { VideoModel } from '../../../models/video/video' import { sendDeleteVideoComment } from '../../../lib/activitypub/send' const auditLogger = auditLoggerFactory('comments') @@ -75,7 +72,7 @@ export { // --------------------------------------------------------------------------- async function listVideoThreads (req: express.Request, res: express.Response) { - const video = res.locals.video + const video = res.locals.onlyVideo const user = res.locals.oauth ? res.locals.oauth.token.User : undefined let resultList: ResultList @@ -86,7 +83,7 @@ async function listVideoThreads (req: express.Request, res: express.Response) { start: req.query.start, count: req.query.count, sort: req.query.sort, - user: user + user }, 'filter:api.video-threads.list.params') resultList = await Hooks.wrapPromiseFun( @@ -105,7 +102,7 @@ async function listVideoThreads (req: express.Request, res: express.Response) { } async function listVideoThreadComments (req: express.Request, res: express.Response) { - const video = res.locals.video + const video = res.locals.onlyVideo const user = res.locals.oauth ? res.locals.oauth.token.User : undefined let resultList: ResultList @@ -141,7 +138,7 @@ async function addVideoCommentThread (req: express.Request, res: express.Respons return createVideoComment({ text: videoCommentInfo.text, inReplyToComment: null, - video: res.locals.video, + video: res.locals.videoAll, account }, t) }) @@ -164,8 +161,8 @@ async function addVideoCommentReply (req: express.Request, res: express.Response return createVideoComment({ text: videoCommentInfo.text, - inReplyToComment: res.locals.videoComment, - video: res.locals.video, + inReplyToComment: res.locals.videoCommentFull, + video: res.locals.videoAll, account }, t) }) @@ -179,7 +176,7 @@ async function addVideoCommentReply (req: express.Request, res: express.Response } async function removeVideoComment (req: express.Request, res: express.Response) { - const videoCommentInstance = res.locals.videoComment + const videoCommentInstance = res.locals.videoCommentFull await sequelizeTypescript.transaction(async t => { 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' import { JobQueue } from '../../../lib/job-queue/job-queue' import { join } from 'path' import { isArray } from '../../../helpers/custom-validators/misc' -import { VideoChannelModel } from '../../../models/video/video-channel' import * as Bluebird from 'bluebird' import * as parseTorrent from 'parse-torrent' import { getSecureTorrentName } from '../../../helpers/utils' @@ -25,8 +24,14 @@ import { CONFIG } from '../../../initializers/config' import { sequelizeTypescript } from '../../../initializers/database' import { createVideoMiniatureFromExisting } from '../../../lib/thumbnail' import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' -import { ThumbnailModel } from '../../../models/video/thumbnail' -import { UserModel } from '../../../models/account/user' +import { + MChannelActorAccountDefault, + MThumbnail, + MUser, + MVideoThumbnailAccountDefault, + MVideoWithBlacklistLight +} from '@server/typings/models' +import { MVideoImport, MVideoImportVideo } from '@server/typings/models/video/video-import' const auditLogger = auditLoggerFactory('video-imports') const videoImportsRouter = express.Router() @@ -225,28 +230,28 @@ async function processPreview (req: express.Request, video: VideoModel) { } function insertIntoDB (parameters: { - video: VideoModel, - thumbnailModel: ThumbnailModel, - previewModel: ThumbnailModel, - videoChannel: VideoChannelModel, + video: MVideoThumbnailAccountDefault, + thumbnailModel: MThumbnail, + previewModel: MThumbnail, + videoChannel: MChannelActorAccountDefault, tags: string[], - videoImportAttributes: Partial, - user: UserModel -}): Bluebird { + videoImportAttributes: Partial, + user: MUser +}): Bluebird { const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters return sequelizeTypescript.transaction(async t => { const sequelizeOptions = { transaction: t } // Save video object in database - const videoCreated = await video.save(sequelizeOptions) + const videoCreated = await video.save(sequelizeOptions) as (MVideoThumbnailAccountDefault & MVideoWithBlacklistLight) videoCreated.VideoChannel = videoChannel if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) if (previewModel) await videoCreated.addAndSaveThumbnail(previewModel, t) await autoBlacklistVideoIfNeeded({ - video, + video: videoCreated, user, notify: false, isRemote: false, @@ -259,16 +264,13 @@ function insertIntoDB (parameters: { const tagInstances = await TagModel.findOrCreateTags(tags, t) await videoCreated.$set('Tags', tagInstances, sequelizeOptions) - videoCreated.Tags = tagInstances - } else { - videoCreated.Tags = [] } // Create video import object in database const videoImport = await VideoImportModel.create( Object.assign({ videoId: videoCreated.id }, videoImportAttributes), sequelizeOptions - ) + ) as MVideoImportVideo videoImport.Video = videoCreated 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 '../../ import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' import { VideoTranscodingPayload } from '../../../lib/job-queue/handlers/video-transcoding' import { Hooks } from '../../../lib/plugins/hooks' +import { MVideoFullLight } from '@server/typings/models' const auditLogger = auditLoggerFactory('videos') const videosRouter = express.Router() @@ -238,7 +239,7 @@ async function addVideo (req: express.Request, res: express.Response) { const { videoCreated } = await sequelizeTypescript.transaction(async t => { const sequelizeOptions = { transaction: t } - const videoCreated = await video.save(sequelizeOptions) + const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight await videoCreated.addAndSaveThumbnail(thumbnailModel, t) await videoCreated.addAndSaveThumbnail(previewModel, t) @@ -318,7 +319,7 @@ async function addVideo (req: express.Request, res: express.Response) { } async function updateVideo (req: express.Request, res: express.Response) { - const videoInstance = res.locals.video + const videoInstance = res.locals.videoAll const videoFieldsSave = videoInstance.toJSON() const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON()) const videoInfoToUpdate: VideoUpdate = req.body @@ -371,7 +372,7 @@ async function updateVideo (req: express.Request, res: express.Response) { } } - const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) + const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) as MVideoFullLight if (thumbnailModel) await videoInstanceUpdated.addAndSaveThumbnail(thumbnailModel, t) if (previewModel) await videoInstanceUpdated.addAndSaveThumbnail(previewModel, t) @@ -447,7 +448,7 @@ async function getVideo (req: express.Request, res: express.Response) { const video = await Hooks.wrapPromiseFun( VideoModel.loadForGetAPI, - { id: res.locals.video.id, userId }, + { id: res.locals.onlyVideoWithRights.id, userId }, 'filter:api.video.get.result' ) @@ -460,7 +461,7 @@ async function getVideo (req: express.Request, res: express.Response) { } async function viewVideo (req: express.Request, res: express.Response) { - const videoInstance = res.locals.video + const videoInstance = res.locals.videoAll const ip = req.ip const exists = await Redis.Instance.doesVideoIPViewExist(ip, videoInstance.uuid) @@ -483,7 +484,7 @@ async function viewVideo (req: express.Request, res: express.Response) { } async function getVideoDescription (req: express.Request, res: express.Response) { - const videoInstance = res.locals.video + const videoInstance = res.locals.videoAll let description = '' if (videoInstance.isOwned()) { @@ -522,7 +523,7 @@ async function listVideos (req: express.Request, res: express.Response) { } async function removeVideo (req: express.Request, res: express.Response) { - const videoInstance = res.locals.video + const videoInstance = res.locals.videoAll await sequelizeTypescript.transaction(async t => { 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' import { changeVideoChannelShare } from '../../../lib/activitypub' import { sendUpdateVideo } from '../../../lib/activitypub/send' import { VideoModel } from '../../../models/video/video' +import { MVideoFullLight } from '@server/typings/models' const ownershipVideoRouter = express.Router() @@ -56,7 +57,7 @@ export { // --------------------------------------------------------------------------- async function giveVideoOwnership (req: express.Request, res: express.Response) { - const videoInstance = res.locals.video + const videoInstance = res.locals.videoAll const initiatorAccountId = res.locals.oauth.token.User.Account.id const nextOwner = res.locals.nextOwner @@ -107,7 +108,7 @@ async function acceptOwnership (req: express.Request, res: express.Response) { targetVideo.channelId = channel.id - const targetVideoUpdated = await targetVideo.save({ transaction: t }) + const targetVideoUpdated = await targetVideo.save({ transaction: t }) as MVideoFullLight targetVideoUpdated.VideoChannel = channel 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 { async function rateVideo (req: express.Request, res: express.Response) { const body: UserVideoRateUpdate = req.body const rateType = body.rating - const videoInstance = res.locals.video + const videoInstance = res.locals.videoAll const userAccount = res.locals.oauth.token.User.Account 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) { const user = res.locals.oauth.token.User const body: UserWatchingVideo = req.body - const { id: videoId } = res.locals.video as { id: number } + const { id: videoId } = res.locals.videoId await UserVideoHistoryModel.upsert({ 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 { async function generateVideoCommentsFeed (req: express.Request, res: express.Response) { const start = 0 - const video = res.locals.video + const video = res.locals.videoAll const videoId: number = video ? video.id : undefined 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 { // --------------------------------------------------------------------------- function generateOEmbed (req: express.Request, res: express.Response) { - const video = res.locals.video + const video = res.locals.videoAll const webserverUrl = WEBSERVER.URL const maxHeight = parseInt(req.query.maxheight, 10) 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) { return res.send(json).end() } -async function downloadTorrent (req: express.Request, res: express.Response, next: express.NextFunction) { +async function downloadTorrent (req: express.Request, res: express.Response) { const { video, videoFile } = getVideoAndFile(req, res) if (!videoFile) return res.status(404).end() return res.download(video.getTorrentFilePath(videoFile), `${video.name}-${videoFile.resolution}p.torrent`) } -async function downloadVideoFile (req: express.Request, res: express.Response, next: express.NextFunction) { +async function downloadVideoFile (req: express.Request, res: express.Response) { const { video, videoFile } = getVideoAndFile(req, res) if (!videoFile) return res.status(404).end() @@ -242,7 +242,7 @@ async function downloadVideoFile (req: express.Request, res: express.Response, n function getVideoAndFile (req: express.Request, res: express.Response) { const resolution = parseInt(req.params.resolution, 10) - const video = res.locals.video + const video = res.locals.videoAll const videoFile = video.VideoFiles.find(f => f.resolution === resolution) 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 { // --------------------------------------------------------------------------- function webfingerController (req: express.Request, res: express.Response) { - const actor = res.locals.actor + const actor = res.locals.actorFull const json = { 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' import { signJsonLDObject } from './peertube-crypto' import { pageToStartAndCount } from './core-utils' import { parse } from 'url' +import { MActor } from '../typings/models' function activityPubContextify (data: T) { return Object.assign(data, { @@ -143,7 +144,7 @@ async function activityPubCollectionPagination (baseUrl: string, handler: Activi } -function buildSignedActivity (byActor: ActorModel, data: Object) { +function buildSignedActivity (byActor: MActor, data: Object) { const activity = activityPubContextify(data) return signJsonLDObject(byActor, activity) as Promise 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 @@ import { ActorModel } from '../models/activitypub/actor' +import * as Bluebird from 'bluebird' +import { MActorFull, MActorAccountChannelId } from '../typings/models' -type ActorFetchByUrlType = 'all' | 'actor-and-association-ids' -function fetchActorByUrl (url: string, fetchType: ActorFetchByUrlType) { +type ActorFetchByUrlType = 'all' | 'association-ids' + +function fetchActorByUrl (url: string, fetchType: ActorFetchByUrlType): Bluebird { if (fetchType === 'all') return ActorModel.loadByUrlAndPopulateAccountAndChannel(url) - if (fetchType === 'actor-and-association-ids') return ActorModel.loadByUrl(url) + if (fetchType === 'association-ids') return ActorModel.loadByUrl(url) } export { 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 @@ import { join } from 'path' import { CONFIG } from '../initializers/config' -import { VideoCaptionModel } from '../models/video/video-caption' import * as srt2vtt from 'srt-to-vtt' -import { createReadStream, createWriteStream, remove, move } from 'fs-extra' +import { createReadStream, createWriteStream, move, remove } from 'fs-extra' +import { MVideoCaption } from '@server/typings/models' -async function moveAndProcessCaptionFile (physicalFile: { filename: string, path: string }, videoCaption: VideoCaptionModel) { +async function moveAndProcessCaptionFile (physicalFile: { filename: string, path: string }, videoCaption: MVideoCaption) { const videoCaptionsDir = CONFIG.STORAGE.CAPTIONS_DIR const destination = join(videoCaptionsDir, videoCaption.getCaptionName()) 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 @@ import { Response } from 'express' -import * as validator from 'validator' import { VideoChangeOwnershipModel } from '../../models/video/video-change-ownership' -import { UserModel } from '../../models/account/user' +import { MVideoChangeOwnershipFull } from '@server/typings/models/video/video-change-ownership' +import { MUserId } from '@server/typings/models' -export async function doesChangeVideoOwnershipExist (id: string, res: Response): Promise { - const videoChangeOwnership = await loadVideoChangeOwnership(id) +export async function doesChangeVideoOwnershipExist (id: number, res: Response) { + const videoChangeOwnership = await VideoChangeOwnershipModel.load(id) if (!videoChangeOwnership) { res.status(404) @@ -18,19 +18,7 @@ export async function doesChangeVideoOwnershipExist (id: string, res: Response): return true } -async function loadVideoChangeOwnership (id: string): Promise { - if (validator.isInt(id)) { - return VideoChangeOwnershipModel.load(parseInt(id, 10)) - } - - return undefined -} - -export function checkUserCanTerminateOwnershipChange ( - user: UserModel, - videoChangeOwnership: VideoChangeOwnershipModel, - res: Response -): boolean { +export function checkUserCanTerminateOwnershipChange (user: MUserId, videoChangeOwnership: MVideoChangeOwnershipFull, res: Response) { if (videoChangeOwnership.NextOwner.userId === user.id) { return true } 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 @@ import { Response } from 'express' import { AccountModel } from '../../models/account/account' import * as Bluebird from 'bluebird' +import { MAccountDefault } from '../../typings/models' function doesAccountIdExist (id: number, res: Response, sendNotFound = true) { const promise = AccountModel.load(id) @@ -15,10 +16,12 @@ function doesLocalAccountNameExist (name: string, res: Response, sendNotFound = } function doesAccountNameWithHostExist (nameWithDomain: string, res: Response, sendNotFound = true) { - return doesAccountExist(AccountModel.loadByNameWithHost(nameWithDomain), res, sendNotFound) + const promise = AccountModel.loadByNameWithHost(nameWithDomain) + + return doesAccountExist(promise, res, sendNotFound) } -async function doesAccountExist (p: Bluebird, res: Response, sendNotFound: boolean) { +async function doesAccountExist (p: Bluebird, res: Response, sendNotFound: boolean) { const account = await p 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 @@ -import * as express from 'express' -import { VideoChannelModel } from '../../models/video/video-channel' +import { Response } from 'express' +import { VideoAbuseModel } from '../../models/video/video-abuse' -async function doesLocalVideoChannelNameExist (name: string, res: express.Response) { - const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name) +async function doesVideoAbuseExist (abuseId: number, videoId: number, res: Response) { + const videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, videoId) - return processVideoChannelExist(videoChannel, res) -} - -async function doesVideoChannelIdExist (id: number, res: express.Response) { - const videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id) - - return processVideoChannelExist(videoChannel, res) -} - -async function doesVideoChannelNameWithHostExist (nameWithDomain: string, res: express.Response) { - const videoChannel = await VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithDomain) - - return processVideoChannelExist(videoChannel, res) -} - -// --------------------------------------------------------------------------- - -export { - doesLocalVideoChannelNameExist, - doesVideoChannelIdExist, - doesVideoChannelNameWithHostExist -} - -function processVideoChannelExist (videoChannel: VideoChannelModel, res: express.Response) { - if (!videoChannel) { + if (videoAbuse === null) { res.status(404) - .json({ error: 'Video channel not found' }) + .json({ error: 'Video abuse not found' }) .end() return false } - res.locals.videoChannel = videoChannel + res.locals.videoAbuse = videoAbuse return true } + +// --------------------------------------------------------------------------- + +export { + doesVideoAbuseExist +} 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 @@ -import { VideoModel } from '../../models/video/video' import { Response } from 'express' import { VideoCaptionModel } from '../../models/video/video-caption' +import { MVideoId } from '@server/typings/models' -async function doesVideoCaptionExist (video: VideoModel, language: string, res: Response) { +async function doesVideoCaptionExist (video: MVideoId, language: string, res: Response) { const videoCaption = await VideoCaptionModel.loadByVideoIdAndLanguage(video.id, language) 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 @@ -import { Response } from 'express' -import { VideoAbuseModel } from '../../models/video/video-abuse' +import * as express from 'express' +import { VideoChannelModel } from '../../models/video/video-channel' +import { MChannelActorAccountDefault } from '../../typings/models' -async function doesVideoAbuseExist (abuseId: number, videoId: number, res: Response) { - const videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, videoId) +async function doesLocalVideoChannelNameExist (name: string, res: express.Response) { + const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name) - if (videoAbuse === null) { - res.status(404) - .json({ error: 'Video abuse not found' }) - .end() + return processVideoChannelExist(videoChannel, res) +} - return false - } +async function doesVideoChannelIdExist (id: number, res: express.Response) { + const videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id) - res.locals.videoAbuse = videoAbuse - return true + return processVideoChannelExist(videoChannel, res) +} + +async function doesVideoChannelNameWithHostExist (nameWithDomain: string, res: express.Response) { + const videoChannel = await VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithDomain) + + return processVideoChannelExist(videoChannel, res) } // --------------------------------------------------------------------------- export { - doesVideoAbuseExist + doesLocalVideoChannelNameExist, + doesVideoChannelIdExist, + doesVideoChannelNameWithHostExist +} + +function processVideoChannelExist (videoChannel: MChannelActorAccountDefault, res: express.Response) { + if (!videoChannel) { + res.status(404) + .json({ error: 'Video channel not found' }) + .end() + + return false + } + + res.locals.videoChannel = videoChannel + return true } 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 @@ import * as express from 'express' import { VideoPlaylistModel } from '../../models/video/video-playlist' +import { MVideoPlaylist } from '../../typings/models/video/video-playlist' -async function doesVideoPlaylistExist (id: number | string, res: express.Response, fetchType: 'summary' | 'all' = 'summary') { - const videoPlaylist = fetchType === 'summary' - ? await VideoPlaylistModel.loadWithAccountAndChannelSummary(id, undefined) - : await VideoPlaylistModel.loadWithAccountAndChannel(id, undefined) +export type VideoPlaylistFetchType = 'summary' | 'all' +async function doesVideoPlaylistExist (id: number | string, res: express.Response, fetchType: VideoPlaylistFetchType = 'summary') { + if (fetchType === 'summary') { + const videoPlaylist = await VideoPlaylistModel.loadWithAccountAndChannelSummary(id, undefined) + res.locals.videoPlaylistSummary = videoPlaylist + return handleVideoPlaylist(videoPlaylist, res) + } + + const videoPlaylist = await VideoPlaylistModel.loadWithAccountAndChannel(id, undefined) + res.locals.videoPlaylistFull = videoPlaylist + + return handleVideoPlaylist(videoPlaylist, res) +} + +// --------------------------------------------------------------------------- + +export { + doesVideoPlaylistExist +} + +// --------------------------------------------------------------------------- + +function handleVideoPlaylist (videoPlaylist: MVideoPlaylist, res: express.Response) { if (!videoPlaylist) { res.status(404) .json({ error: 'Video playlist not found' }) @@ -14,12 +34,5 @@ async function doesVideoPlaylistExist (id: number | string, res: express.Respons return false } - res.locals.videoPlaylist = videoPlaylist return true } - -// --------------------------------------------------------------------------- - -export { - doesVideoPlaylistExist -} 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 @@ import { Response } from 'express' import { fetchVideo, VideoFetchType } from '../video' -import { UserModel } from '../../models/account/user' import { UserRight } from '../../../shared/models/users' import { VideoChannelModel } from '../../models/video/video-channel' -import { VideoModel } from '../../models/video/video' +import { MUser, MUserAccountId, MVideoAccountLight, MVideoFullLight, MVideoWithRights } from '@server/typings/models' async function doesVideoExist (id: number | string, res: Response, fetchType: VideoFetchType = 'all') { 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 return false } - if (fetchType !== 'none') res.locals.video = video + switch (fetchType) { + case 'all': + res.locals.videoAll = video as MVideoFullLight + break + + case 'id': + res.locals.videoId = video + break + + case 'only-video': + res.locals.onlyVideo = video + break + + case 'only-video-with-rights': + res.locals.onlyVideoWithRights = video as MVideoWithRights + break + } + return true } -async function doesVideoChannelOfAccountExist (channelId: number, user: UserModel, res: Response) { +async function doesVideoChannelOfAccountExist (channelId: number, user: MUserAccountId, res: Response) { if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) { const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId) if (videoChannel === null) { @@ -50,7 +66,7 @@ async function doesVideoChannelOfAccountExist (channelId: number, user: UserMode return true } -function checkUserCanManageVideo (user: UserModel, video: VideoModel, right: UserRight, res: Response) { +function checkUserCanManageVideo (user: MUser, video: MVideoAccountLight, right: UserRight, res: Response) { // Retrieve the user who did the request if (video.isOwned() === false) { 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' import { createVerify } from 'crypto' import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils' import * as bcrypt from 'bcrypt' +import { MActor } from '../typings/models' const bcryptComparePromise = promisify2(bcrypt.compare) const bcryptGenSaltPromise = promisify1(bcrypt.genSalt) @@ -46,7 +47,7 @@ function isHTTPSignatureDigestValid (rawBody: Buffer, req: Request): boolean { return true } -function isHTTPSignatureVerified (httpSignatureParsed: any, actor: ActorModel): boolean { +function isHTTPSignatureVerified (httpSignatureParsed: any, actor: MActor): boolean { return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true } @@ -56,7 +57,7 @@ function parseHTTPSignature (req: Request, clockSkew?: number) { // JSONLD -async function isJsonLDSignatureVerified (fromActor: ActorModel, signedDocument: any): Promise { +async function isJsonLDSignatureVerified (fromActor: MActor, signedDocument: any): Promise { if (signedDocument.signature.type === 'RsaSignature2017') { // Mastodon algorithm const res = await isJsonLDRSA2017Verified(fromActor, signedDocument) @@ -93,7 +94,7 @@ async function isJsonLDSignatureVerified (fromActor: ActorModel, signedDocument: } // Backward compatibility with "other" implementations -async function isJsonLDRSA2017Verified (fromActor: ActorModel, signedDocument: any) { +async function isJsonLDRSA2017Verified (fromActor: MActor, signedDocument: any) { function hash (obj: any): Promise { return jsonld.promises .normalize(obj, { @@ -130,7 +131,7 @@ async function isJsonLDRSA2017Verified (fromActor: ActorModel, signedDocument: a return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64') } -function signJsonLDObject (byActor: ActorModel, data: any) { +function signJsonLDObject (byActor: MActor, data: any) { const options = { privateKeyPem: byActor.privateKey, 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 @@ import { VideoModel } from '../models/video/video' +import * as Bluebird from 'bluebird' +import { MVideoAccountAllFiles, MVideoFullLight, MVideoThumbnail, MVideoWithRights, MVideoIdThumbnail } from '@server/typings/models' +import { Response } from 'express' type VideoFetchType = 'all' | 'only-video' | 'only-video-with-rights' | 'id' | 'none' -function fetchVideo (id: number | string, fetchType: VideoFetchType, userId?: number) { +function fetchVideo (id: number | string, fetchType: 'all', userId?: number): Bluebird +function fetchVideo (id: number | string, fetchType: 'only-video', userId?: number): Bluebird +function fetchVideo (id: number | string, fetchType: 'only-video-with-rights', userId?: number): Bluebird +function fetchVideo (id: number | string, fetchType: 'id' | 'none', userId?: number): Bluebird +function fetchVideo ( + id: number | string, + fetchType: VideoFetchType, + userId?: number +): Bluebird +function fetchVideo ( + id: number | string, + fetchType: VideoFetchType, + userId?: number +): Bluebird { if (fetchType === 'all') return VideoModel.loadAndPopulateAccountAndServerAndTags(id, undefined, userId) if (fetchType === 'only-video-with-rights') return VideoModel.loadWithRights(id) @@ -13,15 +29,24 @@ function fetchVideo (id: number | string, fetchType: VideoFetchType, userId?: nu } type VideoFetchByUrlType = 'all' | 'only-video' -function fetchVideoByUrl (url: string, fetchType: VideoFetchByUrlType) { + +function fetchVideoByUrl (url: string, fetchType: 'all'): Bluebird +function fetchVideoByUrl (url: string, fetchType: 'only-video'): Bluebird +function fetchVideoByUrl (url: string, fetchType: VideoFetchByUrlType): Bluebird | Bluebird +function fetchVideoByUrl (url: string, fetchType: VideoFetchByUrlType): Bluebird | Bluebird { if (fetchType === 'all') return VideoModel.loadByUrlAndPopulateAccount(url) if (fetchType === 'only-video') return VideoModel.loadByUrl(url) } +function getVideo (res: Response) { + return res.locals.videoAll || res.locals.onlyVideo || res.locals.onlyVideoWithRights || res.locals.videoId +} + export { VideoFetchType, VideoFetchByUrlType, fetchVideo, + getVideo, fetchVideoByUrl } 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' import { isTestInstance } from './core-utils' import { isActivityPubUrlValid } from './custom-validators/activitypub/misc' import { WEBSERVER } from '../initializers/constants' +import { MActorFull } from '../typings/models' const webfinger = new WebFinger({ webfist_fallback: false, @@ -17,7 +18,7 @@ async function loadActorUrlOrGetFromWebfinger (uriArg: string) { const uri = uriArg.startsWith('@') ? uriArg.slice(1) : uriArg const [ name, host ] = uri.split('@') - let actor: ActorModel + let actor: MActorFull if (!host || host === WEBSERVER.HOST) { 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' import { getServerActor } from '../../helpers/utils' import { ActorFetchByUrlType, fetchActorByUrl } from '../../helpers/actor' import { sequelizeTypescript } from '../../initializers/database' +import { + MAccount, + MActor, + MActorAccountChannelId, + MActorAccountId, + MActorDefault, + MActorFull, + MActorId, + MActorAccountChannelIdActor, + MChannel, + MActorFullActor, MAccountActorDefault, MChannelActorDefault, MChannelActorAccountDefault +} from '../../typings/models' // Set account keys, this could be long so process after the account creation and do not block the client -function setAsyncActorKeys (actor: ActorModel) { +function setAsyncActorKeys (actor: MActor) { return createPrivateAndPublicKeys() .then(({ publicKey, privateKey }) => { - actor.set('publicKey', publicKey) - actor.set('privateKey', privateKey) + actor.publicKey = publicKey + actor.privateKey = privateKey return actor.save() }) .catch(err => { @@ -37,12 +49,26 @@ function setAsyncActorKeys (actor: ActorModel) { }) } +function getOrCreateActorAndServerAndModel ( + activityActor: string | ActivityPubActor, + fetchType: 'all', + recurseIfNeeded?: boolean, + updateCollections?: boolean +): Promise + +function getOrCreateActorAndServerAndModel ( + activityActor: string | ActivityPubActor, + fetchType?: 'association-ids', + recurseIfNeeded?: boolean, + updateCollections?: boolean +): Promise + async function getOrCreateActorAndServerAndModel ( activityActor: string | ActivityPubActor, - fetchType: ActorFetchByUrlType = 'actor-and-association-ids', + fetchType: ActorFetchByUrlType = 'association-ids', recurseIfNeeded = true, updateCollections = false -) { +): Promise { const actorUrl = getAPId(activityActor) let created = false let accountPlaylistsUrl: string @@ -61,7 +87,7 @@ async function getOrCreateActorAndServerAndModel ( // Create the attributed to actor // In PeerTube a video channel is owned by an account - let ownerActor: ActorModel = undefined + let ownerActor: MActorFullActor if (recurseIfNeeded === true && result.actor.type === 'Group') { const accountAttributedTo = result.attributedTo.find(a => a.type === 'Person') if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actor.url) @@ -85,8 +111,8 @@ async function getOrCreateActorAndServerAndModel ( accountPlaylistsUrl = result.playlists } - if (actor.Account) actor.Account.Actor = actor - if (actor.VideoChannel) actor.VideoChannel.Actor = actor + if (actor.Account) (actor as MActorAccountChannelIdActor).Account.Actor = actor + if (actor.VideoChannel) (actor as MActorAccountChannelIdActor).VideoChannel.Actor = actor const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor, fetchType) if (!actorRefreshed) throw new Error('Actor ' + actorRefreshed.url + ' does not exist anymore.') @@ -140,7 +166,8 @@ async function updateActorInstance (actorInstance: ActorModel, attributes: Activ actorInstance.followingUrl = attributes.following } -async function updateActorAvatarInstance (actor: ActorModel, info: { name: string, onDisk: boolean, fileUrl: string }, t: Transaction) { +type AvatarInfo = { name: string, onDisk: boolean, fileUrl: string } +async function updateActorAvatarInstance (actor: MActorDefault, info: AvatarInfo, t: Transaction) { if (info.name !== undefined) { if (actor.avatarId) { try { @@ -212,14 +239,16 @@ async function addFetchOutboxJob (actor: Pick) { return JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload }) } -async function refreshActorIfNeeded ( - actorArg: ActorModel, +async function refreshActorIfNeeded ( + actorArg: T, fetchedType: ActorFetchByUrlType -): Promise<{ actor: ActorModel, refreshed: boolean }> { +): Promise<{ actor: T | MActorFull, refreshed: boolean }> { if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false } // We need more attributes - const actor = fetchedType === 'all' ? actorArg : await ActorModel.loadByUrlAndPopulateAccountAndChannel(actorArg.url) + const actor = fetchedType === 'all' + ? actorArg as MActorFull + : await ActorModel.loadByUrlAndPopulateAccountAndChannel(actorArg.url) try { let actorUrl: string @@ -297,9 +326,9 @@ export { function saveActorAndServerAndModelIfNotExist ( result: FetchRemoteActorResult, - ownerActor?: ActorModel, + ownerActor?: MActorFullActor, t?: Transaction -): Bluebird | Promise { +): Bluebird | Promise { let actor = result.actor if (t !== undefined) return save(t) @@ -336,7 +365,7 @@ function saveActorAndServerAndModelIfNotExist ( // Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists // (which could be false in a retried query) - const [ actorCreated ] = await ActorModel.findOrCreate({ + const [ actorCreated ] = await ActorModel.findOrCreate({ defaults: actor.toJSON(), where: { url: actor.url @@ -345,10 +374,10 @@ function saveActorAndServerAndModelIfNotExist ( }) if (actorCreated.type === 'Person' || actorCreated.type === 'Application') { - actorCreated.Account = await saveAccount(actorCreated, result, t) + actorCreated.Account = await saveAccount(actorCreated, result, t) as MAccountActorDefault actorCreated.Account.Actor = actorCreated } else if (actorCreated.type === 'Group') { // Video channel - actorCreated.VideoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t) + actorCreated.VideoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t) as MChannelActorAccountDefault actorCreated.VideoChannel.Actor = actorCreated actorCreated.VideoChannel.Account = ownerActor.Account } @@ -360,7 +389,7 @@ function saveActorAndServerAndModelIfNotExist ( } type FetchRemoteActorResult = { - actor: ActorModel + actor: MActor name: string summary: string support?: string @@ -429,7 +458,7 @@ async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: numbe } } -async function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t: Transaction) { +async function saveAccount (actor: MActorId, result: FetchRemoteActorResult, t: Transaction) { const [ accountCreated ] = await AccountModel.findOrCreate({ defaults: { name: result.name, @@ -442,10 +471,10 @@ async function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t transaction: t }) - return accountCreated + return accountCreated as MAccount } -async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResult, ownerActor: ActorModel, t: Transaction) { +async function saveVideoChannel (actor: MActorId, result: FetchRemoteActorResult, ownerActor: MActorAccountId, t: Transaction) { const [ videoChannelCreated ] = await VideoChannelModel.findOrCreate({ defaults: { name: result.name, @@ -460,5 +489,5 @@ async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResu transaction: t }) - return videoChannelCreated + return videoChannelCreated as MChannel } 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' import { ACTIVITY_PUB } from '../../initializers/constants' import { ActorModel } from '../../models/activitypub/actor' import { VideoModel } from '../../models/video/video' -import { VideoCommentModel } from '../../models/video/video-comment' import { VideoShareModel } from '../../models/video/video-share' -import { ActorModelOnly } from '../../typings/models' +import { MActorFollowersUrl, MActorLight, MCommentOwner, MCommentOwnerVideo, MVideo, MVideoAccountLight } from '../../typings/models' -function getRemoteVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]): ActivityAudience { +function getRemoteVideoAudience (video: MVideoAccountLight, actorsInvolvedInVideo: MActorFollowersUrl[]): ActivityAudience { return { to: [ video.VideoChannel.Account.Actor.url ], cc: actorsInvolvedInVideo.map(a => a.followersUrl) @@ -15,9 +14,9 @@ function getRemoteVideoAudience (video: VideoModel, actorsInvolvedInVideo: Actor } function getVideoCommentAudience ( - videoComment: VideoCommentModel, - threadParentComments: VideoCommentModel[], - actorsInvolvedInVideo: ActorModel[], + videoComment: MCommentOwnerVideo, + threadParentComments: MCommentOwner[], + actorsInvolvedInVideo: MActorFollowersUrl[], isOrigin = false ): ActivityAudience { const to = [ ACTIVITY_PUB.PUBLIC ] @@ -42,26 +41,28 @@ function getVideoCommentAudience ( } } -function getAudienceFromFollowersOf (actorsInvolvedInObject: ActorModel[]): ActivityAudience { +function getAudienceFromFollowersOf (actorsInvolvedInObject: MActorFollowersUrl[]): ActivityAudience { return { to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)), cc: [] } } -async function getActorsInvolvedInVideo (video: VideoModel, t: Transaction) { - const actors = await VideoShareModel.loadActorsByShare(video.id, t) +async function getActorsInvolvedInVideo (video: MVideo, t: Transaction) { + const actors: MActorLight[] = await VideoShareModel.loadActorsByShare(video.id, t) - const videoActor = video.VideoChannel && video.VideoChannel.Account - ? video.VideoChannel.Account.Actor - : await ActorModel.loadAccountActorByVideoId(video.id, t) + const videoAll = video as VideoModel + + const videoActor = videoAll.VideoChannel && videoAll.VideoChannel.Account + ? videoAll.VideoChannel.Account.Actor + : await ActorModel.loadFromAccountByVideoId(video.id, t) actors.push(videoActor) return actors } -function getAudience (actorSender: ActorModelOnly, isPublic = true) { +function getAudience (actorSender: MActorFollowersUrl, isPublic = true) { return buildAudience([ actorSender.followersUrl ], isPublic) } 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 @@ import { CacheFileObject } from '../../../shared/index' -import { VideoModel } from '../../models/video/video' import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' import { Transaction } from 'sequelize' import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' +import { MActorId, MVideoRedundancy, MVideoWithAllFiles } from '@server/typings/models' -function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }) { +function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject, video: MVideoWithAllFiles, byActor: MActorId) { if (cacheFileObject.url.mediaType === 'application/x-mpegURL') { const url = cacheFileObject.url @@ -39,7 +39,7 @@ function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject } } -async function createOrUpdateCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }, t: Transaction) { +async function createOrUpdateCacheFile (cacheFileObject: CacheFileObject, video: MVideoWithAllFiles, byActor: MActorId, t: Transaction) { const redundancyModel = await VideoRedundancyModel.loadByUrl(cacheFileObject.id, t) if (!redundancyModel) { @@ -49,7 +49,7 @@ async function createOrUpdateCacheFile (cacheFileObject: CacheFileObject, video: } } -function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }, t: Transaction) { +function createCacheFile (cacheFileObject: CacheFileObject, video: MVideoWithAllFiles, byActor: MActorId, t: Transaction) { const attributes = cacheFileActivityObjectToDBAttributes(cacheFileObject, video, byActor) return VideoRedundancyModel.create(attributes, { transaction: t }) @@ -57,9 +57,9 @@ function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, b function updateCacheFile ( cacheFileObject: CacheFileObject, - redundancyModel: VideoRedundancyModel, - video: VideoModel, - byActor: { id?: number }, + redundancyModel: MVideoRedundancy, + video: MVideoWithAllFiles, + byActor: MActorId, t: Transaction ) { 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 @@ import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' import { crawlCollectionPage } from './crawl' import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' -import { AccountModel } from '../../models/account/account' import { isArray } from '../../helpers/custom-validators/misc' import { getOrCreateActorAndServerAndModel } from './actor' import { logger } from '../../helpers/logger' @@ -13,14 +12,14 @@ import { PlaylistElementObject } from '../../../shared/models/activitypub/object import { getOrCreateVideoAndAccountAndChannel } from './videos' import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '../../helpers/custom-validators/activitypub/playlist' import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element' -import { VideoModel } from '../../models/video/video' import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' import { sequelizeTypescript } from '../../initializers/database' import { createPlaylistMiniatureFromUrl } from '../thumbnail' import { FilteredModelAttributes } from '../../typings/sequelize' -import { AccountModelId } from '../../typings/models' +import { MAccountDefault, MAccountId, MVideoId } from '../../typings/models' +import { MVideoPlaylist, MVideoPlaylistId, MVideoPlaylistOwner } from '../../typings/models/video/video-playlist' -function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount: AccountModelId, to: string[]) { +function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount: MAccountId, to: string[]) { const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPlaylistPrivacy.PUBLIC : VideoPlaylistPrivacy.UNLISTED return { @@ -36,7 +35,7 @@ function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount } } -function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObject, videoPlaylist: VideoPlaylistModel, video: VideoModel) { +function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObject, videoPlaylist: MVideoPlaylistId, video: MVideoId) { return { position: elementObject.position, url: elementObject.id, @@ -47,7 +46,7 @@ function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObje } } -async function createAccountPlaylists (playlistUrls: string[], account: AccountModel) { +async function createAccountPlaylists (playlistUrls: string[], account: MAccountDefault) { await Bluebird.map(playlistUrls, async playlistUrl => { try { const exists = await VideoPlaylistModel.doesPlaylistExist(playlistUrl) @@ -75,7 +74,7 @@ async function createAccountPlaylists (playlistUrls: string[], account: AccountM }, { concurrency: CRAWL_REQUEST_CONCURRENCY }) } -async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAccount: AccountModelId, to: string[]) { +async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAccount: MAccountId, to: string[]) { const playlistAttributes = playlistObjectToDBAttributes(playlistObject, byAccount, to) if (isArray(playlistObject.attributedTo) && playlistObject.attributedTo.length === 1) { @@ -88,7 +87,7 @@ async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAc } } - const [ playlist ] = await VideoPlaylistModel.upsert(playlistAttributes, { returning: true }) + const [ playlist ] = await VideoPlaylistModel.upsert(playlistAttributes, { returning: true }) let accItems: string[] = [] await crawlCollectionPage(playlistObject.id, items => { @@ -114,7 +113,7 @@ async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAc return resetVideoPlaylistElements(accItems, refreshedPlaylist) } -async function refreshVideoPlaylistIfNeeded (videoPlaylist: VideoPlaylistModel): Promise { +async function refreshVideoPlaylistIfNeeded (videoPlaylist: MVideoPlaylistOwner): Promise { if (!videoPlaylist.isOutdated()) return videoPlaylist try { @@ -157,7 +156,7 @@ export { // --------------------------------------------------------------------------- -async function resetVideoPlaylistElements (elementUrls: string[], playlist: VideoPlaylistModel) { +async function resetVideoPlaylistElements (elementUrls: string[], playlist: MVideoPlaylist) { const elementsToCreate: FilteredModelAttributes[] = [] 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 @@ import { ActivityAccept } from '../../../../shared/models/activitypub' -import { ActorModel } from '../../../models/activitypub/actor' import { ActorFollowModel } from '../../../models/activitypub/actor-follow' import { addFetchOutboxJob } from '../actor' import { APProcessorOptions } from '../../../typings/activitypub-processor.model' -import { SignatureActorModel } from '../../../typings/models' +import { MActorDefault, MActorSignature } from '../../../typings/models' async function processAcceptActivity (options: APProcessorOptions) { const { byActor: targetActor, inboxActor } = options @@ -20,7 +19,7 @@ export { // --------------------------------------------------------------------------- -async function processAccept (actor: ActorModel, targetActor: SignatureActorModel) { +async function processAccept (actor: MActorDefault, targetActor: MActorSignature) { const follow = await ActorFollowModel.loadByActorAndTarget(actor.id, targetActor.id) if (!follow) throw new Error('Cannot find associated follow.') 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' import { forwardVideoRelatedActivity } from '../send/utils' import { getOrCreateVideoAndAccountAndChannel } from '../videos' import { Notifier } from '../../notifier' -import { VideoModel } from '../../../models/video/video' import { logger } from '../../../helpers/logger' import { APProcessorOptions } from '../../../typings/activitypub-processor.model' -import { SignatureActorModel } from '../../../typings/models' +import { MActorSignature, MVideoAccountAllFiles } from '../../../typings/models' async function processAnnounceActivity (options: APProcessorOptions) { const { activity, byActor: actorAnnouncer } = options @@ -26,10 +25,10 @@ export { // --------------------------------------------------------------------------- -async function processVideoShare (actorAnnouncer: SignatureActorModel, activity: ActivityAnnounce, notify: boolean) { +async function processVideoShare (actorAnnouncer: MActorSignature, activity: ActivityAnnounce, notify: boolean) { const objectUri = typeof activity.object === 'string' ? activity.object : activity.object.id - let video: VideoModel + let video: MVideoAccountAllFiles let videoCreated: boolean 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' import { Notifier } from '../../notifier' import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' import { createOrUpdateVideoPlaylist } from '../playlist' -import { VideoModel } from '../../../models/video/video' import { APProcessorOptions } from '../../../typings/activitypub-processor.model' -import { VideoCommentModel } from '../../../models/video/video-comment' -import { SignatureActorModel } from '../../../typings/models' +import { MActorSignature, MCommentOwnerVideo, MVideoAccountAllFiles } from '../../../typings/models' async function processCreateActivity (options: APProcessorOptions) { const { activity, byActor } = options @@ -61,7 +59,7 @@ async function processCreateVideo (activity: ActivityCreate, notify: boolean) { return video } -async function processCreateCacheFile (activity: ActivityCreate, byActor: SignatureActorModel) { +async function processCreateCacheFile (activity: ActivityCreate, byActor: MActorSignature) { const cacheFile = activity.object as CacheFileObject const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFile.object }) @@ -77,15 +75,15 @@ async function processCreateCacheFile (activity: ActivityCreate, byActor: Signat } } -async function processCreateVideoComment (activity: ActivityCreate, byActor: SignatureActorModel, notify: boolean) { +async function processCreateVideoComment (activity: ActivityCreate, byActor: MActorSignature, notify: boolean) { const commentObject = activity.object as VideoCommentObject const byAccount = byActor.Account if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url) - let video: VideoModel + let video: MVideoAccountAllFiles let created: boolean - let comment: VideoCommentModel + let comment: MCommentOwnerVideo try { const resolveThreadResult = await resolveThread({ url: commentObject.id, isVideo: false }) video = resolveThreadResult.video @@ -110,7 +108,7 @@ async function processCreateVideoComment (activity: ActivityCreate, byActor: Sig if (created && notify) Notifier.Instance.notifyOnNewComment(comment) } -async function processCreatePlaylist (activity: ActivityCreate, byActor: SignatureActorModel) { +async function processCreatePlaylist (activity: ActivityCreate, byActor: MActorSignature) { const playlistObject = activity.object as PlaylistObject const byAccount = byActor.Account 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' import { retryTransactionWrapper } from '../../../helpers/database-utils' import { logger } from '../../../helpers/logger' import { sequelizeTypescript } from '../../../initializers' -import { AccountModel } from '../../../models/account/account' import { ActorModel } from '../../../models/activitypub/actor' import { VideoModel } from '../../../models/video/video' -import { VideoChannelModel } from '../../../models/video/video-channel' import { VideoCommentModel } from '../../../models/video/video-comment' import { forwardVideoRelatedActivity } from '../send/utils' import { VideoPlaylistModel } from '../../../models/video/video-playlist' import { APProcessorOptions } from '../../../typings/activitypub-processor.model' -import { SignatureActorModel } from '../../../typings/models' +import { MAccountActor, MActor, MActorSignature, MChannelActor, MChannelActorAccountActor } from '../../../typings/models' async function processDeleteActivity (options: APProcessorOptions) { const { activity, byActor } = options @@ -24,13 +22,17 @@ async function processDeleteActivity (options: APProcessorOptions { @@ -84,7 +86,7 @@ async function processDeleteVideo (actor: ActorModel, videoToDelete: VideoModel) logger.info('Remote video with uuid %s removed.', videoToDelete.uuid) } -async function processDeleteVideoPlaylist (actor: ActorModel, playlistToDelete: VideoPlaylistModel) { +async function processDeleteVideoPlaylist (actor: MActor, playlistToDelete: VideoPlaylistModel) { logger.debug('Removing remote video playlist "%s".', playlistToDelete.uuid) await sequelizeTypescript.transaction(async t => { @@ -98,7 +100,7 @@ async function processDeleteVideoPlaylist (actor: ActorModel, playlistToDelete: logger.info('Remote video playlist with uuid %s removed.', playlistToDelete.uuid) } -async function processDeleteAccount (accountToRemove: AccountModel) { +async function processDeleteAccount (accountToRemove: MAccountActor) { logger.debug('Removing remote account "%s".', accountToRemove.Actor.url) await sequelizeTypescript.transaction(async t => { @@ -108,7 +110,7 @@ async function processDeleteAccount (accountToRemove: AccountModel) { logger.info('Remote account %s removed.', accountToRemove.Actor.url) } -async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelModel) { +async function processDeleteVideoChannel (videoChannelToRemove: MChannelActor) { logger.debug('Removing remote video channel "%s".', videoChannelToRemove.Actor.url) await sequelizeTypescript.transaction(async t => { @@ -118,7 +120,7 @@ async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelMode logger.info('Remote video channel %s removed.', videoChannelToRemove.Actor.url) } -function processDeleteVideoComment (byActor: SignatureActorModel, videoComment: VideoCommentModel, activity: ActivityDelete) { +function processDeleteVideoComment (byActor: MActorSignature, videoComment: VideoCommentModel, activity: ActivityDelete) { logger.debug('Removing remote video comment "%s".', videoComment.url) 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' import { forwardVideoRelatedActivity } from '../send/utils' import { getVideoDislikeActivityPubUrl } from '../url' import { APProcessorOptions } from '../../../typings/activitypub-processor.model' -import { SignatureActorModel } from '../../../typings/models' +import { MActorSignature } from '../../../typings/models' async function processDislikeActivity (options: APProcessorOptions) { const { activity, byActor } = options @@ -22,7 +22,7 @@ export { // --------------------------------------------------------------------------- -async function processDislike (activity: ActivityCreate | ActivityDislike, byActor: SignatureActorModel) { +async function processDislike (activity: ActivityCreate | ActivityDislike, byActor: MActorSignature) { const dislikeObject = activity.type === 'Dislike' ? activity.object : (activity.object as DislikeObject).object const byAccount = byActor.Account 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' import { Notifier } from '../../notifier' import { getAPId } from '../../../helpers/activitypub' import { APProcessorOptions } from '../../../typings/activitypub-processor.model' -import { SignatureActorModel } from '../../../typings/models' +import { MActorSignature, MVideoAbuseVideo } from '../../../typings/models' async function processFlagActivity (options: APProcessorOptions) { const { activity, byActor } = options @@ -23,7 +23,7 @@ export { // --------------------------------------------------------------------------- -async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag, byActor: SignatureActorModel) { +async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag, byActor: MActorSignature) { const flag = activity.type === 'Flag' ? activity : (activity.object as VideoAbuseObject) logger.debug('Reporting remote abuse for video %s.', getAPId(flag.object)) @@ -41,7 +41,7 @@ async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag, state: VideoAbuseState.PENDING } - const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t }) + const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t }) as MVideoAbuseVideo videoAbuseInstance.Video = video 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' import { getServerActor } from '../../../helpers/utils' import { CONFIG } from '../../../initializers/config' import { APProcessorOptions } from '../../../typings/activitypub-processor.model' -import { SignatureActorModel } from '../../../typings/models' -import { ActorFollowModelLight } from '../../../typings/models/actor-follow' +import { MAccount, MActorFollowActors, MActorFollowFull, MActorSignature } from '../../../typings/models' async function processFollowActivity (options: APProcessorOptions) { const { activity, byActor } = options @@ -28,7 +27,7 @@ export { // --------------------------------------------------------------------------- -async function processFollow (byActor: SignatureActorModel, targetActorURL: string) { +async function processFollow (byActor: MActorSignature, targetActorURL: string) { const { actorFollow, created, isFollowingInstance } = await sequelizeTypescript.transaction(async t => { const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t) @@ -43,10 +42,10 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri await sendReject(byActor, targetActor) - return { actorFollow: undefined } + return { actorFollow: undefined as MActorFollowActors } } - const [ actorFollow, created ] = await ActorFollowModel.findOrCreate({ + const [ actorFollow, created ] = await ActorFollowModel.findOrCreate({ where: { actorId: byActor.id, targetActorId: targetActor.id @@ -57,7 +56,7 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL ? 'pending' : 'accepted' }, transaction: t - }) as [ ActorFollowModelLight, boolean ] + }) if (actorFollow.state !== 'accepted' && CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false) { actorFollow.state = 'accepted' @@ -77,8 +76,14 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri if (!actorFollow) return if (created) { - if (isFollowingInstance) Notifier.Instance.notifyOfNewInstanceFollow(actorFollow) - else Notifier.Instance.notifyOfNewUserFollow(actorFollow) + if (isFollowingInstance) { + Notifier.Instance.notifyOfNewInstanceFollow(actorFollow) + } else { + const actorFollowFull = actorFollow as MActorFollowFull + actorFollowFull.ActorFollower.Account = await actorFollow.ActorFollower.$get('Account') as MAccount + + Notifier.Instance.notifyOfNewUserFollow(actorFollowFull) + } } 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' import { getVideoLikeActivityPubUrl } from '../url' import { getAPId } from '../../../helpers/activitypub' import { APProcessorOptions } from '../../../typings/activitypub-processor.model' -import { SignatureActorModel } from '../../../typings/models' +import { MActorSignature } from '../../../typings/models' async function processLikeActivity (options: APProcessorOptions) { const { activity, byActor } = options @@ -22,7 +22,7 @@ export { // --------------------------------------------------------------------------- -async function processLikeVideo (byActor: SignatureActorModel, activity: ActivityLike) { +async function processLikeVideo (byActor: MActorSignature, activity: ActivityLike) { const videoUrl = getAPId(activity.object) 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' import { sequelizeTypescript } from '../../../initializers' import { ActorFollowModel } from '../../../models/activitypub/actor-follow' import { APProcessorOptions } from '../../../typings/activitypub-processor.model' -import { ActorModelOnly } from '../../../typings/models' +import { MActor } from '../../../typings/models' async function processRejectActivity (options: APProcessorOptions) { const { byActor: targetActor, inboxActor } = options @@ -19,7 +19,7 @@ export { // --------------------------------------------------------------------------- -async function processReject (follower: ActorModelOnly, targetActor: ActorModelOnly) { +async function processReject (follower: MActor, targetActor: MActor) { return sequelizeTypescript.transaction(async t => { const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, targetActor.id, t) 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' import { VideoShareModel } from '../../../models/video/video-share' import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' import { APProcessorOptions } from '../../../typings/activitypub-processor.model' -import { SignatureActorModel } from '../../../typings/models' +import { MActorSignature } from '../../../typings/models' async function processUndoActivity (options: APProcessorOptions) { const { activity, byActor } = options @@ -54,7 +54,7 @@ export { // --------------------------------------------------------------------------- -async function processUndoLike (byActor: SignatureActorModel, activity: ActivityUndo) { +async function processUndoLike (byActor: MActorSignature, activity: ActivityUndo) { const likeActivity = activity.object as ActivityLike const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: likeActivity.object }) @@ -77,7 +77,7 @@ async function processUndoLike (byActor: SignatureActorModel, activity: Activity }) } -async function processUndoDislike (byActor: SignatureActorModel, activity: ActivityUndo) { +async function processUndoDislike (byActor: MActorSignature, activity: ActivityUndo) { const dislike = activity.object.type === 'Dislike' ? activity.object : activity.object.object as DislikeObject @@ -102,7 +102,7 @@ async function processUndoDislike (byActor: SignatureActorModel, activity: Activ }) } -async function processUndoCacheFile (byActor: SignatureActorModel, activity: ActivityUndo) { +async function processUndoCacheFile (byActor: MActorSignature, activity: ActivityUndo) { const cacheFileObject = activity.object.object as CacheFileObject const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFileObject.object }) @@ -127,7 +127,7 @@ async function processUndoCacheFile (byActor: SignatureActorModel, activity: Act }) } -function processUndoFollow (follower: SignatureActorModel, followActivity: ActivityFollow) { +function processUndoFollow (follower: MActorSignature, followActivity: ActivityFollow) { return sequelizeTypescript.transaction(async t => { const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t) const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t) @@ -140,7 +140,7 @@ function processUndoFollow (follower: SignatureActorModel, followActivity: Activ }) } -function processUndoAnnounce (byActor: SignatureActorModel, announceActivity: ActivityAnnounce) { +function processUndoAnnounce (byActor: MActorSignature, announceActivity: ActivityAnnounce) { return sequelizeTypescript.transaction(async t => { const share = await VideoShareModel.loadByUrl(announceActivity.id, t) 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' import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' import { createOrUpdateVideoPlaylist } from '../playlist' import { APProcessorOptions } from '../../../typings/activitypub-processor.model' -import { SignatureActorModel } from '../../../typings/models' +import { MActorSignature } from '../../../typings/models' async function processUpdateActivity (options: APProcessorOptions) { const { activity, byActor } = options @@ -53,7 +53,7 @@ export { // --------------------------------------------------------------------------- -async function processUpdateVideo (actor: SignatureActorModel, activity: ActivityUpdate) { +async function processUpdateVideo (actor: MActorSignature, activity: ActivityUpdate) { const videoObject = activity.object as VideoTorrentObject if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) { @@ -61,20 +61,20 @@ async function processUpdateVideo (actor: SignatureActorModel, activity: Activit return undefined } - const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoObject.id, allowRefresh: false }) + const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoObject.id, allowRefresh: false, fetchType: 'all' }) const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) const updateOptions = { video, videoObject, - account: actor.Account, + account: channelActor.VideoChannel.Account, channel: channelActor.VideoChannel, overrideTo: activity.to } return updateVideoFromAP(updateOptions) } -async function processUpdateCacheFile (byActor: SignatureActorModel, activity: ActivityUpdate) { +async function processUpdateCacheFile (byActor: MActorSignature, activity: ActivityUpdate) { const cacheFileObject = activity.object as CacheFileObject if (!isCacheFileObjectValid(cacheFileObject)) { @@ -150,7 +150,7 @@ async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate) } } -async function processUpdatePlaylist (byActor: SignatureActorModel, activity: ActivityUpdate) { +async function processUpdatePlaylist (byActor: MActorSignature, activity: ActivityUpdate) { const playlistObject = activity.object as PlaylistObject const byAccount = byActor.Account 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' import { Redis } from '../../redis' import { ActivityCreate, ActivityView, ViewObject } from '../../../../shared/models/activitypub' import { APProcessorOptions } from '../../../typings/activitypub-processor.model' -import { SignatureActorModel } from '../../../typings/models' +import { MActorSignature } from '../../../typings/models' async function processViewActivity (options: APProcessorOptions) { const { activity, byActor } = options @@ -18,11 +18,11 @@ export { // --------------------------------------------------------------------------- -async function processCreateView (activity: ActivityView | ActivityCreate, byActor: SignatureActorModel) { +async function processCreateView (activity: ActivityView | ActivityCreate, byActor: MActorSignature) { const videoObject = activity.type === 'View' ? activity.object : (activity.object as ViewObject).object const options = { - videoObject: videoObject, + videoObject, fetchType: 'only-video' as 'only-video' } 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 @@ import { Activity, ActivityType } from '../../../../shared/models/activitypub' import { checkUrlsSameHost, getAPId } from '../../../helpers/activitypub' import { logger } from '../../../helpers/logger' -import { ActorModel } from '../../../models/activitypub/actor' import { processAcceptActivity } from './process-accept' import { processAnnounceActivity } from './process-announce' import { processCreateActivity } from './process-create' @@ -16,7 +15,7 @@ import { processDislikeActivity } from './process-dislike' import { processFlagActivity } from './process-flag' import { processViewActivity } from './process-view' import { APProcessorOptions } from '../../../typings/activitypub-processor.model' -import { SignatureActorModel } from '../../../typings/models' +import { MActorDefault, MActorSignature } from '../../../typings/models' const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions) => Promise } = { Create: processCreateActivity, @@ -36,15 +35,15 @@ const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions unicastTo(createActivity, byActor, comment.Video.VideoChannel.Account.Actor.sharedInboxUrl)) } -function buildCreateActivity (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityCreate { +function buildCreateActivity (url: string, byActor: MActorLight, object: any, audience?: ActivityAudience): ActivityCreate { if (!audience) audience = getAudience(byActor) return audiencify( @@ -122,8 +130,8 @@ export { // --------------------------------------------------------------------------- async function sendVideoRelatedCreateActivity (options: { - byActor: ActorModel, - video: VideoModel, + byActor: MActorLight, + video: MVideoAccountLight, url: string, object: any, 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 @@ import { Transaction } from 'sequelize' import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub' import { ActorModel } from '../../../models/activitypub/actor' -import { VideoModel } from '../../../models/video/video' import { VideoCommentModel } from '../../../models/video/video-comment' import { VideoShareModel } from '../../../models/video/video-share' import { getDeleteActivityPubUrl } from '../url' import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience' import { logger } from '../../../helpers/logger' -import { VideoPlaylistModel } from '../../../models/video/video-playlist' import { getServerActor } from '../../../helpers/utils' +import { MCommentOwnerVideoReply, MVideoAccountLight, MVideoPlaylistFullSummary } from '../../../typings/models/video' +import { MActorUrl } from '../../../typings/models' -async function sendDeleteVideo (video: VideoModel, transaction: Transaction) { +async function sendDeleteVideo (video: MVideoAccountLight, transaction: Transaction) { logger.info('Creating job to broadcast delete of video %s.', video.url) const byActor = video.VideoChannel.Account.Actor @@ -42,7 +42,7 @@ async function sendDeleteActor (byActor: ActorModel, t: Transaction) { return broadcastToFollowers(activity, byActor, actorsInvolved, t) } -async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Transaction) { +async function sendDeleteVideoComment (videoComment: MCommentOwnerVideoReply, t: Transaction) { logger.info('Creating job to send delete of comment %s.', videoComment.url) const isVideoOrigin = videoComment.Video.isOwned() @@ -74,7 +74,7 @@ async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Trans t.afterCommit(() => unicastTo(activity, byActor, videoComment.Video.VideoChannel.Account.Actor.sharedInboxUrl)) } -async function sendDeleteVideoPlaylist (videoPlaylist: VideoPlaylistModel, t: Transaction) { +async function sendDeleteVideoPlaylist (videoPlaylist: MVideoPlaylistFullSummary, t: Transaction) { logger.info('Creating job to send delete of playlist %s.', videoPlaylist.url) const byActor = videoPlaylist.OwnerAccount.Actor @@ -101,7 +101,7 @@ export { // --------------------------------------------------------------------------- -function buildDeleteActivity (url: string, object: string, byActor: ActorModel, audience?: ActivityAudience): ActivityDelete { +function buildDeleteActivity (url: string, object: string, byActor: MActorUrl, audience?: ActivityAudience): ActivityDelete { const activity = { type: 'Delete' as 'Delete', 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 @@ import { Transaction } from 'sequelize' -import { ActorModel } from '../../../models/activitypub/actor' -import { VideoModel } from '../../../models/video/video' import { getVideoDislikeActivityPubUrl } from '../url' import { logger } from '../../../helpers/logger' import { ActivityAudience, ActivityDislike } from '../../../../shared/models/activitypub' import { sendVideoRelatedActivity } from './utils' import { audiencify, getAudience } from '../audience' +import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../typings/models' -async function sendDislike (byActor: ActorModel, video: VideoModel, t: Transaction) { +async function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { logger.info('Creating job to dislike %s.', video.url) const activityBuilder = (audience: ActivityAudience) => { @@ -19,7 +18,7 @@ async function sendDislike (byActor: ActorModel, video: VideoModel, t: Transacti return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) } -function buildDislikeActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityDislike { +function buildDislikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityDislike { if (!audience) audience = getAudience(byActor) 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 @@ -import { ActorModel } from '../../../models/activitypub/actor' -import { VideoModel } from '../../../models/video/video' -import { VideoAbuseModel } from '../../../models/video/video-abuse' import { getVideoAbuseActivityPubUrl } from '../url' import { unicastTo } from './utils' import { logger } from '../../../helpers/logger' import { ActivityAudience, ActivityFlag } from '../../../../shared/models/activitypub' import { audiencify, getAudience } from '../audience' import { Transaction } from 'sequelize' +import { MActor, MVideoFullLight } from '../../../typings/models' +import { MVideoAbuseVideo } from '../../../typings/models/video' -async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel, video: VideoModel, t: Transaction) { +async function sendVideoAbuse (byActor: MActor, videoAbuse: MVideoAbuseVideo, video: MVideoFullLight, t: Transaction) { if (!video.VideoChannel.Account.Actor.serverId) return // Local user const url = getVideoAbuseActivityPubUrl(videoAbuse) @@ -22,7 +21,7 @@ async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel, t.afterCommit(() => unicastTo(flagActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)) } -function buildFlagActivity (url: string, byActor: ActorModel, videoAbuse: VideoAbuseModel, audience: ActivityAudience): ActivityFlag { +function buildFlagActivity (url: string, byActor: MActor, videoAbuse: MVideoAbuseVideo, audience: ActivityAudience): ActivityFlag { if (!audience) audience = getAudience(byActor) 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' import { unicastTo } from './utils' import { logger } from '../../../helpers/logger' import { Transaction } from 'sequelize' -import { ActorModelOnly } from '../../../typings/models' +import { MActor, MActorFollowActors } from '../../../typings/models' -function sendFollow (actorFollow: ActorFollowModel, t: Transaction) { +function sendFollow (actorFollow: MActorFollowActors, t: Transaction) { const me = actorFollow.ActorFollower const following = actorFollow.ActorFollowing @@ -21,7 +21,7 @@ function sendFollow (actorFollow: ActorFollowModel, t: Transaction) { t.afterCommit(() => unicastTo(data, me, following.inboxUrl)) } -function buildFollowActivity (url: string, byActor: ActorModelOnly, targetActor: ActorModelOnly): ActivityFollow { +function buildFollowActivity (url: string, byActor: MActor, targetActor: MActor): ActivityFollow { return { type: 'Follow', 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 @@ import { Transaction } from 'sequelize' import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub' -import { ActorModel } from '../../../models/activitypub/actor' -import { VideoModel } from '../../../models/video/video' import { getVideoLikeActivityPubUrl } from '../url' import { sendVideoRelatedActivity } from './utils' import { audiencify, getAudience } from '../audience' import { logger } from '../../../helpers/logger' +import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../typings/models' -async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction) { +async function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { logger.info('Creating job to like %s.', video.url) const activityBuilder = (audience: ActivityAudience) => { @@ -19,7 +18,7 @@ async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction) return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) } -function buildLikeActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityLike { +function buildLikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityLike { if (!audience) audience = getAudience(byActor) 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 @@ import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub' -import { ActorModel } from '../../../models/activitypub/actor' import { getActorFollowActivityPubUrl, getActorFollowRejectActivityPubUrl } from '../url' import { unicastTo } from './utils' import { buildFollowActivity } from './send-follow' import { logger } from '../../../helpers/logger' -import { SignatureActorModel } from '../../../typings/models' +import { MActor } from '../../../typings/models' -async function sendReject (follower: SignatureActorModel, following: ActorModel) { +async function sendReject (follower: MActor, following: MActor) { if (!follower.serverId) { // This should never happen logger.warn('Do not sending reject to local follower.') return @@ -31,7 +30,7 @@ export { // --------------------------------------------------------------------------- -function buildRejectActivity (url: string, byActor: ActorModel, followActivityData: ActivityFollow): ActivityReject { +function buildRejectActivity (url: string, byActor: MActor, followActivityData: ActivityFollow): ActivityReject { return { type: 'Reject', 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' import { ActivityAnnounce, ActivityAudience, - ActivityCreate, ActivityDislike, + ActivityCreate, + ActivityDislike, ActivityFollow, ActivityLike, ActivityUndo } from '../../../../shared/models/activitypub' -import { ActorModel } from '../../../models/activitypub/actor' -import { ActorFollowModel } from '../../../models/activitypub/actor-follow' import { VideoModel } from '../../../models/video/video' import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url' import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' @@ -16,13 +15,20 @@ import { audiencify, getAudience } from '../audience' import { buildCreateActivity } from './send-create' import { buildFollowActivity } from './send-follow' import { buildLikeActivity } from './send-like' -import { VideoShareModel } from '../../../models/video/video-share' import { buildAnnounceWithVideoAudience } from './send-announce' import { logger } from '../../../helpers/logger' -import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' import { buildDislikeActivity } from './send-dislike' +import { + MActor, MActorAudience, + MActorFollowActors, + MActorLight, + MVideo, + MVideoAccountLight, + MVideoRedundancyVideo, + MVideoShare +} from '../../../typings/models' -async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) { +async function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) { const me = actorFollow.ActorFollower const following = actorFollow.ActorFollowing @@ -40,7 +46,7 @@ async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) { t.afterCommit(() => unicastTo(undoActivity, me, following.inboxUrl)) } -async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) { +async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) { logger.info('Creating job to undo announce %s.', videoShare.url) const undoUrl = getUndoActivityPubUrl(videoShare.url) @@ -52,7 +58,7 @@ async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareMode return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException) } -async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transaction) { +async function sendUndoLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { logger.info('Creating job to undo a like of video %s.', video.url) const likeUrl = getVideoLikeActivityPubUrl(byActor, video) @@ -61,7 +67,7 @@ async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transact return sendUndoVideoRelatedActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t }) } -async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Transaction) { +async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { logger.info('Creating job to undo a dislike of video %s.', video.url) const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video) @@ -70,7 +76,7 @@ async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Trans return sendUndoVideoRelatedActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t }) } -async function sendUndoCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel, t: Transaction) { +async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedundancyVideo, t: Transaction) { logger.info('Creating job to undo cache file %s.', redundancyModel.url) const videoId = redundancyModel.getVideo().id @@ -94,7 +100,7 @@ export { function undoActivityData ( url: string, - byActor: ActorModel, + byActor: MActorAudience, object: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce, audience?: ActivityAudience ): ActivityUndo { @@ -112,8 +118,8 @@ function undoActivityData ( } async function sendUndoVideoRelatedActivity (options: { - byActor: ActorModel, - video: VideoModel, + byActor: MActor, + video: MVideoAccountLight, url: string, activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce, 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' import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub' import { VideoPrivacy } from '../../../../shared/models/videos' import { AccountModel } from '../../../models/account/account' -import { ActorModel } from '../../../models/activitypub/actor' import { VideoModel } from '../../../models/video/video' -import { VideoChannelModel } from '../../../models/video/video-channel' import { VideoShareModel } from '../../../models/video/video-share' import { getUpdateActivityPubUrl } from '../url' import { broadcastToFollowers, sendVideoRelatedActivity } from './utils' import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience' import { logger } from '../../../helpers/logger' import { VideoCaptionModel } from '../../../models/video/video-caption' -import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' -import { VideoPlaylistModel } from '../../../models/video/video-playlist' import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' import { getServerActor } from '../../../helpers/utils' +import { + MAccountActor, + MActor, + MActorLight, + MChannelActor, + MVideoAP, + MVideoAPWithoutCaption, + MVideoPlaylistFull, + MVideoRedundancyVideo +} from '../../../typings/models' + +async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, t: Transaction, overrodeByActor?: MActor) { + const video = videoArg as MVideoAP -async function sendUpdateVideo (video: VideoModel, t: Transaction, overrodeByActor?: ActorModel) { if (video.privacy === VideoPrivacy.PRIVATE) return undefined logger.info('Creating job to update video %s.', video.url) @@ -41,7 +49,7 @@ async function sendUpdateVideo (video: VideoModel, t: Transaction, overrodeByAct return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t) } -async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelModel, t: Transaction) { +async function sendUpdateActor (accountOrChannel: MAccountActor | MChannelActor, t: Transaction) { const byActor = accountOrChannel.Actor logger.info('Creating job to update actor %s.', byActor.url) @@ -51,7 +59,7 @@ async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelMod const audience = getAudience(byActor) const updateActivity = buildUpdateActivity(url, byActor, accountOrChannelObject, audience) - let actorsInvolved: ActorModel[] + let actorsInvolved: MActor[] if (accountOrChannel instanceof AccountModel) { // Actors that shared my videos are involved too actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, t) @@ -65,7 +73,7 @@ async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelMod return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t) } -async function sendUpdateCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel) { +async function sendUpdateCacheFile (byActor: MActorLight, redundancyModel: MVideoRedundancyVideo) { logger.info('Creating job to update cache file %s.', redundancyModel.url) const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.getVideo().id) @@ -80,7 +88,7 @@ async function sendUpdateCacheFile (byActor: ActorModel, redundancyModel: VideoR return sendVideoRelatedActivity(activityBuilder, { byActor, video }) } -async function sendUpdateVideoPlaylist (videoPlaylist: VideoPlaylistModel, t: Transaction) { +async function sendUpdateVideoPlaylist (videoPlaylist: MVideoPlaylistFull, t: Transaction) { if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined const byActor = videoPlaylist.OwnerAccount.Actor @@ -113,7 +121,7 @@ export { // --------------------------------------------------------------------------- -function buildUpdateActivity (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityUpdate { +function buildUpdateActivity (url: string, byActor: MActorLight, object: any, audience?: ActivityAudience): ActivityUpdate { if (!audience) audience = getAudience(byActor) return audiencify( @@ -121,8 +129,7 @@ function buildUpdateActivity (url: string, byActor: ActorModel, object: any, aud type: 'Update' as 'Update', id: url, actor: byActor.url, - object: audiencify(object, audience - ) + object: audiencify(object, audience) }, audience ) 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 @@ import { Transaction } from 'sequelize' import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub' import { ActorModel } from '../../../models/activitypub/actor' -import { VideoModel } from '../../../models/video/video' import { getVideoLikeActivityPubUrl } from '../url' import { sendVideoRelatedActivity } from './utils' import { audiencify, getAudience } from '../audience' import { logger } from '../../../helpers/logger' +import { MActorAudience, MVideoAccountLight, MVideoUrl } from '@server/typings/models' -async function sendView (byActor: ActorModel, video: VideoModel, t: Transaction) { +async function sendView (byActor: ActorModel, video: MVideoAccountLight, t: Transaction) { logger.info('Creating job to send view of %s.', video.url) const activityBuilder = (audience: ActivityAudience) => { @@ -19,7 +19,7 @@ async function sendView (byActor: ActorModel, video: VideoModel, t: Transaction) return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) } -function buildViewActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityView { +function buildViewActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityView { if (!audience) audience = getAudience(byActor) 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' import { ActorModel } from '../../../models/activitypub/actor' import { ActorFollowModel } from '../../../models/activitypub/actor-follow' import { JobQueue } from '../../job-queue' -import { VideoModel } from '../../../models/video/video' import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience' import { getServerActor } from '../../../helpers/utils' import { afterCommitIfTransaction } from '../../../helpers/database-utils' -import { ActorFollowerException, ActorModelId, ActorModelOnly } from '../../../typings/models' +import { MActorFollowerException, MActor, MActorId, MActorLight, MVideo, MVideoAccountLight } from '../../../typings/models' async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { - byActor: ActorModelOnly, - video: VideoModel, + byActor: MActorLight, + video: MVideoAccountLight, transaction?: Transaction }) { const { byActor, video, transaction } = options @@ -41,8 +40,8 @@ async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAud async function forwardVideoRelatedActivity ( activity: Activity, t: Transaction, - followersException: ActorFollowerException[] = [], - video: VideoModel + followersException: MActorFollowerException[] = [], + video: MVideo ) { // Mastodon does not add our announces in audience, so we forward to them manually const additionalActors = await getActorsInvolvedInVideo(video, t) @@ -54,7 +53,7 @@ async function forwardVideoRelatedActivity ( async function forwardActivity ( activity: Activity, t: Transaction, - followersException: ActorFollowerException[] = [], + followersException: MActorFollowerException[] = [], additionalFollowerUrls: string[] = [] ) { logger.info('Forwarding activity %s.', activity.id) @@ -88,10 +87,10 @@ async function forwardActivity ( async function broadcastToFollowers ( data: any, - byActor: ActorModelId, - toFollowersOf: ActorModelId[], + byActor: MActorId, + toFollowersOf: MActorId[], t: Transaction, - actorsException: ActorFollowerException[] = [] + actorsException: MActorFollowerException[] = [] ) { const uris = await computeFollowerUris(toFollowersOf, actorsException, t) @@ -100,16 +99,16 @@ async function broadcastToFollowers ( async function broadcastToActors ( data: any, - byActor: ActorModelId, - toActors: ActorModelOnly[], + byActor: MActorId, + toActors: MActor[], t?: Transaction, - actorsException: ActorFollowerException[] = [] + actorsException: MActorFollowerException[] = [] ) { const uris = await computeUris(toActors, actorsException) return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor)) } -function broadcastTo (uris: string[], data: any, byActor: ActorModelId) { +function broadcastTo (uris: string[], data: any, byActor: MActorId) { if (uris.length === 0) return undefined logger.debug('Creating broadcast job.', { uris }) @@ -123,7 +122,7 @@ function broadcastTo (uris: string[], data: any, byActor: ActorModelId) { return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload }) } -function unicastTo (data: any, byActor: ActorModelId, toActorUrl: string) { +function unicastTo (data: any, byActor: MActorId, toActorUrl: string) { logger.debug('Creating unicast job.', { uri: toActorUrl }) const payload = { @@ -148,7 +147,7 @@ export { // --------------------------------------------------------------------------- -async function computeFollowerUris (toFollowersOf: ActorModelId[], actorsException: ActorFollowerException[], t: Transaction) { +async function computeFollowerUris (toFollowersOf: MActorId[], actorsException: MActorFollowerException[], t: Transaction) { const toActorFollowerIds = toFollowersOf.map(a => a.id) const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t) @@ -157,7 +156,7 @@ async function computeFollowerUris (toFollowersOf: ActorModelId[], actorsExcepti return result.data.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1) } -async function computeUris (toActors: ActorModelOnly[], actorsException: ActorFollowerException[] = []) { +async function computeUris (toActors: MActor[], actorsException: MActorFollowerException[] = []) { const serverActor = await getServerActor() const targetUrls = toActors .filter(a => a.id !== serverActor.id) // Don't send to ourselves @@ -170,7 +169,7 @@ async function computeUris (toActors: ActorModelOnly[], actorsException: ActorFo .filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1) } -async function buildSharedInboxesException (actorsException: ActorFollowerException[]) { +async function buildSharedInboxesException (actorsException: MActorFollowerException[]) { const serverActor = await getServerActor() 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 @@ import { Transaction } from 'sequelize' import { VideoPrivacy } from '../../../shared/models/videos' import { getServerActor } from '../../helpers/utils' -import { VideoModel } from '../../models/video/video' import { VideoShareModel } from '../../models/video/video-share' import { sendUndoAnnounce, sendVideoAnnounce } from './send' import { getVideoAnnounceActivityPubUrl } from './url' -import { VideoChannelModel } from '../../models/video/video-channel' import * as Bluebird from 'bluebird' import { doRequest } from '../../helpers/requests' import { getOrCreateActorAndServerAndModel } from './actor' import { logger } from '../../helpers/logger' import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' +import { MChannelActor, MChannelActorLight, MVideo, MVideoAccountLight, MVideoId } from '../../typings/models/video' -async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) { +async function shareVideoByServerAndChannel (video: MVideoAccountLight, t: Transaction) { if (video.privacy === VideoPrivacy.PRIVATE) return undefined return Promise.all([ @@ -22,7 +21,11 @@ async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) ]) } -async function changeVideoChannelShare (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) { +async function changeVideoChannelShare ( + video: MVideoAccountLight, + oldVideoChannel: MChannelActorLight, + t: Transaction +) { logger.info('Updating video channel of video %s: %s -> %s.', video.uuid, oldVideoChannel.name, video.VideoChannel.name) await undoShareByVideoChannel(video, oldVideoChannel, t) @@ -30,7 +33,7 @@ async function changeVideoChannelShare (video: VideoModel, oldVideoChannel: Vide await shareByVideoChannel(video, t) } -async function addVideoShares (shareUrls: string[], instance: VideoModel) { +async function addVideoShares (shareUrls: string[], video: MVideoId) { await Bluebird.map(shareUrls, async shareUrl => { try { // Fetch url @@ -50,7 +53,7 @@ async function addVideoShares (shareUrls: string[], instance: VideoModel) { const entry = { actorId: actor.id, - videoId: instance.id, + videoId: video.id, url: shareUrl } @@ -69,7 +72,7 @@ export { // --------------------------------------------------------------------------- -async function shareByServer (video: VideoModel, t: Transaction) { +async function shareByServer (video: MVideo, t: Transaction) { const serverActor = await getServerActor() const serverShareUrl = getVideoAnnounceActivityPubUrl(serverActor, video) @@ -88,7 +91,7 @@ async function shareByServer (video: VideoModel, t: Transaction) { return sendVideoAnnounce(serverActor, serverShare, video, t) } -async function shareByVideoChannel (video: VideoModel, t: Transaction) { +async function shareByVideoChannel (video: MVideoAccountLight, t: Transaction) { const videoChannelShareUrl = getVideoAnnounceActivityPubUrl(video.VideoChannel.Actor, video) const [ videoChannelShare ] = await VideoShareModel.findOrCreate({ defaults: { @@ -105,7 +108,7 @@ async function shareByVideoChannel (video: VideoModel, t: Transaction) { return sendVideoAnnounce(video.VideoChannel.Actor, videoChannelShare, video, t) } -async function undoShareByVideoChannel (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) { +async function undoShareByVideoChannel (video: MVideo, oldVideoChannel: MChannelActorLight, t: Transaction) { // Load old share const oldShare = await VideoShareModel.load(oldVideoChannel.actorId, video.id, t) 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 @@ import { WEBSERVER } from '../../initializers/constants' -import { VideoModel } from '../../models/video/video' -import { VideoAbuseModel } from '../../models/video/video-abuse' -import { VideoCommentModel } from '../../models/video/video-comment' -import { VideoFileModel } from '../../models/video/video-file' -import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' -import { VideoPlaylistModel } from '../../models/video/video-playlist' -import { ActorModelOnly, ActorModelUrl } from '../../typings/models' -import { ActorFollowModelLight } from '../../typings/models/actor-follow' +import { + MActor, + MActorFollowActors, + MActorId, + MActorUrl, + MCommentId, + MVideoAbuseId, + MVideoId, + MVideoUrl, + MVideoUUID +} from '../../typings/models' +import { MVideoPlaylist, MVideoPlaylistUUID } from '../../typings/models/video/video-playlist' +import { MVideoFileVideoUUID } from '../../typings/models/video/video-file' +import { MStreamingPlaylist } from '../../typings/models/video/video-streaming-playlist' -function getVideoActivityPubUrl (video: VideoModel) { +function getVideoActivityPubUrl (video: MVideoUUID) { return WEBSERVER.URL + '/videos/watch/' + video.uuid } -function getVideoPlaylistActivityPubUrl (videoPlaylist: VideoPlaylistModel) { +function getVideoPlaylistActivityPubUrl (videoPlaylist: MVideoPlaylist) { return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid } -function getVideoPlaylistElementActivityPubUrl (videoPlaylist: VideoPlaylistModel, video: VideoModel) { +function getVideoPlaylistElementActivityPubUrl (videoPlaylist: MVideoPlaylistUUID, video: MVideoUUID) { return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid + '/' + video.uuid } -function getVideoCacheFileActivityPubUrl (videoFile: VideoFileModel) { +function getVideoCacheFileActivityPubUrl (videoFile: MVideoFileVideoUUID) { const suffixFPS = videoFile.fps && videoFile.fps !== -1 ? '-' + videoFile.fps : '' return `${WEBSERVER.URL}/redundancy/videos/${videoFile.Video.uuid}/${videoFile.resolution}${suffixFPS}` } -function getVideoCacheStreamingPlaylistActivityPubUrl (video: VideoModel, playlist: VideoStreamingPlaylistModel) { +function getVideoCacheStreamingPlaylistActivityPubUrl (video: MVideoUUID, playlist: MStreamingPlaylist) { return `${WEBSERVER.URL}/redundancy/streaming-playlists/${playlist.getStringType()}/${video.uuid}` } -function getVideoCommentActivityPubUrl (video: VideoModel, videoComment: VideoCommentModel) { +function getVideoCommentActivityPubUrl (video: MVideoUUID, videoComment: MCommentId) { return WEBSERVER.URL + '/videos/watch/' + video.uuid + '/comments/' + videoComment.id } @@ -42,54 +48,54 @@ function getAccountActivityPubUrl (accountName: string) { return WEBSERVER.URL + '/accounts/' + accountName } -function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseModel) { +function getVideoAbuseActivityPubUrl (videoAbuse: MVideoAbuseId) { return WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id } -function getVideoViewActivityPubUrl (byActor: ActorModelUrl, video: VideoModel) { +function getVideoViewActivityPubUrl (byActor: MActorUrl, video: MVideoId) { return byActor.url + '/views/videos/' + video.id + '/' + new Date().toISOString() } -function getVideoLikeActivityPubUrl (byActor: ActorModelUrl, video: VideoModel | { id: number }) { +function getVideoLikeActivityPubUrl (byActor: MActorUrl, video: MVideoId) { return byActor.url + '/likes/' + video.id } -function getVideoDislikeActivityPubUrl (byActor: ActorModelUrl, video: VideoModel | { id: number }) { +function getVideoDislikeActivityPubUrl (byActor: MActorUrl, video: MVideoId) { return byActor.url + '/dislikes/' + video.id } -function getVideoSharesActivityPubUrl (video: VideoModel) { +function getVideoSharesActivityPubUrl (video: MVideoUrl) { return video.url + '/announces' } -function getVideoCommentsActivityPubUrl (video: VideoModel) { +function getVideoCommentsActivityPubUrl (video: MVideoUrl) { return video.url + '/comments' } -function getVideoLikesActivityPubUrl (video: VideoModel) { +function getVideoLikesActivityPubUrl (video: MVideoUrl) { return video.url + '/likes' } -function getVideoDislikesActivityPubUrl (video: VideoModel) { +function getVideoDislikesActivityPubUrl (video: MVideoUrl) { return video.url + '/dislikes' } -function getActorFollowActivityPubUrl (follower: ActorModelOnly, following: ActorModelOnly) { +function getActorFollowActivityPubUrl (follower: MActor, following: MActorId) { return follower.url + '/follows/' + following.id } -function getActorFollowAcceptActivityPubUrl (actorFollow: ActorFollowModelLight) { +function getActorFollowAcceptActivityPubUrl (actorFollow: MActorFollowActors) { const follower = actorFollow.ActorFollower const me = actorFollow.ActorFollowing return follower.url + '/accepts/follows/' + me.id } -function getActorFollowRejectActivityPubUrl (follower: ActorModelOnly, following: ActorModelOnly) { +function getActorFollowRejectActivityPubUrl (follower: MActorUrl, following: MActorId) { return follower.url + '/rejects/follows/' + following.id } -function getVideoAnnounceActivityPubUrl (byActor: ActorModelOnly, video: VideoModel) { +function getVideoAnnounceActivityPubUrl (byActor: MActorId, video: MVideoUrl) { return video.url + '/announces/' + byActor.id } 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 import { logger } from '../../helpers/logger' import { doRequest } from '../../helpers/requests' import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' -import { VideoModel } from '../../models/video/video' import { VideoCommentModel } from '../../models/video/video-comment' import { getOrCreateActorAndServerAndModel } from './actor' import { getOrCreateVideoAndAccountAndChannel } from './videos' import * as Bluebird from 'bluebird' import { checkUrlsSameHost } from '../../helpers/activitypub' +import { MCommentOwner, MCommentOwnerVideo, MVideoAccountAllFiles } from '../../typings/models/video' type ResolveThreadParams = { url: string, - comments?: VideoCommentModel[], + comments?: MCommentOwner[], isVideo?: boolean, commentCreated?: boolean } -type ResolveThreadResult = Promise<{ video: VideoModel, comment: VideoCommentModel, commentCreated: boolean }> +type ResolveThreadResult = Promise<{ video: MVideoAccountAllFiles, comment: MCommentOwnerVideo, commentCreated: boolean }> async function addVideoComments (commentUrls: string[]) { return Bluebird.map(commentUrls, commentUrl => { @@ -85,9 +85,9 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) { const syncParam = { likes: true, dislikes: true, shares: true, comments: false, thumbnail: true, refreshVideo: false } const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: url, syncParam }) - let resultComment: VideoCommentModel + let resultComment: MCommentOwnerVideo if (comments.length !== 0) { - const firstReply = comments[ comments.length - 1 ] + const firstReply = comments[ comments.length - 1 ] as MCommentOwnerVideo firstReply.inReplyToCommentId = null firstReply.originCommentId = null firstReply.videoId = video.id @@ -97,7 +97,7 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) { comments[comments.length - 1] = await firstReply.save() for (let i = comments.length - 2; i >= 0; i--) { - const comment = comments[ i ] + const comment = comments[ i ] as MCommentOwnerVideo comment.originCommentId = firstReply.id comment.inReplyToCommentId = comments[ i + 1 ].id comment.videoId = video.id @@ -107,7 +107,7 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) { comments[i] = await comment.save() } - resultComment = comments[0] + resultComment = comments[0] as MCommentOwnerVideo } return { video, comment: resultComment, commentCreated } @@ -151,7 +151,7 @@ async function resolveParentComment (params: ResolveThreadParams) { originCommentId: null, createdAt: new Date(body.published), updatedAt: new Date(body.updated) - }) + }) as MCommentOwner comment.Account = actor.Account 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 @@ import { Transaction } from 'sequelize' -import { AccountModel } from '../../models/account/account' -import { VideoModel } from '../../models/video/video' import { sendLike, sendUndoDislike, sendUndoLike } from './send' import { VideoRateType } from '../../../shared/models/videos' import * as Bluebird from 'bluebird' @@ -10,11 +8,11 @@ import { logger } from '../../helpers/logger' import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' import { doRequest } from '../../helpers/requests' import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' -import { ActorModel } from '../../models/activitypub/actor' import { getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from './url' import { sendDislike } from './send/send-dislike' +import { MAccountActor, MActorUrl, MVideo, MVideoAccountLight, MVideoId } from '../../typings/models' -async function createRates (ratesUrl: string[], video: VideoModel, rate: VideoRateType) { +async function createRates (ratesUrl: string[], video: MVideo, rate: VideoRateType) { let rateCounts = 0 await Bluebird.map(ratesUrl, async rateUrl => { @@ -64,11 +62,13 @@ async function createRates (ratesUrl: string[], video: VideoModel, rate: VideoRa return } -async function sendVideoRateChange (account: AccountModel, - video: VideoModel, - likes: number, - dislikes: number, - t: Transaction) { +async function sendVideoRateChange ( + account: MAccountActor, + video: MVideoAccountLight, + likes: number, + dislikes: number, + t: Transaction +) { const actor = account.Actor // Keep the order: first we undo and then we create @@ -84,8 +84,10 @@ async function sendVideoRateChange (account: AccountModel, if (dislikes > 0) await sendDislike(actor, video, t) } -function getRateUrl (rateType: VideoRateType, actor: ActorModel, video: VideoModel) { - return rateType === 'like' ? getVideoLikeActivityPubUrl(actor, video) : getVideoDislikeActivityPubUrl(actor, video) +function getRateUrl (rateType: VideoRateType, actor: MActorUrl, video: MVideoId) { + return rateType === 'like' + ? getVideoLikeActivityPubUrl(actor, video) + : getVideoDislikeActivityPubUrl(actor, video) } export { 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 { REMOTE_SCHEME, STATIC_PATHS } from '../../initializers/constants' -import { ActorModel } from '../../models/activitypub/actor' import { TagModel } from '../../models/video/tag' import { VideoModel } from '../../models/video/video' import { VideoFileModel } from '../../models/video/video-file' @@ -38,7 +37,6 @@ import { JobQueue } from '../job-queue' import { ActivitypubHttpFetcherPayload } from '../job-queue/handlers/activitypub-http-fetcher' import { createRates } from './video-rates' import { addVideoShares, shareVideoByServerAndChannel } from './share' -import { AccountModel } from '../../models/account/account' import { fetchVideoByUrl, VideoFetchByUrlType } from '../../helpers/video' import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' import { Notifier } from '../notifier' @@ -49,15 +47,33 @@ import { VideoShareModel } from '../../models/video/video-share' import { VideoCommentModel } from '../../models/video/video-comment' import { sequelizeTypescript } from '../../initializers/database' import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '../thumbnail' -import { ThumbnailModel } from '../../models/video/thumbnail' import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' import { join } from 'path' import { FilteredModelAttributes } from '../../typings/sequelize' import { autoBlacklistVideoIfNeeded } from '../video-blacklist' import { ActorFollowScoreCache } from '../files-cache' -import { AccountModelIdActor, VideoChannelModelId, VideoChannelModelIdActor } from '../../typings/models' +import { + MAccountActor, + MChannelAccountLight, + MChannelDefault, + MChannelId, + MVideo, + MVideoAccountAllFiles, + MVideoAccountLight, + MVideoAP, + MVideoAPWithoutCaption, + MVideoFile, + MVideoFullLight, + MVideoId, + MVideoTag, + MVideoThumbnail, + MVideoWithAllFiles +} from '../../typings/models' +import { MThumbnail } from '../../typings/models/video/thumbnail' + +async function federateVideoIfNeeded (videoArg: MVideoAPWithoutCaption, isNewVideo: boolean, transaction?: sequelize.Transaction) { + const video = videoArg as MVideoAP -async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) { if ( // Check this is not a blacklisted video, or unfederated blacklisted video (video.isBlacklisted() === false || (isNewVideo === false && video.VideoBlacklist.unfederated === false)) && @@ -102,7 +118,7 @@ async function fetchRemoteVideo (videoUrl: string): Promise<{ response: request. return { response, videoObject: body } } -async function fetchRemoteVideoDescription (video: VideoModel) { +async function fetchRemoteVideoDescription (video: MVideoAccountLight) { const host = video.VideoChannel.Account.Actor.Server.host const path = video.getDescriptionAPIPath() const options = { @@ -114,14 +130,14 @@ async function fetchRemoteVideoDescription (video: VideoModel) { return body.description ? body.description : '' } -function fetchRemoteVideoStaticFile (video: VideoModel, path: string, destPath: string) { +function fetchRemoteVideoStaticFile (video: MVideoAccountLight, path: string, destPath: string) { const url = buildRemoteBaseUrl(video, path) // We need to provide a callback, if no we could have an uncaught exception return doRequestAndSaveToFile({ uri: url }, destPath) } -function buildRemoteBaseUrl (video: VideoModel, path: string) { +function buildRemoteBaseUrl (video: MVideoAccountLight, path: string) { const host = video.VideoChannel.Account.Actor.Server.host return REMOTE_SCHEME.HTTP + '://' + host + path @@ -146,7 +162,7 @@ type SyncParam = { thumbnail: boolean refreshVideo?: boolean } -async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) { +async function syncVideoExternalAttributes (video: MVideo, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) { logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid) const jobPayloads: ActivitypubHttpFetcherPayload[] = [] @@ -194,12 +210,24 @@ async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: Vid await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })) } +function getOrCreateVideoAndAccountAndChannel (options: { + videoObject: { id: string } | string, + syncParam?: SyncParam, + fetchType?: 'all', + allowRefresh?: boolean +}): Promise<{ video: MVideoAccountAllFiles, created: boolean, autoBlacklisted?: boolean }> +function getOrCreateVideoAndAccountAndChannel (options: { + videoObject: { id: string } | string, + syncParam?: SyncParam, + fetchType?: VideoFetchByUrlType, + allowRefresh?: boolean +}): Promise<{ video: MVideoAccountAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }> async function getOrCreateVideoAndAccountAndChannel (options: { videoObject: { id: string } | string, syncParam?: SyncParam, fetchType?: VideoFetchByUrlType, allowRefresh?: boolean // true by default -}) { +}): Promise<{ video: MVideoAccountAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }> { // Default params const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } const fetchType = options.fetchType || 'all' @@ -227,8 +255,9 @@ async function getOrCreateVideoAndAccountAndChannel (options: { const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl) if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl) - const channelActor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo) - const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, channelActor, syncParam.thumbnail) + const actor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo) + const videoChannel = actor.VideoChannel + const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, videoChannel, syncParam.thumbnail) await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam) @@ -236,22 +265,22 @@ async function getOrCreateVideoAndAccountAndChannel (options: { } async function updateVideoFromAP (options: { - video: VideoModel, + video: MVideoAccountAllFiles, videoObject: VideoTorrentObject, - account: AccountModelIdActor, - channel: VideoChannelModelIdActor, + account: MAccountActor, + channel: MChannelDefault, overrideTo?: string[] }) { const { video, videoObject, account, channel, overrideTo } = options - logger.debug('Updating remote video "%s".', options.videoObject.uuid) + logger.debug('Updating remote video "%s".', options.videoObject.uuid, { account, channel }) let videoFieldsSave: any const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE const wasUnlistedVideo = video.privacy === VideoPrivacy.UNLISTED try { - let thumbnailModel: ThumbnailModel + let thumbnailModel: MThumbnail try { thumbnailModel = await createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) @@ -259,7 +288,7 @@ async function updateVideoFromAP (options: { logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err }) } - await sequelizeTypescript.transaction(async t => { + const videoUpdated = await sequelizeTypescript.transaction(async t => { const sequelizeOptions = { transaction: t } videoFieldsSave = video.toJSON() @@ -293,21 +322,21 @@ async function updateVideoFromAP (options: { video.channelId = videoData.channelId video.views = videoData.views - await video.save(sequelizeOptions) + const videoUpdated = await video.save(sequelizeOptions) as MVideoFullLight - if (thumbnailModel) await video.addAndSaveThumbnail(thumbnailModel, t) + if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel, t) // FIXME: use icon URL instead - const previewUrl = buildRemoteBaseUrl(video, join(STATIC_PATHS.PREVIEWS, video.getPreview().filename)) + const previewUrl = buildRemoteBaseUrl(videoUpdated, join(STATIC_PATHS.PREVIEWS, videoUpdated.getPreview().filename)) const previewModel = createPlaceholderThumbnail(previewUrl, video, ThumbnailType.PREVIEW, PREVIEWS_SIZE) - await video.addAndSaveThumbnail(previewModel, t) + await videoUpdated.addAndSaveThumbnail(previewModel, t) { - const videoFileAttributes = videoFileActivityUrlToDBAttributes(video, videoObject) + const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoUpdated, videoObject) const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a)) // Remove video files that do not exist anymore - const destroyTasks = video.VideoFiles + const destroyTasks = videoUpdated.VideoFiles .filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f))) .map(f => f.destroy(sequelizeOptions)) await Promise.all(destroyTasks) @@ -318,15 +347,15 @@ async function updateVideoFromAP (options: { .then(([ file ]) => file) }) - video.VideoFiles = await Promise.all(upsertTasks) + videoUpdated.VideoFiles = await Promise.all(upsertTasks) } { - const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(video, videoObject, video.VideoFiles) + const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(videoUpdated, videoObject, videoUpdated.VideoFiles) const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) // Remove video files that do not exist anymore - const destroyTasks = video.VideoStreamingPlaylists + const destroyTasks = videoUpdated.VideoStreamingPlaylists .filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f))) .map(f => f.destroy(sequelizeOptions)) await Promise.all(destroyTasks) @@ -337,38 +366,42 @@ async function updateVideoFromAP (options: { .then(([ streamingPlaylist ]) => streamingPlaylist) }) - video.VideoStreamingPlaylists = await Promise.all(upsertTasks) + videoUpdated.VideoStreamingPlaylists = await Promise.all(upsertTasks) } { // Update Tags const tags = videoObject.tag.map(tag => tag.name) const tagInstances = await TagModel.findOrCreateTags(tags, t) - await video.$set('Tags', tagInstances, sequelizeOptions) + await videoUpdated.$set('Tags', tagInstances, sequelizeOptions) } { // Update captions - await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(video.id, t) + await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(videoUpdated.id, t) const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { - return VideoCaptionModel.insertOrReplaceLanguage(video.id, c.identifier, t) + return VideoCaptionModel.insertOrReplaceLanguage(videoUpdated.id, c.identifier, t) }) - video.VideoCaptions = await Promise.all(videoCaptionsPromises) + await Promise.all(videoCaptionsPromises) } + + return videoUpdated }) await autoBlacklistVideoIfNeeded({ - video, + video: videoUpdated, user: undefined, isRemote: true, isNew: false, transaction: undefined }) - if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(video) // Notify our users? + if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated) // Notify our users? logger.info('Remote video with uuid %s updated', videoObject.uuid) + + return videoUpdated } catch (err) { if (video !== undefined && videoFieldsSave !== undefined) { resetSequelizeInstance(video, videoFieldsSave) @@ -381,15 +414,15 @@ async function updateVideoFromAP (options: { } async function refreshVideoIfNeeded (options: { - video: VideoModel, + video: MVideoThumbnail, fetchedType: VideoFetchByUrlType, syncParam: SyncParam -}): Promise { +}): Promise { if (!options.video.isOutdated()) return options.video // We need more attributes if the argument video was fetched with not enough joints const video = options.fetchedType === 'all' - ? options.video + ? options.video as MVideoAccountAllFiles : await VideoModel.loadByUrlAndPopulateAccount(options.video.url) try { @@ -410,12 +443,11 @@ async function refreshVideoIfNeeded (options: { } const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) - const account = await AccountModel.load(channelActor.VideoChannel.accountId) const updateOptions = { video, videoObject, - account, + account: channelActor.VideoChannel.Account, channel: channelActor.VideoChannel } await retryTransactionWrapper(updateVideoFromAP, updateOptions) @@ -467,15 +499,15 @@ function isAPPlaylistSegmentHashesUrlObject (tag: any): tag is ActivityPlaylistS return tag.name === 'sha256' && tag.type === 'Link' && urlMediaType === 'application/json' } -async function createVideo (videoObject: VideoTorrentObject, channelActor: ActorModel, waitThumbnail = false) { +async function createVideo (videoObject: VideoTorrentObject, channel: MChannelAccountLight, waitThumbnail = false) { logger.debug('Adding remote video %s.', videoObject.id) - const videoData = await videoActivityObjectToDBAttributes(channelActor.VideoChannel, videoObject, videoObject.to) - const video = VideoModel.build(videoData) + const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, videoObject.to) + const video = VideoModel.build(videoData) as MVideoThumbnail const promiseThumbnail = createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) - let thumbnailModel: ThumbnailModel + let thumbnailModel: MThumbnail if (waitThumbnail === true) { thumbnailModel = await promiseThumbnail } @@ -483,8 +515,8 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => { const sequelizeOptions = { transaction: t } - const videoCreated = await video.save(sequelizeOptions) - videoCreated.VideoChannel = channelActor.VideoChannel + const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight + videoCreated.VideoChannel = channel if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) @@ -517,15 +549,14 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { return VideoCaptionModel.insertOrReplaceLanguage(videoCreated.id, c.identifier, t) }) - const captions = await Promise.all(videoCaptionsPromises) + await Promise.all(videoCaptionsPromises) - video.VideoFiles = videoFiles - video.VideoStreamingPlaylists = streamingPlaylists - video.Tags = tagInstances - video.VideoCaptions = captions + videoCreated.VideoFiles = videoFiles + videoCreated.VideoStreamingPlaylists = streamingPlaylists + videoCreated.Tags = tagInstances const autoBlacklisted = await autoBlacklistVideoIfNeeded({ - video, + video: videoCreated, user: undefined, isRemote: true, isNew: true, @@ -548,11 +579,7 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor return { autoBlacklisted, videoCreated } } -async function videoActivityObjectToDBAttributes ( - videoChannel: VideoChannelModelId, - videoObject: VideoTorrentObject, - to: string[] = [] -) { +async function videoActivityObjectToDBAttributes (videoChannel: MChannelId, videoObject: VideoTorrentObject, to: string[] = []) { const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED const duration = videoObject.duration.replace(/[^\d]+/, '') @@ -603,7 +630,7 @@ async function videoActivityObjectToDBAttributes ( } } -function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: VideoTorrentObject) { +function videoFileActivityUrlToDBAttributes (video: MVideo, videoObject: VideoTorrentObject) { const fileUrls = videoObject.url.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[] if (fileUrls.length === 0) { @@ -641,7 +668,7 @@ function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: Vid return attributes } -function streamingPlaylistActivityUrlToDBAttributes (video: VideoModel, videoObject: VideoTorrentObject, videoFiles: VideoFileModel[]) { +function streamingPlaylistActivityUrlToDBAttributes (video: MVideoId, videoObject: VideoTorrentObject, videoFiles: MVideoFile[]) { const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[] if (playlistUrls.length === 0) return [] 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' import { AVATARS_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants' import { updateActorAvatarInstance } from './activitypub' import { processImage } from '../helpers/image-utils' -import { AccountModel } from '../models/account/account' -import { VideoChannelModel } from '../models/video/video-channel' import { extname, join } from 'path' import { retryTransactionWrapper } from '../helpers/database-utils' import * as uuidv4 from 'uuid/v4' @@ -13,8 +11,12 @@ import { sequelizeTypescript } from '../initializers/database' import * as LRUCache from 'lru-cache' import { queue } from 'async' import { downloadImage } from '../helpers/requests' +import { MAccountActorDefault, MChannelActorDefault } from '../typings/models' -async function updateActorAvatarFile (avatarPhysicalFile: Express.Multer.File, accountOrChannel: AccountModel | VideoChannelModel) { +async function updateActorAvatarFile ( + avatarPhysicalFile: Express.Multer.File, + accountOrChannel: MAccountActorDefault | MChannelActorDefault +) { const extension = extname(avatarPhysicalFile.filename) const avatarName = uuidv4() + extension 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 @@ import { sequelizeTypescript } from '../initializers' import { AccountBlocklistModel } from '../models/account/account-blocklist' import { ServerBlocklistModel } from '../models/server/server-blocklist' +import { MAccountBlocklist, MServerBlocklist } from '@server/typings/models' function addAccountInBlocklist (byAccountId: number, targetAccountId: number) { return sequelizeTypescript.transaction(async t => { @@ -20,13 +21,13 @@ function addServerInBlocklist (byAccountId: number, targetServerId: number) { }) } -function removeAccountFromBlocklist (accountBlock: AccountBlocklistModel) { +function removeAccountFromBlocklist (accountBlock: MAccountBlocklist) { return sequelizeTypescript.transaction(async t => { return accountBlock.destroy({ transaction: t }) }) } -function removeServerFromBlocklist (serverBlock: ServerBlocklistModel) { +function removeServerFromBlocklist (serverBlock: MServerBlocklist) { return sequelizeTypescript.transaction(async t => { return serverBlock.destroy({ transaction: t }) }) 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' import * as Bluebird from 'bluebird' import { CONFIG } from '../initializers/config' import { logger } from '../helpers/logger' +import { MAccountActor, MChannelActor, MVideo } from '../typings/models' export class ClientHtml { @@ -65,7 +66,7 @@ export class ClientHtml { } private static async getAccountOrChannelHTMLPage ( - loader: () => Bluebird, + loader: () => Bluebird, req: express.Request, res: express.Response ) { @@ -157,7 +158,7 @@ export class ClientHtml { return htmlStringPage.replace('', linkTag + '') } - private static addVideoOpenGraphAndOEmbedTags (htmlStringPage: string, video: VideoModel) { + private static addVideoOpenGraphAndOEmbedTags (htmlStringPage: string, video: MVideo) { const previewUrl = WEBSERVER.URL + video.getPreviewStaticPath() const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() @@ -236,7 +237,7 @@ export class ClientHtml { return this.addOpenGraphAndOEmbedTags(htmlStringPage, tagsString) } - private static addAccountOrChannelMetaTags (htmlStringPage: string, entity: AccountModel | VideoChannelModel) { + private static addAccountOrChannelMetaTags (htmlStringPage: string, entity: MAccountActor | MChannelActor) { // SEO, use origin account or channel URL const metaTags = `` 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' import { bunyanLogger, logger } from '../helpers/logger' import { CONFIG } from '../initializers/config' import { UserModel } from '../models/account/user' -import { VideoModel } from '../models/video/video' import { JobQueue } from './job-queue' import { EmailPayload } from './job-queue/handlers/email' import { readFileSync } from 'fs-extra' -import { VideoCommentModel } from '../models/video/video-comment' -import { VideoAbuseModel } from '../models/video/video-abuse' import { VideoBlacklistModel } from '../models/video/video-blacklist' -import { VideoImportModel } from '../models/video/video-import' -import { ActorFollowModel } from '../models/activitypub/actor-follow' import { WEBSERVER } from '../initializers/constants' +import { MCommentOwnerVideo, MVideo, MVideoAbuseVideo, MVideoAccountLight, MVideoBlacklistVideo } from '../typings/models/video' +import { MActorFollowActors, MActorFollowFull, MUser } from '../typings/models' +import { MVideoImport, MVideoImportVideo } from '@server/typings/models/video/video-import' type SendEmailOptions = { to: string[] @@ -90,7 +88,7 @@ class Emailer { } } - addNewVideoFromSubscriberNotification (to: string[], video: VideoModel) { + addNewVideoFromSubscriberNotification (to: string[], video: MVideoAccountLight) { const channelName = video.VideoChannel.getDisplayName() const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() @@ -111,7 +109,7 @@ class Emailer { return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) } - addNewFollowNotification (to: string[], actorFollow: ActorFollowModel, followType: 'account' | 'channel') { + addNewFollowNotification (to: string[], actorFollow: MActorFollowFull, followType: 'account' | 'channel') { const followerName = actorFollow.ActorFollower.Account.getDisplayName() const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName() @@ -130,7 +128,7 @@ class Emailer { return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) } - addNewInstanceFollowerNotification (to: string[], actorFollow: ActorFollowModel) { + addNewInstanceFollowerNotification (to: string[], actorFollow: MActorFollowActors) { const awaitingApproval = actorFollow.state === 'pending' ? ' awaiting manual approval.' : '' const text = `Hi dear admin,\n\n` + @@ -148,7 +146,7 @@ class Emailer { return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) } - myVideoPublishedNotification (to: string[], video: VideoModel) { + myVideoPublishedNotification (to: string[], video: MVideo) { const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() const text = `Hi dear user,\n\n` + @@ -168,7 +166,7 @@ class Emailer { return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) } - myVideoImportSuccessNotification (to: string[], videoImport: VideoImportModel) { + myVideoImportSuccessNotification (to: string[], videoImport: MVideoImportVideo) { const videoUrl = WEBSERVER.URL + videoImport.Video.getWatchStaticPath() const text = `Hi dear user,\n\n` + @@ -188,7 +186,7 @@ class Emailer { return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) } - myVideoImportErrorNotification (to: string[], videoImport: VideoImportModel) { + myVideoImportErrorNotification (to: string[], videoImport: MVideoImport) { const importUrl = WEBSERVER.URL + '/my-account/video-imports' const text = `Hi dear user,\n\n` + @@ -208,7 +206,7 @@ class Emailer { return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) } - addNewCommentOnMyVideoNotification (to: string[], comment: VideoCommentModel) { + addNewCommentOnMyVideoNotification (to: string[], comment: MCommentOwnerVideo) { const accountName = comment.Account.getDisplayName() const video = comment.Video const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() @@ -230,7 +228,7 @@ class Emailer { return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) } - addNewCommentMentionNotification (to: string[], comment: VideoCommentModel) { + addNewCommentMentionNotification (to: string[], comment: MCommentOwnerVideo) { const accountName = comment.Account.getDisplayName() const video = comment.Video const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() @@ -252,7 +250,7 @@ class Emailer { return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) } - addVideoAbuseModeratorsNotification (to: string[], videoAbuse: VideoAbuseModel) { + addVideoAbuseModeratorsNotification (to: string[], videoAbuse: MVideoAbuseVideo) { const videoUrl = WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath() const text = `Hi,\n\n` + @@ -269,7 +267,7 @@ class Emailer { return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) } - addVideoAutoBlacklistModeratorsNotification (to: string[], video: VideoModel) { + addVideoAutoBlacklistModeratorsNotification (to: string[], video: MVideo) { const VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list' const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() @@ -292,7 +290,7 @@ class Emailer { return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) } - addNewUserRegistrationNotification (to: string[], user: UserModel) { + addNewUserRegistrationNotification (to: string[], user: MUser) { const text = `Hi,\n\n` + `User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` + `Cheers,\n` + @@ -307,7 +305,7 @@ class Emailer { return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) } - addVideoBlacklistNotification (to: string[], videoBlacklist: VideoBlacklistModel) { + addVideoBlacklistNotification (to: string[], videoBlacklist: MVideoBlacklistVideo) { const videoName = videoBlacklist.Video.name const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() @@ -329,7 +327,7 @@ class Emailer { return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) } - addVideoUnblacklistNotification (to: string[], video: VideoModel) { + addVideoUnblacklistNotification (to: string[], video: MVideo) { const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() const text = 'Hi,\n\n' + @@ -381,7 +379,7 @@ class Emailer { return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) } - addUserBlockJob (user: UserModel, blocked: boolean, reason?: string) { + addUserBlockJob (user: MUser, blocked: boolean, reason?: string) { const reasonString = reason ? ` for the following reason: ${reason}` : '' const blockedWord = blocked ? 'blocked' : 'unblocked' 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 @@ -import { VideoModel } from '../models/video/video' import { basename, dirname, join } from 'path' import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from '../initializers/constants' import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, writeFile } from 'fs-extra' @@ -12,6 +11,7 @@ import { flatten, uniq } from 'lodash' import { VideoFileModel } from '../models/video/video-file' import { CONFIG } from '../initializers/config' import { sequelizeTypescript } from '../initializers/database' +import { MVideoWithFile } from '@server/typings/models' async function updateStreamingPlaylistsInfohashesIfNeeded () { const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion() @@ -28,7 +28,7 @@ async function updateStreamingPlaylistsInfohashesIfNeeded () { } } -async function updateMasterHLSPlaylist (video: VideoModel) { +async function updateMasterHLSPlaylist (video: MVideoWithFile) { const directory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ] const masterPlaylistPath = join(directory, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename()) @@ -55,7 +55,7 @@ async function updateMasterHLSPlaylist (video: VideoModel) { await writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n') } -async function updateSha256Segments (video: VideoModel) { +async function updateSha256Segments (video: MVideoWithFile) { const json: { [filename: string]: { [range: string]: string } } = {} 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' import { ActorModel } from '../../../models/activitypub/actor' import { Notifier } from '../../notifier' import { sequelizeTypescript } from '../../../initializers/database' +import { MActorFollowFull, MActorFull } from '../../../typings/models' export type ActivitypubFollowPayload = { followerActorId: number @@ -23,13 +24,13 @@ async function processActivityPubFollow (job: Bull.Job) { logger.info('Processing ActivityPub follow in job %d.', job.id) - let targetActor: ActorModel + let targetActor: MActorFull if (!host || host === WEBSERVER.HOST) { targetActor = await ActorModel.loadLocalByName(payload.name) } else { const sanitizedHost = sanitizeHost(host, REMOTE_SCHEME.HTTP) const actorUrl = await loadActorUrlOrGetFromWebfinger(payload.name + '@' + sanitizedHost) - targetActor = await getOrCreateActorAndServerAndModel(actorUrl) + targetActor = await getOrCreateActorAndServerAndModel(actorUrl, 'all') } const fromActor = await ActorModel.load(payload.followerActorId) @@ -44,7 +45,7 @@ export { // --------------------------------------------------------------------------- -async function follow (fromActor: ActorModel, targetActor: ActorModel) { +async function follow (fromActor: MActorFull, targetActor: MActorFull) { if (fromActor.id === targetActor.id) { throw new Error('Follower is the same than target actor.') } @@ -53,7 +54,7 @@ async function follow (fromActor: ActorModel, targetActor: ActorModel) { const state = !fromActor.serverId && !targetActor.serverId ? 'accepted' : 'pending' const actorFollow = await sequelizeTypescript.transaction(async t => { - const [ actorFollow ] = await ActorFollowModel.findOrCreate({ + const [ actorFollow ] = await ActorFollowModel.findOrCreate({ where: { actorId: fromActor.id, 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' import { AccountVideoRateModel } from '../../../models/account/account-video-rate' import { VideoShareModel } from '../../../models/video/video-share' import { VideoCommentModel } from '../../../models/video/video-comment' +import { MAccountDefault, MVideoFullLight } from '../../../typings/models' type FetchType = 'activity' | 'video-likes' | 'video-dislikes' | 'video-shares' | 'video-comments' | 'account-playlists' @@ -26,10 +27,10 @@ async function processActivityPubHttpFetcher (job: Bull.Job) { const payload = job.data as ActivitypubHttpFetcherPayload - let video: VideoModel + let video: MVideoFullLight if (payload.videoId) video = await VideoModel.loadAndPopulateAccountAndServerAndTags(payload.videoId) - let account: AccountModel + let account: MAccountDefault if (payload.accountId) account = await AccountModel.load(payload.accountId) const fetcherType: { [ id in FetchType ]: (items: any[]) => Promise } = { 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' import { ActorModel } from '../../../../models/activitypub/actor' import { sha256 } from '../../../../helpers/core-utils' import { HTTP_SIGNATURE } from '../../../../initializers/constants' +import { MActor } from '../../../../typings/models' type Payload = { body: any, signatureActorId?: number } @@ -19,7 +20,8 @@ async function computeBody (payload: Payload) { } async function buildSignedRequestOptions (payload: Payload) { - let actor: ActorModel | null + let actor: MActor | null + if (payload.signatureActorId) { actor = await ActorModel.load(payload.signatureActorId) 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 import { copy, stat } from 'fs-extra' import { VideoFileModel } from '../../../models/video/video-file' import { extname } from 'path' +import { MVideoFile, MVideoWithFile } from '@server/typings/models' export type VideoFileImportPayload = { videoUUID: string, @@ -37,7 +38,7 @@ export { // --------------------------------------------------------------------------- -async function updateVideoFile (video: VideoModel, inputFilePath: string) { +async function updateVideoFile (video: MVideoWithFile, inputFilePath: string) { const { videoFileResolution } = await getVideoFileResolution(inputFilePath) const { size } = await stat(inputFilePath) const fps = await getVideoFileFPS(inputFilePath) @@ -48,7 +49,7 @@ async function updateVideoFile (video: VideoModel, inputFilePath: string) { size, fps, videoId: video.id - }) + }) as MVideoFile const currentVideoFile = video.VideoFiles.find(videoFile => videoFile.resolution === updatedVideoFile.resolution) @@ -60,9 +61,9 @@ async function updateVideoFile (video: VideoModel, inputFilePath: string) { video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile) // Update the database - currentVideoFile.set('extname', updatedVideoFile.extname) - currentVideoFile.set('size', updatedVideoFile.size) - currentVideoFile.set('fps', updatedVideoFile.fps) + currentVideoFile.extname = updatedVideoFile.extname + currentVideoFile.size = updatedVideoFile.size + currentVideoFile.fps = updatedVideoFile.fps updatedVideoFile = currentVideoFile } 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' import { Notifier } from '../../notifier' import { CONFIG } from '../../../initializers/config' import { sequelizeTypescript } from '../../../initializers/database' -import { ThumbnailModel } from '../../../models/video/thumbnail' import { createVideoMiniatureFromUrl, generateVideoMiniature } from '../../thumbnail' import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' +import { MThumbnail } from '../../../typings/models/video/thumbnail' +import { MVideoImportDefault, MVideoImportDefaultFiles, MVideoImportVideo } from '@server/typings/models/video/video-import' type VideoImportYoutubeDLPayload = { type: 'youtube-dl' @@ -110,11 +111,13 @@ type ProcessFileOptions = { generateThumbnail: boolean generatePreview: boolean } -async function processFile (downloader: () => Promise, videoImport: VideoImportModel, options: ProcessFileOptions) { +async function processFile (downloader: () => Promise, videoImportArg: MVideoImportDefault, options: ProcessFileOptions) { let tempVideoPath: string let videoDestFile: string let videoFile: VideoFileModel + const videoImport = videoImportArg as MVideoImportDefaultFiles + try { // Download video from youtubeDL tempVideoPath = await downloader() @@ -148,7 +151,7 @@ async function processFile (downloader: () => Promise, videoImport: Vide tempVideoPath = null // This path is not used anymore // Process thumbnail - let thumbnailModel: ThumbnailModel + let thumbnailModel: MThumbnail if (options.downloadThumbnail && options.thumbnailUrl) { thumbnailModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.MINIATURE) } else if (options.generateThumbnail || options.downloadThumbnail) { @@ -156,7 +159,7 @@ async function processFile (downloader: () => Promise, videoImport: Vide } // Process preview - let previewModel: ThumbnailModel + let previewModel: MThumbnail if (options.downloadPreview && options.thumbnailUrl) { previewModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.PREVIEW) } else if (options.generatePreview || options.downloadPreview) { @@ -166,14 +169,15 @@ async function processFile (downloader: () => Promise, videoImport: Vide // Create torrent await videoImport.Video.createTorrentAndSetInfoHash(videoFile) - const videoImportUpdated: VideoImportModel = await sequelizeTypescript.transaction(async t => { + const { videoImportUpdated, video } = await sequelizeTypescript.transaction(async t => { + const videoImportToUpdate = videoImport as MVideoImportVideo + // Refresh video - const video = await VideoModel.load(videoImport.videoId, t) - if (!video) throw new Error('Video linked to import ' + videoImport.videoId + ' does not exist anymore.') - videoImport.Video = video + const video = await VideoModel.load(videoImportToUpdate.videoId, t) + if (!video) throw new Error('Video linked to import ' + videoImportToUpdate.videoId + ' does not exist anymore.') const videoFileCreated = await videoFile.save({ transaction: t }) - video.VideoFiles = [ videoFileCreated ] + videoImportToUpdate.Video = Object.assign(video, { VideoFiles: [ videoFileCreated ] }) // Update video DB object video.duration = duration @@ -188,25 +192,25 @@ async function processFile (downloader: () => Promise, videoImport: Vide await federateVideoIfNeeded(videoForFederation, true, t) // Update video import object - videoImport.state = VideoImportState.SUCCESS - const videoImportUpdated = await videoImport.save({ transaction: t }) + videoImportToUpdate.state = VideoImportState.SUCCESS + const videoImportUpdated = await videoImportToUpdate.save({ transaction: t }) as MVideoImportVideo + videoImportUpdated.Video = video logger.info('Video %s imported.', video.uuid) - videoImportUpdated.Video = videoForFederation - return videoImportUpdated + return { videoImportUpdated, video: videoForFederation } }) Notifier.Instance.notifyOnFinishedVideoImport(videoImportUpdated, true) - if (videoImportUpdated.Video.isBlacklisted()) { - Notifier.Instance.notifyOnVideoAutoBlacklist(videoImportUpdated.Video) + if (video.isBlacklisted()) { + Notifier.Instance.notifyOnVideoAutoBlacklist(video) } else { - Notifier.Instance.notifyOnNewVideoIfNeeded(videoImportUpdated.Video) + Notifier.Instance.notifyOnNewVideoIfNeeded(video) } // Create transcoding jobs? - if (videoImportUpdated.Video.state === VideoState.TO_TRANSCODE) { + if (video.state === VideoState.TO_TRANSCODE) { // Put uuid because we don't have id auto incremented for now const dataInput = { 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' import { generateHlsPlaylist, optimizeVideofile, transcodeOriginalVideofile, mergeAudioVideofile } from '../../video-transcoding' import { Notifier } from '../../notifier' import { CONFIG } from '../../../initializers/config' +import { MVideoUUID, MVideoWithFile } from '@server/typings/models' interface BaseTranscodingPayload { videoUUID: string @@ -73,7 +74,7 @@ async function processVideoTranscoding (job: Bull.Job) { return video } -async function onHlsPlaylistGenerationSuccess (video: VideoModel) { +async function onHlsPlaylistGenerationSuccess (video: MVideoUUID) { if (video === undefined) return undefined await sequelizeTypescript.transaction(async t => { @@ -87,7 +88,7 @@ async function onHlsPlaylistGenerationSuccess (video: VideoModel) { }) } -async function publishNewResolutionIfNeeded (video: VideoModel, payload?: NewResolutionTranscodingPayload | MergeAudioTranscodingPayload) { +async function publishNewResolutionIfNeeded (video: MVideoUUID, payload?: NewResolutionTranscodingPayload | MergeAudioTranscodingPayload) { const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => { // Maybe the video changed in database, refresh it let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t) @@ -119,7 +120,7 @@ async function publishNewResolutionIfNeeded (video: VideoModel, payload?: NewRes await createHlsJobIfEnabled(payload) } -async function onVideoFileOptimizerSuccess (videoArg: VideoModel, payload: OptimizeTranscodingPayload) { +async function onVideoFileOptimizerSuccess (videoArg: MVideoWithFile, payload: OptimizeTranscodingPayload) { if (videoArg === undefined) return undefined // 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' import { PeerTubeSocket } from './peertube-socket' import { CONFIG } from '../initializers/config' import { VideoPrivacy, VideoState } from '../../shared/models/videos' -import { VideoAbuseModel } from '../models/video/video-abuse' import { VideoBlacklistModel } from '../models/video/video-blacklist' import * as Bluebird from 'bluebird' import { VideoImportModel } from '../models/video/video-import' import { AccountBlocklistModel } from '../models/account/account-blocklist' +import { + MCommentOwnerVideo, + MVideo, + MVideoAbuseVideo, + MVideoAccountLight, + MVideoBlacklistVideo, + MVideoFullLight +} from '../typings/models/video' +import { MUser, MUserAccount, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/typings/models/user' +import { MActorFollowActors, MActorFollowFull } from '../typings/models' import { ActorFollowModel } from '../models/activitypub/actor-follow' -import { AccountModel } from '../models/account/account' +import { MVideoImportVideo } from '@server/typings/models/video/video-import' +import { AccountModel } from '@server/models/account/account' class Notifier { @@ -22,7 +32,7 @@ class Notifier { private constructor () {} - notifyOnNewVideoIfNeeded (video: VideoModel): void { + notifyOnNewVideoIfNeeded (video: MVideoAccountLight): void { // Only notify on public and published videos which are not blacklisted if (video.privacy !== VideoPrivacy.PUBLIC || video.state !== VideoState.PUBLISHED || video.isBlacklisted()) return @@ -30,7 +40,7 @@ class Notifier { .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err })) } - notifyOnVideoPublishedAfterTranscoding (video: VideoModel): void { + notifyOnVideoPublishedAfterTranscoding (video: MVideoFullLight): void { // don't notify if didn't wait for transcoding or video is still blacklisted/waiting for scheduled update if (!video.waitTranscoding || video.VideoBlacklist || video.ScheduleVideoUpdate) return @@ -38,7 +48,7 @@ class Notifier { .catch(err => logger.error('Cannot notify owner that its video %s has been published after transcoding.', video.url, { err })) } - notifyOnVideoPublishedAfterScheduledUpdate (video: VideoModel): void { + notifyOnVideoPublishedAfterScheduledUpdate (video: MVideoFullLight): void { // don't notify if video is still blacklisted or waiting for transcoding if (video.VideoBlacklist || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return @@ -46,7 +56,7 @@ class Notifier { .catch(err => logger.error('Cannot notify owner that its video %s has been published after scheduled update.', video.url, { err })) } - notifyOnVideoPublishedAfterRemovedFromAutoBlacklist (video: VideoModel): void { + notifyOnVideoPublishedAfterRemovedFromAutoBlacklist (video: MVideoFullLight): void { // don't notify if video is still waiting for transcoding or scheduled update if (video.ScheduleVideoUpdate || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return @@ -54,7 +64,7 @@ class Notifier { .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 } - notifyOnNewComment (comment: VideoCommentModel): void { + notifyOnNewComment (comment: MCommentOwnerVideo): void { this.notifyVideoOwnerOfNewComment(comment) .catch(err => logger.error('Cannot notify video owner of new comment %s.', comment.url, { err })) @@ -62,37 +72,37 @@ class Notifier { .catch(err => logger.error('Cannot notify mentions of comment %s.', comment.url, { err })) } - notifyOnNewVideoAbuse (videoAbuse: VideoAbuseModel): void { + notifyOnNewVideoAbuse (videoAbuse: MVideoAbuseVideo): void { this.notifyModeratorsOfNewVideoAbuse(videoAbuse) .catch(err => logger.error('Cannot notify of new video abuse of video %s.', videoAbuse.Video.url, { err })) } - notifyOnVideoAutoBlacklist (video: VideoModel): void { + notifyOnVideoAutoBlacklist (video: MVideo): void { this.notifyModeratorsOfVideoAutoBlacklist(video) .catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', video.url, { err })) } - notifyOnVideoBlacklist (videoBlacklist: VideoBlacklistModel): void { + notifyOnVideoBlacklist (videoBlacklist: MVideoBlacklistVideo): void { this.notifyVideoOwnerOfBlacklist(videoBlacklist) .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err })) } - notifyOnVideoUnblacklist (video: VideoModel): void { + notifyOnVideoUnblacklist (video: MVideo): void { this.notifyVideoOwnerOfUnblacklist(video) .catch(err => logger.error('Cannot notify video owner of unblacklist of %s.', video.url, { err })) } - notifyOnFinishedVideoImport (videoImport: VideoImportModel, success: boolean): void { + notifyOnFinishedVideoImport (videoImport: MVideoImportVideo, success: boolean): void { this.notifyOwnerVideoImportIsFinished(videoImport, success) .catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err })) } - notifyOnNewUserRegistration (user: UserModel): void { + notifyOnNewUserRegistration (user: MUserAccount): void { this.notifyModeratorsOfNewUserRegistration(user) .catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err })) } - notifyOfNewUserFollow (actorFollow: ActorFollowModel): void { + notifyOfNewUserFollow (actorFollow: MActorFollowFull): void { this.notifyUserOfNewActorFollow(actorFollow) .catch(err => { logger.error( @@ -104,14 +114,14 @@ class Notifier { }) } - notifyOfNewInstanceFollow (actorFollow: ActorFollowModel): void { + notifyOfNewInstanceFollow (actorFollow: MActorFollowActors): void { this.notifyAdminsOfNewInstanceFollow(actorFollow) .catch(err => { logger.error('Cannot notify administrators of new follower %s.', actorFollow.ActorFollower.url, { err }) }) } - private async notifySubscribersOfNewVideo (video: VideoModel) { + private async notifySubscribersOfNewVideo (video: MVideoAccountLight) { // List all followers that are users const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId) @@ -127,7 +137,7 @@ class Notifier { userId: user.id, videoId: video.id }) - notification.Video = video + notification.Video = video as VideoModel return notification } @@ -139,7 +149,7 @@ class Notifier { return this.notify({ users, settingGetter, notificationCreator, emailSender }) } - private async notifyVideoOwnerOfNewComment (comment: VideoCommentModel) { + private async notifyVideoOwnerOfNewComment (comment: MCommentOwnerVideo) { if (comment.Video.isOwned() === false) return const user = await UserModel.loadByVideoId(comment.videoId) @@ -162,7 +172,7 @@ class Notifier { userId: user.id, commentId: comment.id }) - notification.Comment = comment + notification.Comment = comment as VideoCommentModel return notification } @@ -174,7 +184,7 @@ class Notifier { return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) } - private async notifyOfCommentMention (comment: VideoCommentModel) { + private async notifyOfCommentMention (comment: MCommentOwnerVideo) { const extractedUsernames = comment.extractMentions() logger.debug( 'Extracted %d username from comment %s.', extractedUsernames.length, comment.url, @@ -209,7 +219,7 @@ class Notifier { userId: user.id, commentId: comment.id }) - notification.Comment = comment + notification.Comment = comment as VideoCommentModel return notification } @@ -221,7 +231,7 @@ class Notifier { return this.notify({ users, settingGetter, notificationCreator, emailSender }) } - private async notifyUserOfNewActorFollow (actorFollow: ActorFollowModel) { + private async notifyUserOfNewActorFollow (actorFollow: MActorFollowFull) { if (actorFollow.ActorFollowing.isOwned() === false) return // Account follows one of our account? @@ -236,9 +246,6 @@ class Notifier { if (!user) return - if (!actorFollow.ActorFollower.Account || !actorFollow.ActorFollower.Account.name) { - actorFollow.ActorFollower.Account = await actorFollow.ActorFollower.$get('Account') as AccountModel - } const followerAccount = actorFollow.ActorFollower.Account const accountMuted = await AccountBlocklistModel.isAccountMutedBy(user.Account.id, followerAccount.id) @@ -256,7 +263,7 @@ class Notifier { userId: user.id, actorFollowId: actorFollow.id }) - notification.ActorFollow = actorFollow + notification.ActorFollow = actorFollow as ActorFollowModel return notification } @@ -268,7 +275,7 @@ class Notifier { return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) } - private async notifyAdminsOfNewInstanceFollow (actorFollow: ActorFollowModel) { + private async notifyAdminsOfNewInstanceFollow (actorFollow: MActorFollowActors) { const admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW) logger.info('Notifying %d administrators of new instance follower: %s.', admins.length, actorFollow.ActorFollower.url) @@ -283,7 +290,7 @@ class Notifier { userId: user.id, actorFollowId: actorFollow.id }) - notification.ActorFollow = actorFollow + notification.ActorFollow = actorFollow as ActorFollowModel return notification } @@ -295,7 +302,7 @@ class Notifier { return this.notify({ users: admins, settingGetter, notificationCreator, emailSender }) } - private async notifyModeratorsOfNewVideoAbuse (videoAbuse: VideoAbuseModel) { + private async notifyModeratorsOfNewVideoAbuse (videoAbuse: MVideoAbuseVideo) { const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_ABUSES) if (moderators.length === 0) return @@ -306,7 +313,7 @@ class Notifier { } async function notificationCreator (user: UserModel) { - const notification = await UserNotificationModel.create({ + const notification: UserNotificationModelForApi = await UserNotificationModel.create({ type: UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS, userId: user.id, videoAbuseId: videoAbuse.id @@ -323,7 +330,7 @@ class Notifier { return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) } - private async notifyModeratorsOfVideoAutoBlacklist (video: VideoModel) { + private async notifyModeratorsOfVideoAutoBlacklist (video: MVideo) { const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST) if (moderators.length === 0) return @@ -339,7 +346,7 @@ class Notifier { userId: user.id, videoId: video.id }) - notification.Video = video + notification.Video = video as VideoModel return notification } @@ -351,7 +358,7 @@ class Notifier { return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) } - private async notifyVideoOwnerOfBlacklist (videoBlacklist: VideoBlacklistModel) { + private async notifyVideoOwnerOfBlacklist (videoBlacklist: MVideoBlacklistVideo) { const user = await UserModel.loadByVideoId(videoBlacklist.videoId) if (!user) return @@ -367,7 +374,7 @@ class Notifier { userId: user.id, videoBlacklistId: videoBlacklist.id }) - notification.VideoBlacklist = videoBlacklist + notification.VideoBlacklist = videoBlacklist as VideoBlacklistModel return notification } @@ -379,7 +386,7 @@ class Notifier { return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) } - private async notifyVideoOwnerOfUnblacklist (video: VideoModel) { + private async notifyVideoOwnerOfUnblacklist (video: MVideo) { const user = await UserModel.loadByVideoId(video.id) if (!user) return @@ -395,7 +402,7 @@ class Notifier { userId: user.id, videoId: video.id }) - notification.Video = video + notification.Video = video as VideoModel return notification } @@ -407,7 +414,7 @@ class Notifier { return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) } - private async notifyOwnedVideoHasBeenPublished (video: VideoModel) { + private async notifyOwnedVideoHasBeenPublished (video: MVideoFullLight) { const user = await UserModel.loadByVideoId(video.id) if (!user) return @@ -423,7 +430,7 @@ class Notifier { userId: user.id, videoId: video.id }) - notification.Video = video + notification.Video = video as VideoModel return notification } @@ -435,7 +442,7 @@ class Notifier { return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) } - private async notifyOwnerVideoImportIsFinished (videoImport: VideoImportModel, success: boolean) { + private async notifyOwnerVideoImportIsFinished (videoImport: MVideoImportVideo, success: boolean) { const user = await UserModel.loadByVideoImportId(videoImport.id) if (!user) return @@ -451,7 +458,7 @@ class Notifier { userId: user.id, videoImportId: videoImport.id }) - notification.VideoImport = videoImport + notification.VideoImport = videoImport as VideoImportModel return notification } @@ -465,13 +472,13 @@ class Notifier { return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) } - private async notifyModeratorsOfNewUserRegistration (registeredUser: UserModel) { + private async notifyModeratorsOfNewUserRegistration (registeredUser: MUserAccount) { const moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS) if (moderators.length === 0) return logger.info( 'Notifying %s moderators of new user registration of %s.', - moderators.length, registeredUser.Account.Actor.preferredUsername + moderators.length, registeredUser.username ) function settingGetter (user: UserModel) { @@ -484,7 +491,7 @@ class Notifier { userId: user.id, accountId: registeredUser.Account.id }) - notification.Account = registeredUser.Account + notification.Account = registeredUser.Account as AccountModel return notification } @@ -497,10 +504,10 @@ class Notifier { } private async notify (options: { - users: UserModel[], - notificationCreator: (user: UserModel) => Promise, + users: MUserWithNotificationSetting[], + notificationCreator: (user: MUserWithNotificationSetting) => Promise, emailSender: (emails: string[]) => Promise | Bluebird, - settingGetter: (user: UserModel) => UserNotificationSettingValue + settingGetter: (user: MUserWithNotificationSetting) => UserNotificationSettingValue }) { const emails: string[] = [] @@ -521,7 +528,7 @@ class Notifier { } } - private isEmailEnabled (user: UserModel, value: UserNotificationSettingValue) { + private isEmailEnabled (user: MUser, value: UserNotificationSettingValue) { if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION === true && user.emailVerified === false) return false 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' import { Transaction } from 'sequelize' import { CONFIG } from '../initializers/config' import * as LRUCache from 'lru-cache' +import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token' type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date } -const accessTokenCache = new LRUCache({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE }) +const accessTokenCache = new LRUCache({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE }) const userHavingToken = new LRUCache({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE }) // --------------------------------------------------------------------------- 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 @@ import * as SocketIO from 'socket.io' import { authenticateSocket } from '../middlewares' -import { UserNotificationModel } from '../models/account/user-notification' import { logger } from '../helpers/logger' import { Server } from 'http' +import { UserNotificationModelForApi } from '@server/typings/models/user' class PeerTubeSocket { @@ -32,7 +32,7 @@ class PeerTubeSocket { }) } - sendNotification (userId: number, notification: UserNotificationModel) { + sendNotification (userId: number, notification: UserNotificationModelForApi) { const socket = this.userNotificationSockets[userId] 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' import { sendUndoCacheFile } from './activitypub/send' import { Transaction } from 'sequelize' import { getServerActor } from '../helpers/utils' +import { MVideoRedundancyVideo } from '@server/typings/models' -async function removeVideoRedundancy (videoRedundancy: VideoRedundancyModel, t?: Transaction) { +async function removeVideoRedundancy (videoRedundancy: MVideoRedundancyVideo, t?: Transaction) { const serverActor = await getServerActor() // 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 } import { logger } from '../../helpers/logger' import { VideosRedundancy } from '../../../shared/models/redundancy' import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' -import { VideoFileModel } from '../../models/video/video-file' import { downloadWebTorrentVideo } from '../../helpers/webtorrent' import { join } from 'path' import { move } from 'fs-extra' @@ -12,16 +11,29 @@ import { sendCreateCacheFile, sendUpdateCacheFile } from '../activitypub/send' import { getVideoCacheFileActivityPubUrl, getVideoCacheStreamingPlaylistActivityPubUrl } from '../activitypub/url' import { removeVideoRedundancy } from '../redundancy' import { getOrCreateVideoAndAccountAndChannel } from '../activitypub' -import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' -import { VideoModel } from '../../models/video/video' import { downloadPlaylistSegments } from '../hls' import { CONFIG } from '../../initializers/config' +import { + MStreamingPlaylist, + MStreamingPlaylistVideo, + MVideoAccountLight, + MVideoFile, + MVideoFileVideo, + MVideoRedundancyFileVideo, + MVideoRedundancyStreamingPlaylistVideo, + MVideoRedundancyVideo, + MVideoWithAllFiles +} from '@server/typings/models' type CandidateToDuplicate = { redundancy: VideosRedundancy, - video: VideoModel, - files: VideoFileModel[], - streamingPlaylists: VideoStreamingPlaylistModel[] + video: MVideoWithAllFiles, + files: MVideoFile[], + streamingPlaylists: MStreamingPlaylist[] +} + +function isMVideoRedundancyFileVideo (o: MVideoRedundancyVideo): o is MVideoRedundancyFileVideo { + return !!(o as MVideoRedundancyFileVideo).VideoFile } export class VideosRedundancyScheduler extends AbstractScheduler { @@ -102,7 +114,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { } } - private async extendsRedundancy (redundancyModel: VideoRedundancyModel) { + private async extendsRedundancy (redundancyModel: MVideoRedundancyVideo) { const redundancy = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES.find(s => s.strategy === redundancyModel.strategy) // Redundancy strategy disabled, remove our redundancy instead of extending expiration if (!redundancy) { @@ -172,7 +184,8 @@ export class VideosRedundancyScheduler extends AbstractScheduler { } } - private async createVideoFileRedundancy (redundancy: VideosRedundancy, video: VideoModel, file: VideoFileModel) { + private async createVideoFileRedundancy (redundancy: VideosRedundancy, video: MVideoAccountLight, fileArg: MVideoFile) { + const file = fileArg as MVideoFileVideo file.Video = video const serverActor = await getServerActor() @@ -187,7 +200,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { const destPath = join(CONFIG.STORAGE.REDUNDANCY_DIR, video.getVideoFilename(file)) await move(tmpPath, destPath) - const createdModel = await VideoRedundancyModel.create({ + const createdModel: MVideoRedundancyFileVideo = await VideoRedundancyModel.create({ expiresOn: this.buildNewExpiration(redundancy.minLifetime), url: getVideoCacheFileActivityPubUrl(file), fileUrl: video.getVideoRedundancyUrl(file, WEBSERVER.URL), @@ -203,7 +216,12 @@ export class VideosRedundancyScheduler extends AbstractScheduler { logger.info('Duplicated %s - %d -> %s.', video.url, file.resolution, createdModel.url) } - private async createStreamingPlaylistRedundancy (redundancy: VideosRedundancy, video: VideoModel, playlist: VideoStreamingPlaylistModel) { + private async createStreamingPlaylistRedundancy ( + redundancy: VideosRedundancy, + video: MVideoAccountLight, + playlistArg: MStreamingPlaylist + ) { + const playlist = playlistArg as MStreamingPlaylistVideo playlist.Video = video const serverActor = await getServerActor() @@ -213,7 +231,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid) await downloadPlaylistSegments(playlist.playlistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT) - const createdModel = await VideoRedundancyModel.create({ + const createdModel: MVideoRedundancyStreamingPlaylistVideo = await VideoRedundancyModel.create({ expiresOn: this.buildNewExpiration(redundancy.minLifetime), url: getVideoCacheStreamingPlaylistActivityPubUrl(video, playlist), fileUrl: playlist.getVideoRedundancyUrl(WEBSERVER.URL), @@ -229,7 +247,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { logger.info('Duplicated playlist %s -> %s.', playlist.playlistUrl, createdModel.url) } - private async extendsExpirationOf (redundancy: VideoRedundancyModel, expiresAfterMs: number) { + private async extendsExpirationOf (redundancy: MVideoRedundancyVideo, expiresAfterMs: number) { logger.info('Extending expiration of %s.', redundancy.url) const serverActor = await getServerActor() @@ -243,7 +261,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { private async purgeCacheIfNeeded (candidateToDuplicate: CandidateToDuplicate) { while (this.isTooHeavy(candidateToDuplicate)) { const redundancy = candidateToDuplicate.redundancy - const toDelete = await VideoRedundancyModel.loadOldestLocalThatAlreadyExpired(redundancy.strategy, redundancy.minLifetime) + const toDelete = await VideoRedundancyModel.loadOldestLocalExpired(redundancy.strategy, redundancy.minLifetime) if (!toDelete) return await removeVideoRedundancy(toDelete) @@ -263,14 +281,14 @@ export class VideosRedundancyScheduler extends AbstractScheduler { return new Date(Date.now() + expiresAfterMs) } - private buildEntryLogId (object: VideoRedundancyModel) { - if (object.VideoFile) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}` + private buildEntryLogId (object: MVideoRedundancyFileVideo | MVideoRedundancyStreamingPlaylistVideo) { + if (isMVideoRedundancyFileVideo(object)) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}` return `${object.VideoStreamingPlaylist.playlistUrl}` } - private getTotalFileSizes (files: VideoFileModel[], playlists: VideoStreamingPlaylistModel[]) { - const fileReducer = (previous: number, current: VideoFileModel) => previous + current.size + private getTotalFileSizes (files: MVideoFile[], playlists: MStreamingPlaylist[]) { + const fileReducer = (previous: number, current: MVideoFile) => previous + current.size const totalSize = files.reduce(fileReducer, 0) 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 @@ -import { VideoFileModel } from '../models/video/video-file' import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils' import { CONFIG } from '../initializers/config' -import { PREVIEWS_SIZE, THUMBNAILS_SIZE, ASSETS_PATH } from '../initializers/constants' -import { VideoModel } from '../models/video/video' +import { ASSETS_PATH, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '../initializers/constants' import { ThumbnailModel } from '../models/video/thumbnail' import { ThumbnailType } from '../../shared/models/videos/thumbnail.type' import { processImage } from '../helpers/image-utils' import { join } from 'path' import { downloadImage } from '../helpers/requests' -import { VideoPlaylistModel } from '../models/video/video-playlist' +import { MVideoPlaylistThumbnail } from '../typings/models/video/video-playlist' +import { MVideoFile, MVideoThumbnail } from '../typings/models' +import { MThumbnail } from '../typings/models/video/thumbnail' type ImageSize = { height: number, width: number } function createPlaylistMiniatureFromExisting ( inputPath: string, - playlist: VideoPlaylistModel, + playlist: MVideoPlaylistThumbnail, automaticallyGenerated: boolean, keepOriginal = false, size?: ImageSize @@ -26,7 +26,7 @@ function createPlaylistMiniatureFromExisting ( return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail }) } -function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: VideoPlaylistModel, size?: ImageSize) { +function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: MVideoPlaylistThumbnail, size?: ImageSize) { const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromPlaylist(playlist, size) const type = ThumbnailType.MINIATURE @@ -34,7 +34,7 @@ function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: VideoPlaylis return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl }) } -function createVideoMiniatureFromUrl (fileUrl: string, video: VideoModel, type: ThumbnailType, size?: ImageSize) { +function createVideoMiniatureFromUrl (fileUrl: string, video: MVideoThumbnail, type: ThumbnailType, size?: ImageSize) { const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size) const thumbnailCreator = () => downloadImage(fileUrl, basePath, filename, { width, height }) @@ -43,7 +43,7 @@ function createVideoMiniatureFromUrl (fileUrl: string, video: VideoModel, type: function createVideoMiniatureFromExisting ( inputPath: string, - video: VideoModel, + video: MVideoThumbnail, type: ThumbnailType, automaticallyGenerated: boolean, size?: ImageSize @@ -54,7 +54,7 @@ function createVideoMiniatureFromExisting ( return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail }) } -function generateVideoMiniature (video: VideoModel, videoFile: VideoFileModel, type: ThumbnailType) { +function generateVideoMiniature (video: MVideoThumbnail, videoFile: MVideoFile, type: ThumbnailType) { const input = video.getVideoFilePath(videoFile) const { filename, basePath, height, width, existingThumbnail, outputPath } = buildMetadataFromVideo(video, type) @@ -65,7 +65,7 @@ function generateVideoMiniature (video: VideoModel, videoFile: VideoFileModel, t return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated: true, existingThumbnail }) } -function createPlaceholderThumbnail (fileUrl: string, video: VideoModel, type: ThumbnailType, size: ImageSize) { +function createPlaceholderThumbnail (fileUrl: string, video: MVideoThumbnail, type: ThumbnailType, size: ImageSize) { const { filename, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size) const thumbnail = existingThumbnail ? existingThumbnail : new ThumbnailModel() @@ -90,7 +90,7 @@ export { createPlaylistMiniatureFromExisting } -function buildMetadataFromPlaylist (playlist: VideoPlaylistModel, size: ImageSize) { +function buildMetadataFromPlaylist (playlist: MVideoPlaylistThumbnail, size: ImageSize) { const filename = playlist.generateThumbnailName() const basePath = CONFIG.STORAGE.THUMBNAILS_DIR @@ -104,7 +104,7 @@ function buildMetadataFromPlaylist (playlist: VideoPlaylistModel, size: ImageSiz } } -function buildMetadataFromVideo (video: VideoModel, type: ThumbnailType, size?: ImageSize) { +function buildMetadataFromVideo (video: MVideoThumbnail, type: ThumbnailType, size?: ImageSize) { const existingThumbnail = Array.isArray(video.Thumbnails) ? video.Thumbnails.find(t => t.type === type) : undefined @@ -148,7 +148,7 @@ async function createThumbnailFromFunction (parameters: { type: ThumbnailType, automaticallyGenerated?: boolean, fileUrl?: string, - existingThumbnail?: ThumbnailModel + existingThumbnail?: MThumbnail }) { const { thumbnailCreator, filename, width, height, type, existingThumbnail, automaticallyGenerated = null, fileUrl = null } = parameters 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' import { UserModel } from '../models/account/user' import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub' import { createVideoChannel } from './video-channel' -import { VideoChannelModel } from '../models/video/video-channel' import { ActorModel } from '../models/activitypub/actor' import { UserNotificationSettingModel } from '../models/account/user-notification-setting' import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users' @@ -14,14 +13,17 @@ import { sequelizeTypescript } from '../initializers/database' import { Transaction } from 'sequelize/types' import { Redis } from './redis' import { Emailer } from './emailer' +import { MAccountActor, MActor, MChannelActor } from '../typings/models' +import { MUser, MUserId, MUserNotifSettingAccount } from '../typings/models/user' type ChannelNames = { name: string, displayName: string } + async function createUserAccountAndChannelAndPlaylist (parameters: { userToCreate: UserModel, userDisplayName?: string, channelNames?: ChannelNames, validateUser?: boolean -}) { +}): Promise<{ user: MUserNotifSettingAccount, account: MAccountActor, videoChannel: MChannelActor }> { const { userToCreate, userDisplayName, channelNames, validateUser = true } = parameters const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => { @@ -30,7 +32,7 @@ async function createUserAccountAndChannelAndPlaylist (parameters: { validate: validateUser } - const userCreated = await userToCreate.save(userOptions) + const userCreated: MUserNotifSettingAccount = await userToCreate.save(userOptions) userCreated.NotificationSetting = await createDefaultUserNotificationSettings(userCreated, t) const accountCreated = await createLocalAccountWithoutKeys({ @@ -50,15 +52,15 @@ async function createUserAccountAndChannelAndPlaylist (parameters: { return { user: userCreated, account: accountCreated, videoChannel, videoPlaylist } }) - const [ accountKeys, channelKeys ] = await Promise.all([ + const [ accountActorWithKeys, channelActorWithKeys ] = await Promise.all([ setAsyncActorKeys(account.Actor), setAsyncActorKeys(videoChannel.Actor) ]) - account.Actor = accountKeys - videoChannel.Actor = channelKeys + account.Actor = accountActorWithKeys + videoChannel.Actor = channelActorWithKeys - return { user, account, videoChannel } as { user: UserModel, account: AccountModel, videoChannel: VideoChannelModel } + return { user, account, videoChannel } } async function createLocalAccountWithoutKeys (parameters: { @@ -73,7 +75,7 @@ async function createLocalAccountWithoutKeys (parameters: { const url = getAccountActivityPubUrl(name) const actorInstance = buildActorInstance(type, url, name) - const actorInstanceCreated = await actorInstance.save({ transaction: t }) + const actorInstanceCreated: MActor = await actorInstance.save({ transaction: t }) const accountInstance = new AccountModel({ name: displayName || name, @@ -82,7 +84,7 @@ async function createLocalAccountWithoutKeys (parameters: { actorId: actorInstanceCreated.id }) - const accountInstanceCreated = await accountInstance.save({ transaction: t }) + const accountInstanceCreated: MAccountActor = await accountInstance.save({ transaction: t }) accountInstanceCreated.Actor = actorInstanceCreated return accountInstanceCreated @@ -102,7 +104,7 @@ async function createApplicationActor (applicationId: number) { return accountCreated } -async function sendVerifyUserEmail (user: UserModel, isPendingEmail = false) { +async function sendVerifyUserEmail (user: MUser, isPendingEmail = false) { const verificationString = await Redis.Instance.setVerifyEmailVerificationString(user.id) let url = WEBSERVER.URL + '/verify-account/email?userId=' + user.id + '&verificationString=' + verificationString @@ -124,7 +126,7 @@ export { // --------------------------------------------------------------------------- -function createDefaultUserNotificationSettings (user: UserModel, t: Transaction | undefined) { +function createDefaultUserNotificationSettings (user: MUserId, t: Transaction | undefined) { const values: UserNotificationSetting & { userId: number } = { userId: user.id, newVideoFromSubscription: UserNotificationSettingValue.WEB, @@ -143,7 +145,7 @@ function createDefaultUserNotificationSettings (user: UserModel, t: Transaction return UserNotificationSettingModel.create(values, { transaction: t }) } -async function buildChannelAttributes (user: UserModel, channelNames?: ChannelNames) { +async function buildChannelAttributes (user: MUser, channelNames?: ChannelNames) { if (channelNames) return channelNames 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' import { CONFIG } from '../initializers/config' import { UserRight, VideoBlacklistType } from '../../shared/models' import { VideoBlacklistModel } from '../models/video/video-blacklist' -import { UserModel } from '../models/account/user' -import { VideoModel } from '../models/video/video' import { logger } from '../helpers/logger' import { UserAdminFlag } from '../../shared/models/users/user-flag.model' import { Hooks } from './plugins/hooks' import { Notifier } from './notifier' +import { MUser, MVideoBlacklist, MVideoWithBlacklistLight } from '@server/typings/models' async function autoBlacklistVideoIfNeeded (parameters: { - video: VideoModel, - user?: UserModel, + video: MVideoWithBlacklistLight, + user?: MUser, isRemote: boolean, isNew: boolean, notify?: boolean, @@ -32,7 +31,7 @@ async function autoBlacklistVideoIfNeeded (parameters: { reason: 'Auto-blacklisted. Moderator review required.', type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED } - const [ videoBlacklist ] = await VideoBlacklistModel.findOrCreate({ + const [ videoBlacklist ] = await VideoBlacklistModel.findOrCreate({ where: { videoId: video.id }, @@ -49,10 +48,10 @@ async function autoBlacklistVideoIfNeeded (parameters: { } async function autoBlacklistNeeded (parameters: { - video: VideoModel, + video: MVideoWithBlacklistLight, isRemote: boolean, isNew: boolean, - user?: UserModel + user?: MUser }) { const { user, video, isRemote, isNew } = parameters 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 @@ import * as Sequelize from 'sequelize' import * as uuidv4 from 'uuid/v4' import { VideoChannelCreate } from '../../shared/models' -import { AccountModel } from '../models/account/account' import { VideoChannelModel } from '../models/video/video-channel' import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub' import { VideoModel } from '../models/video/video' +import { MAccountId, MChannelActor, MChannelId } from '../typings/models' -async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) { +type CustomVideoChannelModelAccount = MChannelActor & + { Account?: T } + +async function createVideoChannel ( + videoChannelInfo: VideoChannelCreate, + account: T, + t: Sequelize.Transaction +): Promise> { const uuid = uuidv4() const url = getVideoChannelActivityPubUrl(videoChannelInfo.name) const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid) @@ -21,10 +28,10 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account actorId: actorInstanceCreated.id } - const videoChannel = VideoChannelModel.build(videoChannelData) + const videoChannel = new VideoChannelModel(videoChannelData) const options = { transaction: t } - const videoChannelCreated = await videoChannel.save(options) + const videoChannelCreated: CustomVideoChannelModelAccount = await videoChannel.save(options) as MChannelActor // Do not forget to add Account/Actor information to the created video channel videoChannelCreated.Account = account @@ -34,7 +41,7 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account return videoChannelCreated } -async function federateAllVideosOfChannel (videoChannel: VideoChannelModel) { +async function federateAllVideosOfChannel (videoChannel: MChannelId) { const videoIds = await VideoModel.getAllIdsFromChannel(videoChannel) 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 @@ import * as Sequelize from 'sequelize' import { ResultList } from '../../shared/models' import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model' -import { AccountModel } from '../models/account/account' -import { VideoModel } from '../models/video/video' import { VideoCommentModel } from '../models/video/video-comment' import { getVideoCommentActivityPubUrl } from './activitypub' import { sendCreateVideoComment } from './activitypub/send' +import { MAccountDefault, MComment, MCommentOwnerVideoReply, MVideoFullLight } from '../typings/models' async function createVideoComment (obj: { text: string, - inReplyToComment: VideoCommentModel | null, - video: VideoModel - account: AccountModel + inReplyToComment: MComment | null, + video: MVideoFullLight, + account: MAccountDefault }, t: Sequelize.Transaction) { let originCommentId: number | null = null let inReplyToCommentId: number | null = null @@ -32,7 +31,7 @@ async function createVideoComment (obj: { comment.url = getVideoCommentActivityPubUrl(obj.video, comment) - const savedComment = await comment.save({ transaction: t }) + const savedComment: MCommentOwnerVideoReply = await comment.save({ transaction: t }) savedComment.InReplyToVideoComment = obj.inReplyToComment savedComment.Video = obj.video 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 @@ import * as Sequelize from 'sequelize' -import { AccountModel } from '../models/account/account' import { VideoPlaylistModel } from '../models/video/video-playlist' import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model' import { getVideoPlaylistActivityPubUrl } from './activitypub' import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model' +import { MAccount } from '../typings/models' +import { MVideoPlaylistOwner } from '../typings/models/video/video-playlist' -async function createWatchLaterPlaylist (account: AccountModel, t: Sequelize.Transaction) { - const videoPlaylist = new VideoPlaylistModel({ +async function createWatchLaterPlaylist (account: MAccount, t: Sequelize.Transaction) { + const videoPlaylist: MVideoPlaylistOwner = new VideoPlaylistModel({ name: 'Watch later', privacy: VideoPlaylistPrivacy.PRIVATE, 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' import { logger } from '../helpers/logger' import { VideoResolution } from '../../shared/models/videos' import { VideoFileModel } from '../models/video/video-file' -import { VideoModel } from '../models/video/video' import { updateMasterHLSPlaylist, updateSha256Segments } from './hls' import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' import { CONFIG } from '../initializers/config' +import { MVideoFile, MVideoWithFile, MVideoWithFileThumbnail } from '@server/typings/models' /** * Optimize the original video file and replace it. The resolution is not changed. */ -async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFileModel) { +async function optimizeVideofile (video: MVideoWithFile, inputVideoFileArg?: MVideoFile) { const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR const transcodeDirectory = CONFIG.STORAGE.TMP_DIR const newExtname = '.mp4' @@ -57,7 +57,7 @@ async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFi /** * Transcode the original video file to a lower resolution. */ -async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoResolution, isPortrait: boolean) { +async function transcodeOriginalVideofile (video: MVideoWithFile, resolution: VideoResolution, isPortrait: boolean) { const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR const transcodeDirectory = CONFIG.STORAGE.TMP_DIR const extname = '.mp4' @@ -87,7 +87,7 @@ async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoR return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath) } -async function mergeAudioVideofile (video: VideoModel, resolution: VideoResolution) { +async function mergeAudioVideofile (video: MVideoWithFileThumbnail, resolution: VideoResolution) { const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR const transcodeDirectory = CONFIG.STORAGE.TMP_DIR const newExtname = '.mp4' @@ -117,7 +117,7 @@ async function mergeAudioVideofile (video: VideoModel, resolution: VideoResoluti return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) } -async function generateHlsPlaylist (video: VideoModel, resolution: VideoResolution, isPortraitMode: boolean) { +async function generateHlsPlaylist (video: MVideoWithFile, resolution: VideoResolution, isPortraitMode: boolean) { const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) await ensureDir(join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)) @@ -165,14 +165,14 @@ export { // --------------------------------------------------------------------------- -async function onVideoFileTranscoding (video: VideoModel, videoFile: VideoFileModel, transcodingPath: string, outputPath: string) { +async function onVideoFileTranscoding (video: MVideoWithFile, videoFile: MVideoFile, transcodingPath: string, outputPath: string) { const stats = await stat(transcodingPath) const fps = await getVideoFileFPS(transcodingPath) await move(transcodingPath, outputPath) - videoFile.set('size', stats.size) - videoFile.set('fps', fps) + videoFile.size = stats.size + videoFile.fps = fps await video.createTorrentAndSetInfoHash(videoFile) 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' import { ActorModel } from '../../models/activitypub/actor' import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' +import { MActorFollowActorsDefault } from '@server/typings/models' const followValidator = [ body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'), @@ -65,7 +66,7 @@ const getFollowerValidator = [ if (areValidationErrors(req, res)) return - let follow: ActorFollowModel + let follow: MActorFollowActorsDefault try { const actorUrl = await loadActorUrlOrGetFromWebfinger(req.params.nameWithHost) 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 = [ if (areValidationErrors(req, res)) return if (!await doesVideoExist(req.params.videoId, res)) return - const video = res.locals.video + const video = res.locals.videoAll const videoFile = video.VideoFiles.find(f => { return f.resolution === req.params.resolution && (!req.params.fps || f.fps === req.params.fps) }) @@ -50,7 +50,7 @@ const videoPlaylistRedundancyGetValidator = [ if (areValidationErrors(req, res)) return if (!await doesVideoExist(req.params.videoId, res)) return - const video = res.locals.video + const video = res.locals.videoAll const videoStreamingPlaylist = video.VideoStreamingPlaylists.find(p => p === req.params.streamingPlaylistType) 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' import * as express from 'express' import { body, param } from 'express-validator' import { omit } from 'lodash' -import { isIdOrUUIDValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' +import { isIdOrUUIDValid, toBooleanOrNull } from '../../helpers/custom-validators/misc' import { isUserAdminFlagsValid, isUserAutoPlayVideoValid, @@ -31,6 +31,7 @@ import { isThemeNameValid } from '../../helpers/custom-validators/plugins' import { isThemeRegistered } from '../../lib/plugins/theme-utils' import { doesVideoExist } from '../../helpers/middlewares' import { UserRole } from '../../../shared/models/users' +import { MUserDefault } from '@server/typings/models' const usersAddValidator = [ body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'), @@ -457,7 +458,7 @@ async function checkUserNameOrEmailDoesNotAlreadyExist (username: string, email: return true } -async function checkUserExist (finder: () => Bluebird, res: express.Response, abortResponse = true) { +async function checkUserExist (finder: () => Bluebird, res: express.Response, abortResponse = true) { const user = await finder() 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 = [ if (areValidationErrors(req, res)) return if (!await doesVideoExist(req.params.videoId, res)) return - if (!await doesVideoAbuseExist(req.params.id, res.locals.video.id, res)) return + if (!await doesVideoAbuseExist(req.params.id, res.locals.videoAll.id, res)) return return next() } @@ -54,7 +54,7 @@ const videoAbuseUpdateValidator = [ if (areValidationErrors(req, res)) return if (!await doesVideoExist(req.params.videoId, res)) return - if (!await doesVideoAbuseExist(req.params.id, res.locals.video.id, res)) return + if (!await doesVideoAbuseExist(req.params.id, res.locals.videoAll.id, res)) return return next() } 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 = [ if (areValidationErrors(req, res)) return if (!await doesVideoExist(req.params.videoId, res)) return - if (!await doesVideoBlacklistExist(res.locals.video.id, res)) return + if (!await doesVideoBlacklistExist(res.locals.videoAll.id, res)) return return next() } @@ -36,7 +36,7 @@ const videosBlacklistAddValidator = [ if (areValidationErrors(req, res)) return if (!await doesVideoExist(req.params.videoId, res)) return - const video = res.locals.video + const video = res.locals.videoAll if (req.body.unfederate === true && video.remote === true) { return res .status(409) @@ -59,7 +59,7 @@ const videosBlacklistUpdateValidator = [ if (areValidationErrors(req, res)) return if (!await doesVideoExist(req.params.videoId, res)) return - if (!await doesVideoBlacklistExist(res.locals.video.id, res)) return + if (!await doesVideoBlacklistExist(res.locals.videoAll.id, res)) return return next() } 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 = [ // Check if the user who did the request is able to update the video const user = res.locals.oauth.token.User - if (!checkUserCanManageVideo(user, res.locals.video, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req) + if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req) return next() } @@ -41,11 +41,11 @@ const deleteVideoCaptionValidator = [ if (areValidationErrors(req, res)) return if (!await doesVideoExist(req.params.videoId, res)) return - if (!await doesVideoCaptionExist(res.locals.video, req.params.captionLanguage, res)) return + if (!await doesVideoCaptionExist(res.locals.videoAll, req.params.captionLanguage, res)) return // Check if the user who did the request is able to update the video const user = res.locals.oauth.token.User - if (!checkUserCanManageVideo(user, res.locals.video, UserRight.UPDATE_ANY_VIDEO, res)) return + if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.UPDATE_ANY_VIDEO, res)) return return next() } 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 { isVideoChannelSupportValid } from '../../../helpers/custom-validators/video-channels' import { logger } from '../../../helpers/logger' -import { UserModel } from '../../../models/account/user' import { VideoChannelModel } from '../../../models/video/video-channel' import { areValidationErrors } from '../utils' import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor' import { ActorModel } from '../../../models/activitypub/actor' import { isBooleanValid } from '../../../helpers/custom-validators/misc' import { doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../../../helpers/middlewares' +import { MChannelActorAccountDefault } from '../../../typings/models/video' +import { MUser } from '@server/typings/models' const videoChannelsAddValidator = [ body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), @@ -131,7 +132,7 @@ export { // --------------------------------------------------------------------------- -function checkUserCanDeleteVideoChannel (user: UserModel, videoChannel: VideoChannelModel, res: express.Response) { +function checkUserCanDeleteVideoChannel (user: MUser, videoChannel: MChannelActorAccountDefault, res: express.Response) { if (videoChannel.Actor.isOwned() === false) { res.status(403) .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' import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc' import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments' import { logger } from '../../../helpers/logger' -import { UserModel } from '../../../models/account/user' -import { VideoModel } from '../../../models/video/video' import { VideoCommentModel } from '../../../models/video/video-comment' import { areValidationErrors } from '../utils' import { Hooks } from '../../../lib/plugins/hooks' -import { isLocalVideoThreadAccepted, isLocalVideoCommentReplyAccepted, AcceptResult } from '../../../lib/moderation' +import { AcceptResult, isLocalVideoCommentReplyAccepted, isLocalVideoThreadAccepted } from '../../../lib/moderation' import { doesVideoExist } from '../../../helpers/middlewares' +import { MCommentOwner, MVideo, MVideoFullLight, MVideoId } from '../../../typings/models/video' +import { MUser } from '@server/typings/models' const listVideoCommentThreadsValidator = [ param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), @@ -34,7 +34,7 @@ const listVideoThreadCommentsValidator = [ if (areValidationErrors(req, res)) return if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return - if (!await doesVideoCommentThreadExist(req.params.threadId, res.locals.video, res)) return + if (!await doesVideoCommentThreadExist(req.params.threadId, res.locals.onlyVideo, res)) return return next() } @@ -49,8 +49,8 @@ const addVideoCommentThreadValidator = [ if (areValidationErrors(req, res)) return if (!await doesVideoExist(req.params.videoId, res)) return - if (!isVideoCommentsEnabled(res.locals.video, res)) return - if (!await isVideoCommentAccepted(req, res, false)) return + if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return + if (!await isVideoCommentAccepted(req, res, res.locals.videoAll,false)) return return next() } @@ -66,9 +66,9 @@ const addVideoCommentReplyValidator = [ if (areValidationErrors(req, res)) return if (!await doesVideoExist(req.params.videoId, res)) return - if (!isVideoCommentsEnabled(res.locals.video, res)) return - if (!await doesVideoCommentExist(req.params.commentId, res.locals.video, res)) return - if (!await isVideoCommentAccepted(req, res, true)) return + if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return + if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return + if (!await isVideoCommentAccepted(req, res, res.locals.videoAll, true)) return return next() } @@ -83,7 +83,7 @@ const videoCommentGetValidator = [ if (areValidationErrors(req, res)) return if (!await doesVideoExist(req.params.videoId, res, 'id')) return - if (!await doesVideoCommentExist(req.params.commentId, res.locals.video, res)) return + if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoId, res)) return return next() } @@ -98,10 +98,10 @@ const removeVideoCommentValidator = [ if (areValidationErrors(req, res)) return if (!await doesVideoExist(req.params.videoId, res)) return - if (!await doesVideoCommentExist(req.params.commentId, res.locals.video, res)) return + if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return // Check if the user who did the request is able to delete the video - if (!checkUserCanDeleteVideoComment(res.locals.oauth.token.User, res.locals.videoComment, res)) return + if (!checkUserCanDeleteVideoComment(res.locals.oauth.token.User, res.locals.videoCommentFull, res)) return return next() } @@ -120,7 +120,7 @@ export { // --------------------------------------------------------------------------- -async function doesVideoCommentThreadExist (id: number, video: VideoModel, res: express.Response) { +async function doesVideoCommentThreadExist (id: number, video: MVideoId, res: express.Response) { const videoComment = await VideoCommentModel.loadById(id) if (!videoComment) { @@ -151,7 +151,7 @@ async function doesVideoCommentThreadExist (id: number, video: VideoModel, res: return true } -async function doesVideoCommentExist (id: number, video: VideoModel, res: express.Response) { +async function doesVideoCommentExist (id: number, video: MVideoId, res: express.Response) { const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id) if (!videoComment) { @@ -170,11 +170,11 @@ async function doesVideoCommentExist (id: number, video: VideoModel, res: expres return false } - res.locals.videoComment = videoComment + res.locals.videoCommentFull = videoComment return true } -function isVideoCommentsEnabled (video: VideoModel, res: express.Response) { +function isVideoCommentsEnabled (video: MVideo, res: express.Response) { if (video.commentsEnabled !== true) { res.status(409) .json({ error: 'Video comments are disabled for this video.' }) @@ -186,7 +186,7 @@ function isVideoCommentsEnabled (video: VideoModel, res: express.Response) { return true } -function checkUserCanDeleteVideoComment (user: UserModel, videoComment: VideoCommentModel, res: express.Response) { +function checkUserCanDeleteVideoComment (user: MUser, videoComment: MCommentOwner, res: express.Response) { const account = videoComment.Account if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) === false && account.userId !== user.id) { res.status(403) @@ -198,9 +198,9 @@ function checkUserCanDeleteVideoComment (user: UserModel, videoComment: VideoCom return true } -async function isVideoCommentAccepted (req: express.Request, res: express.Response, isReply: boolean) { +async function isVideoCommentAccepted (req: express.Request, res: express.Response, video: MVideoFullLight, isReply: boolean) { const acceptParameters = { - video: res.locals.video, + video, commentBody: req.body, user: res.locals.oauth.token.User } @@ -208,7 +208,7 @@ async function isVideoCommentAccepted (req: express.Request, res: express.Respon let acceptedResult: AcceptResult if (isReply) { - const acceptReplyParameters = Object.assign(acceptParameters, { parentComment: res.locals.videoComment }) + const acceptReplyParameters = Object.assign(acceptParameters, { parentComment: res.locals.videoCommentFull }) acceptedResult = await Hooks.wrapFun( 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' import { body, param, query, ValidationChain } from 'express-validator' import { UserRight, VideoPlaylistCreate, VideoPlaylistUpdate } from '../../../../shared' import { logger } from '../../../helpers/logger' -import { UserModel } from '../../../models/account/user' import { areValidationErrors } from '../utils' import { isVideoImage } from '../../../helpers/custom-validators/videos' import { CONSTRAINTS_FIELDS } from '../../../initializers/constants' @@ -22,13 +21,14 @@ import { isVideoPlaylistTimestampValid, isVideoPlaylistTypeValid } from '../../../helpers/custom-validators/video-playlists' -import { VideoPlaylistModel } from '../../../models/video/video-playlist' import { cleanUpReqFiles } from '../../../helpers/express-utils' import { VideoPlaylistElementModel } from '../../../models/video/video-playlist-element' import { authenticatePromiseIfNeeded } from '../../oauth' import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model' -import { doesVideoChannelIdExist, doesVideoExist, doesVideoPlaylistExist } from '../../../helpers/middlewares' +import { doesVideoChannelIdExist, doesVideoExist, doesVideoPlaylistExist, VideoPlaylistFetchType } from '../../../helpers/middlewares' +import { MVideoPlaylist } from '../../../typings/models/video/video-playlist' +import { MUserAccountId } from '@server/typings/models' const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([ body('displayName') @@ -67,9 +67,9 @@ const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([ if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return cleanUpReqFiles(req) - const videoPlaylist = res.locals.videoPlaylist + const videoPlaylist = getPlaylist(res) - if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) { + if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) { return cleanUpReqFiles(req) } @@ -110,13 +110,13 @@ const videoPlaylistsDeleteValidator = [ if (!await doesVideoPlaylistExist(req.params.playlistId, res)) return - const videoPlaylist = res.locals.videoPlaylist + const videoPlaylist = getPlaylist(res) if (videoPlaylist.type === VideoPlaylistType.WATCH_LATER) { return res.status(400) .json({ error: 'Cannot delete a watch later playlist.' }) } - if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) { + if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) { return } @@ -124,45 +124,47 @@ const videoPlaylistsDeleteValidator = [ } ] -const videoPlaylistsGetValidator = [ - param('playlistId') - .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), +const videoPlaylistsGetValidator = (fetchType: VideoPlaylistFetchType) => { + return [ + param('playlistId') + .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), - async (req: express.Request, res: express.Response, next: express.NextFunction) => { - logger.debug('Checking videoPlaylistsGetValidator parameters', { parameters: req.params }) + async (req: express.Request, res: express.Response, next: express.NextFunction) => { + logger.debug('Checking videoPlaylistsGetValidator parameters', { parameters: req.params }) - if (areValidationErrors(req, res)) return + if (areValidationErrors(req, res)) return - if (!await doesVideoPlaylistExist(req.params.playlistId, res)) return + if (!await doesVideoPlaylistExist(req.params.playlistId, res, fetchType)) return - const videoPlaylist = res.locals.videoPlaylist + const videoPlaylist = res.locals.videoPlaylistFull || res.locals.videoPlaylistSummary - // Video is unlisted, check we used the uuid to fetch it - if (videoPlaylist.privacy === VideoPlaylistPrivacy.UNLISTED) { - if (isUUIDValid(req.params.playlistId)) return next() + // Video is unlisted, check we used the uuid to fetch it + if (videoPlaylist.privacy === VideoPlaylistPrivacy.UNLISTED) { + if (isUUIDValid(req.params.playlistId)) return next() - return res.status(404).end() - } + return res.status(404).end() + } - if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) { - await authenticatePromiseIfNeeded(req, res) + if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) { + await authenticatePromiseIfNeeded(req, res) - const user = res.locals.oauth ? res.locals.oauth.token.User : null + const user = res.locals.oauth ? res.locals.oauth.token.User : null - if ( - !user || - (videoPlaylist.OwnerAccount.id !== user.Account.id && !user.hasRight(UserRight.UPDATE_ANY_VIDEO_PLAYLIST)) - ) { - return res.status(403) - .json({ error: 'Cannot get this private video playlist.' }) + if ( + !user || + (videoPlaylist.OwnerAccount.id !== user.Account.id && !user.hasRight(UserRight.UPDATE_ANY_VIDEO_PLAYLIST)) + ) { + return res.status(403) + .json({ error: 'Cannot get this private video playlist.' }) + } + + return next() } return next() } - - return next() - } -] + ] +} const videoPlaylistsAddVideoValidator = [ param('playlistId') @@ -184,8 +186,8 @@ const videoPlaylistsAddVideoValidator = [ if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return if (!await doesVideoExist(req.body.videoId, res, 'only-video')) return - const videoPlaylist = res.locals.videoPlaylist - const video = res.locals.video + const videoPlaylist = getPlaylist(res) + const video = res.locals.onlyVideo const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndVideo(videoPlaylist.id, video.id) if (videoPlaylistElement) { @@ -196,7 +198,7 @@ const videoPlaylistsAddVideoValidator = [ return } - if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) { + if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) { return } @@ -223,7 +225,7 @@ const videoPlaylistsUpdateOrRemoveVideoValidator = [ if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return - const videoPlaylist = res.locals.videoPlaylist + const videoPlaylist = getPlaylist(res) const videoPlaylistElement = await VideoPlaylistElementModel.loadById(req.params.playlistElementId) if (!videoPlaylistElement) { @@ -289,7 +291,7 @@ const videoPlaylistsReorderVideosValidator = [ if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return - const videoPlaylist = res.locals.videoPlaylist + const videoPlaylist = getPlaylist(res) if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) return const nextPosition = await VideoPlaylistElementModel.getNextPositionOf(videoPlaylist.id) @@ -388,7 +390,7 @@ function getCommonPlaylistEditAttributes () { ] as (ValidationChain | express.Handler)[] } -function checkUserCanManageVideoPlaylist (user: UserModel, videoPlaylist: VideoPlaylistModel, right: UserRight, res: express.Response) { +function checkUserCanManageVideoPlaylist (user: MUserAccountId, videoPlaylist: MVideoPlaylist, right: UserRight, res: express.Response) { if (videoPlaylist.isOwned() === false) { res.status(403) .json({ error: 'Cannot manage video playlist of another server.' }) @@ -410,3 +412,7 @@ function checkUserCanManageVideoPlaylist (user: UserModel, videoPlaylist: VideoP return true } + +function getPlaylist (res: express.Response) { + return res.locals.videoPlaylistFull || res.locals.videoPlaylistSummary +} 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 = [ if (areValidationErrors(req, res)) return if (!await doesVideoExist(req.params.id, res)) return - const video = res.locals.video + const video = res.locals.videoAll const share = await VideoShareModel.load(req.params.actorId, video.id) 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' import { checkUserCanTerminateOwnershipChange, doesChangeVideoOwnershipExist } from '../../../helpers/custom-validators/video-ownership' import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/video-change-ownership-accept.model' import { AccountModel } from '../../../models/account/account' -import { VideoFetchType } from '../../../helpers/video' import { isNSFWQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search' import { getServerActor } from '../../../helpers/utils' import { CONFIG } from '../../../initializers/config' import { isLocalVideoAccepted } from '../../../lib/moderation' import { Hooks } from '../../../lib/plugins/hooks' import { checkUserCanManageVideo, doesVideoChannelOfAccountExist, doesVideoExist } from '../../../helpers/middlewares' +import { MVideoFullLight } from '@server/typings/models' +import { getVideo } from '../../../helpers/video' const videosAddValidator = getCommonVideoEditAttributes().concat([ body('videofile') @@ -113,7 +114,7 @@ const videosUpdateValidator = getCommonVideoEditAttributes().concat([ // Check if the user who did the request is able to update the video const user = res.locals.oauth.token.User - if (!checkUserCanManageVideo(user, res.locals.video, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req) + if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req) if (req.body.channelId && !await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) @@ -122,7 +123,7 @@ const videosUpdateValidator = getCommonVideoEditAttributes().concat([ ]) async function checkVideoFollowConstraints (req: express.Request, res: express.Response, next: express.NextFunction) { - const video = res.locals.video + const video = getVideo(res) // Anybody can watch local videos if (video.isOwned() === true) return next() @@ -146,7 +147,7 @@ async function checkVideoFollowConstraints (req: express.Request, res: express.R }) } -const videosCustomGetValidator = (fetchType: VideoFetchType) => { +const videosCustomGetValidator = (fetchType: 'all' | 'only-video' | 'only-video-with-rights') => { return [ param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), @@ -156,10 +157,11 @@ const videosCustomGetValidator = (fetchType: VideoFetchType) => { if (areValidationErrors(req, res)) return if (!await doesVideoExist(req.params.id, res, fetchType)) return - const video = res.locals.video + const video = getVideo(res) + const videoAll = video as MVideoFullLight // Video private or blacklisted - if (video.privacy === VideoPrivacy.PRIVATE || video.VideoBlacklist) { + if (video.privacy === VideoPrivacy.PRIVATE || videoAll.VideoBlacklist) { await authenticatePromiseIfNeeded(req, res) const user = res.locals.oauth ? res.locals.oauth.token.User : null @@ -167,7 +169,7 @@ const videosCustomGetValidator = (fetchType: VideoFetchType) => { // Only the owner or a user that have blacklist rights can see the video if ( !user || - (video.VideoChannel.Account.userId !== user.id && !user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)) + (videoAll.VideoChannel && videoAll.VideoChannel.Account.userId !== user.id && !user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)) ) { return res.status(403) .json({ error: 'Cannot get this private or blacklisted video.' }) @@ -202,7 +204,7 @@ const videosRemoveValidator = [ if (!await doesVideoExist(req.params.id, res)) return // Check if the user who did the request is able to delete the video - if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.video, UserRight.REMOVE_ANY_VIDEO, res)) return + if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.videoAll, UserRight.REMOVE_ANY_VIDEO, res)) return return next() } @@ -218,7 +220,7 @@ const videosChangeOwnershipValidator = [ if (!await doesVideoExist(req.params.videoId, res)) return // Check if the user who did the request is able to change the ownership of the video - if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.video, UserRight.CHANGE_VIDEO_OWNERSHIP, res)) return + if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.videoAll, UserRight.CHANGE_VIDEO_OWNERSHIP, res)) return const nextOwner = await AccountModel.loadLocalByName(req.body.username) 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 = [ const nameWithHost = getHostWithPort(req.query.resource.substr(5)) const [ name ] = nameWithHost.split('@') + // FIXME: we don't need the full actor const actor = await ActorModel.loadLocalByName(name) if (!actor) { return res.status(404) @@ -25,7 +26,7 @@ const webfingerValidator = [ .end() } - res.locals.actor = actor + res.locals.actorFull = actor return next() } ] 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' import { getSort } from '../utils' import { AccountBlock } from '../../../shared/models/blocklist' import { Op } from 'sequelize' +import * as Bluebird from 'bluebird' +import { MAccountBlocklist, MAccountBlocklistAccounts } from '@server/typings/models' enum ScopeNames { WITH_ACCOUNTS = 'WITH_ACCOUNTS' @@ -103,7 +105,7 @@ export class AccountBlocklistModel extends Model { }) } - static loadByAccountAndTarget (accountId: number, targetAccountId: number) { + static loadByAccountAndTarget (accountId: number, targetAccountId: number): Bluebird { const query = { where: { accountId, @@ -126,7 +128,7 @@ export class AccountBlocklistModel extends Model { return AccountBlocklistModel .scope([ ScopeNames.WITH_ACCOUNTS ]) - .findAndCountAll(query) + .findAndCountAll(query) .then(({ rows, count }) => { return { total: count, data: rows } }) 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' import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' import { AccountVideoRate } from '../../../shared' import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel' +import * as Bluebird from 'bluebird' +import { MAccountVideoRate, MAccountVideoRateAccountUrl, MAccountVideoRateAccountVideo } from '@server/typings/models/video/video-rate' /* Account rates per video. @@ -77,7 +79,7 @@ export class AccountVideoRateModel extends Model { }) Account: AccountModel - static load (accountId: number, videoId: number, transaction?: Transaction) { + static load (accountId: number, videoId: number, transaction?: Transaction): Bluebird { const options: FindOptions = { where: { accountId, @@ -89,7 +91,7 @@ export class AccountVideoRateModel extends Model { return AccountVideoRateModel.findOne(options) } - static loadByAccountAndVideoOrUrl (accountId: number, videoId: number, url: string, transaction?: Transaction) { + static loadByAccountAndVideoOrUrl (accountId: number, videoId: number, url: string, t?: Transaction): Bluebird { const options: FindOptions = { where: { [ Op.or]: [ @@ -103,7 +105,7 @@ export class AccountVideoRateModel extends Model { ] } } - if (transaction) options.transaction = transaction + if (t) options.transaction = t return AccountVideoRateModel.findOne(options) } @@ -140,7 +142,12 @@ export class AccountVideoRateModel extends Model { return AccountVideoRateModel.findAndCountAll(query) } - static loadLocalAndPopulateVideo (rateType: VideoRateType, accountName: string, videoId: number, transaction?: Transaction) { + static loadLocalAndPopulateVideo ( + rateType: VideoRateType, + accountName: string, + videoId: number, + t?: Transaction + ): Bluebird { const options: FindOptions = { where: { videoId, @@ -152,7 +159,7 @@ export class AccountVideoRateModel extends Model { required: true, include: [ { - attributes: [ 'id', 'url', 'preferredUsername' ], + attributes: [ 'id', 'url', 'followersUrl', 'preferredUsername' ], model: ActorModel.unscoped(), required: true, where: { @@ -167,7 +174,7 @@ export class AccountVideoRateModel extends Model { } ] } - if (transaction) options.transaction = transaction + if (t) options.transaction = t return AccountVideoRateModel.findOne(options) } @@ -208,7 +215,7 @@ export class AccountVideoRateModel extends Model { ] } - return AccountVideoRateModel.findAndCountAll(query) + return AccountVideoRateModel.findAndCountAll(query) } 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 { BeforeDestroy, BelongsTo, Column, - CreatedAt, DataType, + CreatedAt, + DataType, Default, DefaultScope, ForeignKey, @@ -31,6 +32,8 @@ import { FindOptions, IncludeOptions, Op, Transaction, WhereOptions } from 'sequ import { AccountBlocklistModel } from './account-blocklist' import { ServerBlocklistModel } from '../server/server-blocklist' import { ActorFollowModel } from '../activitypub/actor-follow' +import { MAccountActor, MAccountDefault } from '../../typings/models' +import * as Bluebird from 'bluebird' export enum ScopeNames { SUMMARY = 'SUMMARY' @@ -229,11 +232,11 @@ export class AccountModel extends Model { return undefined } - static load (id: number, transaction?: Transaction) { + static load (id: number, transaction?: Transaction): Bluebird { return AccountModel.findByPk(id, { transaction }) } - static loadByNameWithHost (nameWithHost: string) { + static loadByNameWithHost (nameWithHost: string): Bluebird { const [ accountName, host ] = nameWithHost.split('@') if (!host || host === WEBSERVER.HOST) return AccountModel.loadLocalByName(accountName) @@ -241,7 +244,7 @@ export class AccountModel extends Model { return AccountModel.loadByNameAndHost(accountName, host) } - static loadLocalByName (name: string) { + static loadLocalByName (name: string): Bluebird { const query = { where: { [ Op.or ]: [ @@ -271,7 +274,7 @@ export class AccountModel extends Model { return AccountModel.findOne(query) } - static loadByNameAndHost (name: string, host: string) { + static loadByNameAndHost (name: string, host: string): Bluebird { const query = { include: [ { @@ -296,7 +299,7 @@ export class AccountModel extends Model { return AccountModel.findOne(query) } - static loadByUrl (url: string, transaction?: Transaction) { + static loadByUrl (url: string, transaction?: Transaction): Bluebird { const query = { include: [ { @@ -329,7 +332,7 @@ export class AccountModel extends Model { }) } - static listLocalsForSitemap (sort: string) { + static listLocalsForSitemap (sort: string): Bluebird { const query = { attributes: [ ], 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' import { ActorFollowModel } from '../activitypub/actor-follow' import { AvatarModel } from '../avatar/avatar' import { ServerModel } from '../server/server' +import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/typings/models/user' enum ScopeNames { WITH_ALL = 'WITH_ALL' @@ -371,7 +372,7 @@ export class UserNotificationModel extends Model { return UserNotificationModel.update({ read: true }, query) } - toFormattedJSON (): UserNotification { + toFormattedJSON (this: UserNotificationModelForApi): UserNotification { const video = this.Video ? Object.assign(this.formatVideo(this.Video),{ channel: this.formatActor(this.Video.VideoChannel) }) : undefined @@ -436,7 +437,7 @@ export class UserNotificationModel extends Model { } } - private formatVideo (video: VideoModel) { + formatVideo (this: UserNotificationModelForApi, video: UserNotificationIncludes.VideoInclude) { return { id: video.id, uuid: video.uuid, @@ -444,7 +445,10 @@ export class UserNotificationModel extends Model { } } - private formatActor (accountOrChannel: AccountModel | VideoChannelModel) { + formatActor ( + this: UserNotificationModelForApi, + accountOrChannel: UserNotificationIncludes.AccountIncludeActor | UserNotificationIncludes.VideoChannelIncludeActor + ) { const avatar = accountOrChannel.Actor.Avatar ? { path: accountOrChannel.Actor.Avatar.getStaticPath() } : 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 @@ import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, IsInt, Model, Table, UpdatedAt } from 'sequelize-typescript' import { VideoModel } from '../video/video' import { UserModel } from './user' -import { Transaction, Op, DestroyOptions } from 'sequelize' +import { DestroyOptions, Op, Transaction } from 'sequelize' +import { MUserAccountId, MUserId } from '@server/typings/models' @Table({ tableName: 'userVideoHistory', @@ -54,7 +55,7 @@ export class UserVideoHistoryModel extends Model { }) User: UserModel - static listForApi (user: UserModel, start: number, count: number) { + static listForApi (user: MUserAccountId, start: number, count: number) { return VideoModel.listForApi({ start, count, @@ -67,7 +68,7 @@ export class UserVideoHistoryModel extends Model { }) } - static removeUserHistoryBefore (user: UserModel, beforeDate: string, t: Transaction) { + static removeUserHistoryBefore (user: MUserId, beforeDate: string, t: Transaction) { const query: DestroyOptions = { where: { 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' import { UserAdminFlag } from '../../../shared/models/users/user-flag.model' import { isThemeNameValid } from '../../helpers/custom-validators/plugins' import { getThemeOrDefault } from '../../lib/plugins/theme-utils' +import * as Bluebird from 'bluebird' +import { MUserChannel, MUserDefault, MUserId, MUserWithNotificationSetting } from '@server/typings/models' enum ScopeNames { WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL' @@ -303,7 +305,7 @@ export class UserModel extends Model { }) } - static listWithRight (right: UserRight) { + static listWithRight (right: UserRight): Bluebird { const roles = Object.keys(USER_ROLE_LABELS) .map(k => parseInt(k, 10) as UserRole) .filter(role => hasUserRight(role, right)) @@ -319,7 +321,7 @@ export class UserModel extends Model { return UserModel.findAll(query) } - static listUserSubscribersOf (actorId: number) { + static listUserSubscribersOf (actorId: number): Bluebird { const query = { include: [ { @@ -358,7 +360,7 @@ export class UserModel extends Model { return UserModel.unscoped().findAll(query) } - static listByUsernames (usernames: string[]) { + static listByUsernames (usernames: string[]): Bluebird { const query = { where: { username: usernames @@ -368,11 +370,11 @@ export class UserModel extends Model { return UserModel.findAll(query) } - static loadById (id: number) { + static loadById (id: number): Bluebird { return UserModel.findByPk(id) } - static loadByUsername (username: string) { + static loadByUsername (username: string): Bluebird { const query = { where: { username: { [ Op.iLike ]: username } @@ -382,7 +384,7 @@ export class UserModel extends Model { return UserModel.findOne(query) } - static loadByUsernameAndPopulateChannels (username: string) { + static loadByUsernameAndPopulateChannels (username: string): Bluebird { const query = { where: { username: { [ Op.iLike ]: username } @@ -392,7 +394,7 @@ export class UserModel extends Model { return UserModel.scope(ScopeNames.WITH_VIDEO_CHANNEL).findOne(query) } - static loadByEmail (email: string) { + static loadByEmail (email: string): Bluebird { const query = { where: { email @@ -402,7 +404,7 @@ export class UserModel extends Model { return UserModel.findOne(query) } - static loadByUsernameOrEmail (username: string, email?: string) { + static loadByUsernameOrEmail (username: string, email?: string): Bluebird { if (!email) email = username const query = { @@ -414,7 +416,7 @@ export class UserModel extends Model { return UserModel.findOne(query) } - static loadByVideoId (videoId: number) { + static loadByVideoId (videoId: number): Bluebird { const query = { include: [ { @@ -445,7 +447,7 @@ export class UserModel extends Model { return UserModel.findOne(query) } - static loadByVideoImportId (videoImportId: number) { + static loadByVideoImportId (videoImportId: number): Bluebird { const query = { include: [ { @@ -462,7 +464,7 @@ export class UserModel extends Model { return UserModel.findOne(query) } - static loadByChannelActorId (videoChannelActorId: number) { + static loadByChannelActorId (videoChannelActorId: number): Bluebird { const query = { include: [ { @@ -486,7 +488,7 @@ export class UserModel extends Model { return UserModel.findOne(query) } - static loadByAccountActorId (accountActorId: number) { + static loadByAccountActorId (accountActorId: number): Bluebird { const query = { include: [ { @@ -503,7 +505,7 @@ export class UserModel extends Model { return UserModel.findOne(query) } - static getOriginalVideoFileTotalFromUser (user: UserModel) { + static getOriginalVideoFileTotalFromUser (user: MUserId) { // Don't use sequelize because we need to use a sub query const query = UserModel.generateUserQuotaBaseSQL() @@ -511,7 +513,7 @@ export class UserModel extends Model { } // Returns cumulative size of all video files uploaded in the last 24 hours. - static getOriginalVideoFileTotalDailyFromUser (user: UserModel) { + static getOriginalVideoFileTotalDailyFromUser (user: MUserId) { // Don't use sequelize because we need to use a sub query const query = UserModel.generateUserQuotaBaseSQL('"video"."createdAt" > now() - interval \'24 hours\'') 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' import { ActorModel, unusedActorAttributesForAPI } from './actor' import { VideoChannelModel } from '../video/video-channel' import { AccountModel } from '../account/account' -import { IncludeOptions, Op, Transaction, QueryTypes } from 'sequelize' +import { IncludeOptions, Op, QueryTypes, Transaction } from 'sequelize' +import { + MActorFollowActorsDefault, + MActorFollowActorsDefaultSubscription, + MActorFollowFollowingHost, + MActorFollowSubscriptions +} from '@server/typings/models' @Table({ tableName: 'actorFollow', @@ -143,7 +149,7 @@ export class ActorFollowModel extends Model { if (numberOfActorFollowsRemoved) logger.info('Removed bad %d actor follows.', numberOfActorFollowsRemoved) } - static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Transaction) { + static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Transaction): Bluebird { const query = { where: { actorId, @@ -167,7 +173,12 @@ export class ActorFollowModel extends Model { return ActorFollowModel.findOne(query) } - static loadByActorAndTargetNameAndHostForAPI (actorId: number, targetName: string, targetHost: string, t?: Transaction) { + static loadByActorAndTargetNameAndHostForAPI ( + actorId: number, + targetName: string, + targetHost: string, + t?: Transaction + ): Bluebird { const actorFollowingPartInclude: IncludeOptions = { model: ActorModel, required: true, @@ -220,7 +231,7 @@ export class ActorFollowModel extends Model { }) } - static listSubscribedIn (actorId: number, targets: { name: string, host?: string }[]) { + static listSubscribedIn (actorId: number, targets: { name: string, host?: string }[]): Bluebird { const whereTab = targets .map(t => { if (t.host) { @@ -314,7 +325,7 @@ export class ActorFollowModel extends Model { ] } - return ActorFollowModel.findAndCountAll(query) + return ActorFollowModel.findAndCountAll(query) .then(({ rows, count }) => { return { data: rows, @@ -357,7 +368,7 @@ export class ActorFollowModel extends Model { ] } - return ActorFollowModel.findAndCountAll(query) + return ActorFollowModel.findAndCountAll(query) .then(({ rows, count }) => { return { data: rows, @@ -414,7 +425,7 @@ export class ActorFollowModel extends Model { ] } - return ActorFollowModel.findAndCountAll(query) + return ActorFollowModel.findAndCountAll(query) .then(({ rows, count }) => { return { 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' import { VideoChannelModel } from '../video/video-channel' import { ActorFollowModel } from './actor-follow' import { VideoModel } from '../video/video' +import { MActor, MActorAccountChannelId, MActorFull } from '../../typings/models' +import * as Bluebird from 'bluebird' enum ScopeNames { FULL = 'FULL' @@ -252,11 +254,15 @@ export class ActorModel extends Model { }) VideoChannel: VideoChannelModel - static load (id: number) { + static load (id: number): Bluebird { return ActorModel.unscoped().findByPk(id) } - static loadAccountActorByVideoId (videoId: number, transaction: Sequelize.Transaction) { + static loadFull (id: number): Bluebird { + return ActorModel.scope(ScopeNames.FULL).findByPk(id) + } + + static loadFromAccountByVideoId (videoId: number, transaction: Sequelize.Transaction): Bluebird { const query = { include: [ { @@ -300,7 +306,7 @@ export class ActorModel extends Model { .then(a => !!a) } - static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction) { + static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction): Bluebird { const query = { where: { followersUrl: { @@ -313,7 +319,7 @@ export class ActorModel extends Model { return ActorModel.scope(ScopeNames.FULL).findAll(query) } - static loadLocalByName (preferredUsername: string, transaction?: Sequelize.Transaction) { + static loadLocalByName (preferredUsername: string, transaction?: Sequelize.Transaction): Bluebird { const query = { where: { preferredUsername, @@ -325,7 +331,7 @@ export class ActorModel extends Model { return ActorModel.scope(ScopeNames.FULL).findOne(query) } - static loadByNameAndHost (preferredUsername: string, host: string) { + static loadByNameAndHost (preferredUsername: string, host: string): Bluebird { const query = { where: { preferredUsername @@ -344,7 +350,7 @@ export class ActorModel extends Model { return ActorModel.scope(ScopeNames.FULL).findOne(query) } - static loadByUrl (url: string, transaction?: Sequelize.Transaction) { + static loadByUrl (url: string, transaction?: Sequelize.Transaction): Bluebird { const query = { where: { url @@ -367,7 +373,7 @@ export class ActorModel extends Model { return ActorModel.unscoped().findOne(query) } - static loadByUrlAndPopulateAccountAndChannel (url: string, transaction?: Sequelize.Transaction) { + static loadByUrlAndPopulateAccountAndChannel (url: string, transaction?: Sequelize.Transaction): Bluebird { const query = { where: { 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' import { AccountModel } from '../account/account' import { ActorModel } from '../activitypub/actor' import { clearCacheByToken } from '../../lib/oauth-model' +import * as Bluebird from 'bluebird' +import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token' export type OAuthTokenInfo = { refreshToken: string @@ -160,7 +162,7 @@ export class OAuthTokenModel extends Model { }) } - static getByTokenAndPopulateUser (bearerToken: string) { + static getByTokenAndPopulateUser (bearerToken: string): Bluebird { const query = { where: { accessToken: bearerToken @@ -170,13 +172,13 @@ export class OAuthTokenModel extends Model { return OAuthTokenModel.scope(ScopeNames.WITH_USER) .findOne(query) .then(token => { - if (token) token[ 'user' ] = token.User + if (!token) return null - return token + return Object.assign(token, { user: token.User }) }) } - static getByRefreshTokenAndPopulateUser (refreshToken: string) { + static getByRefreshTokenAndPopulateUser (refreshToken: string): Bluebird { const query = { where: { refreshToken: refreshToken @@ -186,12 +188,9 @@ export class OAuthTokenModel extends Model { return OAuthTokenModel.scope(ScopeNames.WITH_USER) .findOne(query) .then(token => { - if (token) { - token['user'] = token.User - return token - } else { - return new OAuthTokenModel() - } + if (!token) return new OAuthTokenModel() + + return Object.assign(token, { user: token.User }) }) } 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' import { col, FindOptions, fn, literal, Op, Transaction } from 'sequelize' import { VideoStreamingPlaylistModel } from '../video/video-streaming-playlist' import { CONFIG } from '../../initializers/config' +import { MVideoRedundancy, MVideoRedundancyVideo } from '@server/typings/models' export enum ScopeNames { WITH_VIDEO = 'WITH_VIDEO' @@ -166,7 +167,7 @@ export class VideoRedundancyModel extends Model { return undefined } - static async loadLocalByFileId (videoFileId: number) { + static async loadLocalByFileId (videoFileId: number): Promise { const actor = await getServerActor() const query = { @@ -179,7 +180,7 @@ export class VideoRedundancyModel extends Model { return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query) } - static async loadLocalByStreamingPlaylistId (videoStreamingPlaylistId: number) { + static async loadLocalByStreamingPlaylistId (videoStreamingPlaylistId: number): Promise { const actor = await getServerActor() const query = { @@ -192,7 +193,7 @@ export class VideoRedundancyModel extends Model { return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query) } - static loadByUrl (url: string, transaction?: Transaction) { + static loadByUrl (url: string, transaction?: Transaction): Bluebird { const query = { where: { url @@ -306,7 +307,7 @@ export class VideoRedundancyModel extends Model { return VideoRedundancyModel.getVideoSample(VideoModel.unscoped().findAll(query)) } - static async loadOldestLocalThatAlreadyExpired (strategy: VideoRedundancyStrategy, expiresAfterMs: number) { + static async loadOldestLocalExpired (strategy: VideoRedundancyStrategy, expiresAfterMs: number): Promise { const expiredDate = new Date() expiredDate.setMilliseconds(expiredDate.getMilliseconds() - expiresAfterMs) 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' import { PeerTubePlugin } from '../../../shared/models/plugins/peertube-plugin.model' import { FindAndCountOptions, json } from 'sequelize' import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model' +import * as Bluebird from 'bluebird' +import { MPlugin } from '@server/typings/models' @DefaultScope(() => ({ attributes: { @@ -85,7 +87,7 @@ export class PluginModel extends Model { @UpdatedAt updatedAt: Date - static listEnabledPluginsAndThemes () { + static listEnabledPluginsAndThemes (): Bluebird { const query = { where: { enabled: true, @@ -96,7 +98,7 @@ export class PluginModel extends Model { return PluginModel.findAll(query) } - static loadByNpmName (npmName: string) { + static loadByNpmName (npmName: string): Bluebird { const name = this.normalizePluginName(npmName) const type = this.getTypeFromNpmName(npmName) @@ -206,13 +208,13 @@ export class PluginModel extends Model { if (options.pluginType) query.where['type'] = options.pluginType return PluginModel - .findAndCountAll(query) + .findAndCountAll(query) .then(({ rows, count }) => { return { total: count, data: rows } }) } - static listInstalled () { + static listInstalled (): Bluebird { const query = { where: { 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' import { ServerModel } from './server' import { ServerBlock } from '../../../shared/models/blocklist' import { getSort } from '../utils' +import * as Bluebird from 'bluebird' +import { MServerBlocklist, MServerBlocklistAccountServer } from '@server/typings/models' enum ScopeNames { WITH_ACCOUNT = 'WITH_ACCOUNT', @@ -73,7 +75,7 @@ export class ServerBlocklistModel extends Model { }) BlockedServer: ServerModel - static loadByAccountAndHost (accountId: number, host: string) { + static loadByAccountAndHost (accountId: number, host: string): Bluebird { const query = { where: { accountId @@ -104,7 +106,7 @@ export class ServerBlocklistModel extends Model { return ServerBlocklistModel .scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_SERVER ]) - .findAndCountAll(query) + .findAndCountAll(query) .then(({ rows, count }) => { return { total: count, data: rows } }) 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 import { isHostValid } from '../../helpers/custom-validators/servers' import { ActorModel } from '../activitypub/actor' import { throwIfNotValid } from '../utils' -import { AccountBlocklistModel } from '../account/account-blocklist' import { ServerBlocklistModel } from './server-blocklist' +import * as Bluebird from 'bluebird' +import { MServer } from '@server/typings/models/server' @Table({ tableName: 'server', @@ -50,7 +51,7 @@ export class ServerModel extends Model { }) BlockedByAccounts: ServerBlocklistModel[] - static loadByHost (host: string) { + static loadByHost (host: string): Bluebird { const query = { where: { 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' import { VideoModel } from './video' import { VideoAbuseState } from '../../../shared' import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants' +import { MVideoAbuse, MVideoAbuseAccountVideo, MVideoAbuseVideo } from '../../typings/models' +import * as Bluebird from 'bluebird' @Table({ tableName: 'videoAbuse', @@ -73,7 +75,7 @@ export class VideoAbuseModel extends Model { }) Video: VideoModel - static loadByIdAndVideoId (id: number, videoId: number) { + static loadByIdAndVideoId (id: number, videoId: number): Bluebird { const query = { where: { id, @@ -106,7 +108,7 @@ export class VideoAbuseModel extends Model { }) } - toFormattedJSON (): VideoAbuse { + toFormattedJSON (this: MVideoAbuseAccountVideo): VideoAbuse { return { id: this.id, reason: this.reason, @@ -125,7 +127,7 @@ export class VideoAbuseModel extends Model { } } - toActivityPubObject (): VideoAbuseObject { + toActivityPubObject (this: MVideoAbuseVideo): VideoAbuseObject { return { type: 'Flag' as 'Flag', 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 @@ import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' import { getSortOnModel, SortType, throwIfNotValid } from '../utils' -import { ScopeNames as VideoModelScopeNames, VideoModel } from './video' +import { VideoModel } from './video' import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from './video-channel' import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist' import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos' import { CONSTRAINTS_FIELDS } from '../../initializers/constants' import { FindOptions } from 'sequelize' import { ThumbnailModel } from './thumbnail' +import * as Bluebird from 'bluebird' +import { MVideoBlacklist } from '@server/typings/models' @Table({ tableName: 'videoBlacklist', @@ -99,7 +101,7 @@ export class VideoBlacklistModel extends Model { }) } - static loadByVideoId (id: number) { + static loadByVideoId (id: number): Bluebird { const query = { where: { 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' import { logger } from '../../helpers/logger' import { remove } from 'fs-extra' import { CONFIG } from '../../initializers/config' +import * as Bluebird from 'bluebird' +import { MVideoCaptionVideo } from '@server/typings/models' export enum ScopeNames { WITH_VIDEO_UUID_AND_REMOTE = 'WITH_VIDEO_UUID_AND_REMOTE' @@ -30,7 +32,7 @@ export enum ScopeNames { [ScopeNames.WITH_VIDEO_UUID_AND_REMOTE]: { include: [ { - attributes: [ 'uuid', 'remote' ], + attributes: [ 'id', 'uuid', 'remote' ], model: VideoModel.unscoped(), required: true } @@ -93,7 +95,7 @@ export class VideoCaptionModel extends Model { return undefined } - static loadByVideoIdAndLanguage (videoId: string | number, language: string) { + static loadByVideoIdAndLanguage (videoId: string | number, language: string): Bluebird { const videoInclude = { model: VideoModel.unscoped(), attributes: [ 'id', 'remote', 'uuid' ], @@ -122,7 +124,7 @@ export class VideoCaptionModel extends Model { .then(([ caption ]) => caption) } - static listVideoCaptions (videoId: number) { + static listVideoCaptions (videoId: number): Bluebird { const query = { order: [ [ 'language', 'ASC' ] ] as OrderItem[], 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' import { ScopeNames as VideoScopeNames, VideoModel } from './video' import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '../../../shared/models/videos' import { getSort } from '../utils' +import { MVideoChangeOwnershipFull } from '@server/typings/models/video/video-change-ownership' +import * as Bluebird from 'bluebird' enum ScopeNames { WITH_ACCOUNTS = 'WITH_ACCOUNTS', @@ -108,11 +110,11 @@ export class VideoChangeOwnershipModel extends Model return Promise.all([ VideoChangeOwnershipModel.scope(ScopeNames.WITH_ACCOUNTS).count(query), - VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ]).findAll(query) + VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ]).findAll(query) ]).then(([ count, rows ]) => ({ total: count, data: rows })) } - static load (id: number) { + static load (id: number): Bluebird { return VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ]) .findByPk(id) } 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' import { FindOptions, ModelIndexesOptions, Op } from 'sequelize' import { AvatarModel } from '../avatar/avatar' import { VideoPlaylistModel } from './video-playlist' +import * as Bluebird from 'bluebird' +import { + MChannelAccountDefault, + MChannelActor, + MChannelActorAccountDefault, + MChannelActorAccountDefaultVideos +} from '../../typings/models/video' // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation const indexes: ModelIndexesOptions[] = [ @@ -47,7 +54,7 @@ const indexes: ModelIndexesOptions[] = [ ] export enum ScopeNames { - AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', + FOR_API = 'FOR_API', WITH_ACCOUNT = 'WITH_ACCOUNT', WITH_ACTOR = 'WITH_ACTOR', WITH_VIDEOS = 'WITH_VIDEOS', @@ -74,10 +81,10 @@ export type SummaryOptions = { @Scopes(() => ({ [ScopeNames.SUMMARY]: (options: SummaryOptions = {}) => { const base: FindOptions = { - attributes: [ 'name', 'description', 'id', 'actorId' ], + attributes: [ 'id', 'name', 'description', 'actorId' ], include: [ { - attributes: [ 'preferredUsername', 'url', 'serverId', 'avatarId' ], + attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ], model: ActorModel.unscoped(), required: true, include: [ @@ -106,7 +113,7 @@ export type SummaryOptions = { return base }, - [ScopeNames.AVAILABLE_FOR_LIST]: (options: AvailableForListOptions) => { + [ScopeNames.FOR_API]: (options: AvailableForListOptions) => { // Only list local channels OR channels that are on an instance followed by actorId const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId) @@ -268,7 +275,7 @@ export class VideoChannelModel extends Model { } const scopes = { - method: [ ScopeNames.AVAILABLE_FOR_LIST, { actorId } as AvailableForListOptions ] + method: [ ScopeNames.FOR_API, { actorId } as AvailableForListOptions ] } return VideoChannelModel .scope(scopes) @@ -278,7 +285,7 @@ export class VideoChannelModel extends Model { }) } - static listLocalsForSitemap (sort: string) { + static listLocalsForSitemap (sort: string): Bluebird { const query = { attributes: [ ], offset: 0, @@ -331,7 +338,7 @@ export class VideoChannelModel extends Model { } const scopes = { - method: [ ScopeNames.AVAILABLE_FOR_LIST, { actorId: options.actorId } as AvailableForListOptions ] + method: [ ScopeNames.FOR_API, { actorId: options.actorId } as AvailableForListOptions ] } return VideoChannelModel .scope(scopes) @@ -369,13 +376,13 @@ export class VideoChannelModel extends Model { }) } - static loadByIdAndPopulateAccount (id: number) { + static loadByIdAndPopulateAccount (id: number): Bluebird { return VideoChannelModel.unscoped() .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) .findByPk(id) } - static loadByIdAndAccount (id: number, accountId: number) { + static loadByIdAndAccount (id: number, accountId: number): Bluebird { const query = { where: { id, @@ -388,13 +395,13 @@ export class VideoChannelModel extends Model { .findOne(query) } - static loadAndPopulateAccount (id: number) { + static loadAndPopulateAccount (id: number): Bluebird { return VideoChannelModel.unscoped() .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) .findByPk(id) } - static loadByUrlAndPopulateAccount (url: string) { + static loadByUrlAndPopulateAccount (url: string): Bluebird { const query = { include: [ { @@ -420,7 +427,7 @@ export class VideoChannelModel extends Model { return VideoChannelModel.loadByNameAndHostAndPopulateAccount(name, host) } - static loadLocalByNameAndPopulateAccount (name: string) { + static loadLocalByNameAndPopulateAccount (name: string): Bluebird { const query = { include: [ { @@ -439,7 +446,7 @@ export class VideoChannelModel extends Model { .findOne(query) } - static loadByNameAndHostAndPopulateAccount (name: string, host: string) { + static loadByNameAndHostAndPopulateAccount (name: string, host: string): Bluebird { const query = { include: [ { @@ -464,7 +471,7 @@ export class VideoChannelModel extends Model { .findOne(query) } - static loadAndPopulateAccountAndVideos (id: number) { + static loadAndPopulateAccountAndVideos (id: number): Bluebird { const options = { include: [ 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 @@ -import { - AllowNull, - BeforeDestroy, - BelongsTo, - Column, - CreatedAt, - DataType, - ForeignKey, - Is, - Model, - Scopes, - Table, - UpdatedAt -} from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects' import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object' import { VideoComment } from '../../../shared/models/videos/video-comment.model' import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants' -import { sendDeleteVideoComment } from '../../lib/activitypub/send' import { AccountModel } from '../account/account' import { ActorModel } from '../activitypub/actor' -import { AvatarModel } from '../avatar/avatar' -import { ServerModel } from '../server/server' import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils' import { VideoModel } from './video' import { VideoChannelModel } from './video-channel' import { getServerActor } from '../../helpers/utils' -import { UserModel } from '../account/user' import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor' import { regexpCapture } from '../../helpers/regexp' import { uniq } from 'lodash' -import { FindOptions, literal, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize' +import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize' +import * as Bluebird from 'bluebird' +import { + MComment, + MCommentId, + MCommentOwner, + MCommentOwnerReplyVideoLight, + MCommentOwnerVideo, + MCommentOwnerVideoFeed, + MCommentOwnerVideoReply +} from '../../typings/models/video' +import { MUserAccountId } from '@server/typings/models' enum ScopeNames { WITH_ACCOUNT = 'WITH_ACCOUNT', @@ -68,22 +62,7 @@ enum ScopeNames { [ScopeNames.WITH_ACCOUNT]: { include: [ { - model: AccountModel, - include: [ - { - model: ActorModel, - include: [ - { - model: ServerModel, - required: false - }, - { - model: AvatarModel, - required: false - } - ] - } - ] + model: AccountModel } ] }, @@ -102,22 +81,12 @@ enum ScopeNames { required: true, include: [ { - model: VideoChannelModel.unscoped(), + model: VideoChannelModel, required: true, include: [ - { - model: ActorModel, - required: true - }, { model: AccountModel, - required: true, - include: [ - { - model: ActorModel, - required: true - } - ] + required: true } ] } @@ -212,7 +181,7 @@ export class VideoCommentModel extends Model { }) Account: AccountModel - static loadById (id: number, t?: Transaction) { + static loadById (id: number, t?: Transaction): Bluebird { const query: FindOptions = { where: { id @@ -224,7 +193,7 @@ export class VideoCommentModel extends Model { return VideoCommentModel.findOne(query) } - static loadByIdAndPopulateVideoAndAccountAndReply (id: number, t?: Transaction) { + static loadByIdAndPopulateVideoAndAccountAndReply (id: number, t?: Transaction): Bluebird { const query: FindOptions = { where: { id @@ -238,7 +207,7 @@ export class VideoCommentModel extends Model { .findOne(query) } - static loadByUrlAndPopulateAccountAndVideo (url: string, t?: Transaction) { + static loadByUrlAndPopulateAccountAndVideo (url: string, t?: Transaction): Bluebird { const query: FindOptions = { where: { url @@ -250,7 +219,7 @@ export class VideoCommentModel extends Model { return VideoCommentModel.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEO ]).findOne(query) } - static loadByUrlAndPopulateReplyAndVideoUrlAndAccount (url: string, t?: Transaction) { + static loadByUrlAndPopulateReplyAndVideoUrlAndAccount (url: string, t?: Transaction): Bluebird { const query: FindOptions = { where: { url @@ -273,7 +242,7 @@ export class VideoCommentModel extends Model { start: number, count: number, sort: string, - user?: UserModel + user?: MUserAccountId }) { const { videoId, start, count, sort, user } = parameters @@ -314,7 +283,7 @@ export class VideoCommentModel extends Model { static async listThreadCommentsForApi (parameters: { videoId: number, threadId: number, - user?: UserModel + user?: MUserAccountId }) { const { videoId, threadId, user } = parameters @@ -353,7 +322,7 @@ export class VideoCommentModel extends Model { }) } - static listThreadParentComments (comment: VideoCommentModel, t: Transaction, order: 'ASC' | 'DESC' = 'ASC') { + static listThreadParentComments (comment: MCommentId, t: Transaction, order: 'ASC' | 'DESC' = 'ASC'): Bluebird { const query = { order: [ [ 'createdAt', order ] ] as Order, where: { @@ -389,10 +358,10 @@ export class VideoCommentModel extends Model { transaction: t } - return VideoCommentModel.findAndCountAll(query) + return VideoCommentModel.findAndCountAll(query) } - static listForFeed (start: number, count: number, videoId?: number) { + static listForFeed (start: number, count: number, videoId?: number): Bluebird { const query = { order: [ [ 'createdAt', 'DESC' ] ] as Order, offset: start, @@ -521,7 +490,7 @@ export class VideoCommentModel extends Model { } as VideoComment } - toActivityPubObject (threadParentComments: VideoCommentModel[]): VideoCommentObject { + toActivityPubObject (threadParentComments: MCommentOwner[]): VideoCommentObject { let inReplyTo: string // New thread, so in AS we reply to the video 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' import { VideoStreamingPlaylistModel } from './video-streaming-playlist' import { FindOptions, QueryTypes, Transaction } from 'sequelize' import { MIMETYPES } from '../../initializers/constants' +import { MVideoFile } from '@server/typings/models' @Table({ tableName: 'videoFile', @@ -166,7 +167,7 @@ export class VideoFileModel extends Model { return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname] } - hasSameUniqueKeysThan (other: VideoFileModel) { + hasSameUniqueKeysThan (other: MVideoFile) { return this.fps === other.fps && this.resolution === other.resolution && 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 @@ import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos' import { VideoModel } from './video' -import { VideoFileModel } from './video-file' import { ActivityPlaylistInfohashesObject, ActivityPlaylistSegmentHashesObject, @@ -17,7 +16,9 @@ import { } from '../../lib/activitypub' import { isArray } from '../../helpers/custom-validators/misc' import { VideoStreamingPlaylist } from '../../../shared/models/videos/video-streaming-playlist.model' -import { VideoStreamingPlaylistModel } from './video-streaming-playlist' +import { MVideo, MVideoAP, MVideoDetails } from '../../typings/models' +import { MStreamingPlaylistRedundancies } from '../../typings/models/video/video-streaming-playlist' +import { MVideoFileRedundanciesOpt } from '../../typings/models/video/video-file' export type VideoFormattingJSONOptions = { completeDescription?: boolean @@ -102,7 +103,7 @@ function videoModelToFormattedJSON (video: VideoModel, options?: VideoFormatting return videoObject } -function videoModelToFormattedDetailsJSON (video: VideoModel): VideoDetails { +function videoModelToFormattedDetailsJSON (video: MVideoDetails): VideoDetails { const formattedJson = video.toFormattedJSON({ additionalAttributes: { scheduledUpdate: true, @@ -114,7 +115,7 @@ function videoModelToFormattedDetailsJSON (video: VideoModel): VideoDetails { const tags = video.Tags ? video.Tags.map(t => t.name) : [] - const streamingPlaylists = streamingPlaylistsModelToFormattedJSON(video, video.VideoStreamingPlaylists) + const streamingPlaylists = streamingPlaylistsModelToFormattedJSON(video.VideoStreamingPlaylists) const detailsJson = { support: video.support, @@ -142,7 +143,7 @@ function videoModelToFormattedDetailsJSON (video: VideoModel): VideoDetails { return Object.assign(formattedJson, detailsJson) } -function streamingPlaylistsModelToFormattedJSON (video: VideoModel, playlists: VideoStreamingPlaylistModel[]): VideoStreamingPlaylist[] { +function streamingPlaylistsModelToFormattedJSON (playlists: MStreamingPlaylistRedundancies[]): VideoStreamingPlaylist[] { if (isArray(playlists) === false) return [] return playlists @@ -161,7 +162,7 @@ function streamingPlaylistsModelToFormattedJSON (video: VideoModel, playlists: V }) } -function videoFilesModelToFormattedJSON (video: VideoModel, videoFiles: VideoFileModel[]): VideoFile[] { +function videoFilesModelToFormattedJSON (video: MVideo, videoFiles: MVideoFileRedundanciesOpt[]): VideoFile[] { const { baseUrlHttp, baseUrlWs } = video.getBaseUrls() return videoFiles @@ -189,7 +190,7 @@ function videoFilesModelToFormattedJSON (video: VideoModel, videoFiles: VideoFil }) } -function videoModelToActivityPubObject (video: VideoModel): VideoTorrentObject { +function videoModelToActivityPubObject (video: MVideoAP): VideoTorrentObject { const { baseUrlHttp, baseUrlWs } = video.getBaseUrls() if (!video.Tags) video.Tags = [] 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 import { VideoImport, VideoImportState } from '../../../shared' import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos' import { UserModel } from '../account/user' +import * as Bluebird from 'bluebird' +import { MVideoImportDefault } from '@server/typings/models/video/video-import' @DefaultScope(() => ({ include: [ @@ -28,7 +30,11 @@ import { UserModel } from '../account/user' required: true }, { - model: VideoModel.scope([ VideoModelScopeNames.WITH_ACCOUNT_DETAILS, VideoModelScopeNames.WITH_TAGS]), + model: VideoModel.scope([ + VideoModelScopeNames.WITH_ACCOUNT_DETAILS, + VideoModelScopeNames.WITH_TAGS, + VideoModelScopeNames.WITH_THUMBNAILS + ]), required: false } ] @@ -114,7 +120,7 @@ export class VideoImportModel extends Model { return undefined } - static loadAndPopulateVideo (id: number) { + static loadAndPopulateVideo (id: number): Bluebird { return VideoImportModel.findByPk(id) } @@ -135,7 +141,7 @@ export class VideoImportModel extends Model { } } - return VideoImportModel.findAndCountAll(query) + return VideoImportModel.findAndCountAll(query) .then(({ rows, count }) => { return { 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' import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../shared/models/videos/playlist/video-playlist-element.model' import { AccountModel } from '../account/account' import { VideoPrivacy } from '../../../shared/models/videos' +import * as Bluebird from 'bluebird' +import { MVideoPlaylistAP, MVideoPlaylistElement, MVideoPlaylistVideoThumbnail } from '@server/typings/models/video/video-playlist-element' +import { MUserAccountId } from '@server/typings/models' @Table({ tableName: 'videoPlaylistElement', @@ -116,7 +119,7 @@ export class VideoPlaylistElementModel extends Model count: number, videoPlaylistId: number, serverAccount: AccountModel, - user?: UserModel + user?: MUserAccountId }) { const accountIds = [ options.serverAccount.id ] const videoScope: (ScopeOptions | string)[] = [ @@ -162,7 +165,7 @@ export class VideoPlaylistElementModel extends Model ]).then(([ total, data ]) => ({ total, data })) } - static loadByPlaylistAndVideo (videoPlaylistId: number, videoId: number) { + static loadByPlaylistAndVideo (videoPlaylistId: number, videoId: number): Bluebird { const query = { where: { videoPlaylistId, @@ -173,11 +176,11 @@ export class VideoPlaylistElementModel extends Model return VideoPlaylistElementModel.findOne(query) } - static loadById (playlistElementId: number) { + static loadById (playlistElementId: number): Bluebird { return VideoPlaylistElementModel.findByPk(playlistElementId) } - static loadByPlaylistAndVideoForAP (playlistId: number | string, videoId: number | string) { + static loadByPlaylistAndVideoForAP (playlistId: number | string, videoId: number | string): Bluebird { const playlistWhere = validator.isUUID('' + playlistId) ? { uuid: playlistId } : { id: playlistId } const videoWhere = validator.isUUID('' + videoId) ? { uuid: videoId } : { id: videoId } @@ -218,7 +221,7 @@ export class VideoPlaylistElementModel extends Model }) } - static loadFirstElementWithVideoThumbnail (videoPlaylistId: number) { + static loadFirstElementWithVideoThumbnail (videoPlaylistId: number): Bluebird { const query = { order: getSort('position'), 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- import { ThumbnailModel } from './thumbnail' import { ActivityIconObject } from '../../../shared/models/activitypub/objects' import { FindOptions, literal, Op, ScopeOptions, Transaction, WhereOptions } from 'sequelize' +import * as Bluebird from 'bluebird' +import { + MVideoPlaylistAccountThumbnail, + MVideoPlaylistFull, + MVideoPlaylistFullSummary, + MVideoPlaylistIdWithElements +} from '../../typings/models/video/video-playlist' +import { MThumbnail } from '../../typings/models/video/thumbnail' enum ScopeNames { AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', @@ -332,7 +340,7 @@ export class VideoPlaylistModel extends Model { }) } - static listPlaylistIdsOf (accountId: number, videoIds: number[]) { + static listPlaylistIdsOf (accountId: number, videoIds: number[]): Bluebird { const query = { attributes: [ 'id' ], where: { @@ -368,7 +376,7 @@ export class VideoPlaylistModel extends Model { .then(e => !!e) } - static loadWithAccountAndChannelSummary (id: number | string, transaction: Transaction) { + static loadWithAccountAndChannelSummary (id: number | string, transaction: Transaction): Bluebird { const where = buildWhereIdOrUUID(id) const query = { @@ -381,7 +389,7 @@ export class VideoPlaylistModel extends Model { .findOne(query) } - static loadWithAccountAndChannel (id: number | string, transaction: Transaction) { + static loadWithAccountAndChannel (id: number | string, transaction: Transaction): Bluebird { const where = buildWhereIdOrUUID(id) const query = { @@ -394,7 +402,7 @@ export class VideoPlaylistModel extends Model { .findOne(query) } - static loadByUrlAndPopulateAccount (url: string) { + static loadByUrlAndPopulateAccount (url: string): Bluebird { const query = { where: { url @@ -423,7 +431,7 @@ export class VideoPlaylistModel extends Model { return VideoPlaylistModel.update({ privacy: VideoPlaylistPrivacy.PRIVATE, videoChannelId: null }, query) } - async setAndSaveThumbnail (thumbnail: ThumbnailModel, t: Transaction) { + async setAndSaveThumbnail (thumbnail: MThumbnail, t: Transaction) { thumbnail.videoPlaylistId = this.id 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' import { VideoModel } from './video' import { VideoChannelModel } from './video-channel' import { Op, Transaction } from 'sequelize' +import { MVideoShareActor, MVideoShareFull } from '../../typings/models/video' +import { MActorDefault } from '../../typings/models' enum ScopeNames { FULL = 'FULL', @@ -88,7 +90,7 @@ export class VideoShareModel extends Model { }) Video: VideoModel - static load (actorId: number, videoId: number, t?: Transaction) { + static load (actorId: number, videoId: number, t?: Transaction): Bluebird { return VideoShareModel.scope(ScopeNames.WITH_ACTOR).findOne({ where: { actorId, @@ -98,7 +100,7 @@ export class VideoShareModel extends Model { }) } - static loadByUrl (url: string, t: Transaction) { + static loadByUrl (url: string, t: Transaction): Bluebird { return VideoShareModel.scope(ScopeNames.FULL).findOne({ where: { url @@ -107,7 +109,7 @@ export class VideoShareModel extends Model { }) } - static loadActorsByShare (videoId: number, t: Transaction) { + static loadActorsByShare (videoId: number, t: Transaction): Bluebird { const query = { where: { videoId @@ -122,10 +124,10 @@ export class VideoShareModel extends Model { } return VideoShareModel.scope(ScopeNames.FULL).findAll(query) - .then(res => res.map(r => r.Actor)) + .then((res: MVideoShareFull[]) => res.map(r => r.Actor)) } - static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Transaction): Bluebird { + static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Transaction): Bluebird { const query = { attributes: [], include: [ @@ -163,7 +165,7 @@ export class VideoShareModel extends Model { .then(res => res.map(r => r.Actor)) } - static loadActorsByVideoChannel (videoChannelId: number, t: Transaction): Bluebird { + static loadActorsByVideoChannel (videoChannelId: number, t: Transaction): Bluebird { const query = { attributes: [], 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 @@ -import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, HasMany, Is, Model, Table, UpdatedAt, DataType } from 'sequelize-typescript' +import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' import { throwIfNotValid } from '../utils' import { VideoModel } from './video' import { VideoRedundancyModel } from '../redundancy/video-redundancy' import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' -import { CONSTRAINTS_FIELDS, STATIC_PATHS, P2P_MEDIA_LOADER_PEER_VERSION } from '../../initializers/constants' -import { VideoFileModel } from './video-file' +import { CONSTRAINTS_FIELDS, P2P_MEDIA_LOADER_PEER_VERSION, STATIC_PATHS } from '../../initializers/constants' import { join } from 'path' import { sha1 } from '../../helpers/core-utils' import { isArrayOf } from '../../helpers/custom-validators/misc' -import { QueryTypes, Op } from 'sequelize' +import { Op, QueryTypes } from 'sequelize' +import { MStreamingPlaylist, MVideoFile } from '@server/typings/models' @Table({ tableName: 'videoStreamingPlaylist', @@ -91,7 +91,7 @@ export class VideoStreamingPlaylistModel extends Model results.length === 1) } - static buildP2PMediaLoaderInfoHashes (playlistUrl: string, videoFiles: VideoFileModel[]) { + static buildP2PMediaLoaderInfoHashes (playlistUrl: string, videoFiles: MVideoFile[]) { const hashes: string[] = [] // 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 { VideoCaptions: VideoCaptionModel[] @BeforeDestroy - static async sendDelete (instance: VideoModel, options) { + static async sendDelete (instance: MVideoAccountLight, options) { if (instance.isOwned()) { if (!instance.VideoChannel) { instance.VideoChannel = await instance.$get('VideoChannel', { include: [ - { - model: AccountModel, - include: [ ActorModel ] - } + ActorModel, + AccountModel ], transaction: options.transaction - }) as VideoChannelModel + }) as MChannelActorAccountDefault } return sendDeleteVideo(instance, options.transaction) @@ -1039,7 +1054,7 @@ export class VideoModel extends Model { return undefined } - static listLocal () { + static listLocal (): Bluebird { const query = { where: { remote: false @@ -1159,7 +1174,7 @@ export class VideoModel extends Model { }) } - static listUserVideosForApi (accountId: number, start: number, count: number, sort: string, withFiles = false) { + static listUserVideosForApi (accountId: number, start: number, count: number, sort: string) { function buildBaseQuery (): FindOptions { return { offset: start, @@ -1192,19 +1207,12 @@ export class VideoModel extends Model { ScopeNames.WITH_THUMBNAILS ] - if (withFiles === true) { - findQuery.include.push({ - model: VideoFileModel.unscoped(), - required: true - }) - } - return Promise.all([ VideoModel.count(countQuery), VideoModel.scope(findScopes).findAll(findQuery) ]).then(([ count, rows ]) => { return { - data: rows, + data: rows as MVideoWithBlacklistThumbnailScheduled[], total: count } }) @@ -1228,8 +1236,8 @@ export class VideoModel extends Model { followerActorId?: number videoPlaylistId?: number, trendingDays?: number, - user?: UserModel, - historyOfUser?: UserModel + user?: MUserAccountId, + historyOfUser?: MUserId }, countVideos = true) { if (options.filter && options.filter === 'all-local' && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) { 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 { tagsAllOf?: string[] durationMin?: number // seconds durationMax?: number // seconds - user?: UserModel, + user?: MUserAccountId, filter?: VideoFilter }) { const whereAnd = [] @@ -1387,7 +1395,7 @@ export class VideoModel extends Model { return VideoModel.getAvailableForApi(query, queryOptions) } - static load (id: number | string, t?: Transaction) { + static load (id: number | string, t?: Transaction): Bluebird { const where = buildWhereIdOrUUID(id) const options = { where, @@ -1397,7 +1405,7 @@ export class VideoModel extends Model { return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) } - static loadWithRights (id: number | string, t?: Transaction) { + static loadWithRights (id: number | string, t?: Transaction): Bluebird { const where = buildWhereIdOrUUID(id) const options = { where, @@ -1411,7 +1419,7 @@ export class VideoModel extends Model { ]).findOne(options) } - static loadOnlyId (id: number | string, t?: Transaction) { + static loadOnlyId (id: number | string, t?: Transaction): Bluebird { const where = buildWhereIdOrUUID(id) const options = { @@ -1423,7 +1431,7 @@ export class VideoModel extends Model { return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) } - static loadWithFiles (id: number | string, t?: Transaction, logging?: boolean) { + static loadWithFiles (id: number | string, t?: Transaction, logging?: boolean): Bluebird { const where = buildWhereIdOrUUID(id) const query = { @@ -1439,7 +1447,7 @@ export class VideoModel extends Model { ]).findOne(query) } - static loadByUUID (uuid: string) { + static loadByUUID (uuid: string): Bluebird { const options = { where: { uuid @@ -1449,7 +1457,7 @@ export class VideoModel extends Model { return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) } - static loadByUrl (url: string, transaction?: Transaction) { + static loadByUrl (url: string, transaction?: Transaction): Bluebird { const query: FindOptions = { where: { url @@ -1460,7 +1468,7 @@ export class VideoModel extends Model { return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(query) } - static loadByUrlAndPopulateAccount (url: string, transaction?: Transaction) { + static loadByUrlAndPopulateAccount (url: string, transaction?: Transaction): Bluebird { const query: FindOptions = { where: { url @@ -1472,11 +1480,12 @@ export class VideoModel extends Model { ScopeNames.WITH_ACCOUNT_DETAILS, ScopeNames.WITH_FILES, ScopeNames.WITH_STREAMING_PLAYLISTS, - ScopeNames.WITH_THUMBNAILS + ScopeNames.WITH_THUMBNAILS, + ScopeNames.WITH_BLACKLISTED ]).findOne(query) } - static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Transaction, userId?: number) { + static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Transaction, userId?: number): Bluebird { const where = buildWhereIdOrUUID(id) const options = { @@ -1508,7 +1517,7 @@ export class VideoModel extends Model { id: number | string, t?: Transaction, userId?: number - }) { + }): Bluebird { const { id, t, userId } = parameters const where = buildWhereIdOrUUID(id) @@ -1586,7 +1595,7 @@ export class VideoModel extends Model { .then(results => results.length === 1) } - static bulkUpdateSupportField (videoChannel: VideoChannelModel, t: Transaction) { + static bulkUpdateSupportField (videoChannel: MChannel, t: Transaction) { const options = { where: { channelId: videoChannel.id @@ -1597,7 +1606,7 @@ export class VideoModel extends Model { return VideoModel.update({ support: videoChannel.support }, options) } - static getAllIdsFromChannel (videoChannel: VideoChannelModel) { + static getAllIdsFromChannel (videoChannel: MChannelId): Bluebird { const query = { attributes: [ 'id' ], where: { @@ -1769,7 +1778,7 @@ export class VideoModel extends Model { return this.VideoFiles.find(f => f.resolution === resolution) } - async addAndSaveThumbnail (thumbnail: ThumbnailModel, transaction: Transaction) { + async addAndSaveThumbnail (thumbnail: MThumbnail, transaction: Transaction) { thumbnail.videoId = this.id const savedThumbnail = await thumbnail.save({ transaction }) @@ -1782,7 +1791,7 @@ export class VideoModel extends Model { this.Thumbnails.push(savedThumbnail) } - getVideoFilename (videoFile: VideoFileModel) { + getVideoFilename (videoFile: MVideoFile) { return this.uuid + '-' + videoFile.resolution + videoFile.extname } @@ -1806,7 +1815,7 @@ export class VideoModel extends Model { return this.Thumbnails.find(t => t.type === ThumbnailType.PREVIEW) } - getTorrentFileName (videoFile: VideoFileModel) { + getTorrentFileName (videoFile: MVideoFile) { const extension = '.torrent' return this.uuid + '-' + videoFile.resolution + extension } @@ -1815,15 +1824,15 @@ export class VideoModel extends Model { return this.remote === false } - getTorrentFilePath (videoFile: VideoFileModel) { + getTorrentFilePath (videoFile: MVideoFile) { return join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile)) } - getVideoFilePath (videoFile: VideoFileModel) { + getVideoFilePath (videoFile: MVideoFile) { return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile)) } - async createTorrentAndSetInfoHash (videoFile: VideoFileModel) { + async createTorrentAndSetInfoHash (videoFile: MVideoFile) { const options = { // Keep the extname, it's used by the client to stream the file inside a web browser name: `${this.name} ${videoFile.resolution}p${videoFile.extname}`, @@ -1908,7 +1917,7 @@ export class VideoModel extends Model { return this.VideoStreamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS) } - removeFile (videoFile: VideoFileModel, isRedundancy = false) { + removeFile (videoFile: MVideoFile, isRedundancy = false) { const baseDir = isRedundancy ? CONFIG.STORAGE.REDUNDANCY_DIR : CONFIG.STORAGE.VIDEOS_DIR const filePath = join(baseDir, this.getVideoFilename(videoFile)) @@ -1916,7 +1925,7 @@ export class VideoModel extends Model { .catch(err => logger.warn('Cannot delete file %s.', filePath, { err })) } - removeTorrent (videoFile: VideoFileModel) { + removeTorrent (videoFile: MVideoFile) { const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile)) return remove(torrentPath) .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err })) @@ -1957,7 +1966,7 @@ export class VideoModel extends Model { return { baseUrlHttp, baseUrlWs } } - generateMagnetUri (videoFile: VideoFileModel, baseUrlHttp: string, baseUrlWs: string) { + generateMagnetUri (videoFile: MVideoFileRedundanciesOpt, baseUrlHttp: string, baseUrlWs: string) { const xs = this.getTorrentUrl(videoFile, baseUrlHttp) const announce = this.getTrackerUrls(baseUrlHttp, baseUrlWs) let urlList = [ this.getVideoFileUrl(videoFile, baseUrlHttp) ] @@ -1980,27 +1989,27 @@ export class VideoModel extends Model { return [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ] } - getTorrentUrl (videoFile: VideoFileModel, baseUrlHttp: string) { + getTorrentUrl (videoFile: MVideoFile, baseUrlHttp: string) { return baseUrlHttp + STATIC_PATHS.TORRENTS + this.getTorrentFileName(videoFile) } - getTorrentDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) { + getTorrentDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) { return baseUrlHttp + STATIC_DOWNLOAD_PATHS.TORRENTS + this.getTorrentFileName(videoFile) } - getVideoFileUrl (videoFile: VideoFileModel, baseUrlHttp: string) { + getVideoFileUrl (videoFile: MVideoFile, baseUrlHttp: string) { return baseUrlHttp + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile) } - getVideoRedundancyUrl (videoFile: VideoFileModel, baseUrlHttp: string) { + getVideoRedundancyUrl (videoFile: MVideoFile, baseUrlHttp: string) { return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getVideoFilename(videoFile) } - getVideoFileDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) { + getVideoFileDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) { return baseUrlHttp + STATIC_DOWNLOAD_PATHS.VIDEOS + this.getVideoFilename(videoFile) } - getBandwidthBits (videoFile: VideoFileModel) { + getBandwidthBits (videoFile: MVideoFile) { return Math.ceil((videoFile.size * 8) / this.duration) } } 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 @@ import { Activity } from '../../shared/models/activitypub' -import { ActorModel } from '../models/activitypub/actor' -import { SignatureActorModel } from './models' +import { MActorDefault, MActorSignature } from './models' export type APProcessorOptions = { activity: T - byActor: SignatureActorModel - inboxActor?: ActorModel + byActor: MActorSignature + inboxActor?: MActorDefault fromFetch?: boolean } 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 @@ -import { VideoChannelModel } from '../models/video/video-channel' -import { VideoPlaylistModel } from '../models/video/video-playlist' -import { VideoPlaylistElementModel } from '../models/video/video-playlist-element' -import { UserModel } from '../models/account/user' -import { VideoModel } from '../models/video/video' -import { AccountModel } from '../models/account/account' -import { VideoChangeOwnershipModel } from '../models/video/video-change-ownership' -import { ActorModel } from '../models/activitypub/actor' -import { VideoCommentModel } from '../models/video/video-comment' -import { VideoShareModel } from '../models/video/video-share' -import { AccountVideoRateModel } from '../models/account/account-video-rate' -import { ActorFollowModel } from '../models/activitypub/actor-follow' -import { ServerModel } from '../models/server/server' -import { VideoFileModel } from '../models/video/video-file' -import { VideoRedundancyModel } from '../models/redundancy/video-redundancy' -import { ServerBlocklistModel } from '../models/server/server-blocklist' -import { AccountBlocklistModel } from '../models/account/account-blocklist' -import { VideoImportModel } from '../models/video/video-import' -import { VideoAbuseModel } from '../models/video/video-abuse' -import { VideoBlacklistModel } from '../models/video/video-blacklist' -import { VideoCaptionModel } from '../models/video/video-caption' -import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' import { RegisteredPlugin } from '../lib/plugins/plugin-manager' -import { PluginModel } from '../models/server/plugin' -import { SignatureActorModel } from './models' +import { + MAccountDefault, + MActorAccountChannelId, + MActorFollowActorsDefault, + MActorFollowActorsDefaultSubscription, + MActorFull, + MChannelActorAccountDefault, + MComment, + MCommentOwnerVideoReply, + MUserDefault, + MVideoAbuse, + MVideoBlacklist, + MVideoCaptionVideo, + MVideoFullLight, + MVideoIdThumbnail, + MVideoRedundancyVideo, + MVideoShareActor, + MVideoThumbnail, + MVideoWithRights +} from './models' +import { MVideoPlaylistFull, MVideoPlaylistFullSummary } from './models/video/video-playlist' +import { MVideoImportDefault } from '@server/typings/models/video/video-import' +import { MAccountBlocklist, MStreamingPlaylist, MVideoFile } from '@server/typings/models' +import { MVideoPlaylistElement } from '@server/typings/models/video/video-playlist-element' +import { MAccountVideoRateAccountVideo } from '@server/typings/models/video/video-rate' +import { MVideoChangeOwnershipFull } from './models/video/video-change-ownership' +import { MPlugin, MServer } from '@server/typings/models/server' +import { MServerBlocklist } from './models/server/server-blocklist' +import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token' declare module 'express' { interface Response { + locals: { - video?: VideoModel - videoShare?: VideoShareModel - videoFile?: VideoFileModel + videoAll?: MVideoFullLight + onlyVideo?: MVideoThumbnail + onlyVideoWithRights?: MVideoWithRights + videoId?: MVideoIdThumbnail - videoImport?: VideoImportModel + videoShare?: MVideoShareActor - videoBlacklist?: VideoBlacklistModel + videoFile?: MVideoFile - videoCaption?: VideoCaptionModel + videoImport?: MVideoImportDefault - videoAbuse?: VideoAbuseModel + videoBlacklist?: MVideoBlacklist - videoStreamingPlaylist?: VideoStreamingPlaylistModel + videoCaption?: MVideoCaptionVideo - videoChannel?: VideoChannelModel + videoAbuse?: MVideoAbuse - videoPlaylist?: VideoPlaylistModel - videoPlaylistElement?: VideoPlaylistElementModel + videoStreamingPlaylist?: MStreamingPlaylist - accountVideoRate?: AccountVideoRateModel + videoChannel?: MChannelActorAccountDefault - videoComment?: VideoCommentModel - videoCommentThread?: VideoCommentModel + videoPlaylistFull?: MVideoPlaylistFull + videoPlaylistSummary?: MVideoPlaylistFullSummary - follow?: ActorFollowModel - subscription?: ActorFollowModel + videoPlaylistElement?: MVideoPlaylistElement - nextOwner?: AccountModel - videoChangeOwnership?: VideoChangeOwnershipModel - account?: AccountModel - actor?: ActorModel - user?: UserModel + accountVideoRate?: MAccountVideoRateAccountVideo - server?: ServerModel + videoCommentFull?: MCommentOwnerVideoReply + videoCommentThread?: MComment - videoRedundancy?: VideoRedundancyModel + follow?: MActorFollowActorsDefault + subscription?: MActorFollowActorsDefaultSubscription - accountBlock?: AccountBlocklistModel - serverBlock?: ServerBlocklistModel + nextOwner?: MAccountDefault + videoChangeOwnership?: MVideoChangeOwnershipFull + + account?: MAccountDefault + + actorFull?: MActorFull + + user?: MUserDefault + + server?: MServer + + videoRedundancy?: MVideoRedundancyVideo + + accountBlock?: MAccountBlocklist + serverBlock?: MServerBlocklist oauth?: { - token: { - User: UserModel - user: UserModel - } + token: MOAuthTokenUser } signature?: { - actor: SignatureActorModel + actor: MActorAccountChannelId } authenticated?: boolean registeredPlugin?: RegisteredPlugin - plugin?: PluginModel + plugin?: MPlugin } } } 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 @@ +import { AccountBlocklistModel } from '../../../models/account/account-blocklist' +import { PickWith } from '../../utils' +import { MAccountDefault } from './account' + +export type MAccountBlocklist = Omit + +export type MAccountBlocklistId = Pick + +export type MAccountBlocklistAccounts = MAccountBlocklist & + PickWith & + PickWith 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 @@ +import { AccountModel } from '../../../models/account/account' +import { + MActor, + MActorAccountChannelId, + MActorAPI, + MActorAudience, + MActorDefault, + MActorDefaultLight, MActorId, + MActorServer, + MActorSummary, + MActorUrl +} from './actor' +import { PickWith } from '../../utils' +import { MAccountBlocklistId } from './account-blocklist' +import { MChannelDefault } from '@server/typings/models' + +export type MAccountId = Pick +export type MAccountIdActor = MAccountId & + PickWith +export type MAccountIdActorId = MAccountId & + PickWith + +export type MAccount = Omit + +// Default scope +export type MAccountDefault = MAccount & + PickWith + +export type MAccountDefaultChannelDefault = MAccountDefault & + PickWith + +export type MAccountLight = MAccount & + PickWith + +export type MAccountUserId = Pick + +export type MAccountActor = MAccount & + PickWith +export type MAccountServer = MAccountActor & + PickWith + +export type MAccountActorDefault = MAccount & + PickWith + +export type MAccountSummary = Pick & + PickWith + +export type MAccountBlocks = MAccountSummary & + PickWith + +export type MAccountAPI = MAccountDefault & + PickWith + +export type MAccountUrl = PickWith +export type MAccountAudience = PickWith 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 @@ +import { ActorFollowModel } from '../../../models/activitypub/actor-follow' +import { MActor, MActorAccountChannel, MActorChannel, MActorChannelAccount, MActorDefault, MActorHost, MActorUsername } from './actor' +import { PickWith } from '../../utils' + +export type MActorFollow = Omit + +export type MActorFollowActors = MActorFollow & + PickWith & + PickWith + +export type MActorFollowActorsDefault = MActorFollow & + PickWith & + PickWith + +export type MActorFollowActorsDefaultSubscription = MActorFollow & + PickWith & + PickWith + +export type MActorFollowFull = MActorFollow & + PickWith & + PickWith + +export type MActorFollowFollowingHost = MActorFollow & + PickWith + +export type MActorFollowSubscriptions = MActorFollow & + PickWith 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 @@ +import { ActorModel } from '../../../models/activitypub/actor' +import { PickWith } from '../../utils' +import { MAccount, MAccountActorDefault, MAccountId, MAccountIdActor } from './account' +import { MServerHost, MServerHostBlocks, MServer } from '../server' +import { MAvatar } from './avatar' +import { MChannel, MChannelAccountActor, MChannelActorAccountDefault, MChannelId, MChannelIdActor } from '../video' + +export type MActor = Omit + +export type MActorUrl = Pick +export type MActorId = Pick +export type MActorUsername = Pick +export type MActorHost = PickWith + +export type MActorFollowersUrl = Pick +export type MActorAudience = MActorUrl & MActorFollowersUrl + +export type MActorLight = Omit + +export type MActorDefaultLight = MActorLight & + MActorHost & + PickWith + +export type MActorAccountId = MActor & + PickWith +export type MActorAccountIdActor = MActor & + PickWith + +export type MActorChannelId = MActor & + PickWith +export type MActorChannelIdActor = MActor & + PickWith + +export type MActorAccountChannelId = MActorAccountId & MActorChannelId +export type MActorAccountChannelIdActor = MActorAccountIdActor & MActorChannelIdActor + +export type MActorAccount = MActor & + PickWith + +export type MActorChannel = MActor & + PickWith + +export type MActorAccountChannel = MActorAccount & MActorChannel + +export type MActorChannelAccount = MActor & + PickWith + +export type MActorServer = MActor & + PickWith + +export type MActorDefault = MActorServer & + PickWith + +export type MActorFull = MActorDefault & + PickWith & + PickWith + +export type MActorFullActor = MActorDefault & + PickWith & + PickWith + +export type MActorSummary = Pick & + MActorHost & + PickWith + +export type MActorSummaryBlocks = Omit & + PickWith + +export type MActorFollowerException = Pick + +export type MActorAPI = Omit + +export 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 @@ +import { AvatarModel } from '../../../models/avatar/avatar' + +export 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 @@ +export * from './account' +export * from './account-blocklist' +export * from './actor' +export * from './actor-follow' +export * 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 @@ -import { ActorFollowModel } from '../../models/activitypub/actor-follow' -import { ActorModelOnly } from './actor' - -export type ActorFollowModelOnly = Omit -export type ActorFollowModelLight = ActorFollowModelOnly & { - ActorFollower: ActorModelOnly - ActorFollowing: ActorModelOnly -} 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 @@ -import { ActorModel } from '../../models/activitypub/actor' -import { VideoChannelModel } from '../../models/video/video-channel' -import { AccountModel } from '../../models/account/account' -import { FunctionProperties } from '../utils' - -export type VideoChannelModelId = FunctionProperties -export type AccountModelId = FunctionProperties | Pick - -export type VideoChannelModelIdActor = VideoChannelModelId & Pick -export type AccountModelIdActor = AccountModelId & Pick - -export type ActorModelUrl = Pick -export type ActorModelOnly = Omit -export type ActorModelId = Pick - -export type SignatureActorModel = ActorModelOnly & { - VideoChannel: VideoChannelModelIdActor - - Account: AccountModelIdActor -} - -export type ActorFollowerException = Pick 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 @@ -export * from './actor' +export * from './account' +export * from './server' +export * from './user' +export * 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 @@ +import { OAuthClientModel } from '@server/models/oauth/oauth-client' + +export type MOAuthClient = Omit 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 @@ +import { OAuthTokenModel } from '@server/models/oauth/oauth-token' +import { PickWith } from '@server/typings/utils' +import { MUserAccountUrl } from '@server/typings/models' + +export type MOAuthToken = Omit + +export type MOAuthTokenUser = MOAuthToken & + PickWith & + { 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 @@ +export * from './plugin' +export * from './server' +export * 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 @@ +import { PluginModel } from '@server/models/server/plugin' + +export 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 @@ +import { ServerBlocklistModel } from '@server/models/server/server-blocklist' +import { PickWith } from '@server/typings/utils' +import { MAccountDefault, MServer } from '@server/typings/models' + +export type MServerBlocklist = Omit + +export type MServerBlocklistAccountServer = MServerBlocklist & + PickWith & + PickWith 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 @@ +import { ServerModel } from '../../../models/server/server' +import { PickWith } from '../../utils' +import { MAccountBlocklistId } from '../account' + +export type MServer = Omit + +export type MServerHost = Pick + +export type MServerHostBlocks = MServerHost & + PickWith 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 @@ +export * from './user' +export * from './user-notification' +export * 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 @@ +import { UserNotificationSettingModel } from '@server/models/account/user-notification-setting' + +export type MNotificationSetting = Omit 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 @@ +import { UserNotificationModel } from '../../../models/account/user-notification' +import { PickWith } from '../../utils' +import { VideoModel } from '../../../models/video/video' +import { ActorModel } from '../../../models/activitypub/actor' +import { ServerModel } from '../../../models/server/server' +import { AvatarModel } from '../../../models/avatar/avatar' +import { VideoChannelModel } from '../../../models/video/video-channel' +import { AccountModel } from '../../../models/account/account' +import { VideoCommentModel } from '../../../models/video/video-comment' +import { VideoAbuseModel } from '../../../models/video/video-abuse' +import { VideoBlacklistModel } from '../../../models/video/video-blacklist' +import { VideoImportModel } from '../../../models/video/video-import' +import { ActorFollowModel } from '../../../models/activitypub/actor-follow' + +export namespace UserNotificationIncludes { + export type VideoInclude = Pick + export type VideoIncludeChannel = VideoInclude & + PickWith + + export type ActorInclude = Pick & + PickWith> & + PickWith> + + export type VideoChannelInclude = Pick + export type VideoChannelIncludeActor = VideoChannelInclude & + PickWith + + export type AccountInclude = Pick + export type AccountIncludeActor = AccountInclude & + PickWith + + export type VideoCommentInclude = Pick & + PickWith & + PickWith + + export type VideoAbuseInclude = Pick & + PickWith + + export type VideoBlacklistInclude = Pick & + PickWith + + export type VideoImportInclude = Pick & + PickWith + + export type ActorFollower = Pick & + PickWith & + PickWith> & + PickWith> + + export type ActorFollowing = Pick & + PickWith & + PickWith + + export type ActorFollowInclude = Pick & + PickWith & + PickWith +} + +export type UserNotificationModelOnly = Omit + +export type UserNotificationModelForApi = UserNotificationModelOnly & + PickWith & + PickWith & + PickWith & + PickWith & + PickWith & + PickWith & + PickWith 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 @@ +import { UserVideoHistoryModel } from '../../../models/account/user-video-history' + +export type MUserVideoHistory = Omit + +export type MUserVideoHistoryTime = Pick 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 @@ +import { UserModel } from '../../../models/account/user' +import { PickWith } from '../../utils' +import { MAccount, MAccountDefault, MAccountDefaultChannelDefault, MAccountId, MAccountIdActorId, MAccountUrl } from '../account' +import { MNotificationSetting } from './user-notification-setting' + +export type MUser = Omit + +export type MUserId = Pick + +export type MUserWithNotificationSetting = MUser & + PickWith + +export type MUserAccountDefault = MUser & + PickWith + +export type MUserAccount = MUser & + PickWith + +export type MUserAccountId = MUser & + PickWith + +export type MUserNotifSettingAccount = MUserWithNotificationSetting & MUserAccount + +export type MUserDefault = MUser & + MUserWithNotificationSetting & + MUserAccountDefault + +export type MUserChannel = MUserWithNotificationSetting & + PickWith + +export type MUserAccountUrl = MUser & + PickWith 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 @@ -import { VideoShareModel } from '../../models/video/video-share' - -export type VideoShareModelOnly = Omit 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 @@ +export * from './schedule-video-update' +export * from './tag' +export * from './thumbnail' +export * from './video' +export * from './video-abuse' +export * from './video-blacklist' +export * from './video-caption' +export * from './video-channels' +export * from './video-comment' +export * from './video-file' +export * from './video-playlist' +export * from './video-redundancy' +export * from './video-share' +export * 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 @@ +import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' + +export type MScheduleVideoUpdate = Omit 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 @@ +import { TagModel } from '../../../models/video/tag' + +export type MTag = Omit 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 @@ +import { ThumbnailModel } from '../../../models/video/thumbnail' + +export type MThumbnail = Omit 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 @@ +import { VideoAbuseModel } from '../../../models/video/video-abuse' +import { PickWith } from '../../utils' +import { MVideo } from './video' +import { MAccountDefault } from '../account' + +export type MVideoAbuse = Omit + +export type MVideoAbuseId = Pick + +export type MVideoAbuseVideo = MVideoAbuse & + Pick & + PickWith + +export type MVideoAbuseAccountVideo = MVideoAbuseVideo & + PickWith 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 @@ +import { VideoBlacklistModel } from '../../../models/video/video-blacklist' +import { PickWith } from '@server/typings/utils' +import { MVideo } from '@server/typings/models' + +export type MVideoBlacklist = Omit + +export type MVideoBlacklistLight = Pick +export type MVideoBlacklistUnfederated = Pick + +export type MVideoBlacklistVideo = MVideoBlacklist & + PickWith 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 @@ +import { VideoCaptionModel } from '../../../models/video/video-caption' +import { PickWith } from '@server/typings/utils' +import { VideoModel } from '@server/models/video/video' + +export type MVideoCaption = Omit + +export type MVideoCaptionLanguage = Pick + +export type MVideoCaptionVideo = MVideoCaption & + PickWith> 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 @@ +import { VideoChangeOwnershipModel } from '@server/models/video/video-change-ownership' +import { PickWith } from '@server/typings/utils' +import { MAccountDefault, MVideoWithFileThumbnail } from '@server/typings/models' + +export type MVideoChangeOwnership = Omit + +export type MVideoChangeOwnershipFull = MVideoChangeOwnership & + PickWith & + PickWith & + PickWith 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 @@ +import { FunctionProperties, PickWith } from '../../utils' +import { VideoChannelModel } from '../../../models/video/video-channel' +import { + MAccountActor, + MAccountAPI, + MAccountBlocks, + MAccountDefault, + MAccountLight, + MAccountUserId, + MActor, + MActorAccountChannelId, + MActorAPI, + MActorDefault, + MActorDefaultLight, MActorLight, + MActorSummary +} from '../account' +import { MVideo } from './video' + +export type MChannelId = FunctionProperties +export type MChannelIdActor = MChannelId & + PickWith + +export type MChannel = Omit + +export type MChannelUserId = Pick & + PickWith + +// Default scope +export type MChannelDefault = MChannel & + PickWith + +export type MChannelLight = MChannel & + PickWith + +export type MChannelAccountLight = MChannel & + PickWith & + PickWith + +export type MChannelSummary = Pick & + PickWith + +export type MChannelSummaryAccount = MChannelSummary & + PickWith + +export type MChannelAPI = MChannel & + PickWith & + PickWith + +export type MChannelAccountActor = MChannel & + PickWith +export type MChannelAccountDefault = MChannelActor & + PickWith + +export type MChannelVideos = MChannel & + PickWith + +export type MChannelActor = MChannel & + PickWith +export type MChannelActorLight = MChannel & + PickWith +export type MChannelActorDefault = MChannel & + PickWith + +export type MChannelActorAccountActor = MChannelAccountActor & MChannelActor + +export type MChannelActorAccountDefault = MChannel & + PickWith & + PickWith + +export 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 @@ +import { VideoCommentModel } from '../../../models/video/video-comment' +import { PickWith } from '../../utils' +import { MAccountDefault } from '../account' +import { MVideoAccountDefault, MVideoAccountLight, MVideoFeed, MVideoIdUrl } from './video' + +export type MComment = Omit +export type MCommentId = Pick + +export type MCommentAPI = MComment & { totalReplies: number } + +export type MCommentOwner = MComment & + PickWith + +export type MCommentVideo = MComment & + PickWith + +export type MCommentReply = MComment & + PickWith + +export type MCommentOwnerReply = MCommentOwner & MCommentReply +export type MCommentOwnerVideo = MCommentOwner & MCommentVideo +export type MCommentReplyVideo = MCommentReply & MCommentVideo +export type MCommentOwnerVideoReply = MCommentOwnerVideo & MCommentReply + +export type MCommentOwnerReplyVideoLight = MCommentOwnerReply & + PickWith + +export type MCommentOwnerVideoFeed = MCommentOwner & + PickWith 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 @@ +import { VideoFileModel } from '../../../models/video/video-file' +import { PickWith, PickWithOpt } from '../../utils' +import { MVideo, MVideoUUID } from './video' +import { MVideoRedundancyFileUrl } from './video-redundancy' + +export type MVideoFile = Omit + +export type MVideoFileVideo = MVideoFile & + PickWith + +export type MVideoFileVideoUUID = MVideoFile & + PickWith + +export type MVideoFileRedundanciesOpt = MVideoFile & + PickWithOpt 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 @@ +import { VideoImportModel } from '@server/models/video/video-import' +import { PickWith } from '@server/typings/utils' +import { MUser, MVideo, MVideoAccountLight, MVideoTag, MVideoThumbnail, MVideoWithFile } from '@server/typings/models' + +export type MVideoImport = Omit + +export type MVideoImportDefault = MVideoImport & + PickWith & + PickWith + +export type MVideoImportDefaultFiles = MVideoImportDefault & + PickWith + +export type MVideoImportVideo = MVideoImport & + PickWith 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 @@ +import { VideoPlaylistElementModel } from '@server/models/video/video-playlist-element' +import { PickWith } from '@server/typings/utils' +import { MVideoPlaylistPrivacy, MVideoThumbnail, MVideoUrl } from '@server/typings/models' + +export type MVideoPlaylistElement = Omit +export type MVideoPlaylistElementId = Pick + +export type MVideoPlaylistElementLight = Pick + +export type MVideoPlaylistVideoThumbnail = MVideoPlaylistElement & + PickWith + +export type MVideoPlaylistAP = MVideoPlaylistElement & + PickWith & + PickWith 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 @@ +import { VideoPlaylistModel } from '../../../models/video/video-playlist' +import { PickWith } from '../../utils' +import { MAccount, MAccountDefault, MAccountSummary } from '../account' +import { MThumbnail } from './thumbnail' +import { MChannelDefault, MChannelSummary } from './video-channels' +import { MVideoPlaylistElementLight } from '@server/typings/models/video/video-playlist-element' + +export type MVideoPlaylist = Omit +export type MVideoPlaylistId = Pick +export type MVideoPlaylistPrivacy = Pick + +export type MVideoPlaylistWithElements = MVideoPlaylist & + PickWith +export type MVideoPlaylistIdWithElements = MVideoPlaylistId & MVideoPlaylistWithElements + +export type MVideoPlaylistUUID = Pick + +export type MVideoPlaylistOwner = MVideoPlaylist & + PickWith + +export type MVideoPlaylistOwnerDefault = MVideoPlaylist & + PickWith + +export type MVideoPlaylistThumbnail = MVideoPlaylist & + PickWith + +export type MVideoPlaylistAccountThumbnail = MVideoPlaylistOwnerDefault & + PickWith + +export type MVideoPlaylistAccountChannelSummary = MVideoPlaylist & + PickWith & + PickWith + +export type MVideoPlaylistAccountChannelDefault = MVideoPlaylist & + PickWith & + PickWith + +export type MVideoPlaylistVideosLength = MVideoPlaylist & { videosLength: number } + +export type MVideoPlaylistFullSummary = MVideoPlaylistAccountChannelSummary & MVideoPlaylistThumbnail + +export 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 @@ +import { AccountVideoRateModel } from '@server/models/account/account-video-rate' +import { PickWith } from '@server/typings/utils' +import { MAccountAudience, MAccountUrl, MVideo } from '..' + +export type MAccountVideoRate = Omit + +export type MAccountVideoRateAccountUrl = MAccountVideoRate & + PickWith + +export type MAccountVideoRateAccountVideo = MAccountVideoRate & + PickWith & + PickWith 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 @@ +import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' +import { PickWith } from '@server/typings/utils' +import { MStreamingPlaylistVideo, MVideoFile, MVideoFileVideo } from '@server/typings/models' + +export type MVideoRedundancy = Omit + +export type MVideoRedundancyFileUrl = Pick + +export type MVideoRedundancyFile = MVideoRedundancy & + PickWith + +export type MVideoRedundancyFileVideo = MVideoRedundancy & + PickWith + +export type MVideoRedundancyStreamingPlaylistVideo = MVideoRedundancy & + PickWith + +export 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 @@ +import { VideoShareModel } from '../../../models/video/video-share' +import { PickWith } from '../../utils' +import { MActorDefault } from '../account' +import { MVideo } from './video' + +export type MVideoShare = Omit + +export type MVideoShareActor = MVideoShare & + PickWith + +export type MVideoShareFull = MVideoShareActor & + PickWith 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 @@ +import { VideoStreamingPlaylistModel } from '../../../models/video/video-streaming-playlist' +import { PickWith } from '../../utils' +import { MVideoRedundancyFileUrl } from './video-redundancy' +import { MVideo } from '@server/typings/models' + +export type MStreamingPlaylist = Omit + +export type MStreamingPlaylistVideo = MStreamingPlaylist & + PickWith + +export type MStreamingPlaylistRedundancies = MStreamingPlaylist & + PickWith 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 @@ +import { VideoModel } from '../../../models/video/video' +import { PickWith, PickWithOpt } from '../../utils' +import { MChannelAccountLight, MChannelActor, MChannelActorAccountDefault, MChannelUserId } from './video-channels' +import { MTag } from './tag' +import { MVideoCaptionLanguage } from './video-caption' +import { MStreamingPlaylist, MStreamingPlaylistRedundancies } from './video-streaming-playlist' +import { MVideoFile, MVideoFileRedundanciesOpt } from './video-file' +import { MThumbnail } from './thumbnail' +import { MVideoBlacklistLight, MVideoBlacklistUnfederated } from './video-blacklist' +import { MScheduleVideoUpdate } from './schedule-video-update' +import { MUserVideoHistoryTime } from '../user/user-video-history' + +export type MVideo = Omit + +export type MVideoId = Pick +export type MVideoUrl = Pick +export type MVideoUUID = Pick + +export type MVideoIdUrl = MVideoId & MVideoUrl +export type MVideoFeed = Pick + +export type MVideoWithFile = MVideo & + PickWith + +export type MVideoThumbnail = MVideo & + PickWith +export type MVideoIdThumbnail = MVideoThumbnail & MVideoId + +export type MVideoTag = MVideo & + PickWith + +export type MVideoWithSchedule = MVideo & + PickWithOpt + +export type MVideoWithFileThumbnail = MVideoWithFile & MVideoThumbnail + +export type MVideoUser = MVideo & + PickWith + +export type MVideoWithCaptions = MVideo & + PickWith + +export type MVideoWithBlacklistLight = MVideo & + PickWith + +export type MVideoAccountLight = MVideo & + PickWith + +export type MVideoWithRights = MVideoWithBlacklistLight & MVideoThumbnail & MVideoUser + +export type MVideoWithStreamingPlaylist = MVideo & + PickWith + +export type MVideoWithAllFiles = MVideoWithFileThumbnail & MVideoWithStreamingPlaylist + +export type MVideoAccountAllFiles = MVideoWithAllFiles & MVideoAccountLight & MVideoWithBlacklistLight +export type MVideoAccountAllFilesCaptions = MVideoAccountAllFiles & MVideoWithCaptions + +export type MVideoUserHistory = MVideo & + PickWith + +export type MVideoWithBlacklistThumbnailScheduled = MVideoWithSchedule & MVideoWithBlacklistLight & MVideoWithFileThumbnail + +export type MVideoAccountDefault = MVideo & + PickWith + +export type MVideoThumbnailAccountDefault = MVideoThumbnail & + PickWith + +export type MVideoWithChannelActor = MVideo & + PickWith + +export type MVideoFullLight = MVideoThumbnail & + MVideoWithBlacklistLight & + MVideoTag & + MVideoAccountLight & + MVideoUserHistory & + MVideoWithFile & + MVideoWithSchedule & + MVideoWithStreamingPlaylist & + MVideoUserHistory + +export type MVideoAP = MVideo & + MVideoTag & + MVideoAccountLight & + MVideoWithStreamingPlaylist & + MVideoWithCaptions & + PickWith & + PickWith + +export type MVideoAPWithoutCaption = Omit + +export type MVideoDetails = MVideo & + MVideoWithBlacklistLight & + MVideoTag & + MVideoAccountLight & + MVideoWithSchedule & + MVideoThumbnail & + MVideoUserHistory & + PickWith & + PickWith 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 @@ -export type FunctionPropertyNames = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T] +export type FunctionPropertyNames = { + [K in keyof T]: T[K] extends Function ? K : never +}[keyof T] export type FunctionProperties = Pick> + +export type ValueOf = T[KT] + +export type PickWith = { + [P in KT]: T[P] extends V ? V : never +} + +export type PickWithOpt = { + [P in KT]?: T[P] extends V ? V : never +} diff --git a/tsconfig.json b/tsconfig.json index 4d2bdd6ba..5ed870c5c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,11 +14,15 @@ "es2016", "es2017" ], - "typeRoots": [ "node_modules/@types", "server/typings" ] + "typeRoots": [ "node_modules/@types", "server/typings" ], + "baseUrl": "./", + "paths": { + "@server/typings/*": [ "server/typings/*" ], + "@server/models/*": [ "server/models/*" ] + } }, "exclude": [ "server/tools/", - "client/node_modules", "node_modules", "dist", "storage",