diff options
Diffstat (limited to 'server')
210 files changed, 4184 insertions, 1489 deletions
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index 11504b354..453ced8bf 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts | |||
@@ -16,7 +16,6 @@ import { | |||
16 | } from '../../middlewares' | 16 | } from '../../middlewares' |
17 | import { getAccountVideoRateValidator, videoCommentGetValidator } from '../../middlewares/validators' | 17 | import { getAccountVideoRateValidator, videoCommentGetValidator } from '../../middlewares/validators' |
18 | import { AccountModel } from '../../models/account/account' | 18 | import { AccountModel } from '../../models/account/account' |
19 | import { ActorModel } from '../../models/activitypub/actor' | ||
20 | import { ActorFollowModel } from '../../models/activitypub/actor-follow' | 19 | import { ActorFollowModel } from '../../models/activitypub/actor-follow' |
21 | import { VideoModel } from '../../models/video/video' | 20 | import { VideoModel } from '../../models/video/video' |
22 | import { VideoCommentModel } from '../../models/video/video-comment' | 21 | import { VideoCommentModel } from '../../models/video/video-comment' |
@@ -38,6 +37,7 @@ import { buildDislikeActivity } from '../../lib/activitypub/send/send-dislike' | |||
38 | import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists' | 37 | import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists' |
39 | import { VideoPlaylistModel } from '../../models/video/video-playlist' | 38 | import { VideoPlaylistModel } from '../../models/video/video-playlist' |
40 | import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' | 39 | import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' |
40 | import { MAccountId, MActorId, MVideo, MVideoAPWithoutCaption } from '@server/typings/models' | ||
41 | 41 | ||
42 | const activityPubClientRouter = express.Router() | 42 | const activityPubClientRouter = express.Router() |
43 | 43 | ||
@@ -148,7 +148,7 @@ activityPubClientRouter.get('/redundancy/streaming-playlists/:streamingPlaylistT | |||
148 | 148 | ||
149 | activityPubClientRouter.get('/video-playlists/:playlistId', | 149 | activityPubClientRouter.get('/video-playlists/:playlistId', |
150 | executeIfActivityPub, | 150 | executeIfActivityPub, |
151 | asyncMiddleware(videoPlaylistsGetValidator), | 151 | asyncMiddleware(videoPlaylistsGetValidator('all')), |
152 | asyncMiddleware(videoPlaylistController) | 152 | asyncMiddleware(videoPlaylistController) |
153 | ) | 153 | ) |
154 | activityPubClientRouter.get('/video-playlists/:playlistId/:videoId', | 154 | activityPubClientRouter.get('/video-playlists/:playlistId/:videoId', |
@@ -208,18 +208,19 @@ function getAccountVideoRate (rateType: VideoRateType) { | |||
208 | 208 | ||
209 | async function videoController (req: express.Request, res: express.Response) { | 209 | async function videoController (req: express.Request, res: express.Response) { |
210 | // We need more attributes | 210 | // We need more attributes |
211 | const video = await VideoModel.loadForGetAPI({ id: res.locals.video.id }) | 211 | const video = await VideoModel.loadForGetAPI({ id: res.locals.onlyVideoWithRights.id }) as MVideoAPWithoutCaption |
212 | 212 | ||
213 | if (video.url.startsWith(WEBSERVER.URL) === false) return res.redirect(video.url) | 213 | if (video.url.startsWith(WEBSERVER.URL) === false) return res.redirect(video.url) |
214 | 214 | ||
215 | // We need captions to render AP object | 215 | // We need captions to render AP object |
216 | video.VideoCaptions = await VideoCaptionModel.listVideoCaptions(video.id) | 216 | const captions = await VideoCaptionModel.listVideoCaptions(video.id) |
217 | const videoWithCaptions = Object.assign(video, { VideoCaptions: captions }) | ||
217 | 218 | ||
218 | const audience = getAudience(video.VideoChannel.Account.Actor, video.privacy === VideoPrivacy.PUBLIC) | 219 | const audience = getAudience(videoWithCaptions.VideoChannel.Account.Actor, videoWithCaptions.privacy === VideoPrivacy.PUBLIC) |
219 | const videoObject = audiencify(video.toActivityPubObject(), audience) | 220 | const videoObject = audiencify(videoWithCaptions.toActivityPubObject(), audience) |
220 | 221 | ||
221 | if (req.path.endsWith('/activity')) { | 222 | if (req.path.endsWith('/activity')) { |
222 | const data = buildCreateActivity(video.url, video.VideoChannel.Account.Actor, videoObject, audience) | 223 | const data = buildCreateActivity(videoWithCaptions.url, video.VideoChannel.Account.Actor, videoObject, audience) |
223 | return activityPubResponse(activityPubContextify(data), res) | 224 | return activityPubResponse(activityPubContextify(data), res) |
224 | } | 225 | } |
225 | 226 | ||
@@ -231,13 +232,13 @@ async function videoAnnounceController (req: express.Request, res: express.Respo | |||
231 | 232 | ||
232 | if (share.url.startsWith(WEBSERVER.URL) === false) return res.redirect(share.url) | 233 | if (share.url.startsWith(WEBSERVER.URL) === false) return res.redirect(share.url) |
233 | 234 | ||
234 | const { activity } = await buildAnnounceWithVideoAudience(share.Actor, share, res.locals.video, undefined) | 235 | const { activity } = await buildAnnounceWithVideoAudience(share.Actor, share, res.locals.videoAll, undefined) |
235 | 236 | ||
236 | return activityPubResponse(activityPubContextify(activity), res) | 237 | return activityPubResponse(activityPubContextify(activity), res) |
237 | } | 238 | } |
238 | 239 | ||
239 | async function videoAnnouncesController (req: express.Request, res: express.Response) { | 240 | async function videoAnnouncesController (req: express.Request, res: express.Response) { |
240 | const video = res.locals.video | 241 | const video = res.locals.onlyVideo |
241 | 242 | ||
242 | const handler = async (start: number, count: number) => { | 243 | const handler = async (start: number, count: number) => { |
243 | const result = await VideoShareModel.listAndCountByVideoId(video.id, start, count) | 244 | const result = await VideoShareModel.listAndCountByVideoId(video.id, start, count) |
@@ -252,21 +253,21 @@ async function videoAnnouncesController (req: express.Request, res: express.Resp | |||
252 | } | 253 | } |
253 | 254 | ||
254 | async function videoLikesController (req: express.Request, res: express.Response) { | 255 | async function videoLikesController (req: express.Request, res: express.Response) { |
255 | const video = res.locals.video | 256 | const video = res.locals.onlyVideo |
256 | const json = await videoRates(req, 'like', video, getVideoLikesActivityPubUrl(video)) | 257 | const json = await videoRates(req, 'like', video, getVideoLikesActivityPubUrl(video)) |
257 | 258 | ||
258 | return activityPubResponse(activityPubContextify(json), res) | 259 | return activityPubResponse(activityPubContextify(json), res) |
259 | } | 260 | } |
260 | 261 | ||
261 | async function videoDislikesController (req: express.Request, res: express.Response) { | 262 | async function videoDislikesController (req: express.Request, res: express.Response) { |
262 | const video = res.locals.video | 263 | const video = res.locals.onlyVideo |
263 | const json = await videoRates(req, 'dislike', video, getVideoDislikesActivityPubUrl(video)) | 264 | const json = await videoRates(req, 'dislike', video, getVideoDislikesActivityPubUrl(video)) |
264 | 265 | ||
265 | return activityPubResponse(activityPubContextify(json), res) | 266 | return activityPubResponse(activityPubContextify(json), res) |
266 | } | 267 | } |
267 | 268 | ||
268 | async function videoCommentsController (req: express.Request, res: express.Response) { | 269 | async function videoCommentsController (req: express.Request, res: express.Response) { |
269 | const video = res.locals.video | 270 | const video = res.locals.onlyVideo |
270 | 271 | ||
271 | const handler = async (start: number, count: number) => { | 272 | const handler = async (start: number, count: number) => { |
272 | const result = await VideoCommentModel.listAndCountByVideoId(video.id, start, count) | 273 | const result = await VideoCommentModel.listAndCountByVideoId(video.id, start, count) |
@@ -301,7 +302,7 @@ async function videoChannelFollowingController (req: express.Request, res: expre | |||
301 | } | 302 | } |
302 | 303 | ||
303 | async function videoCommentController (req: express.Request, res: express.Response) { | 304 | async function videoCommentController (req: express.Request, res: express.Response) { |
304 | const videoComment = res.locals.videoComment | 305 | const videoComment = res.locals.videoCommentFull |
305 | 306 | ||
306 | if (videoComment.url.startsWith(WEBSERVER.URL) === false) return res.redirect(videoComment.url) | 307 | if (videoComment.url.startsWith(WEBSERVER.URL) === false) return res.redirect(videoComment.url) |
307 | 308 | ||
@@ -337,7 +338,7 @@ async function videoRedundancyController (req: express.Request, res: express.Res | |||
337 | } | 338 | } |
338 | 339 | ||
339 | async function videoPlaylistController (req: express.Request, res: express.Response) { | 340 | async function videoPlaylistController (req: express.Request, res: express.Response) { |
340 | const playlist = res.locals.videoPlaylist | 341 | const playlist = res.locals.videoPlaylistFull |
341 | 342 | ||
342 | // We need more attributes | 343 | // We need more attributes |
343 | playlist.OwnerAccount = await AccountModel.load(playlist.ownerAccountId) | 344 | playlist.OwnerAccount = await AccountModel.load(playlist.ownerAccountId) |
@@ -350,7 +351,7 @@ async function videoPlaylistController (req: express.Request, res: express.Respo | |||
350 | } | 351 | } |
351 | 352 | ||
352 | async function videoPlaylistElementController (req: express.Request, res: express.Response) { | 353 | async function videoPlaylistElementController (req: express.Request, res: express.Response) { |
353 | const videoPlaylistElement = res.locals.videoPlaylistElement | 354 | const videoPlaylistElement = res.locals.videoPlaylistElementAP |
354 | 355 | ||
355 | const json = videoPlaylistElement.toActivityPubObject() | 356 | const json = videoPlaylistElement.toActivityPubObject() |
356 | return activityPubResponse(activityPubContextify(json), res) | 357 | return activityPubResponse(activityPubContextify(json), res) |
@@ -358,7 +359,7 @@ async function videoPlaylistElementController (req: express.Request, res: expres | |||
358 | 359 | ||
359 | // --------------------------------------------------------------------------- | 360 | // --------------------------------------------------------------------------- |
360 | 361 | ||
361 | async function actorFollowing (req: express.Request, actor: ActorModel) { | 362 | async function actorFollowing (req: express.Request, actor: MActorId) { |
362 | const handler = (start: number, count: number) => { | 363 | const handler = (start: number, count: number) => { |
363 | return ActorFollowModel.listAcceptedFollowingUrlsForApi([ actor.id ], undefined, start, count) | 364 | return ActorFollowModel.listAcceptedFollowingUrlsForApi([ actor.id ], undefined, start, count) |
364 | } | 365 | } |
@@ -366,7 +367,7 @@ async function actorFollowing (req: express.Request, actor: ActorModel) { | |||
366 | return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page) | 367 | return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page) |
367 | } | 368 | } |
368 | 369 | ||
369 | async function actorFollowers (req: express.Request, actor: ActorModel) { | 370 | async function actorFollowers (req: express.Request, actor: MActorId) { |
370 | const handler = (start: number, count: number) => { | 371 | const handler = (start: number, count: number) => { |
371 | return ActorFollowModel.listAcceptedFollowerUrlsForAP([ actor.id ], undefined, start, count) | 372 | return ActorFollowModel.listAcceptedFollowerUrlsForAP([ actor.id ], undefined, start, count) |
372 | } | 373 | } |
@@ -374,7 +375,7 @@ async function actorFollowers (req: express.Request, actor: ActorModel) { | |||
374 | return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page) | 375 | return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page) |
375 | } | 376 | } |
376 | 377 | ||
377 | async function actorPlaylists (req: express.Request, account: AccountModel) { | 378 | async function actorPlaylists (req: express.Request, account: MAccountId) { |
378 | const handler = (start: number, count: number) => { | 379 | const handler = (start: number, count: number) => { |
379 | return VideoPlaylistModel.listPublicUrlsOfForAP(account.id, start, count) | 380 | return VideoPlaylistModel.listPublicUrlsOfForAP(account.id, start, count) |
380 | } | 381 | } |
@@ -382,7 +383,7 @@ async function actorPlaylists (req: express.Request, account: AccountModel) { | |||
382 | return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page) | 383 | return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page) |
383 | } | 384 | } |
384 | 385 | ||
385 | function videoRates (req: express.Request, rateType: VideoRateType, video: VideoModel, url: string) { | 386 | function videoRates (req: express.Request, rateType: VideoRateType, video: MVideo, url: string) { |
386 | const handler = async (start: number, count: number) => { | 387 | const handler = async (start: number, count: number) => { |
387 | const result = await AccountVideoRateModel.listAndCountAccountUrlsByVideoId(rateType, video.id, start, count) | 388 | const result = await AccountVideoRateModel.listAndCountAccountUrlsByVideoId(rateType, video.id, start, count) |
388 | return { | 389 | return { |
diff --git a/server/controllers/activitypub/inbox.ts b/server/controllers/activitypub/inbox.ts index 2d3eef222..d9df253aa 100644 --- a/server/controllers/activitypub/inbox.ts +++ b/server/controllers/activitypub/inbox.ts | |||
@@ -7,7 +7,7 @@ import { asyncMiddleware, checkSignature, localAccountValidator, localVideoChann | |||
7 | import { activityPubValidator } from '../../middlewares/validators/activitypub/activity' | 7 | import { activityPubValidator } from '../../middlewares/validators/activitypub/activity' |
8 | import { queue } from 'async' | 8 | import { queue } from 'async' |
9 | import { ActorModel } from '../../models/activitypub/actor' | 9 | import { ActorModel } from '../../models/activitypub/actor' |
10 | import { SignatureActorModel } from '../../typings/models' | 10 | import { MActorDefault, MActorSignature } from '../../typings/models' |
11 | 11 | ||
12 | const inboxRouter = express.Router() | 12 | const inboxRouter = express.Router() |
13 | 13 | ||
@@ -41,7 +41,8 @@ export { | |||
41 | 41 | ||
42 | // --------------------------------------------------------------------------- | 42 | // --------------------------------------------------------------------------- |
43 | 43 | ||
44 | const inboxQueue = queue<{ activities: Activity[], signatureActor?: SignatureActorModel, inboxActor?: ActorModel }, Error>((task, cb) => { | 44 | type QueueParam = { activities: Activity[], signatureActor?: MActorSignature, inboxActor?: MActorDefault } |
45 | const inboxQueue = queue<QueueParam, Error>((task, cb) => { | ||
45 | const options = { signatureActor: task.signatureActor, inboxActor: task.inboxActor } | 46 | const options = { signatureActor: task.signatureActor, inboxActor: task.inboxActor } |
46 | 47 | ||
47 | processActivities(task.activities, options) | 48 | processActivities(task.activities, options) |
diff --git a/server/controllers/activitypub/outbox.ts b/server/controllers/activitypub/outbox.ts index 38b6ec976..f3dd2ad7d 100644 --- a/server/controllers/activitypub/outbox.ts +++ b/server/controllers/activitypub/outbox.ts | |||
@@ -6,11 +6,9 @@ import { logger } from '../../helpers/logger' | |||
6 | import { buildAnnounceActivity, buildCreateActivity } from '../../lib/activitypub/send' | 6 | import { buildAnnounceActivity, buildCreateActivity } from '../../lib/activitypub/send' |
7 | import { buildAudience } from '../../lib/activitypub/audience' | 7 | import { buildAudience } from '../../lib/activitypub/audience' |
8 | import { asyncMiddleware, localAccountValidator, localVideoChannelValidator } from '../../middlewares' | 8 | import { asyncMiddleware, localAccountValidator, localVideoChannelValidator } from '../../middlewares' |
9 | import { AccountModel } from '../../models/account/account' | ||
10 | import { ActorModel } from '../../models/activitypub/actor' | ||
11 | import { VideoModel } from '../../models/video/video' | 9 | import { VideoModel } from '../../models/video/video' |
12 | import { activityPubResponse } from './utils' | 10 | import { activityPubResponse } from './utils' |
13 | import { VideoChannelModel } from '../../models/video/video-channel' | 11 | import { MActorLight } from '@server/typings/models' |
14 | 12 | ||
15 | const outboxRouter = express.Router() | 13 | const outboxRouter = express.Router() |
16 | 14 | ||
@@ -45,14 +43,10 @@ async function outboxController (req: express.Request, res: express.Response) { | |||
45 | return activityPubResponse(activityPubContextify(json), res) | 43 | return activityPubResponse(activityPubContextify(json), res) |
46 | } | 44 | } |
47 | 45 | ||
48 | async function buildActivities (actor: ActorModel, start: number, count: number) { | 46 | async function buildActivities (actor: MActorLight, start: number, count: number) { |
49 | const data = await VideoModel.listAllAndSharedByActorForOutbox(actor.id, start, count) | 47 | const data = await VideoModel.listAllAndSharedByActorForOutbox(actor.id, start, count) |
50 | const activities: Activity[] = [] | 48 | const activities: Activity[] = [] |
51 | 49 | ||
52 | // Avoid too many SQL requests | ||
53 | const actors = data.data.map(v => v.VideoChannel.Account.Actor) | ||
54 | actors.push(actor) | ||
55 | |||
56 | for (const video of data.data) { | 50 | for (const video of data.data) { |
57 | const byActor = video.VideoChannel.Account.Actor | 51 | const byActor = video.VideoChannel.Account.Actor |
58 | const createActivityAudience = buildAudience([ byActor.followersUrl ], video.privacy === VideoPrivacy.PUBLIC) | 52 | const createActivityAudience = buildAudience([ byActor.followersUrl ], video.privacy === VideoPrivacy.PUBLIC) |
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index 21fa85a08..39a124fc5 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts | |||
@@ -158,7 +158,19 @@ function getAbout (req: express.Request, res: express.Response) { | |||
158 | name: CONFIG.INSTANCE.NAME, | 158 | name: CONFIG.INSTANCE.NAME, |
159 | shortDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION, | 159 | shortDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION, |
160 | description: CONFIG.INSTANCE.DESCRIPTION, | 160 | description: CONFIG.INSTANCE.DESCRIPTION, |
161 | terms: CONFIG.INSTANCE.TERMS | 161 | terms: CONFIG.INSTANCE.TERMS, |
162 | codeOfConduct: CONFIG.INSTANCE.CODE_OF_CONDUCT, | ||
163 | |||
164 | hardwareInformation: CONFIG.INSTANCE.HARDWARE_INFORMATION, | ||
165 | |||
166 | creationReason: CONFIG.INSTANCE.CREATION_REASON, | ||
167 | moderationInformation: CONFIG.INSTANCE.MODERATION_INFORMATION, | ||
168 | administrator: CONFIG.INSTANCE.ADMINISTRATOR, | ||
169 | maintenanceLifetime: CONFIG.INSTANCE.MAINTENANCE_LIFETIME, | ||
170 | businessModel: CONFIG.INSTANCE.BUSINESS_MODEL, | ||
171 | |||
172 | languages: CONFIG.INSTANCE.LANGUAGES, | ||
173 | categories: CONFIG.INSTANCE.CATEGORIES | ||
162 | } | 174 | } |
163 | } | 175 | } |
164 | 176 | ||
@@ -221,6 +233,18 @@ function customConfig (): CustomConfig { | |||
221 | shortDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION, | 233 | shortDescription: CONFIG.INSTANCE.SHORT_DESCRIPTION, |
222 | description: CONFIG.INSTANCE.DESCRIPTION, | 234 | description: CONFIG.INSTANCE.DESCRIPTION, |
223 | terms: CONFIG.INSTANCE.TERMS, | 235 | terms: CONFIG.INSTANCE.TERMS, |
236 | codeOfConduct: CONFIG.INSTANCE.CODE_OF_CONDUCT, | ||
237 | |||
238 | creationReason: CONFIG.INSTANCE.CREATION_REASON, | ||
239 | moderationInformation: CONFIG.INSTANCE.MODERATION_INFORMATION, | ||
240 | administrator: CONFIG.INSTANCE.ADMINISTRATOR, | ||
241 | maintenanceLifetime: CONFIG.INSTANCE.MAINTENANCE_LIFETIME, | ||
242 | businessModel: CONFIG.INSTANCE.BUSINESS_MODEL, | ||
243 | hardwareInformation: CONFIG.INSTANCE.HARDWARE_INFORMATION, | ||
244 | |||
245 | languages: CONFIG.INSTANCE.LANGUAGES, | ||
246 | categories: CONFIG.INSTANCE.CATEGORIES, | ||
247 | |||
224 | isNSFW: CONFIG.INSTANCE.IS_NSFW, | 248 | isNSFW: CONFIG.INSTANCE.IS_NSFW, |
225 | defaultClientRoute: CONFIG.INSTANCE.DEFAULT_CLIENT_ROUTE, | 249 | defaultClientRoute: CONFIG.INSTANCE.DEFAULT_CLIENT_ROUTE, |
226 | defaultNSFWPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY, | 250 | defaultNSFWPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY, |
@@ -300,6 +324,18 @@ function customConfig (): CustomConfig { | |||
300 | enabled: CONFIG.FOLLOWERS.INSTANCE.ENABLED, | 324 | enabled: CONFIG.FOLLOWERS.INSTANCE.ENABLED, |
301 | manualApproval: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL | 325 | manualApproval: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL |
302 | } | 326 | } |
327 | }, | ||
328 | followings: { | ||
329 | instance: { | ||
330 | autoFollowBack: { | ||
331 | enabled: CONFIG.FOLLOWINGS.INSTANCE.AUTO_FOLLOW_BACK.ENABLED | ||
332 | }, | ||
333 | |||
334 | autoFollowIndex: { | ||
335 | enabled: CONFIG.FOLLOWINGS.INSTANCE.AUTO_FOLLOW_INDEX.ENABLED, | ||
336 | indexUrl: CONFIG.FOLLOWINGS.INSTANCE.AUTO_FOLLOW_INDEX.INDEX_URL | ||
337 | } | ||
338 | } | ||
303 | } | 339 | } |
304 | } | 340 | } |
305 | } | 341 | } |
diff --git a/server/controllers/api/search.ts b/server/controllers/api/search.ts index 9a1e30b83..349650aca 100644 --- a/server/controllers/api/search.ts +++ b/server/controllers/api/search.ts | |||
@@ -19,6 +19,7 @@ import { getOrCreateActorAndServerAndModel, getOrCreateVideoAndAccountAndChannel | |||
19 | import { logger } from '../../helpers/logger' | 19 | import { logger } from '../../helpers/logger' |
20 | import { VideoChannelModel } from '../../models/video/video-channel' | 20 | import { VideoChannelModel } from '../../models/video/video-channel' |
21 | import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' | 21 | import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' |
22 | import { MChannelAccountDefault, MVideoAccountLightBlacklistAllFiles } from '../../typings/models' | ||
22 | 23 | ||
23 | const searchRouter = express.Router() | 24 | const searchRouter = express.Router() |
24 | 25 | ||
@@ -84,7 +85,7 @@ async function searchVideoChannelsDB (query: VideoChannelsSearchQuery, res: expr | |||
84 | } | 85 | } |
85 | 86 | ||
86 | async function searchVideoChannelURI (search: string, isWebfingerSearch: boolean, res: express.Response) { | 87 | async function searchVideoChannelURI (search: string, isWebfingerSearch: boolean, res: express.Response) { |
87 | let videoChannel: VideoChannelModel | 88 | let videoChannel: MChannelAccountDefault |
88 | let uri = search | 89 | let uri = search |
89 | 90 | ||
90 | if (isWebfingerSearch) { | 91 | if (isWebfingerSearch) { |
@@ -137,7 +138,7 @@ async function searchVideosDB (query: VideosSearchQuery, res: express.Response) | |||
137 | } | 138 | } |
138 | 139 | ||
139 | async function searchVideoURI (url: string, res: express.Response) { | 140 | async function searchVideoURI (url: string, res: express.Response) { |
140 | let video: VideoModel | 141 | let video: MVideoAccountLightBlacklistAllFiles |
141 | 142 | ||
142 | // Check if we can fetch a remote video with the URL | 143 | // Check if we can fetch a remote video with the URL |
143 | if (isUserAbleToSearchRemoteURI(res)) { | 144 | if (isUserAbleToSearchRemoteURI(res)) { |
diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts index d38ce91de..37647622b 100644 --- a/server/controllers/api/server/follows.ts +++ b/server/controllers/api/server/follows.ts | |||
@@ -25,6 +25,7 @@ import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | |||
25 | import { JobQueue } from '../../../lib/job-queue' | 25 | import { JobQueue } from '../../../lib/job-queue' |
26 | import { removeRedundancyOf } from '../../../lib/redundancy' | 26 | import { removeRedundancyOf } from '../../../lib/redundancy' |
27 | import { sequelizeTypescript } from '../../../initializers/database' | 27 | import { sequelizeTypescript } from '../../../initializers/database' |
28 | import { autoFollowBackIfNeeded } from '../../../lib/activitypub/follow' | ||
28 | 29 | ||
29 | const serverFollowsRouter = express.Router() | 30 | const serverFollowsRouter = express.Router() |
30 | serverFollowsRouter.get('/following', | 31 | serverFollowsRouter.get('/following', |
@@ -172,5 +173,7 @@ async function acceptFollower (req: express.Request, res: express.Response) { | |||
172 | follow.state = 'accepted' | 173 | follow.state = 'accepted' |
173 | await follow.save() | 174 | await follow.save() |
174 | 175 | ||
176 | await autoFollowBackIfNeeded(follow) | ||
177 | |||
175 | return res.status(204).end() | 178 | return res.status(204).end() |
176 | } | 179 | } |
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index ae40e86f8..27351c1a9 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts | |||
@@ -48,6 +48,7 @@ import { CONFIG } from '../../../initializers/config' | |||
48 | import { sequelizeTypescript } from '../../../initializers/database' | 48 | import { sequelizeTypescript } from '../../../initializers/database' |
49 | import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' | 49 | import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' |
50 | import { UserRegister } from '../../../../shared/models/users/user-register.model' | 50 | import { UserRegister } from '../../../../shared/models/users/user-register.model' |
51 | import { MUser, MUserAccountDefault } from '@server/typings/models' | ||
51 | 52 | ||
52 | const auditLogger = auditLoggerFactory('users') | 53 | const auditLogger = auditLoggerFactory('users') |
53 | 54 | ||
@@ -195,7 +196,7 @@ async function createUser (req: express.Request, res: express.Response) { | |||
195 | videoQuota: body.videoQuota, | 196 | videoQuota: body.videoQuota, |
196 | videoQuotaDaily: body.videoQuotaDaily, | 197 | videoQuotaDaily: body.videoQuotaDaily, |
197 | adminFlags: body.adminFlags || UserAdminFlag.NONE | 198 | adminFlags: body.adminFlags || UserAdminFlag.NONE |
198 | }) | 199 | }) as MUser |
199 | 200 | ||
200 | const { user, account } = await createUserAccountAndChannelAndPlaylist({ userToCreate: userToCreate }) | 201 | const { user, account } = await createUserAccountAndChannelAndPlaylist({ userToCreate: userToCreate }) |
201 | 202 | ||
@@ -359,7 +360,7 @@ function success (req: express.Request, res: express.Response) { | |||
359 | res.end() | 360 | res.end() |
360 | } | 361 | } |
361 | 362 | ||
362 | async function changeUserBlock (res: express.Response, user: UserModel, block: boolean, reason?: string) { | 363 | async function changeUserBlock (res: express.Response, user: MUserAccountDefault, block: boolean, reason?: string) { |
363 | const oldUserAuditView = new UserAuditView(user.toFormattedJSON()) | 364 | const oldUserAuditView = new UserAuditView(user.toFormattedJSON()) |
364 | 365 | ||
365 | user.blocked = block | 366 | user.blocked = block |
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index e7ed3de64..bf872ca52 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts | |||
@@ -23,15 +23,12 @@ import { createReqFiles } from '../../../helpers/express-utils' | |||
23 | import { UserVideoQuota } from '../../../../shared/models/users/user-video-quota.model' | 23 | import { UserVideoQuota } from '../../../../shared/models/users/user-video-quota.model' |
24 | import { updateAvatarValidator } from '../../../middlewares/validators/avatar' | 24 | import { updateAvatarValidator } from '../../../middlewares/validators/avatar' |
25 | import { updateActorAvatarFile } from '../../../lib/avatar' | 25 | import { updateActorAvatarFile } from '../../../lib/avatar' |
26 | import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger' | ||
27 | import { VideoImportModel } from '../../../models/video/video-import' | 26 | import { VideoImportModel } from '../../../models/video/video-import' |
28 | import { AccountModel } from '../../../models/account/account' | 27 | import { AccountModel } from '../../../models/account/account' |
29 | import { CONFIG } from '../../../initializers/config' | 28 | import { CONFIG } from '../../../initializers/config' |
30 | import { sequelizeTypescript } from '../../../initializers/database' | 29 | import { sequelizeTypescript } from '../../../initializers/database' |
31 | import { sendVerifyUserEmail } from '../../../lib/user' | 30 | import { sendVerifyUserEmail } from '../../../lib/user' |
32 | 31 | ||
33 | const auditLogger = auditLoggerFactory('users-me') | ||
34 | |||
35 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) | 32 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) |
36 | 33 | ||
37 | const meRouter = express.Router() | 34 | const meRouter = express.Router() |
@@ -130,7 +127,7 @@ async function getUserInformation (req: express.Request, res: express.Response) | |||
130 | // We did not load channels in res.locals.user | 127 | // We did not load channels in res.locals.user |
131 | const user = await UserModel.loadByUsernameAndPopulateChannels(res.locals.oauth.token.user.username) | 128 | const user = await UserModel.loadByUsernameAndPopulateChannels(res.locals.oauth.token.user.username) |
132 | 129 | ||
133 | return res.json(user.toFormattedJSON({})) | 130 | return res.json(user.toFormattedJSON()) |
134 | } | 131 | } |
135 | 132 | ||
136 | async function getUserVideoQuotaUsed (req: express.Request, res: express.Response) { | 133 | async function getUserVideoQuotaUsed (req: express.Request, res: express.Response) { |
@@ -147,7 +144,7 @@ async function getUserVideoQuotaUsed (req: express.Request, res: express.Respons | |||
147 | } | 144 | } |
148 | 145 | ||
149 | async function getUserVideoRating (req: express.Request, res: express.Response) { | 146 | async function getUserVideoRating (req: express.Request, res: express.Response) { |
150 | const videoId = res.locals.video.id | 147 | const videoId = res.locals.videoId.id |
151 | const accountId = +res.locals.oauth.token.User.Account.id | 148 | const accountId = +res.locals.oauth.token.User.Account.id |
152 | 149 | ||
153 | const ratingObj = await AccountVideoRateModel.load(accountId, videoId, null) | 150 | const ratingObj = await AccountVideoRateModel.load(accountId, videoId, null) |
@@ -165,8 +162,6 @@ async function deleteMe (req: express.Request, res: express.Response) { | |||
165 | 162 | ||
166 | await user.destroy() | 163 | await user.destroy() |
167 | 164 | ||
168 | auditLogger.delete(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON({}))) | ||
169 | |||
170 | return res.sendStatus(204) | 165 | return res.sendStatus(204) |
171 | } | 166 | } |
172 | 167 | ||
@@ -175,7 +170,6 @@ async function updateMe (req: express.Request, res: express.Response) { | |||
175 | let sendVerificationEmail = false | 170 | let sendVerificationEmail = false |
176 | 171 | ||
177 | const user = res.locals.oauth.token.user | 172 | const user = res.locals.oauth.token.user |
178 | const oldUserAuditView = new UserAuditView(user.toFormattedJSON({})) | ||
179 | 173 | ||
180 | if (body.password !== undefined) user.password = body.password | 174 | if (body.password !== undefined) user.password = body.password |
181 | if (body.nsfwPolicy !== undefined) user.nsfwPolicy = body.nsfwPolicy | 175 | if (body.nsfwPolicy !== undefined) user.nsfwPolicy = body.nsfwPolicy |
@@ -184,6 +178,8 @@ async function updateMe (req: express.Request, res: express.Response) { | |||
184 | if (body.videosHistoryEnabled !== undefined) user.videosHistoryEnabled = body.videosHistoryEnabled | 178 | if (body.videosHistoryEnabled !== undefined) user.videosHistoryEnabled = body.videosHistoryEnabled |
185 | if (body.videoLanguages !== undefined) user.videoLanguages = body.videoLanguages | 179 | if (body.videoLanguages !== undefined) user.videoLanguages = body.videoLanguages |
186 | if (body.theme !== undefined) user.theme = body.theme | 180 | if (body.theme !== undefined) user.theme = body.theme |
181 | if (body.noInstanceConfigWarningModal !== undefined) user.noInstanceConfigWarningModal = body.noInstanceConfigWarningModal | ||
182 | if (body.noWelcomeModal !== undefined) user.noWelcomeModal = body.noWelcomeModal | ||
187 | 183 | ||
188 | if (body.email !== undefined) { | 184 | if (body.email !== undefined) { |
189 | if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) { | 185 | if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) { |
@@ -195,17 +191,17 @@ async function updateMe (req: express.Request, res: express.Response) { | |||
195 | } | 191 | } |
196 | 192 | ||
197 | await sequelizeTypescript.transaction(async t => { | 193 | await sequelizeTypescript.transaction(async t => { |
198 | const userAccount = await AccountModel.load(user.Account.id) | ||
199 | |||
200 | await user.save({ transaction: t }) | 194 | await user.save({ transaction: t }) |
201 | 195 | ||
202 | if (body.displayName !== undefined) userAccount.name = body.displayName | 196 | if (body.displayName !== undefined || body.description !== undefined) { |
203 | if (body.description !== undefined) userAccount.description = body.description | 197 | const userAccount = await AccountModel.load(user.Account.id, t) |
204 | await userAccount.save({ transaction: t }) | ||
205 | 198 | ||
206 | await sendUpdateActor(userAccount, t) | 199 | if (body.displayName !== undefined) userAccount.name = body.displayName |
200 | if (body.description !== undefined) userAccount.description = body.description | ||
201 | await userAccount.save({ transaction: t }) | ||
207 | 202 | ||
208 | auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON({})), oldUserAuditView) | 203 | await sendUpdateActor(userAccount, t) |
204 | } | ||
209 | }) | 205 | }) |
210 | 206 | ||
211 | if (sendVerificationEmail === true) { | 207 | if (sendVerificationEmail === true) { |
@@ -218,13 +214,10 @@ async function updateMe (req: express.Request, res: express.Response) { | |||
218 | async function updateMyAvatar (req: express.Request, res: express.Response) { | 214 | async function updateMyAvatar (req: express.Request, res: express.Response) { |
219 | const avatarPhysicalFile = req.files[ 'avatarfile' ][ 0 ] | 215 | const avatarPhysicalFile = req.files[ 'avatarfile' ][ 0 ] |
220 | const user = res.locals.oauth.token.user | 216 | const user = res.locals.oauth.token.user |
221 | const oldUserAuditView = new UserAuditView(user.toFormattedJSON({})) | ||
222 | 217 | ||
223 | const userAccount = await AccountModel.load(user.Account.id) | 218 | const userAccount = await AccountModel.load(user.Account.id) |
224 | 219 | ||
225 | const avatar = await updateActorAvatarFile(avatarPhysicalFile, userAccount) | 220 | const avatar = await updateActorAvatarFile(avatarPhysicalFile, userAccount) |
226 | 221 | ||
227 | auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON({})), oldUserAuditView) | ||
228 | |||
229 | return res.json({ avatar: avatar.toFormattedJSON() }) | 222 | return res.json({ avatar: avatar.toFormattedJSON() }) |
230 | } | 223 | } |
diff --git a/server/controllers/api/users/my-history.ts b/server/controllers/api/users/my-history.ts index 7025c0ff1..4da1f3496 100644 --- a/server/controllers/api/users/my-history.ts +++ b/server/controllers/api/users/my-history.ts | |||
@@ -7,7 +7,6 @@ import { | |||
7 | setDefaultPagination, | 7 | setDefaultPagination, |
8 | userHistoryRemoveValidator | 8 | userHistoryRemoveValidator |
9 | } from '../../../middlewares' | 9 | } from '../../../middlewares' |
10 | import { UserModel } from '../../../models/account/user' | ||
11 | import { getFormattedObjects } from '../../../helpers/utils' | 10 | import { getFormattedObjects } from '../../../helpers/utils' |
12 | import { UserVideoHistoryModel } from '../../../models/account/user-video-history' | 11 | import { UserVideoHistoryModel } from '../../../models/account/user-video-history' |
13 | import { sequelizeTypescript } from '../../../initializers' | 12 | import { sequelizeTypescript } from '../../../initializers' |
diff --git a/server/controllers/api/users/my-notifications.ts b/server/controllers/api/users/my-notifications.ts index f146284e4..017f5219e 100644 --- a/server/controllers/api/users/my-notifications.ts +++ b/server/controllers/api/users/my-notifications.ts | |||
@@ -76,7 +76,8 @@ async function updateNotificationSettings (req: express.Request, res: express.Re | |||
76 | newFollow: body.newFollow, | 76 | newFollow: body.newFollow, |
77 | newUserRegistration: body.newUserRegistration, | 77 | newUserRegistration: body.newUserRegistration, |
78 | commentMention: body.commentMention, | 78 | commentMention: body.commentMention, |
79 | newInstanceFollower: body.newInstanceFollower | 79 | newInstanceFollower: body.newInstanceFollower, |
80 | autoInstanceFollowing: body.autoInstanceFollowing | ||
80 | } | 81 | } |
81 | 82 | ||
82 | await UserNotificationSettingModel.update(values, query) | 83 | await UserNotificationSettingModel.update(values, query) |
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index 81a03a62b..acc5b2987 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts | |||
@@ -19,7 +19,7 @@ import { VideoChannelModel } from '../../models/video/video-channel' | |||
19 | import { videoChannelsNameWithHostValidator, videosSortValidator } from '../../middlewares/validators' | 19 | import { videoChannelsNameWithHostValidator, videosSortValidator } from '../../middlewares/validators' |
20 | import { sendUpdateActor } from '../../lib/activitypub/send' | 20 | import { sendUpdateActor } from '../../lib/activitypub/send' |
21 | import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared' | 21 | import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared' |
22 | import { createVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel' | 22 | import { createLocalVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel' |
23 | import { buildNSFWFilter, createReqFiles, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' | 23 | import { buildNSFWFilter, createReqFiles, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' |
24 | import { setAsyncActorKeys } from '../../lib/activitypub' | 24 | import { setAsyncActorKeys } from '../../lib/activitypub' |
25 | import { AccountModel } from '../../models/account/account' | 25 | import { AccountModel } from '../../models/account/account' |
@@ -35,6 +35,7 @@ import { VideoPlaylistModel } from '../../models/video/video-playlist' | |||
35 | import { commonVideoPlaylistFiltersValidator } from '../../middlewares/validators/videos/video-playlists' | 35 | import { commonVideoPlaylistFiltersValidator } from '../../middlewares/validators/videos/video-playlists' |
36 | import { CONFIG } from '../../initializers/config' | 36 | import { CONFIG } from '../../initializers/config' |
37 | import { sequelizeTypescript } from '../../initializers/database' | 37 | import { sequelizeTypescript } from '../../initializers/database' |
38 | import { MChannelAccountDefault } from '@server/typings/models' | ||
38 | 39 | ||
39 | const auditLogger = auditLoggerFactory('channels') | 40 | const auditLogger = auditLoggerFactory('channels') |
40 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) | 41 | const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) |
@@ -136,10 +137,10 @@ async function updateVideoChannelAvatar (req: express.Request, res: express.Resp | |||
136 | async function addVideoChannel (req: express.Request, res: express.Response) { | 137 | async function addVideoChannel (req: express.Request, res: express.Response) { |
137 | const videoChannelInfo: VideoChannelCreate = req.body | 138 | const videoChannelInfo: VideoChannelCreate = req.body |
138 | 139 | ||
139 | const videoChannelCreated: VideoChannelModel = await sequelizeTypescript.transaction(async t => { | 140 | const videoChannelCreated = await sequelizeTypescript.transaction(async t => { |
140 | const account = await AccountModel.load(res.locals.oauth.token.User.Account.id, t) | 141 | const account = await AccountModel.load(res.locals.oauth.token.User.Account.id, t) |
141 | 142 | ||
142 | return createVideoChannel(videoChannelInfo, account, t) | 143 | return createLocalVideoChannel(videoChannelInfo, account, t) |
143 | }) | 144 | }) |
144 | 145 | ||
145 | setAsyncActorKeys(videoChannelCreated.Actor) | 146 | setAsyncActorKeys(videoChannelCreated.Actor) |
@@ -181,7 +182,7 @@ async function updateVideoChannel (req: express.Request, res: express.Response) | |||
181 | } | 182 | } |
182 | } | 183 | } |
183 | 184 | ||
184 | const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions) | 185 | const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions) as MChannelAccountDefault |
185 | await sendUpdateActor(videoChannelInstanceUpdated, t) | 186 | await sendUpdateActor(videoChannelInstanceUpdated, t) |
186 | 187 | ||
187 | auditLogger.update( | 188 | auditLogger.update( |
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' | |||
40 | import { CONFIG } from '../../initializers/config' | 40 | import { CONFIG } from '../../initializers/config' |
41 | import { sequelizeTypescript } from '../../initializers/database' | 41 | import { sequelizeTypescript } from '../../initializers/database' |
42 | import { createPlaylistMiniatureFromExisting } from '../../lib/thumbnail' | 42 | import { createPlaylistMiniatureFromExisting } from '../../lib/thumbnail' |
43 | import { VideoModel } from '../../models/video/video' | 43 | import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/typings/models' |
44 | 44 | ||
45 | const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { thumbnailfile: CONFIG.STORAGE.TMP_DIR }) | 45 | const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { thumbnailfile: CONFIG.STORAGE.TMP_DIR }) |
46 | 46 | ||
@@ -58,7 +58,7 @@ videoPlaylistRouter.get('/', | |||
58 | ) | 58 | ) |
59 | 59 | ||
60 | videoPlaylistRouter.get('/:playlistId', | 60 | videoPlaylistRouter.get('/:playlistId', |
61 | asyncMiddleware(videoPlaylistsGetValidator), | 61 | asyncMiddleware(videoPlaylistsGetValidator('summary')), |
62 | getVideoPlaylist | 62 | getVideoPlaylist |
63 | ) | 63 | ) |
64 | 64 | ||
@@ -83,7 +83,7 @@ videoPlaylistRouter.delete('/:playlistId', | |||
83 | ) | 83 | ) |
84 | 84 | ||
85 | videoPlaylistRouter.get('/:playlistId/videos', | 85 | videoPlaylistRouter.get('/:playlistId/videos', |
86 | asyncMiddleware(videoPlaylistsGetValidator), | 86 | asyncMiddleware(videoPlaylistsGetValidator('summary')), |
87 | paginationValidator, | 87 | paginationValidator, |
88 | setDefaultPagination, | 88 | setDefaultPagination, |
89 | optionalAuthenticate, | 89 | optionalAuthenticate, |
@@ -140,7 +140,7 @@ async function listVideoPlaylists (req: express.Request, res: express.Response) | |||
140 | } | 140 | } |
141 | 141 | ||
142 | function getVideoPlaylist (req: express.Request, res: express.Response) { | 142 | function getVideoPlaylist (req: express.Request, res: express.Response) { |
143 | const videoPlaylist = res.locals.videoPlaylist | 143 | const videoPlaylist = res.locals.videoPlaylistSummary |
144 | 144 | ||
145 | if (videoPlaylist.isOutdated()) { | 145 | if (videoPlaylist.isOutdated()) { |
146 | JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video-playlist', url: videoPlaylist.url } }) | 146 | JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video-playlist', url: videoPlaylist.url } }) |
@@ -159,7 +159,7 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) { | |||
159 | description: videoPlaylistInfo.description, | 159 | description: videoPlaylistInfo.description, |
160 | privacy: videoPlaylistInfo.privacy || VideoPlaylistPrivacy.PRIVATE, | 160 | privacy: videoPlaylistInfo.privacy || VideoPlaylistPrivacy.PRIVATE, |
161 | ownerAccountId: user.Account.id | 161 | ownerAccountId: user.Account.id |
162 | }) | 162 | }) as MVideoPlaylistFull |
163 | 163 | ||
164 | videoPlaylist.url = getVideoPlaylistActivityPubUrl(videoPlaylist) // We use the UUID, so set the URL after building the object | 164 | videoPlaylist.url = getVideoPlaylistActivityPubUrl(videoPlaylist) // We use the UUID, so set the URL after building the object |
165 | 165 | ||
@@ -175,8 +175,8 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) { | |||
175 | ? await createPlaylistMiniatureFromExisting(thumbnailField[0].path, videoPlaylist, false) | 175 | ? await createPlaylistMiniatureFromExisting(thumbnailField[0].path, videoPlaylist, false) |
176 | : undefined | 176 | : undefined |
177 | 177 | ||
178 | const videoPlaylistCreated: VideoPlaylistModel = await sequelizeTypescript.transaction(async t => { | 178 | const videoPlaylistCreated = await sequelizeTypescript.transaction(async t => { |
179 | const videoPlaylistCreated = await videoPlaylist.save({ transaction: t }) | 179 | const videoPlaylistCreated = await videoPlaylist.save({ transaction: t }) as MVideoPlaylistFull |
180 | 180 | ||
181 | if (thumbnailModel) { | 181 | if (thumbnailModel) { |
182 | thumbnailModel.automaticallyGenerated = false | 182 | thumbnailModel.automaticallyGenerated = false |
@@ -201,7 +201,7 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) { | |||
201 | } | 201 | } |
202 | 202 | ||
203 | async function updateVideoPlaylist (req: express.Request, res: express.Response) { | 203 | async function updateVideoPlaylist (req: express.Request, res: express.Response) { |
204 | const videoPlaylistInstance = res.locals.videoPlaylist | 204 | const videoPlaylistInstance = res.locals.videoPlaylistFull |
205 | const videoPlaylistFieldsSave = videoPlaylistInstance.toJSON() | 205 | const videoPlaylistFieldsSave = videoPlaylistInstance.toJSON() |
206 | const videoPlaylistInfoToUpdate = req.body as VideoPlaylistUpdate | 206 | const videoPlaylistInfoToUpdate = req.body as VideoPlaylistUpdate |
207 | 207 | ||
@@ -275,7 +275,7 @@ async function updateVideoPlaylist (req: express.Request, res: express.Response) | |||
275 | } | 275 | } |
276 | 276 | ||
277 | async function removeVideoPlaylist (req: express.Request, res: express.Response) { | 277 | async function removeVideoPlaylist (req: express.Request, res: express.Response) { |
278 | const videoPlaylistInstance = res.locals.videoPlaylist | 278 | const videoPlaylistInstance = res.locals.videoPlaylistSummary |
279 | 279 | ||
280 | await sequelizeTypescript.transaction(async t => { | 280 | await sequelizeTypescript.transaction(async t => { |
281 | await videoPlaylistInstance.destroy({ transaction: t }) | 281 | await videoPlaylistInstance.destroy({ transaction: t }) |
@@ -290,10 +290,10 @@ async function removeVideoPlaylist (req: express.Request, res: express.Response) | |||
290 | 290 | ||
291 | async function addVideoInPlaylist (req: express.Request, res: express.Response) { | 291 | async function addVideoInPlaylist (req: express.Request, res: express.Response) { |
292 | const body: VideoPlaylistElementCreate = req.body | 292 | const body: VideoPlaylistElementCreate = req.body |
293 | const videoPlaylist = res.locals.videoPlaylist | 293 | const videoPlaylist = res.locals.videoPlaylistFull |
294 | const video = res.locals.video | 294 | const video = res.locals.onlyVideo |
295 | 295 | ||
296 | const playlistElement: VideoPlaylistElementModel = await sequelizeTypescript.transaction(async t => { | 296 | const playlistElement = await sequelizeTypescript.transaction(async t => { |
297 | const position = await VideoPlaylistElementModel.getNextPositionOf(videoPlaylist.id, t) | 297 | const position = await VideoPlaylistElementModel.getNextPositionOf(videoPlaylist.id, t) |
298 | 298 | ||
299 | const playlistElement = await VideoPlaylistElementModel.create({ | 299 | const playlistElement = await VideoPlaylistElementModel.create({ |
@@ -330,7 +330,7 @@ async function addVideoInPlaylist (req: express.Request, res: express.Response) | |||
330 | 330 | ||
331 | async function updateVideoPlaylistElement (req: express.Request, res: express.Response) { | 331 | async function updateVideoPlaylistElement (req: express.Request, res: express.Response) { |
332 | const body: VideoPlaylistElementUpdate = req.body | 332 | const body: VideoPlaylistElementUpdate = req.body |
333 | const videoPlaylist = res.locals.videoPlaylist | 333 | const videoPlaylist = res.locals.videoPlaylistFull |
334 | const videoPlaylistElement = res.locals.videoPlaylistElement | 334 | const videoPlaylistElement = res.locals.videoPlaylistElement |
335 | 335 | ||
336 | const playlistElement: VideoPlaylistElementModel = await sequelizeTypescript.transaction(async t => { | 336 | const playlistElement: VideoPlaylistElementModel = await sequelizeTypescript.transaction(async t => { |
@@ -354,7 +354,7 @@ async function updateVideoPlaylistElement (req: express.Request, res: express.Re | |||
354 | 354 | ||
355 | async function removeVideoFromPlaylist (req: express.Request, res: express.Response) { | 355 | async function removeVideoFromPlaylist (req: express.Request, res: express.Response) { |
356 | const videoPlaylistElement = res.locals.videoPlaylistElement | 356 | const videoPlaylistElement = res.locals.videoPlaylistElement |
357 | const videoPlaylist = res.locals.videoPlaylist | 357 | const videoPlaylist = res.locals.videoPlaylistFull |
358 | const positionToDelete = videoPlaylistElement.position | 358 | const positionToDelete = videoPlaylistElement.position |
359 | 359 | ||
360 | await sequelizeTypescript.transaction(async t => { | 360 | await sequelizeTypescript.transaction(async t => { |
@@ -381,7 +381,7 @@ async function removeVideoFromPlaylist (req: express.Request, res: express.Respo | |||
381 | } | 381 | } |
382 | 382 | ||
383 | async function reorderVideosPlaylist (req: express.Request, res: express.Response) { | 383 | async function reorderVideosPlaylist (req: express.Request, res: express.Response) { |
384 | const videoPlaylist = res.locals.videoPlaylist | 384 | const videoPlaylist = res.locals.videoPlaylistFull |
385 | const body: VideoPlaylistReorder = req.body | 385 | const body: VideoPlaylistReorder = req.body |
386 | 386 | ||
387 | const start: number = body.startPosition | 387 | const start: number = body.startPosition |
@@ -434,7 +434,7 @@ async function reorderVideosPlaylist (req: express.Request, res: express.Respons | |||
434 | } | 434 | } |
435 | 435 | ||
436 | async function getVideoPlaylistVideos (req: express.Request, res: express.Response) { | 436 | async function getVideoPlaylistVideos (req: express.Request, res: express.Response) { |
437 | const videoPlaylistInstance = res.locals.videoPlaylist | 437 | const videoPlaylistInstance = res.locals.videoPlaylistSummary |
438 | const user = res.locals.oauth ? res.locals.oauth.token.User : undefined | 438 | const user = res.locals.oauth ? res.locals.oauth.token.User : undefined |
439 | const server = await getServerActor() | 439 | const server = await getServerActor() |
440 | 440 | ||
@@ -453,7 +453,7 @@ async function getVideoPlaylistVideos (req: express.Request, res: express.Respon | |||
453 | return res.json(getFormattedObjects(resultList.data, resultList.total, options)) | 453 | return res.json(getFormattedObjects(resultList.data, resultList.total, options)) |
454 | } | 454 | } |
455 | 455 | ||
456 | async function regeneratePlaylistThumbnail (videoPlaylist: VideoPlaylistModel) { | 456 | async function regeneratePlaylistThumbnail (videoPlaylist: MVideoPlaylistThumbnail) { |
457 | await videoPlaylist.Thumbnail.destroy() | 457 | await videoPlaylist.Thumbnail.destroy() |
458 | videoPlaylist.Thumbnail = null | 458 | videoPlaylist.Thumbnail = null |
459 | 459 | ||
@@ -461,7 +461,7 @@ async function regeneratePlaylistThumbnail (videoPlaylist: VideoPlaylistModel) { | |||
461 | if (firstElement) await generateThumbnailForPlaylist(videoPlaylist, firstElement.Video) | 461 | if (firstElement) await generateThumbnailForPlaylist(videoPlaylist, firstElement.Video) |
462 | } | 462 | } |
463 | 463 | ||
464 | async function generateThumbnailForPlaylist (videoPlaylist: VideoPlaylistModel, video: VideoModel) { | 464 | async function generateThumbnailForPlaylist (videoPlaylist: MVideoPlaylistThumbnail, video: MVideoThumbnail) { |
465 | logger.info('Generating default thumbnail to playlist %s.', videoPlaylist.url) | 465 | logger.info('Generating default thumbnail to playlist %s.', videoPlaylist.url) |
466 | 466 | ||
467 | const inputPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, video.getMiniature().filename) | 467 | const inputPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, video.getMiniature().filename) |
diff --git a/server/controllers/api/videos/abuse.ts b/server/controllers/api/videos/abuse.ts index 77808466c..4ae899b7e 100644 --- a/server/controllers/api/videos/abuse.ts +++ b/server/controllers/api/videos/abuse.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { UserRight, VideoAbuseCreate, VideoAbuseState } from '../../../../shared' | 2 | import { UserRight, VideoAbuseCreate, VideoAbuseState } from '../../../../shared' |
3 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
4 | import { getFormattedObjects } from '../../../helpers/utils' | 4 | import { getFormattedObjects, getServerActor } from '../../../helpers/utils' |
5 | import { sequelizeTypescript } from '../../../initializers' | 5 | import { sequelizeTypescript } from '../../../initializers' |
6 | import { | 6 | import { |
7 | asyncMiddleware, | 7 | asyncMiddleware, |
@@ -21,6 +21,7 @@ import { VideoAbuseModel } from '../../../models/video/video-abuse' | |||
21 | import { auditLoggerFactory, VideoAbuseAuditView } from '../../../helpers/audit-logger' | 21 | import { auditLoggerFactory, VideoAbuseAuditView } from '../../../helpers/audit-logger' |
22 | import { Notifier } from '../../../lib/notifier' | 22 | import { Notifier } from '../../../lib/notifier' |
23 | import { sendVideoAbuse } from '../../../lib/activitypub/send/send-flag' | 23 | import { sendVideoAbuse } from '../../../lib/activitypub/send/send-flag' |
24 | import { MVideoAbuseAccountVideo } from '../../../typings/models/video' | ||
24 | 25 | ||
25 | const auditLogger = auditLoggerFactory('abuse') | 26 | const auditLogger = auditLoggerFactory('abuse') |
26 | const abuseVideoRouter = express.Router() | 27 | const abuseVideoRouter = express.Router() |
@@ -61,7 +62,16 @@ export { | |||
61 | // --------------------------------------------------------------------------- | 62 | // --------------------------------------------------------------------------- |
62 | 63 | ||
63 | async function listVideoAbuses (req: express.Request, res: express.Response) { | 64 | async function listVideoAbuses (req: express.Request, res: express.Response) { |
64 | const resultList = await VideoAbuseModel.listForApi(req.query.start, req.query.count, req.query.sort) | 65 | const user = res.locals.oauth.token.user |
66 | const serverActor = await getServerActor() | ||
67 | |||
68 | const resultList = await VideoAbuseModel.listForApi({ | ||
69 | start: req.query.start, | ||
70 | count: req.query.count, | ||
71 | sort: req.query.sort, | ||
72 | serverAccountId: serverActor.Account.id, | ||
73 | user | ||
74 | }) | ||
65 | 75 | ||
66 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 76 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
67 | } | 77 | } |
@@ -94,10 +104,10 @@ async function deleteVideoAbuse (req: express.Request, res: express.Response) { | |||
94 | } | 104 | } |
95 | 105 | ||
96 | async function reportVideoAbuse (req: express.Request, res: express.Response) { | 106 | async function reportVideoAbuse (req: express.Request, res: express.Response) { |
97 | const videoInstance = res.locals.video | 107 | const videoInstance = res.locals.videoAll |
98 | const body: VideoAbuseCreate = req.body | 108 | const body: VideoAbuseCreate = req.body |
99 | 109 | ||
100 | const videoAbuse: VideoAbuseModel = await sequelizeTypescript.transaction(async t => { | 110 | const videoAbuse = await sequelizeTypescript.transaction(async t => { |
101 | const reporterAccount = await AccountModel.load(res.locals.oauth.token.User.Account.id, t) | 111 | const reporterAccount = await AccountModel.load(res.locals.oauth.token.User.Account.id, t) |
102 | 112 | ||
103 | const abuseToCreate = { | 113 | const abuseToCreate = { |
@@ -107,7 +117,7 @@ async function reportVideoAbuse (req: express.Request, res: express.Response) { | |||
107 | state: VideoAbuseState.PENDING | 117 | state: VideoAbuseState.PENDING |
108 | } | 118 | } |
109 | 119 | ||
110 | const videoAbuseInstance = await VideoAbuseModel.create(abuseToCreate, { transaction: t }) | 120 | const videoAbuseInstance: MVideoAbuseAccountVideo = await VideoAbuseModel.create(abuseToCreate, { transaction: t }) |
111 | videoAbuseInstance.Video = videoInstance | 121 | videoAbuseInstance.Video = videoInstance |
112 | videoAbuseInstance.Account = reporterAccount | 122 | videoAbuseInstance.Account = reporterAccount |
113 | 123 | ||
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 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { VideoBlacklist, UserRight, VideoBlacklistCreate, VideoBlacklistType } from '../../../../shared' | 2 | import { UserRight, VideoBlacklistCreate, VideoBlacklistType } from '../../../../shared' |
3 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
4 | import { getFormattedObjects } from '../../../helpers/utils' | 4 | import { getFormattedObjects } from '../../../helpers/utils' |
5 | import { | 5 | import { |
@@ -11,15 +11,16 @@ import { | |||
11 | setBlacklistSort, | 11 | setBlacklistSort, |
12 | setDefaultPagination, | 12 | setDefaultPagination, |
13 | videosBlacklistAddValidator, | 13 | videosBlacklistAddValidator, |
14 | videosBlacklistFiltersValidator, | ||
14 | videosBlacklistRemoveValidator, | 15 | videosBlacklistRemoveValidator, |
15 | videosBlacklistUpdateValidator, | 16 | videosBlacklistUpdateValidator |
16 | videosBlacklistFiltersValidator | ||
17 | } from '../../../middlewares' | 17 | } from '../../../middlewares' |
18 | import { VideoBlacklistModel } from '../../../models/video/video-blacklist' | 18 | import { VideoBlacklistModel } from '../../../models/video/video-blacklist' |
19 | import { sequelizeTypescript } from '../../../initializers' | 19 | import { sequelizeTypescript } from '../../../initializers' |
20 | import { Notifier } from '../../../lib/notifier' | 20 | import { Notifier } from '../../../lib/notifier' |
21 | import { sendDeleteVideo } from '../../../lib/activitypub/send' | 21 | import { sendDeleteVideo } from '../../../lib/activitypub/send' |
22 | import { federateVideoIfNeeded } from '../../../lib/activitypub' | 22 | import { federateVideoIfNeeded } from '../../../lib/activitypub' |
23 | import { MVideoBlacklistVideo } from '@server/typings/models' | ||
23 | 24 | ||
24 | const blacklistRouter = express.Router() | 25 | const blacklistRouter = express.Router() |
25 | 26 | ||
@@ -64,7 +65,7 @@ export { | |||
64 | // --------------------------------------------------------------------------- | 65 | // --------------------------------------------------------------------------- |
65 | 66 | ||
66 | async function addVideoToBlacklist (req: express.Request, res: express.Response) { | 67 | async function addVideoToBlacklist (req: express.Request, res: express.Response) { |
67 | const videoInstance = res.locals.video | 68 | const videoInstance = res.locals.videoAll |
68 | const body: VideoBlacklistCreate = req.body | 69 | const body: VideoBlacklistCreate = req.body |
69 | 70 | ||
70 | const toCreate = { | 71 | const toCreate = { |
@@ -74,7 +75,7 @@ async function addVideoToBlacklist (req: express.Request, res: express.Response) | |||
74 | type: VideoBlacklistType.MANUAL | 75 | type: VideoBlacklistType.MANUAL |
75 | } | 76 | } |
76 | 77 | ||
77 | const blacklist = await VideoBlacklistModel.create(toCreate) | 78 | const blacklist: MVideoBlacklistVideo = await VideoBlacklistModel.create(toCreate) |
78 | blacklist.Video = videoInstance | 79 | blacklist.Video = videoInstance |
79 | 80 | ||
80 | if (body.unfederate === true) { | 81 | if (body.unfederate === true) { |
@@ -83,7 +84,7 @@ async function addVideoToBlacklist (req: express.Request, res: express.Response) | |||
83 | 84 | ||
84 | Notifier.Instance.notifyOnVideoBlacklist(blacklist) | 85 | Notifier.Instance.notifyOnVideoBlacklist(blacklist) |
85 | 86 | ||
86 | logger.info('Video %s blacklisted.', res.locals.video.uuid) | 87 | logger.info('Video %s blacklisted.', videoInstance.uuid) |
87 | 88 | ||
88 | return res.type('json').status(204).end() | 89 | return res.type('json').status(204).end() |
89 | } | 90 | } |
@@ -108,7 +109,7 @@ async function listBlacklist (req: express.Request, res: express.Response) { | |||
108 | 109 | ||
109 | async function removeVideoFromBlacklistController (req: express.Request, res: express.Response) { | 110 | async function removeVideoFromBlacklistController (req: express.Request, res: express.Response) { |
110 | const videoBlacklist = res.locals.videoBlacklist | 111 | const videoBlacklist = res.locals.videoBlacklist |
111 | const video = res.locals.video | 112 | const video = res.locals.videoAll |
112 | 113 | ||
113 | const videoBlacklistType = await sequelizeTypescript.transaction(async t => { | 114 | const videoBlacklistType = await sequelizeTypescript.transaction(async t => { |
114 | const unfederated = videoBlacklist.unfederated | 115 | const unfederated = videoBlacklist.unfederated |
@@ -135,7 +136,7 @@ async function removeVideoFromBlacklistController (req: express.Request, res: ex | |||
135 | Notifier.Instance.notifyOnNewVideoIfNeeded(video) | 136 | Notifier.Instance.notifyOnNewVideoIfNeeded(video) |
136 | } | 137 | } |
137 | 138 | ||
138 | logger.info('Video %s removed from blacklist.', res.locals.video.uuid) | 139 | logger.info('Video %s removed from blacklist.', video.uuid) |
139 | 140 | ||
140 | return res.type('json').status(204).end() | 141 | return res.type('json').status(204).end() |
141 | } | 142 | } |
diff --git a/server/controllers/api/videos/captions.ts b/server/controllers/api/videos/captions.ts index 44c255232..37481d12f 100644 --- a/server/controllers/api/videos/captions.ts +++ b/server/controllers/api/videos/captions.ts | |||
@@ -10,6 +10,7 @@ import { federateVideoIfNeeded } from '../../../lib/activitypub' | |||
10 | import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' | 10 | import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' |
11 | import { CONFIG } from '../../../initializers/config' | 11 | import { CONFIG } from '../../../initializers/config' |
12 | import { sequelizeTypescript } from '../../../initializers/database' | 12 | import { sequelizeTypescript } from '../../../initializers/database' |
13 | import { MVideoCaptionVideo } from '@server/typings/models' | ||
13 | 14 | ||
14 | const reqVideoCaptionAdd = createReqFiles( | 15 | const reqVideoCaptionAdd = createReqFiles( |
15 | [ 'captionfile' ], | 16 | [ 'captionfile' ], |
@@ -46,19 +47,19 @@ export { | |||
46 | // --------------------------------------------------------------------------- | 47 | // --------------------------------------------------------------------------- |
47 | 48 | ||
48 | async function listVideoCaptions (req: express.Request, res: express.Response) { | 49 | async function listVideoCaptions (req: express.Request, res: express.Response) { |
49 | const data = await VideoCaptionModel.listVideoCaptions(res.locals.video.id) | 50 | const data = await VideoCaptionModel.listVideoCaptions(res.locals.videoId.id) |
50 | 51 | ||
51 | return res.json(getFormattedObjects(data, data.length)) | 52 | return res.json(getFormattedObjects(data, data.length)) |
52 | } | 53 | } |
53 | 54 | ||
54 | async function addVideoCaption (req: express.Request, res: express.Response) { | 55 | async function addVideoCaption (req: express.Request, res: express.Response) { |
55 | const videoCaptionPhysicalFile = req.files['captionfile'][0] | 56 | const videoCaptionPhysicalFile = req.files['captionfile'][0] |
56 | const video = res.locals.video | 57 | const video = res.locals.videoAll |
57 | 58 | ||
58 | const videoCaption = new VideoCaptionModel({ | 59 | const videoCaption = new VideoCaptionModel({ |
59 | videoId: video.id, | 60 | videoId: video.id, |
60 | language: req.params.captionLanguage | 61 | language: req.params.captionLanguage |
61 | }) | 62 | }) as MVideoCaptionVideo |
62 | videoCaption.Video = video | 63 | videoCaption.Video = video |
63 | 64 | ||
64 | // Move physical file | 65 | // Move physical file |
@@ -75,7 +76,7 @@ async function addVideoCaption (req: express.Request, res: express.Response) { | |||
75 | } | 76 | } |
76 | 77 | ||
77 | async function deleteVideoCaption (req: express.Request, res: express.Response) { | 78 | async function deleteVideoCaption (req: express.Request, res: express.Response) { |
78 | const video = res.locals.video | 79 | const video = res.locals.videoAll |
79 | const videoCaption = res.locals.videoCaption | 80 | const videoCaption = res.locals.videoCaption |
80 | 81 | ||
81 | await sequelizeTypescript.transaction(async t => { | 82 | await sequelizeTypescript.transaction(async t => { |
diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts index bc6d81a7c..b2b06b170 100644 --- a/server/controllers/api/videos/comment.ts +++ b/server/controllers/api/videos/comment.ts | |||
@@ -27,9 +27,6 @@ import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../. | |||
27 | import { AccountModel } from '../../../models/account/account' | 27 | import { AccountModel } from '../../../models/account/account' |
28 | import { Notifier } from '../../../lib/notifier' | 28 | import { Notifier } from '../../../lib/notifier' |
29 | import { Hooks } from '../../../lib/plugins/hooks' | 29 | import { Hooks } from '../../../lib/plugins/hooks' |
30 | import { ActorModel } from '../../../models/activitypub/actor' | ||
31 | import { VideoChannelModel } from '../../../models/video/video-channel' | ||
32 | import { VideoModel } from '../../../models/video/video' | ||
33 | import { sendDeleteVideoComment } from '../../../lib/activitypub/send' | 30 | import { sendDeleteVideoComment } from '../../../lib/activitypub/send' |
34 | 31 | ||
35 | const auditLogger = auditLoggerFactory('comments') | 32 | const auditLogger = auditLoggerFactory('comments') |
@@ -75,7 +72,7 @@ export { | |||
75 | // --------------------------------------------------------------------------- | 72 | // --------------------------------------------------------------------------- |
76 | 73 | ||
77 | async function listVideoThreads (req: express.Request, res: express.Response) { | 74 | async function listVideoThreads (req: express.Request, res: express.Response) { |
78 | const video = res.locals.video | 75 | const video = res.locals.onlyVideo |
79 | const user = res.locals.oauth ? res.locals.oauth.token.User : undefined | 76 | const user = res.locals.oauth ? res.locals.oauth.token.User : undefined |
80 | 77 | ||
81 | let resultList: ResultList<VideoCommentModel> | 78 | let resultList: ResultList<VideoCommentModel> |
@@ -86,7 +83,7 @@ async function listVideoThreads (req: express.Request, res: express.Response) { | |||
86 | start: req.query.start, | 83 | start: req.query.start, |
87 | count: req.query.count, | 84 | count: req.query.count, |
88 | sort: req.query.sort, | 85 | sort: req.query.sort, |
89 | user: user | 86 | user |
90 | }, 'filter:api.video-threads.list.params') | 87 | }, 'filter:api.video-threads.list.params') |
91 | 88 | ||
92 | resultList = await Hooks.wrapPromiseFun( | 89 | resultList = await Hooks.wrapPromiseFun( |
@@ -105,7 +102,7 @@ async function listVideoThreads (req: express.Request, res: express.Response) { | |||
105 | } | 102 | } |
106 | 103 | ||
107 | async function listVideoThreadComments (req: express.Request, res: express.Response) { | 104 | async function listVideoThreadComments (req: express.Request, res: express.Response) { |
108 | const video = res.locals.video | 105 | const video = res.locals.onlyVideo |
109 | const user = res.locals.oauth ? res.locals.oauth.token.User : undefined | 106 | const user = res.locals.oauth ? res.locals.oauth.token.User : undefined |
110 | 107 | ||
111 | let resultList: ResultList<VideoCommentModel> | 108 | let resultList: ResultList<VideoCommentModel> |
@@ -141,7 +138,7 @@ async function addVideoCommentThread (req: express.Request, res: express.Respons | |||
141 | return createVideoComment({ | 138 | return createVideoComment({ |
142 | text: videoCommentInfo.text, | 139 | text: videoCommentInfo.text, |
143 | inReplyToComment: null, | 140 | inReplyToComment: null, |
144 | video: res.locals.video, | 141 | video: res.locals.videoAll, |
145 | account | 142 | account |
146 | }, t) | 143 | }, t) |
147 | }) | 144 | }) |
@@ -164,8 +161,8 @@ async function addVideoCommentReply (req: express.Request, res: express.Response | |||
164 | 161 | ||
165 | return createVideoComment({ | 162 | return createVideoComment({ |
166 | text: videoCommentInfo.text, | 163 | text: videoCommentInfo.text, |
167 | inReplyToComment: res.locals.videoComment, | 164 | inReplyToComment: res.locals.videoCommentFull, |
168 | video: res.locals.video, | 165 | video: res.locals.videoAll, |
169 | account | 166 | account |
170 | }, t) | 167 | }, t) |
171 | }) | 168 | }) |
@@ -179,7 +176,7 @@ async function addVideoCommentReply (req: express.Request, res: express.Response | |||
179 | } | 176 | } |
180 | 177 | ||
181 | async function removeVideoComment (req: express.Request, res: express.Response) { | 178 | async function removeVideoComment (req: express.Request, res: express.Response) { |
182 | const videoCommentInstance = res.locals.videoComment | 179 | const videoCommentInstance = res.locals.videoCommentFull |
183 | 180 | ||
184 | await sequelizeTypescript.transaction(async t => { | 181 | await sequelizeTypescript.transaction(async t => { |
185 | await videoCommentInstance.destroy({ transaction: t }) | 182 | await videoCommentInstance.destroy({ transaction: t }) |
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts index 04c9b547b..28ced5836 100644 --- a/server/controllers/api/videos/import.ts +++ b/server/controllers/api/videos/import.ts | |||
@@ -1,6 +1,5 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import * as magnetUtil from 'magnet-uri' | 2 | import * as magnetUtil from 'magnet-uri' |
3 | import 'multer' | ||
4 | import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' | 3 | import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' |
5 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoImportAddValidator } from '../../../middlewares' | 4 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoImportAddValidator } from '../../../middlewares' |
6 | import { MIMETYPES } from '../../../initializers/constants' | 5 | import { MIMETYPES } from '../../../initializers/constants' |
@@ -15,7 +14,6 @@ import { VideoImportModel } from '../../../models/video/video-import' | |||
15 | import { JobQueue } from '../../../lib/job-queue/job-queue' | 14 | import { JobQueue } from '../../../lib/job-queue/job-queue' |
16 | import { join } from 'path' | 15 | import { join } from 'path' |
17 | import { isArray } from '../../../helpers/custom-validators/misc' | 16 | import { isArray } from '../../../helpers/custom-validators/misc' |
18 | import { VideoChannelModel } from '../../../models/video/video-channel' | ||
19 | import * as Bluebird from 'bluebird' | 17 | import * as Bluebird from 'bluebird' |
20 | import * as parseTorrent from 'parse-torrent' | 18 | import * as parseTorrent from 'parse-torrent' |
21 | import { getSecureTorrentName } from '../../../helpers/utils' | 19 | import { getSecureTorrentName } from '../../../helpers/utils' |
@@ -25,8 +23,16 @@ import { CONFIG } from '../../../initializers/config' | |||
25 | import { sequelizeTypescript } from '../../../initializers/database' | 23 | import { sequelizeTypescript } from '../../../initializers/database' |
26 | import { createVideoMiniatureFromExisting } from '../../../lib/thumbnail' | 24 | import { createVideoMiniatureFromExisting } from '../../../lib/thumbnail' |
27 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' | 25 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' |
28 | import { ThumbnailModel } from '../../../models/video/thumbnail' | 26 | import { |
29 | import { UserModel } from '../../../models/account/user' | 27 | MChannelAccountDefault, |
28 | MThumbnail, | ||
29 | MUser, | ||
30 | MVideoAccountDefault, | ||
31 | MVideoTag, | ||
32 | MVideoThumbnailAccountDefault, | ||
33 | MVideoWithBlacklistLight | ||
34 | } from '@server/typings/models' | ||
35 | import { MVideoImport, MVideoImportFormattable } from '@server/typings/models/video/video-import' | ||
30 | 36 | ||
31 | const auditLogger = auditLoggerFactory('video-imports') | 37 | const auditLogger = auditLoggerFactory('video-imports') |
32 | const videoImportsRouter = express.Router() | 38 | const videoImportsRouter = express.Router() |
@@ -184,8 +190,8 @@ function buildVideo (channelId: number, body: VideoImportCreate, importData: You | |||
184 | category: body.category || importData.category, | 190 | category: body.category || importData.category, |
185 | licence: body.licence || importData.licence, | 191 | licence: body.licence || importData.licence, |
186 | language: body.language || undefined, | 192 | language: body.language || undefined, |
187 | commentsEnabled: body.commentsEnabled || true, | 193 | commentsEnabled: body.commentsEnabled !== false, // If the value is not "false", the default is "true" |
188 | downloadEnabled: body.downloadEnabled || true, | 194 | downloadEnabled: body.downloadEnabled !== false, |
189 | waitTranscoding: body.waitTranscoding || false, | 195 | waitTranscoding: body.waitTranscoding || false, |
190 | state: VideoState.TO_IMPORT, | 196 | state: VideoState.TO_IMPORT, |
191 | nsfw: body.nsfw || importData.nsfw || false, | 197 | nsfw: body.nsfw || importData.nsfw || false, |
@@ -225,28 +231,28 @@ async function processPreview (req: express.Request, video: VideoModel) { | |||
225 | } | 231 | } |
226 | 232 | ||
227 | function insertIntoDB (parameters: { | 233 | function insertIntoDB (parameters: { |
228 | video: VideoModel, | 234 | video: MVideoThumbnailAccountDefault, |
229 | thumbnailModel: ThumbnailModel, | 235 | thumbnailModel: MThumbnail, |
230 | previewModel: ThumbnailModel, | 236 | previewModel: MThumbnail, |
231 | videoChannel: VideoChannelModel, | 237 | videoChannel: MChannelAccountDefault, |
232 | tags: string[], | 238 | tags: string[], |
233 | videoImportAttributes: Partial<VideoImportModel>, | 239 | videoImportAttributes: Partial<MVideoImport>, |
234 | user: UserModel | 240 | user: MUser |
235 | }): Bluebird<VideoImportModel> { | 241 | }): Bluebird<MVideoImportFormattable> { |
236 | const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters | 242 | const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters |
237 | 243 | ||
238 | return sequelizeTypescript.transaction(async t => { | 244 | return sequelizeTypescript.transaction(async t => { |
239 | const sequelizeOptions = { transaction: t } | 245 | const sequelizeOptions = { transaction: t } |
240 | 246 | ||
241 | // Save video object in database | 247 | // Save video object in database |
242 | const videoCreated = await video.save(sequelizeOptions) | 248 | const videoCreated = await video.save(sequelizeOptions) as (MVideoAccountDefault & MVideoWithBlacklistLight & MVideoTag) |
243 | videoCreated.VideoChannel = videoChannel | 249 | videoCreated.VideoChannel = videoChannel |
244 | 250 | ||
245 | if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) | 251 | if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) |
246 | if (previewModel) await videoCreated.addAndSaveThumbnail(previewModel, t) | 252 | if (previewModel) await videoCreated.addAndSaveThumbnail(previewModel, t) |
247 | 253 | ||
248 | await autoBlacklistVideoIfNeeded({ | 254 | await autoBlacklistVideoIfNeeded({ |
249 | video, | 255 | video: videoCreated, |
250 | user, | 256 | user, |
251 | notify: false, | 257 | notify: false, |
252 | isRemote: false, | 258 | isRemote: false, |
@@ -268,7 +274,7 @@ function insertIntoDB (parameters: { | |||
268 | const videoImport = await VideoImportModel.create( | 274 | const videoImport = await VideoImportModel.create( |
269 | Object.assign({ videoId: videoCreated.id }, videoImportAttributes), | 275 | Object.assign({ videoId: videoCreated.id }, videoImportAttributes), |
270 | sequelizeOptions | 276 | sequelizeOptions |
271 | ) | 277 | ) as MVideoImportFormattable |
272 | videoImport.Video = videoCreated | 278 | videoImport.Video = videoCreated |
273 | 279 | ||
274 | return videoImport | 280 | return videoImport |
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 155ca4678..19da504c7 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -63,6 +63,7 @@ import { createVideoMiniatureFromExisting, generateVideoMiniature } from '../../ | |||
63 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' | 63 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' |
64 | import { VideoTranscodingPayload } from '../../../lib/job-queue/handlers/video-transcoding' | 64 | import { VideoTranscodingPayload } from '../../../lib/job-queue/handlers/video-transcoding' |
65 | import { Hooks } from '../../../lib/plugins/hooks' | 65 | import { Hooks } from '../../../lib/plugins/hooks' |
66 | import { MVideoDetails, MVideoFullLight } from '@server/typings/models' | ||
66 | 67 | ||
67 | const auditLogger = auditLoggerFactory('videos') | 68 | const auditLogger = auditLoggerFactory('videos') |
68 | const videosRouter = express.Router() | 69 | const videosRouter = express.Router() |
@@ -185,7 +186,7 @@ async function addVideo (req: express.Request, res: express.Response) { | |||
185 | licence: videoInfo.licence, | 186 | licence: videoInfo.licence, |
186 | language: videoInfo.language, | 187 | language: videoInfo.language, |
187 | commentsEnabled: videoInfo.commentsEnabled || false, | 188 | commentsEnabled: videoInfo.commentsEnabled || false, |
188 | downloadEnabled: videoInfo.downloadEnabled || true, | 189 | downloadEnabled: videoInfo.downloadEnabled !== false, // If the value is not "false", the default is "true" |
189 | waitTranscoding: videoInfo.waitTranscoding || false, | 190 | waitTranscoding: videoInfo.waitTranscoding || false, |
190 | state: CONFIG.TRANSCODING.ENABLED ? VideoState.TO_TRANSCODE : VideoState.PUBLISHED, | 191 | state: CONFIG.TRANSCODING.ENABLED ? VideoState.TO_TRANSCODE : VideoState.PUBLISHED, |
191 | nsfw: videoInfo.nsfw || false, | 192 | nsfw: videoInfo.nsfw || false, |
@@ -197,7 +198,7 @@ async function addVideo (req: express.Request, res: express.Response) { | |||
197 | originallyPublishedAt: videoInfo.originallyPublishedAt | 198 | originallyPublishedAt: videoInfo.originallyPublishedAt |
198 | } | 199 | } |
199 | 200 | ||
200 | const video = new VideoModel(videoData) | 201 | const video = new VideoModel(videoData) as MVideoDetails |
201 | video.url = getVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object | 202 | video.url = getVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object |
202 | 203 | ||
203 | const videoFile = new VideoFileModel({ | 204 | const videoFile = new VideoFileModel({ |
@@ -238,7 +239,7 @@ async function addVideo (req: express.Request, res: express.Response) { | |||
238 | const { videoCreated } = await sequelizeTypescript.transaction(async t => { | 239 | const { videoCreated } = await sequelizeTypescript.transaction(async t => { |
239 | const sequelizeOptions = { transaction: t } | 240 | const sequelizeOptions = { transaction: t } |
240 | 241 | ||
241 | const videoCreated = await video.save(sequelizeOptions) | 242 | const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight |
242 | 243 | ||
243 | await videoCreated.addAndSaveThumbnail(thumbnailModel, t) | 244 | await videoCreated.addAndSaveThumbnail(thumbnailModel, t) |
244 | await videoCreated.addAndSaveThumbnail(previewModel, t) | 245 | await videoCreated.addAndSaveThumbnail(previewModel, t) |
@@ -318,7 +319,7 @@ async function addVideo (req: express.Request, res: express.Response) { | |||
318 | } | 319 | } |
319 | 320 | ||
320 | async function updateVideo (req: express.Request, res: express.Response) { | 321 | async function updateVideo (req: express.Request, res: express.Response) { |
321 | const videoInstance = res.locals.video | 322 | const videoInstance = res.locals.videoAll |
322 | const videoFieldsSave = videoInstance.toJSON() | 323 | const videoFieldsSave = videoInstance.toJSON() |
323 | const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON()) | 324 | const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON()) |
324 | const videoInfoToUpdate: VideoUpdate = req.body | 325 | const videoInfoToUpdate: VideoUpdate = req.body |
@@ -371,7 +372,7 @@ async function updateVideo (req: express.Request, res: express.Response) { | |||
371 | } | 372 | } |
372 | } | 373 | } |
373 | 374 | ||
374 | const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) | 375 | const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) as MVideoFullLight |
375 | 376 | ||
376 | if (thumbnailModel) await videoInstanceUpdated.addAndSaveThumbnail(thumbnailModel, t) | 377 | if (thumbnailModel) await videoInstanceUpdated.addAndSaveThumbnail(thumbnailModel, t) |
377 | if (previewModel) await videoInstanceUpdated.addAndSaveThumbnail(previewModel, t) | 378 | if (previewModel) await videoInstanceUpdated.addAndSaveThumbnail(previewModel, t) |
@@ -447,7 +448,7 @@ async function getVideo (req: express.Request, res: express.Response) { | |||
447 | 448 | ||
448 | const video = await Hooks.wrapPromiseFun( | 449 | const video = await Hooks.wrapPromiseFun( |
449 | VideoModel.loadForGetAPI, | 450 | VideoModel.loadForGetAPI, |
450 | { id: res.locals.video.id, userId }, | 451 | { id: res.locals.onlyVideoWithRights.id, userId }, |
451 | 'filter:api.video.get.result' | 452 | 'filter:api.video.get.result' |
452 | ) | 453 | ) |
453 | 454 | ||
@@ -460,7 +461,7 @@ async function getVideo (req: express.Request, res: express.Response) { | |||
460 | } | 461 | } |
461 | 462 | ||
462 | async function viewVideo (req: express.Request, res: express.Response) { | 463 | async function viewVideo (req: express.Request, res: express.Response) { |
463 | const videoInstance = res.locals.video | 464 | const videoInstance = res.locals.videoAll |
464 | 465 | ||
465 | const ip = req.ip | 466 | const ip = req.ip |
466 | const exists = await Redis.Instance.doesVideoIPViewExist(ip, videoInstance.uuid) | 467 | const exists = await Redis.Instance.doesVideoIPViewExist(ip, videoInstance.uuid) |
@@ -483,7 +484,7 @@ async function viewVideo (req: express.Request, res: express.Response) { | |||
483 | } | 484 | } |
484 | 485 | ||
485 | async function getVideoDescription (req: express.Request, res: express.Response) { | 486 | async function getVideoDescription (req: express.Request, res: express.Response) { |
486 | const videoInstance = res.locals.video | 487 | const videoInstance = res.locals.videoAll |
487 | let description = '' | 488 | let description = '' |
488 | 489 | ||
489 | if (videoInstance.isOwned()) { | 490 | if (videoInstance.isOwned()) { |
@@ -522,7 +523,7 @@ async function listVideos (req: express.Request, res: express.Response) { | |||
522 | } | 523 | } |
523 | 524 | ||
524 | async function removeVideo (req: express.Request, res: express.Response) { | 525 | async function removeVideo (req: express.Request, res: express.Response) { |
525 | const videoInstance = res.locals.video | 526 | const videoInstance = res.locals.videoAll |
526 | 527 | ||
527 | await sequelizeTypescript.transaction(async t => { | 528 | await sequelizeTypescript.transaction(async t => { |
528 | await videoInstance.destroy({ transaction: t }) | 529 | await videoInstance.destroy({ transaction: t }) |
diff --git a/server/controllers/api/videos/ownership.ts b/server/controllers/api/videos/ownership.ts index 5272c1385..abb34082e 100644 --- a/server/controllers/api/videos/ownership.ts +++ b/server/controllers/api/videos/ownership.ts | |||
@@ -18,6 +18,7 @@ import { getFormattedObjects } from '../../../helpers/utils' | |||
18 | import { changeVideoChannelShare } from '../../../lib/activitypub' | 18 | import { changeVideoChannelShare } from '../../../lib/activitypub' |
19 | import { sendUpdateVideo } from '../../../lib/activitypub/send' | 19 | import { sendUpdateVideo } from '../../../lib/activitypub/send' |
20 | import { VideoModel } from '../../../models/video/video' | 20 | import { VideoModel } from '../../../models/video/video' |
21 | import { MVideoFullLight } from '@server/typings/models' | ||
21 | 22 | ||
22 | const ownershipVideoRouter = express.Router() | 23 | const ownershipVideoRouter = express.Router() |
23 | 24 | ||
@@ -56,7 +57,7 @@ export { | |||
56 | // --------------------------------------------------------------------------- | 57 | // --------------------------------------------------------------------------- |
57 | 58 | ||
58 | async function giveVideoOwnership (req: express.Request, res: express.Response) { | 59 | async function giveVideoOwnership (req: express.Request, res: express.Response) { |
59 | const videoInstance = res.locals.video | 60 | const videoInstance = res.locals.videoAll |
60 | const initiatorAccountId = res.locals.oauth.token.User.Account.id | 61 | const initiatorAccountId = res.locals.oauth.token.User.Account.id |
61 | const nextOwner = res.locals.nextOwner | 62 | const nextOwner = res.locals.nextOwner |
62 | 63 | ||
@@ -107,7 +108,7 @@ async function acceptOwnership (req: express.Request, res: express.Response) { | |||
107 | 108 | ||
108 | targetVideo.channelId = channel.id | 109 | targetVideo.channelId = channel.id |
109 | 110 | ||
110 | const targetVideoUpdated = await targetVideo.save({ transaction: t }) | 111 | const targetVideoUpdated = await targetVideo.save({ transaction: t }) as MVideoFullLight |
111 | targetVideoUpdated.VideoChannel = channel | 112 | targetVideoUpdated.VideoChannel = channel |
112 | 113 | ||
113 | if (targetVideoUpdated.privacy !== VideoPrivacy.PRIVATE && targetVideoUpdated.state === VideoState.PUBLISHED) { | 114 | if (targetVideoUpdated.privacy !== VideoPrivacy.PRIVATE && targetVideoUpdated.state === VideoState.PUBLISHED) { |
diff --git a/server/controllers/api/videos/rate.ts b/server/controllers/api/videos/rate.ts index b65babedf..3d2f3d728 100644 --- a/server/controllers/api/videos/rate.ts +++ b/server/controllers/api/videos/rate.ts | |||
@@ -27,7 +27,7 @@ export { | |||
27 | async function rateVideo (req: express.Request, res: express.Response) { | 27 | async function rateVideo (req: express.Request, res: express.Response) { |
28 | const body: UserVideoRateUpdate = req.body | 28 | const body: UserVideoRateUpdate = req.body |
29 | const rateType = body.rating | 29 | const rateType = body.rating |
30 | const videoInstance = res.locals.video | 30 | const videoInstance = res.locals.videoAll |
31 | const userAccount = res.locals.oauth.token.User.Account | 31 | const userAccount = res.locals.oauth.token.User.Account |
32 | 32 | ||
33 | await sequelizeTypescript.transaction(async t => { | 33 | await sequelizeTypescript.transaction(async t => { |
diff --git a/server/controllers/api/videos/watching.ts b/server/controllers/api/videos/watching.ts index dcd1f070d..036e16f3a 100644 --- a/server/controllers/api/videos/watching.ts +++ b/server/controllers/api/videos/watching.ts | |||
@@ -23,7 +23,7 @@ async function userWatchVideo (req: express.Request, res: express.Response) { | |||
23 | const user = res.locals.oauth.token.User | 23 | const user = res.locals.oauth.token.User |
24 | 24 | ||
25 | const body: UserWatchingVideo = req.body | 25 | const body: UserWatchingVideo = req.body |
26 | const { id: videoId } = res.locals.video as { id: number } | 26 | const { id: videoId } = res.locals.videoId |
27 | 27 | ||
28 | await UserVideoHistoryModel.upsert({ | 28 | await UserVideoHistoryModel.upsert({ |
29 | videoId, | 29 | videoId, |
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts index d3f581615..468f7a668 100644 --- a/server/controllers/feeds.ts +++ b/server/controllers/feeds.ts | |||
@@ -43,7 +43,7 @@ export { | |||
43 | async function generateVideoCommentsFeed (req: express.Request, res: express.Response) { | 43 | async function generateVideoCommentsFeed (req: express.Request, res: express.Response) { |
44 | const start = 0 | 44 | const start = 0 |
45 | 45 | ||
46 | const video = res.locals.video | 46 | const video = res.locals.videoAll |
47 | const videoId: number = video ? video.id : undefined | 47 | const videoId: number = video ? video.id : undefined |
48 | 48 | ||
49 | const comments = await VideoCommentModel.listForFeed(start, FEEDS.COUNT, videoId) | 49 | const comments = await VideoCommentModel.listForFeed(start, FEEDS.COUNT, videoId) |
diff --git a/server/controllers/services.ts b/server/controllers/services.ts index c1c53c3fc..ec057235f 100644 --- a/server/controllers/services.ts +++ b/server/controllers/services.ts | |||
@@ -23,7 +23,7 @@ export { | |||
23 | // --------------------------------------------------------------------------- | 23 | // --------------------------------------------------------------------------- |
24 | 24 | ||
25 | function generateOEmbed (req: express.Request, res: express.Response) { | 25 | function generateOEmbed (req: express.Request, res: express.Response) { |
26 | const video = res.locals.video | 26 | const video = res.locals.videoAll |
27 | const webserverUrl = WEBSERVER.URL | 27 | const webserverUrl = WEBSERVER.URL |
28 | const maxHeight = parseInt(req.query.maxheight, 10) | 28 | const maxHeight = parseInt(req.query.maxheight, 10) |
29 | const maxWidth = parseInt(req.query.maxwidth, 10) | 29 | const maxWidth = parseInt(req.query.maxwidth, 10) |
diff --git a/server/controllers/static.ts b/server/controllers/static.ts index 8979ef5f3..0f4772310 100644 --- a/server/controllers/static.ts +++ b/server/controllers/static.ts | |||
@@ -226,14 +226,14 @@ async function generateNodeinfo (req: express.Request, res: express.Response) { | |||
226 | return res.send(json).end() | 226 | return res.send(json).end() |
227 | } | 227 | } |
228 | 228 | ||
229 | async function downloadTorrent (req: express.Request, res: express.Response, next: express.NextFunction) { | 229 | async function downloadTorrent (req: express.Request, res: express.Response) { |
230 | const { video, videoFile } = getVideoAndFile(req, res) | 230 | const { video, videoFile } = getVideoAndFile(req, res) |
231 | if (!videoFile) return res.status(404).end() | 231 | if (!videoFile) return res.status(404).end() |
232 | 232 | ||
233 | return res.download(video.getTorrentFilePath(videoFile), `${video.name}-${videoFile.resolution}p.torrent`) | 233 | return res.download(video.getTorrentFilePath(videoFile), `${video.name}-${videoFile.resolution}p.torrent`) |
234 | } | 234 | } |
235 | 235 | ||
236 | async function downloadVideoFile (req: express.Request, res: express.Response, next: express.NextFunction) { | 236 | async function downloadVideoFile (req: express.Request, res: express.Response) { |
237 | const { video, videoFile } = getVideoAndFile(req, res) | 237 | const { video, videoFile } = getVideoAndFile(req, res) |
238 | if (!videoFile) return res.status(404).end() | 238 | if (!videoFile) return res.status(404).end() |
239 | 239 | ||
@@ -242,7 +242,7 @@ async function downloadVideoFile (req: express.Request, res: express.Response, n | |||
242 | 242 | ||
243 | function getVideoAndFile (req: express.Request, res: express.Response) { | 243 | function getVideoAndFile (req: express.Request, res: express.Response) { |
244 | const resolution = parseInt(req.params.resolution, 10) | 244 | const resolution = parseInt(req.params.resolution, 10) |
245 | const video = res.locals.video | 245 | const video = res.locals.videoAll |
246 | 246 | ||
247 | const videoFile = video.VideoFiles.find(f => f.resolution === resolution) | 247 | const videoFile = video.VideoFiles.find(f => f.resolution === resolution) |
248 | 248 | ||
diff --git a/server/controllers/webfinger.ts b/server/controllers/webfinger.ts index f2ba3c826..fc9575160 100644 --- a/server/controllers/webfinger.ts +++ b/server/controllers/webfinger.ts | |||
@@ -18,7 +18,7 @@ export { | |||
18 | // --------------------------------------------------------------------------- | 18 | // --------------------------------------------------------------------------- |
19 | 19 | ||
20 | function webfingerController (req: express.Request, res: express.Response) { | 20 | function webfingerController (req: express.Request, res: express.Response) { |
21 | const actor = res.locals.actor | 21 | const actor = res.locals.actorFull |
22 | 22 | ||
23 | const json = { | 23 | const json = { |
24 | subject: req.query.resource, | 24 | subject: req.query.resource, |
diff --git a/server/helpers/activitypub.ts b/server/helpers/activitypub.ts index 951a25669..97c809a0c 100644 --- a/server/helpers/activitypub.ts +++ b/server/helpers/activitypub.ts | |||
@@ -7,6 +7,7 @@ import { ActorModel } from '../models/activitypub/actor' | |||
7 | import { signJsonLDObject } from './peertube-crypto' | 7 | import { signJsonLDObject } from './peertube-crypto' |
8 | import { pageToStartAndCount } from './core-utils' | 8 | import { pageToStartAndCount } from './core-utils' |
9 | import { parse } from 'url' | 9 | import { parse } from 'url' |
10 | import { MActor } from '../typings/models' | ||
10 | 11 | ||
11 | function activityPubContextify <T> (data: T) { | 12 | function activityPubContextify <T> (data: T) { |
12 | return Object.assign(data, { | 13 | return Object.assign(data, { |
@@ -143,7 +144,7 @@ async function activityPubCollectionPagination (baseUrl: string, handler: Activi | |||
143 | 144 | ||
144 | } | 145 | } |
145 | 146 | ||
146 | function buildSignedActivity (byActor: ActorModel, data: Object) { | 147 | function buildSignedActivity (byActor: MActor, data: Object) { |
147 | const activity = activityPubContextify(data) | 148 | const activity = activityPubContextify(data) |
148 | 149 | ||
149 | return signJsonLDObject(byActor, activity) as Promise<Activity> | 150 | return signJsonLDObject(byActor, activity) as Promise<Activity> |
diff --git a/server/helpers/actor.ts b/server/helpers/actor.ts index 12a7ace9f..117548a60 100644 --- a/server/helpers/actor.ts +++ b/server/helpers/actor.ts | |||
@@ -1,10 +1,13 @@ | |||
1 | import { ActorModel } from '../models/activitypub/actor' | 1 | import { ActorModel } from '../models/activitypub/actor' |
2 | import * as Bluebird from 'bluebird' | ||
3 | import { MActorFull, MActorAccountChannelId } from '../typings/models' | ||
2 | 4 | ||
3 | type ActorFetchByUrlType = 'all' | 'actor-and-association-ids' | 5 | type ActorFetchByUrlType = 'all' | 'association-ids' |
4 | function fetchActorByUrl (url: string, fetchType: ActorFetchByUrlType) { | 6 | |
7 | function fetchActorByUrl (url: string, fetchType: ActorFetchByUrlType): Bluebird<MActorFull | MActorAccountChannelId> { | ||
5 | if (fetchType === 'all') return ActorModel.loadByUrlAndPopulateAccountAndChannel(url) | 8 | if (fetchType === 'all') return ActorModel.loadByUrlAndPopulateAccountAndChannel(url) |
6 | 9 | ||
7 | if (fetchType === 'actor-and-association-ids') return ActorModel.loadByUrl(url) | 10 | if (fetchType === 'association-ids') return ActorModel.loadByUrl(url) |
8 | } | 11 | } |
9 | 12 | ||
10 | export { | 13 | export { |
diff --git a/server/helpers/captions-utils.ts b/server/helpers/captions-utils.ts index 7174d4654..2830ae017 100644 --- a/server/helpers/captions-utils.ts +++ b/server/helpers/captions-utils.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | import { join } from 'path' | 1 | import { join } from 'path' |
2 | import { CONFIG } from '../initializers/config' | 2 | import { CONFIG } from '../initializers/config' |
3 | import { VideoCaptionModel } from '../models/video/video-caption' | ||
4 | import * as srt2vtt from 'srt-to-vtt' | 3 | import * as srt2vtt from 'srt-to-vtt' |
5 | import { createReadStream, createWriteStream, remove, move } from 'fs-extra' | 4 | import { createReadStream, createWriteStream, move, remove } from 'fs-extra' |
5 | import { MVideoCaptionFormattable } from '@server/typings/models' | ||
6 | 6 | ||
7 | async function moveAndProcessCaptionFile (physicalFile: { filename: string, path: string }, videoCaption: VideoCaptionModel) { | 7 | async function moveAndProcessCaptionFile (physicalFile: { filename: string, path: string }, videoCaption: MVideoCaptionFormattable) { |
8 | const videoCaptionsDir = CONFIG.STORAGE.CAPTIONS_DIR | 8 | const videoCaptionsDir = CONFIG.STORAGE.CAPTIONS_DIR |
9 | const destination = join(videoCaptionsDir, videoCaption.getCaptionName()) | 9 | const destination = join(videoCaptionsDir, videoCaption.getCaptionName()) |
10 | 10 | ||
diff --git a/server/helpers/custom-jsonld-signature.ts b/server/helpers/custom-jsonld-signature.ts index a3bceb047..cb07fa3b2 100644 --- a/server/helpers/custom-jsonld-signature.ts +++ b/server/helpers/custom-jsonld-signature.ts | |||
@@ -1,6 +1,5 @@ | |||
1 | import * as AsyncLRU from 'async-lru' | 1 | import * as AsyncLRU from 'async-lru' |
2 | import * as jsonld from 'jsonld' | 2 | import * as jsonld from 'jsonld' |
3 | import * as jsig from 'jsonld-signatures' | ||
4 | import { logger } from './logger' | 3 | import { logger } from './logger' |
5 | 4 | ||
6 | const CACHE = { | 5 | const CACHE = { |
@@ -79,6 +78,4 @@ jsonld.documentLoader = (url, cb) => { | |||
79 | lru.get(url, cb) | 78 | lru.get(url, cb) |
80 | } | 79 | } |
81 | 80 | ||
82 | jsig.use('jsonld', jsonld) | 81 | export { jsonld } |
83 | |||
84 | export { jsig, jsonld } | ||
diff --git a/server/helpers/custom-validators/activitypub/actor.ts b/server/helpers/custom-validators/activitypub/actor.ts index deb331abb..55bc8cc96 100644 --- a/server/helpers/custom-validators/activitypub/actor.ts +++ b/server/helpers/custom-validators/activitypub/actor.ts | |||
@@ -27,7 +27,7 @@ function isActorPublicKeyValid (publicKey: string) { | |||
27 | validator.isLength(publicKey, CONSTRAINTS_FIELDS.ACTORS.PUBLIC_KEY) | 27 | validator.isLength(publicKey, CONSTRAINTS_FIELDS.ACTORS.PUBLIC_KEY) |
28 | } | 28 | } |
29 | 29 | ||
30 | const actorNameAlphabet = '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\\-_.]' | 30 | const actorNameAlphabet = '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\\-_.:]' |
31 | const actorNameRegExp = new RegExp(`^${actorNameAlphabet}+$`) | 31 | const actorNameRegExp = new RegExp(`^${actorNameAlphabet}+$`) |
32 | function isActorPreferredUsernameValid (preferredUsername: string) { | 32 | function isActorPreferredUsernameValid (preferredUsername: string) { |
33 | return exists(preferredUsername) && validator.matches(preferredUsername, actorNameRegExp) | 33 | return exists(preferredUsername) && validator.matches(preferredUsername, actorNameRegExp) |
@@ -46,19 +46,20 @@ function isActorObjectValid (actor: any) { | |||
46 | return exists(actor) && | 46 | return exists(actor) && |
47 | isActivityPubUrlValid(actor.id) && | 47 | isActivityPubUrlValid(actor.id) && |
48 | isActorTypeValid(actor.type) && | 48 | isActorTypeValid(actor.type) && |
49 | isActivityPubUrlValid(actor.following) && | ||
50 | isActivityPubUrlValid(actor.followers) && | ||
51 | isActivityPubUrlValid(actor.inbox) && | 49 | isActivityPubUrlValid(actor.inbox) && |
52 | isActivityPubUrlValid(actor.outbox) && | ||
53 | isActorPreferredUsernameValid(actor.preferredUsername) && | 50 | isActorPreferredUsernameValid(actor.preferredUsername) && |
54 | isActivityPubUrlValid(actor.url) && | 51 | isActivityPubUrlValid(actor.url) && |
55 | isActorPublicKeyObjectValid(actor.publicKey) && | 52 | isActorPublicKeyObjectValid(actor.publicKey) && |
56 | isActorEndpointsObjectValid(actor.endpoints) && | 53 | isActorEndpointsObjectValid(actor.endpoints) && |
57 | setValidAttributedTo(actor) && | ||
58 | 54 | ||
59 | // If this is not an account, it should be attributed to an account | 55 | (!actor.outbox || isActivityPubUrlValid(actor.outbox)) && |
56 | (!actor.following || isActivityPubUrlValid(actor.following)) && | ||
57 | (!actor.followers || isActivityPubUrlValid(actor.followers)) && | ||
58 | |||
59 | setValidAttributedTo(actor) && | ||
60 | // If this is a group (a channel), it should be attributed to an account | ||
60 | // In PeerTube we use this to attach a video channel to a specific account | 61 | // In PeerTube we use this to attach a video channel to a specific account |
61 | (actor.type === 'Person' || actor.attributedTo.length !== 0) | 62 | (actor.type !== 'Group' || actor.attributedTo.length !== 0) |
62 | } | 63 | } |
63 | 64 | ||
64 | function isActorFollowingCountValid (value: string) { | 65 | function isActorFollowingCountValid (value: string) { |
diff --git a/server/helpers/custom-validators/users.ts b/server/helpers/custom-validators/users.ts index c56ae14ef..68e84d9eb 100644 --- a/server/helpers/custom-validators/users.ts +++ b/server/helpers/custom-validators/users.ts | |||
@@ -65,6 +65,14 @@ function isUserBlockedValid (value: any) { | |||
65 | return isBooleanValid(value) | 65 | return isBooleanValid(value) |
66 | } | 66 | } |
67 | 67 | ||
68 | function isNoInstanceConfigWarningModal (value: any) { | ||
69 | return isBooleanValid(value) | ||
70 | } | ||
71 | |||
72 | function isNoWelcomeModal (value: any) { | ||
73 | return isBooleanValid(value) | ||
74 | } | ||
75 | |||
68 | function isUserBlockedReasonValid (value: any) { | 76 | function isUserBlockedReasonValid (value: any) { |
69 | return value === null || (exists(value) && validator.isLength(value, CONSTRAINTS_FIELDS.USERS.BLOCKED_REASON)) | 77 | return value === null || (exists(value) && validator.isLength(value, CONSTRAINTS_FIELDS.USERS.BLOCKED_REASON)) |
70 | } | 78 | } |
@@ -100,5 +108,7 @@ export { | |||
100 | isUserAutoPlayVideoValid, | 108 | isUserAutoPlayVideoValid, |
101 | isUserDisplayNameValid, | 109 | isUserDisplayNameValid, |
102 | isUserDescriptionValid, | 110 | isUserDescriptionValid, |
111 | isNoInstanceConfigWarningModal, | ||
112 | isNoWelcomeModal, | ||
103 | isAvatarFile | 113 | isAvatarFile |
104 | } | 114 | } |
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 @@ | |||
1 | import { Response } from 'express' | 1 | import { Response } from 'express' |
2 | import * as validator from 'validator' | ||
3 | import { VideoChangeOwnershipModel } from '../../models/video/video-change-ownership' | 2 | import { VideoChangeOwnershipModel } from '../../models/video/video-change-ownership' |
4 | import { UserModel } from '../../models/account/user' | 3 | import { MVideoChangeOwnershipFull } from '@server/typings/models/video/video-change-ownership' |
4 | import { MUserId } from '@server/typings/models' | ||
5 | 5 | ||
6 | export async function doesChangeVideoOwnershipExist (id: string, res: Response): Promise<boolean> { | 6 | export async function doesChangeVideoOwnershipExist (id: number, res: Response) { |
7 | const videoChangeOwnership = await loadVideoChangeOwnership(id) | 7 | const videoChangeOwnership = await VideoChangeOwnershipModel.load(id) |
8 | 8 | ||
9 | if (!videoChangeOwnership) { | 9 | if (!videoChangeOwnership) { |
10 | res.status(404) | 10 | res.status(404) |
@@ -18,19 +18,7 @@ export async function doesChangeVideoOwnershipExist (id: string, res: Response): | |||
18 | return true | 18 | return true |
19 | } | 19 | } |
20 | 20 | ||
21 | async function loadVideoChangeOwnership (id: string): Promise<VideoChangeOwnershipModel | undefined> { | 21 | export function checkUserCanTerminateOwnershipChange (user: MUserId, videoChangeOwnership: MVideoChangeOwnershipFull, res: Response) { |
22 | if (validator.isInt(id)) { | ||
23 | return VideoChangeOwnershipModel.load(parseInt(id, 10)) | ||
24 | } | ||
25 | |||
26 | return undefined | ||
27 | } | ||
28 | |||
29 | export function checkUserCanTerminateOwnershipChange ( | ||
30 | user: UserModel, | ||
31 | videoChangeOwnership: VideoChangeOwnershipModel, | ||
32 | res: Response | ||
33 | ): boolean { | ||
34 | if (videoChangeOwnership.NextOwner.userId === user.id) { | 22 | if (videoChangeOwnership.NextOwner.userId === user.id) { |
35 | return true | 23 | return true |
36 | } | 24 | } |
diff --git a/server/helpers/middlewares/accounts.ts b/server/helpers/middlewares/accounts.ts index 791022b97..f5aa0bada 100644 --- a/server/helpers/middlewares/accounts.ts +++ b/server/helpers/middlewares/accounts.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { Response } from 'express' | 1 | import { Response } from 'express' |
2 | import { AccountModel } from '../../models/account/account' | 2 | import { AccountModel } from '../../models/account/account' |
3 | import * as Bluebird from 'bluebird' | 3 | import * as Bluebird from 'bluebird' |
4 | import { MAccountDefault } from '../../typings/models' | ||
4 | 5 | ||
5 | function doesAccountIdExist (id: number, res: Response, sendNotFound = true) { | 6 | function doesAccountIdExist (id: number, res: Response, sendNotFound = true) { |
6 | const promise = AccountModel.load(id) | 7 | const promise = AccountModel.load(id) |
@@ -15,10 +16,12 @@ function doesLocalAccountNameExist (name: string, res: Response, sendNotFound = | |||
15 | } | 16 | } |
16 | 17 | ||
17 | function doesAccountNameWithHostExist (nameWithDomain: string, res: Response, sendNotFound = true) { | 18 | function doesAccountNameWithHostExist (nameWithDomain: string, res: Response, sendNotFound = true) { |
18 | return doesAccountExist(AccountModel.loadByNameWithHost(nameWithDomain), res, sendNotFound) | 19 | const promise = AccountModel.loadByNameWithHost(nameWithDomain) |
20 | |||
21 | return doesAccountExist(promise, res, sendNotFound) | ||
19 | } | 22 | } |
20 | 23 | ||
21 | async function doesAccountExist (p: Bluebird<AccountModel>, res: Response, sendNotFound: boolean) { | 24 | async function doesAccountExist (p: Bluebird<MAccountDefault>, res: Response, sendNotFound: boolean) { |
22 | const account = await p | 25 | const account = await p |
23 | 26 | ||
24 | if (!account) { | 27 | if (!account) { |
diff --git a/server/helpers/middlewares/video-abuses.ts b/server/helpers/middlewares/video-abuses.ts index b23f1f021..1b573ca37 100644 --- a/server/helpers/middlewares/video-abuses.ts +++ b/server/helpers/middlewares/video-abuses.ts | |||
@@ -1,41 +1,23 @@ | |||
1 | import * as express from 'express' | 1 | import { Response } from 'express' |
2 | import { VideoChannelModel } from '../../models/video/video-channel' | 2 | import { VideoAbuseModel } from '../../models/video/video-abuse' |
3 | 3 | ||
4 | async function doesLocalVideoChannelNameExist (name: string, res: express.Response) { | 4 | async function doesVideoAbuseExist (abuseId: number, videoId: number, res: Response) { |
5 | const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name) | 5 | const videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, videoId) |
6 | 6 | ||
7 | return processVideoChannelExist(videoChannel, res) | 7 | if (videoAbuse === null) { |
8 | } | ||
9 | |||
10 | async function doesVideoChannelIdExist (id: number, res: express.Response) { | ||
11 | const videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id) | ||
12 | |||
13 | return processVideoChannelExist(videoChannel, res) | ||
14 | } | ||
15 | |||
16 | async function doesVideoChannelNameWithHostExist (nameWithDomain: string, res: express.Response) { | ||
17 | const videoChannel = await VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithDomain) | ||
18 | |||
19 | return processVideoChannelExist(videoChannel, res) | ||
20 | } | ||
21 | |||
22 | // --------------------------------------------------------------------------- | ||
23 | |||
24 | export { | ||
25 | doesLocalVideoChannelNameExist, | ||
26 | doesVideoChannelIdExist, | ||
27 | doesVideoChannelNameWithHostExist | ||
28 | } | ||
29 | |||
30 | function processVideoChannelExist (videoChannel: VideoChannelModel, res: express.Response) { | ||
31 | if (!videoChannel) { | ||
32 | res.status(404) | 8 | res.status(404) |
33 | .json({ error: 'Video channel not found' }) | 9 | .json({ error: 'Video abuse not found' }) |
34 | .end() | 10 | .end() |
35 | 11 | ||
36 | return false | 12 | return false |
37 | } | 13 | } |
38 | 14 | ||
39 | res.locals.videoChannel = videoChannel | 15 | res.locals.videoAbuse = videoAbuse |
40 | return true | 16 | return true |
41 | } | 17 | } |
18 | |||
19 | // --------------------------------------------------------------------------- | ||
20 | |||
21 | export { | ||
22 | doesVideoAbuseExist | ||
23 | } | ||
diff --git a/server/helpers/middlewares/video-captions.ts b/server/helpers/middlewares/video-captions.ts index dc3d0144b..1b2513b60 100644 --- a/server/helpers/middlewares/video-captions.ts +++ b/server/helpers/middlewares/video-captions.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | import { VideoModel } from '../../models/video/video' | ||
2 | import { Response } from 'express' | 1 | import { Response } from 'express' |
3 | import { VideoCaptionModel } from '../../models/video/video-caption' | 2 | import { VideoCaptionModel } from '../../models/video/video-caption' |
3 | import { MVideoId } from '@server/typings/models' | ||
4 | 4 | ||
5 | async function doesVideoCaptionExist (video: VideoModel, language: string, res: Response) { | 5 | async function doesVideoCaptionExist (video: MVideoId, language: string, res: Response) { |
6 | const videoCaption = await VideoCaptionModel.loadByVideoIdAndLanguage(video.id, language) | 6 | const videoCaption = await VideoCaptionModel.loadByVideoIdAndLanguage(video.id, language) |
7 | 7 | ||
8 | if (!videoCaption) { | 8 | if (!videoCaption) { |
diff --git a/server/helpers/middlewares/video-channels.ts b/server/helpers/middlewares/video-channels.ts index 1b573ca37..1595ecd94 100644 --- a/server/helpers/middlewares/video-channels.ts +++ b/server/helpers/middlewares/video-channels.ts | |||
@@ -1,23 +1,42 @@ | |||
1 | import { Response } from 'express' | 1 | import * as express from 'express' |
2 | import { VideoAbuseModel } from '../../models/video/video-abuse' | 2 | import { VideoChannelModel } from '../../models/video/video-channel' |
3 | import { MChannelAccountDefault } from '@server/typings/models' | ||
3 | 4 | ||
4 | async function doesVideoAbuseExist (abuseId: number, videoId: number, res: Response) { | 5 | async function doesLocalVideoChannelNameExist (name: string, res: express.Response) { |
5 | const videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, videoId) | 6 | const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name) |
6 | 7 | ||
7 | if (videoAbuse === null) { | 8 | return processVideoChannelExist(videoChannel, res) |
8 | res.status(404) | 9 | } |
9 | .json({ error: 'Video abuse not found' }) | ||
10 | .end() | ||
11 | 10 | ||
12 | return false | 11 | async function doesVideoChannelIdExist (id: number, res: express.Response) { |
13 | } | 12 | const videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id) |
14 | 13 | ||
15 | res.locals.videoAbuse = videoAbuse | 14 | return processVideoChannelExist(videoChannel, res) |
16 | return true | 15 | } |
16 | |||
17 | async function doesVideoChannelNameWithHostExist (nameWithDomain: string, res: express.Response) { | ||
18 | const videoChannel = await VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithDomain) | ||
19 | |||
20 | return processVideoChannelExist(videoChannel, res) | ||
17 | } | 21 | } |
18 | 22 | ||
19 | // --------------------------------------------------------------------------- | 23 | // --------------------------------------------------------------------------- |
20 | 24 | ||
21 | export { | 25 | export { |
22 | doesVideoAbuseExist | 26 | doesLocalVideoChannelNameExist, |
27 | doesVideoChannelIdExist, | ||
28 | doesVideoChannelNameWithHostExist | ||
29 | } | ||
30 | |||
31 | function processVideoChannelExist (videoChannel: MChannelAccountDefault, res: express.Response) { | ||
32 | if (!videoChannel) { | ||
33 | res.status(404) | ||
34 | .json({ error: 'Video channel not found' }) | ||
35 | .end() | ||
36 | |||
37 | return false | ||
38 | } | ||
39 | |||
40 | res.locals.videoChannel = videoChannel | ||
41 | return true | ||
23 | } | 42 | } |
diff --git a/server/helpers/middlewares/video-playlists.ts b/server/helpers/middlewares/video-playlists.ts index 735bf362f..8e7484483 100644 --- a/server/helpers/middlewares/video-playlists.ts +++ b/server/helpers/middlewares/video-playlists.ts | |||
@@ -1,11 +1,31 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { VideoPlaylistModel } from '../../models/video/video-playlist' | 2 | import { VideoPlaylistModel } from '../../models/video/video-playlist' |
3 | import { MVideoPlaylist } from '../../typings/models/video/video-playlist' | ||
3 | 4 | ||
4 | async function doesVideoPlaylistExist (id: number | string, res: express.Response, fetchType: 'summary' | 'all' = 'summary') { | 5 | export type VideoPlaylistFetchType = 'summary' | 'all' |
5 | const videoPlaylist = fetchType === 'summary' | 6 | async function doesVideoPlaylistExist (id: number | string, res: express.Response, fetchType: VideoPlaylistFetchType = 'summary') { |
6 | ? await VideoPlaylistModel.loadWithAccountAndChannelSummary(id, undefined) | 7 | if (fetchType === 'summary') { |
7 | : await VideoPlaylistModel.loadWithAccountAndChannel(id, undefined) | 8 | const videoPlaylist = await VideoPlaylistModel.loadWithAccountAndChannelSummary(id, undefined) |
9 | res.locals.videoPlaylistSummary = videoPlaylist | ||
8 | 10 | ||
11 | return handleVideoPlaylist(videoPlaylist, res) | ||
12 | } | ||
13 | |||
14 | const videoPlaylist = await VideoPlaylistModel.loadWithAccountAndChannel(id, undefined) | ||
15 | res.locals.videoPlaylistFull = videoPlaylist | ||
16 | |||
17 | return handleVideoPlaylist(videoPlaylist, res) | ||
18 | } | ||
19 | |||
20 | // --------------------------------------------------------------------------- | ||
21 | |||
22 | export { | ||
23 | doesVideoPlaylistExist | ||
24 | } | ||
25 | |||
26 | // --------------------------------------------------------------------------- | ||
27 | |||
28 | function handleVideoPlaylist (videoPlaylist: MVideoPlaylist, res: express.Response) { | ||
9 | if (!videoPlaylist) { | 29 | if (!videoPlaylist) { |
10 | res.status(404) | 30 | res.status(404) |
11 | .json({ error: 'Video playlist not found' }) | 31 | .json({ error: 'Video playlist not found' }) |
@@ -14,12 +34,5 @@ async function doesVideoPlaylistExist (id: number | string, res: express.Respons | |||
14 | return false | 34 | return false |
15 | } | 35 | } |
16 | 36 | ||
17 | res.locals.videoPlaylist = videoPlaylist | ||
18 | return true | 37 | return true |
19 | } | 38 | } |
20 | |||
21 | // --------------------------------------------------------------------------- | ||
22 | |||
23 | export { | ||
24 | doesVideoPlaylistExist | ||
25 | } | ||
diff --git a/server/helpers/middlewares/videos.ts b/server/helpers/middlewares/videos.ts index ceb1058ec..74f529804 100644 --- a/server/helpers/middlewares/videos.ts +++ b/server/helpers/middlewares/videos.ts | |||
@@ -1,9 +1,8 @@ | |||
1 | import { Response } from 'express' | 1 | import { Response } from 'express' |
2 | import { fetchVideo, VideoFetchType } from '../video' | 2 | import { fetchVideo, VideoFetchType } from '../video' |
3 | import { UserModel } from '../../models/account/user' | ||
4 | import { UserRight } from '../../../shared/models/users' | 3 | import { UserRight } from '../../../shared/models/users' |
5 | import { VideoChannelModel } from '../../models/video/video-channel' | 4 | import { VideoChannelModel } from '../../models/video/video-channel' |
6 | import { VideoModel } from '../../models/video/video' | 5 | import { MUser, MUserAccountId, MVideoAccountLight, MVideoFullLight, MVideoThumbnail, MVideoWithRights } from '@server/typings/models' |
7 | 6 | ||
8 | async function doesVideoExist (id: number | string, res: Response, fetchType: VideoFetchType = 'all') { | 7 | async function doesVideoExist (id: number | string, res: Response, fetchType: VideoFetchType = 'all') { |
9 | const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined | 8 | const userId = res.locals.oauth ? res.locals.oauth.token.User.id : undefined |
@@ -18,11 +17,28 @@ async function doesVideoExist (id: number | string, res: Response, fetchType: Vi | |||
18 | return false | 17 | return false |
19 | } | 18 | } |
20 | 19 | ||
21 | if (fetchType !== 'none') res.locals.video = video | 20 | switch (fetchType) { |
21 | case 'all': | ||
22 | res.locals.videoAll = video as MVideoFullLight | ||
23 | break | ||
24 | |||
25 | case 'id': | ||
26 | res.locals.videoId = video | ||
27 | break | ||
28 | |||
29 | case 'only-video': | ||
30 | res.locals.onlyVideo = video as MVideoThumbnail | ||
31 | break | ||
32 | |||
33 | case 'only-video-with-rights': | ||
34 | res.locals.onlyVideoWithRights = video as MVideoWithRights | ||
35 | break | ||
36 | } | ||
37 | |||
22 | return true | 38 | return true |
23 | } | 39 | } |
24 | 40 | ||
25 | async function doesVideoChannelOfAccountExist (channelId: number, user: UserModel, res: Response) { | 41 | async function doesVideoChannelOfAccountExist (channelId: number, user: MUserAccountId, res: Response) { |
26 | if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) { | 42 | if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) { |
27 | const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId) | 43 | const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId) |
28 | if (videoChannel === null) { | 44 | if (videoChannel === null) { |
@@ -50,7 +66,7 @@ async function doesVideoChannelOfAccountExist (channelId: number, user: UserMode | |||
50 | return true | 66 | return true |
51 | } | 67 | } |
52 | 68 | ||
53 | function checkUserCanManageVideo (user: UserModel, video: VideoModel, right: UserRight, res: Response) { | 69 | function checkUserCanManageVideo (user: MUser, video: MVideoAccountLight, right: UserRight, res: Response) { |
54 | // Retrieve the user who did the request | 70 | // Retrieve the user who did the request |
55 | if (video.isOwned() === false) { | 71 | if (video.isOwned() === false) { |
56 | res.status(403) | 72 | res.status(403) |
diff --git a/server/helpers/peertube-crypto.ts b/server/helpers/peertube-crypto.ts index 1424949d0..9eb782302 100644 --- a/server/helpers/peertube-crypto.ts +++ b/server/helpers/peertube-crypto.ts | |||
@@ -1,13 +1,13 @@ | |||
1 | import { Request } from 'express' | 1 | import { Request } from 'express' |
2 | import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants' | 2 | import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants' |
3 | import { ActorModel } from '../models/activitypub/actor' | ||
4 | import { createPrivateKey, getPublicKey, promisify1, promisify2, sha256 } from './core-utils' | 3 | import { createPrivateKey, getPublicKey, promisify1, promisify2, sha256 } from './core-utils' |
5 | import { jsig, jsonld } from './custom-jsonld-signature' | 4 | import { jsonld } from './custom-jsonld-signature' |
6 | import { logger } from './logger' | 5 | import { logger } from './logger' |
7 | import { cloneDeep } from 'lodash' | 6 | import { cloneDeep } from 'lodash' |
8 | import { createVerify } from 'crypto' | 7 | import { createSign, createVerify } from 'crypto' |
9 | import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils' | 8 | import { buildDigest } from '../lib/job-queue/handlers/utils/activitypub-http-utils' |
10 | import * as bcrypt from 'bcrypt' | 9 | import * as bcrypt from 'bcrypt' |
10 | import { MActor } from '../typings/models' | ||
11 | 11 | ||
12 | const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare) | 12 | const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare) |
13 | const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt) | 13 | const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt) |
@@ -46,7 +46,7 @@ function isHTTPSignatureDigestValid (rawBody: Buffer, req: Request): boolean { | |||
46 | return true | 46 | return true |
47 | } | 47 | } |
48 | 48 | ||
49 | function isHTTPSignatureVerified (httpSignatureParsed: any, actor: ActorModel): boolean { | 49 | function isHTTPSignatureVerified (httpSignatureParsed: any, actor: MActor): boolean { |
50 | return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true | 50 | return httpSignature.verifySignature(httpSignatureParsed, actor.publicKey) === true |
51 | } | 51 | } |
52 | 52 | ||
@@ -56,70 +56,21 @@ function parseHTTPSignature (req: Request, clockSkew?: number) { | |||
56 | 56 | ||
57 | // JSONLD | 57 | // JSONLD |
58 | 58 | ||
59 | async function isJsonLDSignatureVerified (fromActor: ActorModel, signedDocument: any): Promise<boolean> { | 59 | function isJsonLDSignatureVerified (fromActor: MActor, signedDocument: any): Promise<boolean> { |
60 | if (signedDocument.signature.type === 'RsaSignature2017') { | 60 | if (signedDocument.signature.type === 'RsaSignature2017') { |
61 | // Mastodon algorithm | 61 | return isJsonLDRSA2017Verified(fromActor, signedDocument) |
62 | const res = await isJsonLDRSA2017Verified(fromActor, signedDocument) | ||
63 | // Success? If no, try with our library | ||
64 | if (res === true) return true | ||
65 | } | 62 | } |
66 | 63 | ||
67 | const publicKeyObject = { | 64 | logger.warn('Unknown JSON LD signature %s.', signedDocument.signature.type, signedDocument) |
68 | '@context': jsig.SECURITY_CONTEXT_URL, | ||
69 | id: fromActor.url, | ||
70 | type: 'CryptographicKey', | ||
71 | owner: fromActor.url, | ||
72 | publicKeyPem: fromActor.publicKey | ||
73 | } | ||
74 | |||
75 | const publicKeyOwnerObject = { | ||
76 | '@context': jsig.SECURITY_CONTEXT_URL, | ||
77 | id: fromActor.url, | ||
78 | publicKey: [ publicKeyObject ] | ||
79 | } | ||
80 | 65 | ||
81 | const options = { | 66 | return Promise.resolve(false) |
82 | publicKey: publicKeyObject, | ||
83 | publicKeyOwner: publicKeyOwnerObject | ||
84 | } | ||
85 | |||
86 | return jsig.promises | ||
87 | .verify(signedDocument, options) | ||
88 | .then((result: { verified: boolean }) => result.verified) | ||
89 | .catch(err => { | ||
90 | logger.error('Cannot check signature.', { err }) | ||
91 | return false | ||
92 | }) | ||
93 | } | 67 | } |
94 | 68 | ||
95 | // Backward compatibility with "other" implementations | 69 | // Backward compatibility with "other" implementations |
96 | async function isJsonLDRSA2017Verified (fromActor: ActorModel, signedDocument: any) { | 70 | async function isJsonLDRSA2017Verified (fromActor: MActor, signedDocument: any) { |
97 | function hash (obj: any): Promise<any> { | ||
98 | return jsonld.promises | ||
99 | .normalize(obj, { | ||
100 | algorithm: 'URDNA2015', | ||
101 | format: 'application/n-quads' | ||
102 | }) | ||
103 | .then(res => sha256(res)) | ||
104 | } | ||
105 | |||
106 | const signatureCopy = cloneDeep(signedDocument.signature) | ||
107 | Object.assign(signatureCopy, { | ||
108 | '@context': [ | ||
109 | 'https://w3id.org/security/v1', | ||
110 | { RsaSignature2017: 'https://w3id.org/security#RsaSignature2017' } | ||
111 | ] | ||
112 | }) | ||
113 | delete signatureCopy.type | ||
114 | delete signatureCopy.id | ||
115 | delete signatureCopy.signatureValue | ||
116 | |||
117 | const docWithoutSignature = cloneDeep(signedDocument) | ||
118 | delete docWithoutSignature.signature | ||
119 | |||
120 | const [ documentHash, optionsHash ] = await Promise.all([ | 71 | const [ documentHash, optionsHash ] = await Promise.all([ |
121 | hash(docWithoutSignature), | 72 | createDocWithoutSignatureHash(signedDocument), |
122 | hash(signatureCopy) | 73 | createSignatureHash(signedDocument.signature) |
123 | ]) | 74 | ]) |
124 | 75 | ||
125 | const toVerify = optionsHash + documentHash | 76 | const toVerify = optionsHash + documentHash |
@@ -130,14 +81,27 @@ async function isJsonLDRSA2017Verified (fromActor: ActorModel, signedDocument: a | |||
130 | return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64') | 81 | return verify.verify(fromActor.publicKey, signedDocument.signature.signatureValue, 'base64') |
131 | } | 82 | } |
132 | 83 | ||
133 | function signJsonLDObject (byActor: ActorModel, data: any) { | 84 | async function signJsonLDObject (byActor: MActor, data: any) { |
134 | const options = { | 85 | const signature = { |
135 | privateKeyPem: byActor.privateKey, | 86 | type: 'RsaSignature2017', |
136 | creator: byActor.url, | 87 | creator: byActor.url, |
137 | algorithm: 'RsaSignature2017' | 88 | created: new Date().toISOString() |
138 | } | 89 | } |
139 | 90 | ||
140 | return jsig.promises.sign(data, options) | 91 | const [ documentHash, optionsHash ] = await Promise.all([ |
92 | createDocWithoutSignatureHash(data), | ||
93 | createSignatureHash(signature) | ||
94 | ]) | ||
95 | |||
96 | const toSign = optionsHash + documentHash | ||
97 | |||
98 | const sign = createSign('RSA-SHA256') | ||
99 | sign.update(toSign, 'utf8') | ||
100 | |||
101 | const signatureValue = sign.sign(byActor.privateKey, 'base64') | ||
102 | Object.assign(signature, { signatureValue }) | ||
103 | |||
104 | return Object.assign(data, { signature }) | ||
141 | } | 105 | } |
142 | 106 | ||
143 | // --------------------------------------------------------------------------- | 107 | // --------------------------------------------------------------------------- |
@@ -154,3 +118,35 @@ export { | |||
154 | } | 118 | } |
155 | 119 | ||
156 | // --------------------------------------------------------------------------- | 120 | // --------------------------------------------------------------------------- |
121 | |||
122 | function hash (obj: any): Promise<any> { | ||
123 | return jsonld.promises | ||
124 | .normalize(obj, { | ||
125 | algorithm: 'URDNA2015', | ||
126 | format: 'application/n-quads' | ||
127 | }) | ||
128 | .then(res => sha256(res)) | ||
129 | } | ||
130 | |||
131 | function createSignatureHash (signature: any) { | ||
132 | const signatureCopy = cloneDeep(signature) | ||
133 | Object.assign(signatureCopy, { | ||
134 | '@context': [ | ||
135 | 'https://w3id.org/security/v1', | ||
136 | { RsaSignature2017: 'https://w3id.org/security#RsaSignature2017' } | ||
137 | ] | ||
138 | }) | ||
139 | |||
140 | delete signatureCopy.type | ||
141 | delete signatureCopy.id | ||
142 | delete signatureCopy.signatureValue | ||
143 | |||
144 | return hash(signatureCopy) | ||
145 | } | ||
146 | |||
147 | function createDocWithoutSignatureHash (doc: any) { | ||
148 | const docWithoutSignature = cloneDeep(doc) | ||
149 | delete docWithoutSignature.signature | ||
150 | |||
151 | return hash(docWithoutSignature) | ||
152 | } | ||
diff --git a/server/helpers/utils.ts b/server/helpers/utils.ts index 1464b1477..ba07eaaf3 100644 --- a/server/helpers/utils.ts +++ b/server/helpers/utils.ts | |||
@@ -19,7 +19,10 @@ async function generateRandomString (size: number) { | |||
19 | return raw.toString('hex') | 19 | return raw.toString('hex') |
20 | } | 20 | } |
21 | 21 | ||
22 | interface FormattableToJSON<U, V> { toFormattedJSON (args?: U): V } | 22 | interface FormattableToJSON<U, V> { |
23 | toFormattedJSON (args?: U): V | ||
24 | } | ||
25 | |||
23 | function getFormattedObjects<U, V, T extends FormattableToJSON<U, V>> (objects: T[], objectsTotal: number, formattedArg?: U) { | 26 | function getFormattedObjects<U, V, T extends FormattableToJSON<U, V>> (objects: T[], objectsTotal: number, formattedArg?: U) { |
24 | const formattedObjects = objects.map(o => o.toFormattedJSON(formattedArg)) | 27 | const formattedObjects = objects.map(o => o.toFormattedJSON(formattedArg)) |
25 | 28 | ||
diff --git a/server/helpers/video.ts b/server/helpers/video.ts index c90fe06c7..d066e2b1f 100644 --- a/server/helpers/video.ts +++ b/server/helpers/video.ts | |||
@@ -1,8 +1,30 @@ | |||
1 | import { VideoModel } from '../models/video/video' | 1 | import { VideoModel } from '../models/video/video' |
2 | import * as Bluebird from 'bluebird' | ||
3 | import { | ||
4 | MVideoAccountLightBlacklistAllFiles, | ||
5 | MVideoFullLight, | ||
6 | MVideoIdThumbnail, | ||
7 | MVideoThumbnail, | ||
8 | MVideoWithRights | ||
9 | } from '@server/typings/models' | ||
10 | import { Response } from 'express' | ||
2 | 11 | ||
3 | type VideoFetchType = 'all' | 'only-video' | 'only-video-with-rights' | 'id' | 'none' | 12 | type VideoFetchType = 'all' | 'only-video' | 'only-video-with-rights' | 'id' | 'none' |
4 | 13 | ||
5 | function fetchVideo (id: number | string, fetchType: VideoFetchType, userId?: number) { | 14 | function fetchVideo (id: number | string, fetchType: 'all', userId?: number): Bluebird<MVideoFullLight> |
15 | function fetchVideo (id: number | string, fetchType: 'only-video', userId?: number): Bluebird<MVideoThumbnail> | ||
16 | function fetchVideo (id: number | string, fetchType: 'only-video-with-rights', userId?: number): Bluebird<MVideoWithRights> | ||
17 | function fetchVideo (id: number | string, fetchType: 'id' | 'none', userId?: number): Bluebird<MVideoIdThumbnail> | ||
18 | function fetchVideo ( | ||
19 | id: number | string, | ||
20 | fetchType: VideoFetchType, | ||
21 | userId?: number | ||
22 | ): Bluebird<MVideoFullLight | MVideoThumbnail | MVideoWithRights | MVideoIdThumbnail> | ||
23 | function fetchVideo ( | ||
24 | id: number | string, | ||
25 | fetchType: VideoFetchType, | ||
26 | userId?: number | ||
27 | ): Bluebird<MVideoFullLight | MVideoThumbnail | MVideoWithRights | MVideoIdThumbnail> { | ||
6 | if (fetchType === 'all') return VideoModel.loadAndPopulateAccountAndServerAndTags(id, undefined, userId) | 28 | if (fetchType === 'all') return VideoModel.loadAndPopulateAccountAndServerAndTags(id, undefined, userId) |
7 | 29 | ||
8 | if (fetchType === 'only-video-with-rights') return VideoModel.loadWithRights(id) | 30 | if (fetchType === 'only-video-with-rights') return VideoModel.loadWithRights(id) |
@@ -13,15 +35,29 @@ function fetchVideo (id: number | string, fetchType: VideoFetchType, userId?: nu | |||
13 | } | 35 | } |
14 | 36 | ||
15 | type VideoFetchByUrlType = 'all' | 'only-video' | 37 | type VideoFetchByUrlType = 'all' | 'only-video' |
16 | function fetchVideoByUrl (url: string, fetchType: VideoFetchByUrlType) { | 38 | |
39 | function fetchVideoByUrl (url: string, fetchType: 'all'): Bluebird<MVideoAccountLightBlacklistAllFiles> | ||
40 | function fetchVideoByUrl (url: string, fetchType: 'only-video'): Bluebird<MVideoThumbnail> | ||
41 | function fetchVideoByUrl (url: string, fetchType: VideoFetchByUrlType): Bluebird<MVideoAccountLightBlacklistAllFiles | MVideoThumbnail> | ||
42 | function fetchVideoByUrl (url: string, fetchType: VideoFetchByUrlType): Bluebird<MVideoAccountLightBlacklistAllFiles | MVideoThumbnail> { | ||
17 | if (fetchType === 'all') return VideoModel.loadByUrlAndPopulateAccount(url) | 43 | if (fetchType === 'all') return VideoModel.loadByUrlAndPopulateAccount(url) |
18 | 44 | ||
19 | if (fetchType === 'only-video') return VideoModel.loadByUrl(url) | 45 | if (fetchType === 'only-video') return VideoModel.loadByUrl(url) |
20 | } | 46 | } |
21 | 47 | ||
48 | function getVideo (res: Response) { | ||
49 | return res.locals.videoAll || res.locals.onlyVideo || res.locals.onlyVideoWithRights || res.locals.videoId | ||
50 | } | ||
51 | |||
52 | function getVideoWithAttributes (res: Response) { | ||
53 | return res.locals.videoAll || res.locals.onlyVideo || res.locals.onlyVideoWithRights | ||
54 | } | ||
55 | |||
22 | export { | 56 | export { |
23 | VideoFetchType, | 57 | VideoFetchType, |
24 | VideoFetchByUrlType, | 58 | VideoFetchByUrlType, |
25 | fetchVideo, | 59 | fetchVideo, |
60 | getVideo, | ||
61 | getVideoWithAttributes, | ||
26 | fetchVideoByUrl | 62 | fetchVideoByUrl |
27 | } | 63 | } |
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' | |||
4 | import { isTestInstance } from './core-utils' | 4 | import { isTestInstance } from './core-utils' |
5 | import { isActivityPubUrlValid } from './custom-validators/activitypub/misc' | 5 | import { isActivityPubUrlValid } from './custom-validators/activitypub/misc' |
6 | import { WEBSERVER } from '../initializers/constants' | 6 | import { WEBSERVER } from '../initializers/constants' |
7 | import { MActorFull } from '../typings/models' | ||
7 | 8 | ||
8 | const webfinger = new WebFinger({ | 9 | const webfinger = new WebFinger({ |
9 | webfist_fallback: false, | 10 | webfist_fallback: false, |
@@ -17,7 +18,7 @@ async function loadActorUrlOrGetFromWebfinger (uriArg: string) { | |||
17 | const uri = uriArg.startsWith('@') ? uriArg.slice(1) : uriArg | 18 | const uri = uriArg.startsWith('@') ? uriArg.slice(1) : uriArg |
18 | 19 | ||
19 | const [ name, host ] = uri.split('@') | 20 | const [ name, host ] = uri.split('@') |
20 | let actor: ActorModel | 21 | let actor: MActorFull |
21 | 22 | ||
22 | if (!host || host === WEBSERVER.HOST) { | 23 | if (!host || host === WEBSERVER.HOST) { |
23 | actor = await ActorModel.loadLocalByName(name) | 24 | actor = await ActorModel.loadLocalByName(name) |
diff --git a/server/initializers/config.ts b/server/initializers/config.ts index 510f7d64d..164d714d6 100644 --- a/server/initializers/config.ts +++ b/server/initializers/config.ts | |||
@@ -209,6 +209,19 @@ const CONFIG = { | |||
209 | get SHORT_DESCRIPTION () { return config.get<string>('instance.short_description') }, | 209 | get SHORT_DESCRIPTION () { return config.get<string>('instance.short_description') }, |
210 | get DESCRIPTION () { return config.get<string>('instance.description') }, | 210 | get DESCRIPTION () { return config.get<string>('instance.description') }, |
211 | get TERMS () { return config.get<string>('instance.terms') }, | 211 | get TERMS () { return config.get<string>('instance.terms') }, |
212 | get CODE_OF_CONDUCT () { return config.get<string>('instance.code_of_conduct') }, | ||
213 | |||
214 | get CREATION_REASON () { return config.get<string>('instance.creation_reason') }, | ||
215 | |||
216 | get MODERATION_INFORMATION () { return config.get<string>('instance.moderation_information') }, | ||
217 | get ADMINISTRATOR () { return config.get<string>('instance.administrator') }, | ||
218 | get MAINTENANCE_LIFETIME () { return config.get<string>('instance.maintenance_lifetime') }, | ||
219 | get BUSINESS_MODEL () { return config.get<string>('instance.business_model') }, | ||
220 | get HARDWARE_INFORMATION () { return config.get<string>('instance.hardware_information') }, | ||
221 | |||
222 | get LANGUAGES () { return config.get<string[]>('instance.languages') || [] }, | ||
223 | get CATEGORIES () { return config.get<number[]>('instance.categories') || [] }, | ||
224 | |||
212 | get IS_NSFW () { return config.get<boolean>('instance.is_nsfw') }, | 225 | get IS_NSFW () { return config.get<boolean>('instance.is_nsfw') }, |
213 | get DEFAULT_CLIENT_ROUTE () { return config.get<string>('instance.default_client_route') }, | 226 | get DEFAULT_CLIENT_ROUTE () { return config.get<string>('instance.default_client_route') }, |
214 | get DEFAULT_NSFW_POLICY () { return config.get<NSFWPolicyType>('instance.default_nsfw_policy') }, | 227 | get DEFAULT_NSFW_POLICY () { return config.get<NSFWPolicyType>('instance.default_nsfw_policy') }, |
@@ -232,6 +245,23 @@ const CONFIG = { | |||
232 | get MANUAL_APPROVAL () { return config.get<boolean>('followers.instance.manual_approval') } | 245 | get MANUAL_APPROVAL () { return config.get<boolean>('followers.instance.manual_approval') } |
233 | } | 246 | } |
234 | }, | 247 | }, |
248 | FOLLOWINGS: { | ||
249 | INSTANCE: { | ||
250 | AUTO_FOLLOW_BACK: { | ||
251 | get ENABLED () { | ||
252 | return config.get<boolean>('followings.instance.auto_follow_back.enabled') | ||
253 | } | ||
254 | }, | ||
255 | AUTO_FOLLOW_INDEX: { | ||
256 | get ENABLED () { | ||
257 | return config.get<boolean>('followings.instance.auto_follow_index.enabled') | ||
258 | }, | ||
259 | get INDEX_URL () { | ||
260 | return config.get<string>('followings.instance.auto_follow_index.index_url') | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | }, | ||
235 | THEME: { | 265 | THEME: { |
236 | get DEFAULT () { return config.get<string>('theme.default') } | 266 | get DEFAULT () { return config.get<string>('theme.default') } |
237 | } | 267 | } |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 3dc178b11..01d4f1d74 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -14,7 +14,7 @@ import { CONFIG, registerConfigChangedHandler } from './config' | |||
14 | 14 | ||
15 | // --------------------------------------------------------------------------- | 15 | // --------------------------------------------------------------------------- |
16 | 16 | ||
17 | const LAST_MIGRATION_VERSION = 420 | 17 | const LAST_MIGRATION_VERSION = 430 |
18 | 18 | ||
19 | // --------------------------------------------------------------------------- | 19 | // --------------------------------------------------------------------------- |
20 | 20 | ||
@@ -168,10 +168,15 @@ const SCHEDULER_INTERVALS_MS = { | |||
168 | updateVideos: 60000, // 1 minute | 168 | updateVideos: 60000, // 1 minute |
169 | youtubeDLUpdate: 60000 * 60 * 24, // 1 day | 169 | youtubeDLUpdate: 60000 * 60 * 24, // 1 day |
170 | checkPlugins: CONFIG.PLUGINS.INDEX.CHECK_LATEST_VERSIONS_INTERVAL, | 170 | checkPlugins: CONFIG.PLUGINS.INDEX.CHECK_LATEST_VERSIONS_INTERVAL, |
171 | autoFollowIndexInstances: 60000 * 60 * 24, // 1 day | ||
171 | removeOldViews: 60000 * 60 * 24, // 1 day | 172 | removeOldViews: 60000 * 60 * 24, // 1 day |
172 | removeOldHistory: 60000 * 60 * 24 // 1 day | 173 | removeOldHistory: 60000 * 60 * 24 // 1 day |
173 | } | 174 | } |
174 | 175 | ||
176 | const INSTANCES_INDEX = { | ||
177 | HOSTS_PATH: '/api/v1/instances/hosts' | ||
178 | } | ||
179 | |||
175 | // --------------------------------------------------------------------------- | 180 | // --------------------------------------------------------------------------- |
176 | 181 | ||
177 | const CONSTRAINTS_FIELDS = { | 182 | const CONSTRAINTS_FIELDS = { |
@@ -633,6 +638,7 @@ if (isTestInstance() === true) { | |||
633 | SCHEDULER_INTERVALS_MS.removeOldHistory = 5000 | 638 | SCHEDULER_INTERVALS_MS.removeOldHistory = 5000 |
634 | SCHEDULER_INTERVALS_MS.removeOldViews = 5000 | 639 | SCHEDULER_INTERVALS_MS.removeOldViews = 5000 |
635 | SCHEDULER_INTERVALS_MS.updateVideos = 5000 | 640 | SCHEDULER_INTERVALS_MS.updateVideos = 5000 |
641 | SCHEDULER_INTERVALS_MS.autoFollowIndexInstances = 5000 | ||
636 | REPEAT_JOBS[ 'videos-views' ] = { every: 5000 } | 642 | REPEAT_JOBS[ 'videos-views' ] = { every: 5000 } |
637 | 643 | ||
638 | REDUNDANCY.VIDEOS.RANDOMIZED_FACTOR = 1 | 644 | REDUNDANCY.VIDEOS.RANDOMIZED_FACTOR = 1 |
@@ -683,6 +689,7 @@ export { | |||
683 | PREVIEWS_SIZE, | 689 | PREVIEWS_SIZE, |
684 | REMOTE_SCHEME, | 690 | REMOTE_SCHEME, |
685 | FOLLOW_STATES, | 691 | FOLLOW_STATES, |
692 | INSTANCES_INDEX, | ||
686 | DEFAULT_USER_THEME_NAME, | 693 | DEFAULT_USER_THEME_NAME, |
687 | SERVER_ACTOR_NAME, | 694 | SERVER_ACTOR_NAME, |
688 | PLUGIN_GLOBAL_CSS_FILE_NAME, | 695 | PLUGIN_GLOBAL_CSS_FILE_NAME, |
diff --git a/server/initializers/migrations/0425-nullable-actor-fields.ts b/server/initializers/migrations/0425-nullable-actor-fields.ts new file mode 100644 index 000000000..4e5f9e6ab --- /dev/null +++ b/server/initializers/migrations/0425-nullable-actor-fields.ts | |||
@@ -0,0 +1,26 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction, | ||
5 | queryInterface: Sequelize.QueryInterface, | ||
6 | sequelize: Sequelize.Sequelize, | ||
7 | db: any | ||
8 | }): Promise<void> { | ||
9 | const data = { | ||
10 | type: Sequelize.STRING, | ||
11 | allowNull: true | ||
12 | } | ||
13 | |||
14 | await utils.queryInterface.changeColumn('actor', 'outboxUrl', data) | ||
15 | await utils.queryInterface.changeColumn('actor', 'followersUrl', data) | ||
16 | await utils.queryInterface.changeColumn('actor', 'followingUrl', data) | ||
17 | } | ||
18 | |||
19 | function down (options) { | ||
20 | throw new Error('Not implemented.') | ||
21 | } | ||
22 | |||
23 | export { | ||
24 | up, | ||
25 | down | ||
26 | } | ||
diff --git a/server/initializers/migrations/0425-user-modals.ts b/server/initializers/migrations/0425-user-modals.ts new file mode 100644 index 000000000..5c2aa85b5 --- /dev/null +++ b/server/initializers/migrations/0425-user-modals.ts | |||
@@ -0,0 +1,40 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction, | ||
5 | queryInterface: Sequelize.QueryInterface, | ||
6 | sequelize: Sequelize.Sequelize, | ||
7 | db: any | ||
8 | }): Promise<void> { | ||
9 | { | ||
10 | const data = { | ||
11 | type: Sequelize.BOOLEAN, | ||
12 | allowNull: false, | ||
13 | defaultValue: false | ||
14 | } | ||
15 | |||
16 | await utils.queryInterface.addColumn('user', 'noInstanceConfigWarningModal', data) | ||
17 | } | ||
18 | |||
19 | { | ||
20 | const data = { | ||
21 | type: Sequelize.BOOLEAN, | ||
22 | allowNull: false, | ||
23 | defaultValue: true | ||
24 | } | ||
25 | |||
26 | await utils.queryInterface.addColumn('user', 'noWelcomeModal', data) | ||
27 | data.defaultValue = false | ||
28 | |||
29 | await utils.queryInterface.changeColumn('user', 'noWelcomeModal', data) | ||
30 | } | ||
31 | } | ||
32 | |||
33 | function down (options) { | ||
34 | throw new Error('Not implemented.') | ||
35 | } | ||
36 | |||
37 | export { | ||
38 | up, | ||
39 | down | ||
40 | } | ||
diff --git a/server/initializers/migrations/0430-auto-follow-notification-setting.ts b/server/initializers/migrations/0430-auto-follow-notification-setting.ts new file mode 100644 index 000000000..034bdd46d --- /dev/null +++ b/server/initializers/migrations/0430-auto-follow-notification-setting.ts | |||
@@ -0,0 +1,40 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction, | ||
5 | queryInterface: Sequelize.QueryInterface, | ||
6 | sequelize: Sequelize.Sequelize, | ||
7 | db: any | ||
8 | }): Promise<void> { | ||
9 | { | ||
10 | const data = { | ||
11 | type: Sequelize.INTEGER, | ||
12 | defaultValue: null, | ||
13 | allowNull: true | ||
14 | } | ||
15 | await utils.queryInterface.addColumn('userNotificationSetting', 'autoInstanceFollowing', data) | ||
16 | } | ||
17 | |||
18 | { | ||
19 | const query = 'UPDATE "userNotificationSetting" SET "autoInstanceFollowing" = 1' | ||
20 | await utils.sequelize.query(query) | ||
21 | } | ||
22 | |||
23 | { | ||
24 | const data = { | ||
25 | type: Sequelize.INTEGER, | ||
26 | defaultValue: null, | ||
27 | allowNull: false | ||
28 | } | ||
29 | await utils.queryInterface.changeColumn('userNotificationSetting', 'autoInstanceFollowing', data) | ||
30 | } | ||
31 | } | ||
32 | |||
33 | function down (options) { | ||
34 | throw new Error('Not implemented.') | ||
35 | } | ||
36 | |||
37 | export { | ||
38 | up, | ||
39 | down | ||
40 | } | ||
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts index 9f5d12eb4..13b73077e 100644 --- a/server/lib/activitypub/actor.ts +++ b/server/lib/activitypub/actor.ts | |||
@@ -22,13 +22,27 @@ import { JobQueue } from '../job-queue' | |||
22 | import { getServerActor } from '../../helpers/utils' | 22 | import { getServerActor } from '../../helpers/utils' |
23 | import { ActorFetchByUrlType, fetchActorByUrl } from '../../helpers/actor' | 23 | import { ActorFetchByUrlType, fetchActorByUrl } from '../../helpers/actor' |
24 | import { sequelizeTypescript } from '../../initializers/database' | 24 | import { sequelizeTypescript } from '../../initializers/database' |
25 | import { | ||
26 | MAccount, | ||
27 | MAccountDefault, | ||
28 | MActor, | ||
29 | MActorAccountChannelId, | ||
30 | MActorAccountChannelIdActor, | ||
31 | MActorAccountId, | ||
32 | MActorDefault, | ||
33 | MActorFull, | ||
34 | MActorFullActor, | ||
35 | MActorId, | ||
36 | MChannel, | ||
37 | MChannelAccountDefault | ||
38 | } from '../../typings/models' | ||
25 | 39 | ||
26 | // Set account keys, this could be long so process after the account creation and do not block the client | 40 | // Set account keys, this could be long so process after the account creation and do not block the client |
27 | function setAsyncActorKeys (actor: ActorModel) { | 41 | function setAsyncActorKeys <T extends MActor> (actor: T) { |
28 | return createPrivateAndPublicKeys() | 42 | return createPrivateAndPublicKeys() |
29 | .then(({ publicKey, privateKey }) => { | 43 | .then(({ publicKey, privateKey }) => { |
30 | actor.set('publicKey', publicKey) | 44 | actor.publicKey = publicKey |
31 | actor.set('privateKey', privateKey) | 45 | actor.privateKey = privateKey |
32 | return actor.save() | 46 | return actor.save() |
33 | }) | 47 | }) |
34 | .catch(err => { | 48 | .catch(err => { |
@@ -37,12 +51,26 @@ function setAsyncActorKeys (actor: ActorModel) { | |||
37 | }) | 51 | }) |
38 | } | 52 | } |
39 | 53 | ||
54 | function getOrCreateActorAndServerAndModel ( | ||
55 | activityActor: string | ActivityPubActor, | ||
56 | fetchType: 'all', | ||
57 | recurseIfNeeded?: boolean, | ||
58 | updateCollections?: boolean | ||
59 | ): Promise<MActorFullActor> | ||
60 | |||
61 | function getOrCreateActorAndServerAndModel ( | ||
62 | activityActor: string | ActivityPubActor, | ||
63 | fetchType?: 'association-ids', | ||
64 | recurseIfNeeded?: boolean, | ||
65 | updateCollections?: boolean | ||
66 | ): Promise<MActorAccountChannelId> | ||
67 | |||
40 | async function getOrCreateActorAndServerAndModel ( | 68 | async function getOrCreateActorAndServerAndModel ( |
41 | activityActor: string | ActivityPubActor, | 69 | activityActor: string | ActivityPubActor, |
42 | fetchType: ActorFetchByUrlType = 'actor-and-association-ids', | 70 | fetchType: ActorFetchByUrlType = 'association-ids', |
43 | recurseIfNeeded = true, | 71 | recurseIfNeeded = true, |
44 | updateCollections = false | 72 | updateCollections = false |
45 | ) { | 73 | ): Promise<MActorFullActor | MActorAccountChannelId> { |
46 | const actorUrl = getAPId(activityActor) | 74 | const actorUrl = getAPId(activityActor) |
47 | let created = false | 75 | let created = false |
48 | let accountPlaylistsUrl: string | 76 | let accountPlaylistsUrl: string |
@@ -61,7 +89,7 @@ async function getOrCreateActorAndServerAndModel ( | |||
61 | 89 | ||
62 | // Create the attributed to actor | 90 | // Create the attributed to actor |
63 | // In PeerTube a video channel is owned by an account | 91 | // In PeerTube a video channel is owned by an account |
64 | let ownerActor: ActorModel = undefined | 92 | let ownerActor: MActorFullActor |
65 | if (recurseIfNeeded === true && result.actor.type === 'Group') { | 93 | if (recurseIfNeeded === true && result.actor.type === 'Group') { |
66 | const accountAttributedTo = result.attributedTo.find(a => a.type === 'Person') | 94 | const accountAttributedTo = result.attributedTo.find(a => a.type === 'Person') |
67 | if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actor.url) | 95 | if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actor.url) |
@@ -85,8 +113,8 @@ async function getOrCreateActorAndServerAndModel ( | |||
85 | accountPlaylistsUrl = result.playlists | 113 | accountPlaylistsUrl = result.playlists |
86 | } | 114 | } |
87 | 115 | ||
88 | if (actor.Account) actor.Account.Actor = actor | 116 | if (actor.Account) (actor as MActorAccountChannelIdActor).Account.Actor = actor |
89 | if (actor.VideoChannel) actor.VideoChannel.Actor = actor | 117 | if (actor.VideoChannel) (actor as MActorAccountChannelIdActor).VideoChannel.Actor = actor |
90 | 118 | ||
91 | const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor, fetchType) | 119 | const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor, fetchType) |
92 | if (!actorRefreshed) throw new Error('Actor ' + actorRefreshed.url + ' does not exist anymore.') | 120 | if (!actorRefreshed) throw new Error('Actor ' + actorRefreshed.url + ' does not exist anymore.') |
@@ -120,7 +148,7 @@ function buildActorInstance (type: ActivityPubActorType, url: string, preferredU | |||
120 | sharedInboxUrl: WEBSERVER.URL + '/inbox', | 148 | sharedInboxUrl: WEBSERVER.URL + '/inbox', |
121 | followersUrl: url + '/followers', | 149 | followersUrl: url + '/followers', |
122 | followingUrl: url + '/following' | 150 | followingUrl: url + '/following' |
123 | }) | 151 | }) as MActor |
124 | } | 152 | } |
125 | 153 | ||
126 | async function updateActorInstance (actorInstance: ActorModel, attributes: ActivityPubActor) { | 154 | async function updateActorInstance (actorInstance: ActorModel, attributes: ActivityPubActor) { |
@@ -140,7 +168,8 @@ async function updateActorInstance (actorInstance: ActorModel, attributes: Activ | |||
140 | actorInstance.followingUrl = attributes.following | 168 | actorInstance.followingUrl = attributes.following |
141 | } | 169 | } |
142 | 170 | ||
143 | async function updateActorAvatarInstance (actor: ActorModel, info: { name: string, onDisk: boolean, fileUrl: string }, t: Transaction) { | 171 | type AvatarInfo = { name: string, onDisk: boolean, fileUrl: string } |
172 | async function updateActorAvatarInstance (actor: MActorDefault, info: AvatarInfo, t: Transaction) { | ||
144 | if (info.name !== undefined) { | 173 | if (info.name !== undefined) { |
145 | if (actor.avatarId) { | 174 | if (actor.avatarId) { |
146 | try { | 175 | try { |
@@ -212,14 +241,16 @@ async function addFetchOutboxJob (actor: Pick<ActorModel, 'id' | 'outboxUrl'>) { | |||
212 | return JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload }) | 241 | return JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload }) |
213 | } | 242 | } |
214 | 243 | ||
215 | async function refreshActorIfNeeded ( | 244 | async function refreshActorIfNeeded <T extends MActorFull | MActorAccountChannelId> ( |
216 | actorArg: ActorModel, | 245 | actorArg: T, |
217 | fetchedType: ActorFetchByUrlType | 246 | fetchedType: ActorFetchByUrlType |
218 | ): Promise<{ actor: ActorModel, refreshed: boolean }> { | 247 | ): Promise<{ actor: T | MActorFull, refreshed: boolean }> { |
219 | if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false } | 248 | if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false } |
220 | 249 | ||
221 | // We need more attributes | 250 | // We need more attributes |
222 | const actor = fetchedType === 'all' ? actorArg : await ActorModel.loadByUrlAndPopulateAccountAndChannel(actorArg.url) | 251 | const actor = fetchedType === 'all' |
252 | ? actorArg as MActorFull | ||
253 | : await ActorModel.loadByUrlAndPopulateAccountAndChannel(actorArg.url) | ||
223 | 254 | ||
224 | try { | 255 | try { |
225 | let actorUrl: string | 256 | let actorUrl: string |
@@ -297,9 +328,9 @@ export { | |||
297 | 328 | ||
298 | function saveActorAndServerAndModelIfNotExist ( | 329 | function saveActorAndServerAndModelIfNotExist ( |
299 | result: FetchRemoteActorResult, | 330 | result: FetchRemoteActorResult, |
300 | ownerActor?: ActorModel, | 331 | ownerActor?: MActorFullActor, |
301 | t?: Transaction | 332 | t?: Transaction |
302 | ): Bluebird<ActorModel> | Promise<ActorModel> { | 333 | ): Bluebird<MActorFullActor> | Promise<MActorFullActor> { |
303 | let actor = result.actor | 334 | let actor = result.actor |
304 | 335 | ||
305 | if (t !== undefined) return save(t) | 336 | if (t !== undefined) return save(t) |
@@ -336,7 +367,7 @@ function saveActorAndServerAndModelIfNotExist ( | |||
336 | 367 | ||
337 | // Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists | 368 | // Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists |
338 | // (which could be false in a retried query) | 369 | // (which could be false in a retried query) |
339 | const [ actorCreated ] = await ActorModel.findOrCreate({ | 370 | const [ actorCreated ] = await ActorModel.findOrCreate<MActorFullActor>({ |
340 | defaults: actor.toJSON(), | 371 | defaults: actor.toJSON(), |
341 | where: { | 372 | where: { |
342 | url: actor.url | 373 | url: actor.url |
@@ -345,12 +376,11 @@ function saveActorAndServerAndModelIfNotExist ( | |||
345 | }) | 376 | }) |
346 | 377 | ||
347 | if (actorCreated.type === 'Person' || actorCreated.type === 'Application') { | 378 | if (actorCreated.type === 'Person' || actorCreated.type === 'Application') { |
348 | actorCreated.Account = await saveAccount(actorCreated, result, t) | 379 | actorCreated.Account = await saveAccount(actorCreated, result, t) as MAccountDefault |
349 | actorCreated.Account.Actor = actorCreated | 380 | actorCreated.Account.Actor = actorCreated |
350 | } else if (actorCreated.type === 'Group') { // Video channel | 381 | } else if (actorCreated.type === 'Group') { // Video channel |
351 | actorCreated.VideoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t) | 382 | const channel = await saveVideoChannel(actorCreated, result, ownerActor, t) |
352 | actorCreated.VideoChannel.Actor = actorCreated | 383 | actorCreated.VideoChannel = Object.assign(channel, { Actor: actorCreated, Account: ownerActor.Account }) |
353 | actorCreated.VideoChannel.Account = ownerActor.Account | ||
354 | } | 384 | } |
355 | 385 | ||
356 | actorCreated.Server = server | 386 | actorCreated.Server = server |
@@ -360,7 +390,7 @@ function saveActorAndServerAndModelIfNotExist ( | |||
360 | } | 390 | } |
361 | 391 | ||
362 | type FetchRemoteActorResult = { | 392 | type FetchRemoteActorResult = { |
363 | actor: ActorModel | 393 | actor: MActor |
364 | name: string | 394 | name: string |
365 | summary: string | 395 | summary: string |
366 | support?: string | 396 | support?: string |
@@ -429,7 +459,7 @@ async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: numbe | |||
429 | } | 459 | } |
430 | } | 460 | } |
431 | 461 | ||
432 | async function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t: Transaction) { | 462 | async function saveAccount (actor: MActorId, result: FetchRemoteActorResult, t: Transaction) { |
433 | const [ accountCreated ] = await AccountModel.findOrCreate({ | 463 | const [ accountCreated ] = await AccountModel.findOrCreate({ |
434 | defaults: { | 464 | defaults: { |
435 | name: result.name, | 465 | name: result.name, |
@@ -442,10 +472,10 @@ async function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t | |||
442 | transaction: t | 472 | transaction: t |
443 | }) | 473 | }) |
444 | 474 | ||
445 | return accountCreated | 475 | return accountCreated as MAccount |
446 | } | 476 | } |
447 | 477 | ||
448 | async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResult, ownerActor: ActorModel, t: Transaction) { | 478 | async function saveVideoChannel (actor: MActorId, result: FetchRemoteActorResult, ownerActor: MActorAccountId, t: Transaction) { |
449 | const [ videoChannelCreated ] = await VideoChannelModel.findOrCreate({ | 479 | const [ videoChannelCreated ] = await VideoChannelModel.findOrCreate({ |
450 | defaults: { | 480 | defaults: { |
451 | name: result.name, | 481 | name: result.name, |
@@ -460,5 +490,5 @@ async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResu | |||
460 | transaction: t | 490 | transaction: t |
461 | }) | 491 | }) |
462 | 492 | ||
463 | return videoChannelCreated | 493 | return videoChannelCreated as MChannel |
464 | } | 494 | } |
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' | |||
3 | import { ACTIVITY_PUB } from '../../initializers/constants' | 3 | import { ACTIVITY_PUB } from '../../initializers/constants' |
4 | import { ActorModel } from '../../models/activitypub/actor' | 4 | import { ActorModel } from '../../models/activitypub/actor' |
5 | import { VideoModel } from '../../models/video/video' | 5 | import { VideoModel } from '../../models/video/video' |
6 | import { VideoCommentModel } from '../../models/video/video-comment' | ||
7 | import { VideoShareModel } from '../../models/video/video-share' | 6 | import { VideoShareModel } from '../../models/video/video-share' |
8 | import { ActorModelOnly } from '../../typings/models' | 7 | import { MActorFollowersUrl, MActorLight, MCommentOwner, MCommentOwnerVideo, MVideo, MVideoAccountLight } from '../../typings/models' |
9 | 8 | ||
10 | function getRemoteVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]): ActivityAudience { | 9 | function getRemoteVideoAudience (video: MVideoAccountLight, actorsInvolvedInVideo: MActorFollowersUrl[]): ActivityAudience { |
11 | return { | 10 | return { |
12 | to: [ video.VideoChannel.Account.Actor.url ], | 11 | to: [ video.VideoChannel.Account.Actor.url ], |
13 | cc: actorsInvolvedInVideo.map(a => a.followersUrl) | 12 | cc: actorsInvolvedInVideo.map(a => a.followersUrl) |
@@ -15,9 +14,9 @@ function getRemoteVideoAudience (video: VideoModel, actorsInvolvedInVideo: Actor | |||
15 | } | 14 | } |
16 | 15 | ||
17 | function getVideoCommentAudience ( | 16 | function getVideoCommentAudience ( |
18 | videoComment: VideoCommentModel, | 17 | videoComment: MCommentOwnerVideo, |
19 | threadParentComments: VideoCommentModel[], | 18 | threadParentComments: MCommentOwner[], |
20 | actorsInvolvedInVideo: ActorModel[], | 19 | actorsInvolvedInVideo: MActorFollowersUrl[], |
21 | isOrigin = false | 20 | isOrigin = false |
22 | ): ActivityAudience { | 21 | ): ActivityAudience { |
23 | const to = [ ACTIVITY_PUB.PUBLIC ] | 22 | const to = [ ACTIVITY_PUB.PUBLIC ] |
@@ -42,26 +41,28 @@ function getVideoCommentAudience ( | |||
42 | } | 41 | } |
43 | } | 42 | } |
44 | 43 | ||
45 | function getAudienceFromFollowersOf (actorsInvolvedInObject: ActorModel[]): ActivityAudience { | 44 | function getAudienceFromFollowersOf (actorsInvolvedInObject: MActorFollowersUrl[]): ActivityAudience { |
46 | return { | 45 | return { |
47 | to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)), | 46 | to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)), |
48 | cc: [] | 47 | cc: [] |
49 | } | 48 | } |
50 | } | 49 | } |
51 | 50 | ||
52 | async function getActorsInvolvedInVideo (video: VideoModel, t: Transaction) { | 51 | async function getActorsInvolvedInVideo (video: MVideo, t: Transaction) { |
53 | const actors = await VideoShareModel.loadActorsByShare(video.id, t) | 52 | const actors: MActorLight[] = await VideoShareModel.loadActorsByShare(video.id, t) |
54 | 53 | ||
55 | const videoActor = video.VideoChannel && video.VideoChannel.Account | 54 | const videoAll = video as VideoModel |
56 | ? video.VideoChannel.Account.Actor | 55 | |
57 | : await ActorModel.loadAccountActorByVideoId(video.id, t) | 56 | const videoActor = videoAll.VideoChannel && videoAll.VideoChannel.Account |
57 | ? videoAll.VideoChannel.Account.Actor | ||
58 | : await ActorModel.loadFromAccountByVideoId(video.id, t) | ||
58 | 59 | ||
59 | actors.push(videoActor) | 60 | actors.push(videoActor) |
60 | 61 | ||
61 | return actors | 62 | return actors |
62 | } | 63 | } |
63 | 64 | ||
64 | function getAudience (actorSender: ActorModelOnly, isPublic = true) { | 65 | function getAudience (actorSender: MActorFollowersUrl, isPublic = true) { |
65 | return buildAudience([ actorSender.followersUrl ], isPublic) | 66 | return buildAudience([ actorSender.followersUrl ], isPublic) |
66 | } | 67 | } |
67 | 68 | ||
diff --git a/server/lib/activitypub/cache-file.ts b/server/lib/activitypub/cache-file.ts index de5cc54ac..65b2dcb49 100644 --- a/server/lib/activitypub/cache-file.ts +++ b/server/lib/activitypub/cache-file.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | import { CacheFileObject } from '../../../shared/index' | 1 | import { CacheFileObject } from '../../../shared/index' |
2 | import { VideoModel } from '../../models/video/video' | ||
3 | import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' | 2 | import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' |
4 | import { Transaction } from 'sequelize' | 3 | import { Transaction } from 'sequelize' |
5 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' | 4 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' |
5 | import { MActorId, MVideoRedundancy, MVideoWithAllFiles } from '@server/typings/models' | ||
6 | 6 | ||
7 | function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }) { | 7 | function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject, video: MVideoWithAllFiles, byActor: MActorId) { |
8 | 8 | ||
9 | if (cacheFileObject.url.mediaType === 'application/x-mpegURL') { | 9 | if (cacheFileObject.url.mediaType === 'application/x-mpegURL') { |
10 | const url = cacheFileObject.url | 10 | const url = cacheFileObject.url |
@@ -39,7 +39,7 @@ function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject | |||
39 | } | 39 | } |
40 | } | 40 | } |
41 | 41 | ||
42 | async function createOrUpdateCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }, t: Transaction) { | 42 | async function createOrUpdateCacheFile (cacheFileObject: CacheFileObject, video: MVideoWithAllFiles, byActor: MActorId, t: Transaction) { |
43 | const redundancyModel = await VideoRedundancyModel.loadByUrl(cacheFileObject.id, t) | 43 | const redundancyModel = await VideoRedundancyModel.loadByUrl(cacheFileObject.id, t) |
44 | 44 | ||
45 | if (!redundancyModel) { | 45 | if (!redundancyModel) { |
@@ -49,7 +49,7 @@ async function createOrUpdateCacheFile (cacheFileObject: CacheFileObject, video: | |||
49 | } | 49 | } |
50 | } | 50 | } |
51 | 51 | ||
52 | function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }, t: Transaction) { | 52 | function createCacheFile (cacheFileObject: CacheFileObject, video: MVideoWithAllFiles, byActor: MActorId, t: Transaction) { |
53 | const attributes = cacheFileActivityObjectToDBAttributes(cacheFileObject, video, byActor) | 53 | const attributes = cacheFileActivityObjectToDBAttributes(cacheFileObject, video, byActor) |
54 | 54 | ||
55 | return VideoRedundancyModel.create(attributes, { transaction: t }) | 55 | return VideoRedundancyModel.create(attributes, { transaction: t }) |
@@ -57,9 +57,9 @@ function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, b | |||
57 | 57 | ||
58 | function updateCacheFile ( | 58 | function updateCacheFile ( |
59 | cacheFileObject: CacheFileObject, | 59 | cacheFileObject: CacheFileObject, |
60 | redundancyModel: VideoRedundancyModel, | 60 | redundancyModel: MVideoRedundancy, |
61 | video: VideoModel, | 61 | video: MVideoWithAllFiles, |
62 | byActor: { id?: number }, | 62 | byActor: MActorId, |
63 | t: Transaction | 63 | t: Transaction |
64 | ) { | 64 | ) { |
65 | if (redundancyModel.actorId !== byActor.id) { | 65 | if (redundancyModel.actorId !== byActor.id) { |
diff --git a/server/lib/activitypub/follow.ts b/server/lib/activitypub/follow.ts new file mode 100644 index 000000000..1abf43cd4 --- /dev/null +++ b/server/lib/activitypub/follow.ts | |||
@@ -0,0 +1,36 @@ | |||
1 | import { MActorFollowActors } from '../../typings/models' | ||
2 | import { CONFIG } from '../../initializers/config' | ||
3 | import { SERVER_ACTOR_NAME } from '../../initializers/constants' | ||
4 | import { JobQueue } from '../job-queue' | ||
5 | import { logger } from '../../helpers/logger' | ||
6 | import { getServerActor } from '../../helpers/utils' | ||
7 | import { ServerModel } from '../../models/server/server' | ||
8 | |||
9 | async function autoFollowBackIfNeeded (actorFollow: MActorFollowActors) { | ||
10 | if (!CONFIG.FOLLOWINGS.INSTANCE.AUTO_FOLLOW_BACK.ENABLED) return | ||
11 | |||
12 | const follower = actorFollow.ActorFollower | ||
13 | |||
14 | if (follower.type === 'Application' && follower.preferredUsername === SERVER_ACTOR_NAME) { | ||
15 | logger.info('Auto follow back %s.', follower.url) | ||
16 | |||
17 | const me = await getServerActor() | ||
18 | |||
19 | const server = await ServerModel.load(follower.serverId) | ||
20 | const host = server.host | ||
21 | |||
22 | const payload = { | ||
23 | host, | ||
24 | name: SERVER_ACTOR_NAME, | ||
25 | followerActorId: me.id, | ||
26 | isAutoFollow: true | ||
27 | } | ||
28 | |||
29 | JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) | ||
30 | .catch(err => logger.error('Cannot create auto follow back job for %s.', host, err)) | ||
31 | } | ||
32 | } | ||
33 | |||
34 | export { | ||
35 | autoFollowBackIfNeeded | ||
36 | } | ||
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 @@ | |||
1 | import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' | 1 | import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' |
2 | import { crawlCollectionPage } from './crawl' | 2 | import { crawlCollectionPage } from './crawl' |
3 | import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' | 3 | import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' |
4 | import { AccountModel } from '../../models/account/account' | ||
5 | import { isArray } from '../../helpers/custom-validators/misc' | 4 | import { isArray } from '../../helpers/custom-validators/misc' |
6 | import { getOrCreateActorAndServerAndModel } from './actor' | 5 | import { getOrCreateActorAndServerAndModel } from './actor' |
7 | import { logger } from '../../helpers/logger' | 6 | import { logger } from '../../helpers/logger' |
@@ -13,14 +12,14 @@ import { PlaylistElementObject } from '../../../shared/models/activitypub/object | |||
13 | import { getOrCreateVideoAndAccountAndChannel } from './videos' | 12 | import { getOrCreateVideoAndAccountAndChannel } from './videos' |
14 | import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '../../helpers/custom-validators/activitypub/playlist' | 13 | import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '../../helpers/custom-validators/activitypub/playlist' |
15 | import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element' | 14 | import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element' |
16 | import { VideoModel } from '../../models/video/video' | ||
17 | import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' | 15 | import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' |
18 | import { sequelizeTypescript } from '../../initializers/database' | 16 | import { sequelizeTypescript } from '../../initializers/database' |
19 | import { createPlaylistMiniatureFromUrl } from '../thumbnail' | 17 | import { createPlaylistMiniatureFromUrl } from '../thumbnail' |
20 | import { FilteredModelAttributes } from '../../typings/sequelize' | 18 | import { FilteredModelAttributes } from '../../typings/sequelize' |
21 | import { AccountModelId } from '../../typings/models' | 19 | import { MAccountDefault, MAccountId, MVideoId } from '../../typings/models' |
20 | import { MVideoPlaylist, MVideoPlaylistId, MVideoPlaylistOwner } from '../../typings/models/video/video-playlist' | ||
22 | 21 | ||
23 | function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount: AccountModelId, to: string[]) { | 22 | function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount: MAccountId, to: string[]) { |
24 | const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPlaylistPrivacy.PUBLIC : VideoPlaylistPrivacy.UNLISTED | 23 | const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPlaylistPrivacy.PUBLIC : VideoPlaylistPrivacy.UNLISTED |
25 | 24 | ||
26 | return { | 25 | return { |
@@ -36,7 +35,7 @@ function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount | |||
36 | } | 35 | } |
37 | } | 36 | } |
38 | 37 | ||
39 | function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObject, videoPlaylist: VideoPlaylistModel, video: VideoModel) { | 38 | function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObject, videoPlaylist: MVideoPlaylistId, video: MVideoId) { |
40 | return { | 39 | return { |
41 | position: elementObject.position, | 40 | position: elementObject.position, |
42 | url: elementObject.id, | 41 | url: elementObject.id, |
@@ -47,7 +46,7 @@ function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObje | |||
47 | } | 46 | } |
48 | } | 47 | } |
49 | 48 | ||
50 | async function createAccountPlaylists (playlistUrls: string[], account: AccountModel) { | 49 | async function createAccountPlaylists (playlistUrls: string[], account: MAccountDefault) { |
51 | await Bluebird.map(playlistUrls, async playlistUrl => { | 50 | await Bluebird.map(playlistUrls, async playlistUrl => { |
52 | try { | 51 | try { |
53 | const exists = await VideoPlaylistModel.doesPlaylistExist(playlistUrl) | 52 | const exists = await VideoPlaylistModel.doesPlaylistExist(playlistUrl) |
@@ -75,7 +74,7 @@ async function createAccountPlaylists (playlistUrls: string[], account: AccountM | |||
75 | }, { concurrency: CRAWL_REQUEST_CONCURRENCY }) | 74 | }, { concurrency: CRAWL_REQUEST_CONCURRENCY }) |
76 | } | 75 | } |
77 | 76 | ||
78 | async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAccount: AccountModelId, to: string[]) { | 77 | async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAccount: MAccountId, to: string[]) { |
79 | const playlistAttributes = playlistObjectToDBAttributes(playlistObject, byAccount, to) | 78 | const playlistAttributes = playlistObjectToDBAttributes(playlistObject, byAccount, to) |
80 | 79 | ||
81 | if (isArray(playlistObject.attributedTo) && playlistObject.attributedTo.length === 1) { | 80 | if (isArray(playlistObject.attributedTo) && playlistObject.attributedTo.length === 1) { |
@@ -88,7 +87,7 @@ async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAc | |||
88 | } | 87 | } |
89 | } | 88 | } |
90 | 89 | ||
91 | const [ playlist ] = await VideoPlaylistModel.upsert<VideoPlaylistModel>(playlistAttributes, { returning: true }) | 90 | const [ playlist ] = await VideoPlaylistModel.upsert<MVideoPlaylist>(playlistAttributes, { returning: true }) |
92 | 91 | ||
93 | let accItems: string[] = [] | 92 | let accItems: string[] = [] |
94 | await crawlCollectionPage<string>(playlistObject.id, items => { | 93 | await crawlCollectionPage<string>(playlistObject.id, items => { |
@@ -114,7 +113,7 @@ async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAc | |||
114 | return resetVideoPlaylistElements(accItems, refreshedPlaylist) | 113 | return resetVideoPlaylistElements(accItems, refreshedPlaylist) |
115 | } | 114 | } |
116 | 115 | ||
117 | async function refreshVideoPlaylistIfNeeded (videoPlaylist: VideoPlaylistModel): Promise<VideoPlaylistModel> { | 116 | async function refreshVideoPlaylistIfNeeded (videoPlaylist: MVideoPlaylistOwner): Promise<MVideoPlaylistOwner> { |
118 | if (!videoPlaylist.isOutdated()) return videoPlaylist | 117 | if (!videoPlaylist.isOutdated()) return videoPlaylist |
119 | 118 | ||
120 | try { | 119 | try { |
@@ -157,7 +156,7 @@ export { | |||
157 | 156 | ||
158 | // --------------------------------------------------------------------------- | 157 | // --------------------------------------------------------------------------- |
159 | 158 | ||
160 | async function resetVideoPlaylistElements (elementUrls: string[], playlist: VideoPlaylistModel) { | 159 | async function resetVideoPlaylistElements (elementUrls: string[], playlist: MVideoPlaylist) { |
161 | const elementsToCreate: FilteredModelAttributes<VideoPlaylistElementModel>[] = [] | 160 | const elementsToCreate: FilteredModelAttributes<VideoPlaylistElementModel>[] = [] |
162 | 161 | ||
163 | await Bluebird.map(elementUrls, async elementUrl => { | 162 | await Bluebird.map(elementUrls, async elementUrl => { |
diff --git a/server/lib/activitypub/process/process-accept.ts b/server/lib/activitypub/process/process-accept.ts index cf27e6c32..dcfbb2c84 100644 --- a/server/lib/activitypub/process/process-accept.ts +++ b/server/lib/activitypub/process/process-accept.ts | |||
@@ -1,9 +1,8 @@ | |||
1 | import { ActivityAccept } from '../../../../shared/models/activitypub' | 1 | import { ActivityAccept } from '../../../../shared/models/activitypub' |
2 | import { ActorModel } from '../../../models/activitypub/actor' | ||
3 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 2 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
4 | import { addFetchOutboxJob } from '../actor' | 3 | import { addFetchOutboxJob } from '../actor' |
5 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 4 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
6 | import { SignatureActorModel } from '../../../typings/models' | 5 | import { MActorDefault, MActorSignature } from '../../../typings/models' |
7 | 6 | ||
8 | async function processAcceptActivity (options: APProcessorOptions<ActivityAccept>) { | 7 | async function processAcceptActivity (options: APProcessorOptions<ActivityAccept>) { |
9 | const { byActor: targetActor, inboxActor } = options | 8 | const { byActor: targetActor, inboxActor } = options |
@@ -20,12 +19,12 @@ export { | |||
20 | 19 | ||
21 | // --------------------------------------------------------------------------- | 20 | // --------------------------------------------------------------------------- |
22 | 21 | ||
23 | async function processAccept (actor: ActorModel, targetActor: SignatureActorModel) { | 22 | async function processAccept (actor: MActorDefault, targetActor: MActorSignature) { |
24 | const follow = await ActorFollowModel.loadByActorAndTarget(actor.id, targetActor.id) | 23 | const follow = await ActorFollowModel.loadByActorAndTarget(actor.id, targetActor.id) |
25 | if (!follow) throw new Error('Cannot find associated follow.') | 24 | if (!follow) throw new Error('Cannot find associated follow.') |
26 | 25 | ||
27 | if (follow.state !== 'accepted') { | 26 | if (follow.state !== 'accepted') { |
28 | follow.set('state', 'accepted') | 27 | follow.state = 'accepted' |
29 | await follow.save() | 28 | await follow.save() |
30 | 29 | ||
31 | await addFetchOutboxJob(targetActor) | 30 | await addFetchOutboxJob(targetActor) |
diff --git a/server/lib/activitypub/process/process-announce.ts b/server/lib/activitypub/process/process-announce.ts index b3cdc4441..7e22125d5 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' | |||
5 | import { forwardVideoRelatedActivity } from '../send/utils' | 5 | import { forwardVideoRelatedActivity } from '../send/utils' |
6 | import { getOrCreateVideoAndAccountAndChannel } from '../videos' | 6 | import { getOrCreateVideoAndAccountAndChannel } from '../videos' |
7 | import { Notifier } from '../../notifier' | 7 | import { Notifier } from '../../notifier' |
8 | import { VideoModel } from '../../../models/video/video' | ||
9 | import { logger } from '../../../helpers/logger' | 8 | import { logger } from '../../../helpers/logger' |
10 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 9 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
11 | import { SignatureActorModel } from '../../../typings/models' | 10 | import { MActorSignature, MVideoAccountLightBlacklistAllFiles } from '../../../typings/models' |
12 | 11 | ||
13 | async function processAnnounceActivity (options: APProcessorOptions<ActivityAnnounce>) { | 12 | async function processAnnounceActivity (options: APProcessorOptions<ActivityAnnounce>) { |
14 | const { activity, byActor: actorAnnouncer } = options | 13 | const { activity, byActor: actorAnnouncer } = options |
@@ -26,10 +25,10 @@ export { | |||
26 | 25 | ||
27 | // --------------------------------------------------------------------------- | 26 | // --------------------------------------------------------------------------- |
28 | 27 | ||
29 | async function processVideoShare (actorAnnouncer: SignatureActorModel, activity: ActivityAnnounce, notify: boolean) { | 28 | async function processVideoShare (actorAnnouncer: MActorSignature, activity: ActivityAnnounce, notify: boolean) { |
30 | const objectUri = typeof activity.object === 'string' ? activity.object : activity.object.id | 29 | const objectUri = typeof activity.object === 'string' ? activity.object : activity.object.id |
31 | 30 | ||
32 | let video: VideoModel | 31 | let video: MVideoAccountLightBlacklistAllFiles |
33 | let videoCreated: boolean | 32 | let videoCreated: boolean |
34 | 33 | ||
35 | try { | 34 | try { |
diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts index 6815c6997..bee853721 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' | |||
10 | import { Notifier } from '../../notifier' | 10 | import { Notifier } from '../../notifier' |
11 | import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' | 11 | import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' |
12 | import { createOrUpdateVideoPlaylist } from '../playlist' | 12 | import { createOrUpdateVideoPlaylist } from '../playlist' |
13 | import { VideoModel } from '../../../models/video/video' | ||
14 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 13 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
15 | import { VideoCommentModel } from '../../../models/video/video-comment' | 14 | import { MActorSignature, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFiles } from '../../../typings/models' |
16 | import { SignatureActorModel } from '../../../typings/models' | ||
17 | 15 | ||
18 | async function processCreateActivity (options: APProcessorOptions<ActivityCreate>) { | 16 | async function processCreateActivity (options: APProcessorOptions<ActivityCreate>) { |
19 | const { activity, byActor } = options | 17 | const { activity, byActor } = options |
@@ -61,7 +59,7 @@ async function processCreateVideo (activity: ActivityCreate, notify: boolean) { | |||
61 | return video | 59 | return video |
62 | } | 60 | } |
63 | 61 | ||
64 | async function processCreateCacheFile (activity: ActivityCreate, byActor: SignatureActorModel) { | 62 | async function processCreateCacheFile (activity: ActivityCreate, byActor: MActorSignature) { |
65 | const cacheFile = activity.object as CacheFileObject | 63 | const cacheFile = activity.object as CacheFileObject |
66 | 64 | ||
67 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFile.object }) | 65 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFile.object }) |
@@ -77,15 +75,15 @@ async function processCreateCacheFile (activity: ActivityCreate, byActor: Signat | |||
77 | } | 75 | } |
78 | } | 76 | } |
79 | 77 | ||
80 | async function processCreateVideoComment (activity: ActivityCreate, byActor: SignatureActorModel, notify: boolean) { | 78 | async function processCreateVideoComment (activity: ActivityCreate, byActor: MActorSignature, notify: boolean) { |
81 | const commentObject = activity.object as VideoCommentObject | 79 | const commentObject = activity.object as VideoCommentObject |
82 | const byAccount = byActor.Account | 80 | const byAccount = byActor.Account |
83 | 81 | ||
84 | if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url) | 82 | if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url) |
85 | 83 | ||
86 | let video: VideoModel | 84 | let video: MVideoAccountLightBlacklistAllFiles |
87 | let created: boolean | 85 | let created: boolean |
88 | let comment: VideoCommentModel | 86 | let comment: MCommentOwnerVideo |
89 | try { | 87 | try { |
90 | const resolveThreadResult = await resolveThread({ url: commentObject.id, isVideo: false }) | 88 | const resolveThreadResult = await resolveThread({ url: commentObject.id, isVideo: false }) |
91 | video = resolveThreadResult.video | 89 | video = resolveThreadResult.video |
@@ -110,7 +108,7 @@ async function processCreateVideoComment (activity: ActivityCreate, byActor: Sig | |||
110 | if (created && notify) Notifier.Instance.notifyOnNewComment(comment) | 108 | if (created && notify) Notifier.Instance.notifyOnNewComment(comment) |
111 | } | 109 | } |
112 | 110 | ||
113 | async function processCreatePlaylist (activity: ActivityCreate, byActor: SignatureActorModel) { | 111 | async function processCreatePlaylist (activity: ActivityCreate, byActor: MActorSignature) { |
114 | const playlistObject = activity.object as PlaylistObject | 112 | const playlistObject = activity.object as PlaylistObject |
115 | const byAccount = byActor.Account | 113 | const byAccount = byActor.Account |
116 | 114 | ||
diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts index 344d14322..79d0e0d79 100644 --- a/server/lib/activitypub/process/process-delete.ts +++ b/server/lib/activitypub/process/process-delete.ts | |||
@@ -2,15 +2,13 @@ import { ActivityDelete } from '../../../../shared/models/activitypub' | |||
2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
3 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
4 | import { sequelizeTypescript } from '../../../initializers' | 4 | import { sequelizeTypescript } from '../../../initializers' |
5 | import { AccountModel } from '../../../models/account/account' | ||
6 | import { ActorModel } from '../../../models/activitypub/actor' | 5 | import { ActorModel } from '../../../models/activitypub/actor' |
7 | import { VideoModel } from '../../../models/video/video' | 6 | import { VideoModel } from '../../../models/video/video' |
8 | import { VideoChannelModel } from '../../../models/video/video-channel' | ||
9 | import { VideoCommentModel } from '../../../models/video/video-comment' | 7 | import { VideoCommentModel } from '../../../models/video/video-comment' |
10 | import { forwardVideoRelatedActivity } from '../send/utils' | 8 | import { forwardVideoRelatedActivity } from '../send/utils' |
11 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' | 9 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' |
12 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 10 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
13 | import { SignatureActorModel } from '../../../typings/models' | 11 | import { MAccountActor, MActor, MActorSignature, MChannelActor, MChannelActorAccountActor } from '../../../typings/models' |
14 | 12 | ||
15 | async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) { | 13 | async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) { |
16 | const { activity, byActor } = options | 14 | const { activity, byActor } = options |
@@ -24,13 +22,17 @@ async function processDeleteActivity (options: APProcessorOptions<ActivityDelete | |||
24 | if (byActorFull.type === 'Person') { | 22 | if (byActorFull.type === 'Person') { |
25 | if (!byActorFull.Account) throw new Error('Actor ' + byActorFull.url + ' is a person but we cannot find it in database.') | 23 | if (!byActorFull.Account) throw new Error('Actor ' + byActorFull.url + ' is a person but we cannot find it in database.') |
26 | 24 | ||
27 | byActorFull.Account.Actor = await byActorFull.Account.$get('Actor') as ActorModel | 25 | const accountToDelete = byActorFull.Account as MAccountActor |
28 | return retryTransactionWrapper(processDeleteAccount, byActorFull.Account) | 26 | accountToDelete.Actor = byActorFull |
27 | |||
28 | return retryTransactionWrapper(processDeleteAccount, accountToDelete) | ||
29 | } else if (byActorFull.type === 'Group') { | 29 | } else if (byActorFull.type === 'Group') { |
30 | if (!byActorFull.VideoChannel) throw new Error('Actor ' + byActorFull.url + ' is a group but we cannot find it in database.') | 30 | if (!byActorFull.VideoChannel) throw new Error('Actor ' + byActorFull.url + ' is a group but we cannot find it in database.') |
31 | 31 | ||
32 | byActorFull.VideoChannel.Actor = await byActorFull.VideoChannel.$get('Actor') as ActorModel | 32 | const channelToDelete = byActorFull.VideoChannel as MChannelActorAccountActor |
33 | return retryTransactionWrapper(processDeleteVideoChannel, byActorFull.VideoChannel) | 33 | channelToDelete.Actor = byActorFull |
34 | |||
35 | return retryTransactionWrapper(processDeleteVideoChannel, channelToDelete) | ||
34 | } | 36 | } |
35 | } | 37 | } |
36 | 38 | ||
@@ -70,7 +72,7 @@ export { | |||
70 | 72 | ||
71 | // --------------------------------------------------------------------------- | 73 | // --------------------------------------------------------------------------- |
72 | 74 | ||
73 | async function processDeleteVideo (actor: ActorModel, videoToDelete: VideoModel) { | 75 | async function processDeleteVideo (actor: MActor, videoToDelete: VideoModel) { |
74 | logger.debug('Removing remote video "%s".', videoToDelete.uuid) | 76 | logger.debug('Removing remote video "%s".', videoToDelete.uuid) |
75 | 77 | ||
76 | await sequelizeTypescript.transaction(async t => { | 78 | await sequelizeTypescript.transaction(async t => { |
@@ -84,7 +86,7 @@ async function processDeleteVideo (actor: ActorModel, videoToDelete: VideoModel) | |||
84 | logger.info('Remote video with uuid %s removed.', videoToDelete.uuid) | 86 | logger.info('Remote video with uuid %s removed.', videoToDelete.uuid) |
85 | } | 87 | } |
86 | 88 | ||
87 | async function processDeleteVideoPlaylist (actor: ActorModel, playlistToDelete: VideoPlaylistModel) { | 89 | async function processDeleteVideoPlaylist (actor: MActor, playlistToDelete: VideoPlaylistModel) { |
88 | logger.debug('Removing remote video playlist "%s".', playlistToDelete.uuid) | 90 | logger.debug('Removing remote video playlist "%s".', playlistToDelete.uuid) |
89 | 91 | ||
90 | await sequelizeTypescript.transaction(async t => { | 92 | await sequelizeTypescript.transaction(async t => { |
@@ -98,7 +100,7 @@ async function processDeleteVideoPlaylist (actor: ActorModel, playlistToDelete: | |||
98 | logger.info('Remote video playlist with uuid %s removed.', playlistToDelete.uuid) | 100 | logger.info('Remote video playlist with uuid %s removed.', playlistToDelete.uuid) |
99 | } | 101 | } |
100 | 102 | ||
101 | async function processDeleteAccount (accountToRemove: AccountModel) { | 103 | async function processDeleteAccount (accountToRemove: MAccountActor) { |
102 | logger.debug('Removing remote account "%s".', accountToRemove.Actor.url) | 104 | logger.debug('Removing remote account "%s".', accountToRemove.Actor.url) |
103 | 105 | ||
104 | await sequelizeTypescript.transaction(async t => { | 106 | await sequelizeTypescript.transaction(async t => { |
@@ -108,7 +110,7 @@ async function processDeleteAccount (accountToRemove: AccountModel) { | |||
108 | logger.info('Remote account %s removed.', accountToRemove.Actor.url) | 110 | logger.info('Remote account %s removed.', accountToRemove.Actor.url) |
109 | } | 111 | } |
110 | 112 | ||
111 | async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelModel) { | 113 | async function processDeleteVideoChannel (videoChannelToRemove: MChannelActor) { |
112 | logger.debug('Removing remote video channel "%s".', videoChannelToRemove.Actor.url) | 114 | logger.debug('Removing remote video channel "%s".', videoChannelToRemove.Actor.url) |
113 | 115 | ||
114 | await sequelizeTypescript.transaction(async t => { | 116 | await sequelizeTypescript.transaction(async t => { |
@@ -118,7 +120,7 @@ async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelMode | |||
118 | logger.info('Remote video channel %s removed.', videoChannelToRemove.Actor.url) | 120 | logger.info('Remote video channel %s removed.', videoChannelToRemove.Actor.url) |
119 | } | 121 | } |
120 | 122 | ||
121 | function processDeleteVideoComment (byActor: SignatureActorModel, videoComment: VideoCommentModel, activity: ActivityDelete) { | 123 | function processDeleteVideoComment (byActor: MActorSignature, videoComment: VideoCommentModel, activity: ActivityDelete) { |
122 | logger.debug('Removing remote video comment "%s".', videoComment.url) | 124 | logger.debug('Removing remote video comment "%s".', videoComment.url) |
123 | 125 | ||
124 | return sequelizeTypescript.transaction(async t => { | 126 | return sequelizeTypescript.transaction(async t => { |
diff --git a/server/lib/activitypub/process/process-dislike.ts b/server/lib/activitypub/process/process-dislike.ts index 727fcfee0..debd8a67c 100644 --- a/server/lib/activitypub/process/process-dislike.ts +++ b/server/lib/activitypub/process/process-dislike.ts | |||
@@ -7,7 +7,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos' | |||
7 | import { forwardVideoRelatedActivity } from '../send/utils' | 7 | import { forwardVideoRelatedActivity } from '../send/utils' |
8 | import { getVideoDislikeActivityPubUrl } from '../url' | 8 | import { getVideoDislikeActivityPubUrl } from '../url' |
9 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 9 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
10 | import { SignatureActorModel } from '../../../typings/models' | 10 | import { MActorSignature } from '../../../typings/models' |
11 | 11 | ||
12 | async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) { | 12 | async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) { |
13 | const { activity, byActor } = options | 13 | const { activity, byActor } = options |
@@ -22,7 +22,7 @@ export { | |||
22 | 22 | ||
23 | // --------------------------------------------------------------------------- | 23 | // --------------------------------------------------------------------------- |
24 | 24 | ||
25 | async function processDislike (activity: ActivityCreate | ActivityDislike, byActor: SignatureActorModel) { | 25 | async function processDislike (activity: ActivityCreate | ActivityDislike, byActor: MActorSignature) { |
26 | const dislikeObject = activity.type === 'Dislike' ? activity.object : (activity.object as DislikeObject).object | 26 | const dislikeObject = activity.type === 'Dislike' ? activity.object : (activity.object as DislikeObject).object |
27 | const byAccount = byActor.Account | 27 | const byAccount = byActor.Account |
28 | 28 | ||
diff --git a/server/lib/activitypub/process/process-flag.ts b/server/lib/activitypub/process/process-flag.ts index 1f8a80c14..e6e9084de 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' | |||
8 | import { Notifier } from '../../notifier' | 8 | import { Notifier } from '../../notifier' |
9 | import { getAPId } from '../../../helpers/activitypub' | 9 | import { getAPId } from '../../../helpers/activitypub' |
10 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 10 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
11 | import { SignatureActorModel } from '../../../typings/models' | 11 | import { MActorSignature, MVideoAbuseVideo } from '../../../typings/models' |
12 | 12 | ||
13 | async function processFlagActivity (options: APProcessorOptions<ActivityCreate | ActivityFlag>) { | 13 | async function processFlagActivity (options: APProcessorOptions<ActivityCreate | ActivityFlag>) { |
14 | const { activity, byActor } = options | 14 | const { activity, byActor } = options |
@@ -23,31 +23,39 @@ export { | |||
23 | 23 | ||
24 | // --------------------------------------------------------------------------- | 24 | // --------------------------------------------------------------------------- |
25 | 25 | ||
26 | async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag, byActor: SignatureActorModel) { | 26 | async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag, byActor: MActorSignature) { |
27 | const flag = activity.type === 'Flag' ? activity : (activity.object as VideoAbuseObject) | 27 | const flag = activity.type === 'Flag' ? activity : (activity.object as VideoAbuseObject) |
28 | 28 | ||
29 | logger.debug('Reporting remote abuse for video %s.', getAPId(flag.object)) | ||
30 | |||
31 | const account = byActor.Account | 29 | const account = byActor.Account |
32 | if (!account) throw new Error('Cannot create video abuse with the non account actor ' + byActor.url) | 30 | if (!account) throw new Error('Cannot create video abuse with the non account actor ' + byActor.url) |
33 | 31 | ||
34 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: flag.object }) | 32 | const objects = Array.isArray(flag.object) ? flag.object : [ flag.object ] |
35 | 33 | ||
36 | const videoAbuse = await sequelizeTypescript.transaction(async t => { | 34 | for (const object of objects) { |
37 | const videoAbuseData = { | 35 | try { |
38 | reporterAccountId: account.id, | 36 | logger.debug('Reporting remote abuse for video %s.', getAPId(object)) |
39 | reason: flag.content, | 37 | |
40 | videoId: video.id, | 38 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: object }) |
41 | state: VideoAbuseState.PENDING | ||
42 | } | ||
43 | 39 | ||
44 | const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t }) | 40 | const videoAbuse = await sequelizeTypescript.transaction(async t => { |
45 | videoAbuseInstance.Video = video | 41 | const videoAbuseData = { |
42 | reporterAccountId: account.id, | ||
43 | reason: flag.content, | ||
44 | videoId: video.id, | ||
45 | state: VideoAbuseState.PENDING | ||
46 | } | ||
46 | 47 | ||
47 | logger.info('Remote abuse for video uuid %s created', flag.object) | 48 | const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t }) as MVideoAbuseVideo |
49 | videoAbuseInstance.Video = video | ||
48 | 50 | ||
49 | return videoAbuseInstance | 51 | logger.info('Remote abuse for video uuid %s created', flag.object) |
50 | }) | ||
51 | 52 | ||
52 | Notifier.Instance.notifyOnNewVideoAbuse(videoAbuse) | 53 | return videoAbuseInstance |
54 | }) | ||
55 | |||
56 | Notifier.Instance.notifyOnNewVideoAbuse(videoAbuse) | ||
57 | } catch (err) { | ||
58 | logger.debug('Cannot process report of %s. (Maybe not a video abuse).', getAPId(object), { err }) | ||
59 | } | ||
60 | } | ||
53 | } | 61 | } |
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts index 240aa5799..85f22d654 100644 --- a/server/lib/activitypub/process/process-follow.ts +++ b/server/lib/activitypub/process/process-follow.ts | |||
@@ -10,8 +10,8 @@ import { getAPId } from '../../../helpers/activitypub' | |||
10 | import { getServerActor } from '../../../helpers/utils' | 10 | import { getServerActor } from '../../../helpers/utils' |
11 | import { CONFIG } from '../../../initializers/config' | 11 | import { CONFIG } from '../../../initializers/config' |
12 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 12 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
13 | import { SignatureActorModel } from '../../../typings/models' | 13 | import { MActorFollowActors, MActorSignature } from '../../../typings/models' |
14 | import { ActorFollowModelLight } from '../../../typings/models/actor-follow' | 14 | import { autoFollowBackIfNeeded } from '../follow' |
15 | 15 | ||
16 | async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) { | 16 | async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) { |
17 | const { activity, byActor } = options | 17 | const { activity, byActor } = options |
@@ -28,8 +28,8 @@ export { | |||
28 | 28 | ||
29 | // --------------------------------------------------------------------------- | 29 | // --------------------------------------------------------------------------- |
30 | 30 | ||
31 | async function processFollow (byActor: SignatureActorModel, targetActorURL: string) { | 31 | async function processFollow (byActor: MActorSignature, targetActorURL: string) { |
32 | const { actorFollow, created, isFollowingInstance } = await sequelizeTypescript.transaction(async t => { | 32 | const { actorFollow, created, isFollowingInstance, targetActor } = await sequelizeTypescript.transaction(async t => { |
33 | const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t) | 33 | const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t) |
34 | 34 | ||
35 | if (!targetActor) throw new Error('Unknown actor') | 35 | if (!targetActor) throw new Error('Unknown actor') |
@@ -43,10 +43,10 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri | |||
43 | 43 | ||
44 | await sendReject(byActor, targetActor) | 44 | await sendReject(byActor, targetActor) |
45 | 45 | ||
46 | return { actorFollow: undefined } | 46 | return { actorFollow: undefined as MActorFollowActors } |
47 | } | 47 | } |
48 | 48 | ||
49 | const [ actorFollow, created ] = await ActorFollowModel.findOrCreate({ | 49 | const [ actorFollow, created ] = await ActorFollowModel.findOrCreate<MActorFollowActors>({ |
50 | where: { | 50 | where: { |
51 | actorId: byActor.id, | 51 | actorId: byActor.id, |
52 | targetActorId: targetActor.id | 52 | targetActorId: targetActor.id |
@@ -57,7 +57,7 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri | |||
57 | state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL ? 'pending' : 'accepted' | 57 | state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL ? 'pending' : 'accepted' |
58 | }, | 58 | }, |
59 | transaction: t | 59 | transaction: t |
60 | }) as [ ActorFollowModelLight, boolean ] | 60 | }) |
61 | 61 | ||
62 | if (actorFollow.state !== 'accepted' && CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false) { | 62 | if (actorFollow.state !== 'accepted' && CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false) { |
63 | actorFollow.state = 'accepted' | 63 | actorFollow.state = 'accepted' |
@@ -68,17 +68,26 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri | |||
68 | actorFollow.ActorFollowing = targetActor | 68 | actorFollow.ActorFollowing = targetActor |
69 | 69 | ||
70 | // Target sends to actor he accepted the follow request | 70 | // Target sends to actor he accepted the follow request |
71 | if (actorFollow.state === 'accepted') await sendAccept(actorFollow) | 71 | if (actorFollow.state === 'accepted') { |
72 | await sendAccept(actorFollow) | ||
73 | await autoFollowBackIfNeeded(actorFollow) | ||
74 | } | ||
72 | 75 | ||
73 | return { actorFollow, created, isFollowingInstance } | 76 | return { actorFollow, created, isFollowingInstance, targetActor } |
74 | }) | 77 | }) |
75 | 78 | ||
76 | // Rejected | 79 | // Rejected |
77 | if (!actorFollow) return | 80 | if (!actorFollow) return |
78 | 81 | ||
79 | if (created) { | 82 | if (created) { |
80 | if (isFollowingInstance) Notifier.Instance.notifyOfNewInstanceFollow(actorFollow) | 83 | const follower = await ActorModel.loadFull(byActor.id) |
81 | else Notifier.Instance.notifyOfNewUserFollow(actorFollow) | 84 | const actorFollowFull = Object.assign(actorFollow, { ActorFollowing: targetActor, ActorFollower: follower }) |
85 | |||
86 | if (isFollowingInstance) { | ||
87 | Notifier.Instance.notifyOfNewInstanceFollow(actorFollowFull) | ||
88 | } else { | ||
89 | Notifier.Instance.notifyOfNewUserFollow(actorFollowFull) | ||
90 | } | ||
82 | } | 91 | } |
83 | 92 | ||
84 | logger.info('Actor %s is followed by actor %s.', targetActorURL, byActor.url) | 93 | 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' | |||
7 | import { getVideoLikeActivityPubUrl } from '../url' | 7 | import { getVideoLikeActivityPubUrl } from '../url' |
8 | import { getAPId } from '../../../helpers/activitypub' | 8 | import { getAPId } from '../../../helpers/activitypub' |
9 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 9 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
10 | import { SignatureActorModel } from '../../../typings/models' | 10 | import { MActorSignature } from '../../../typings/models' |
11 | 11 | ||
12 | async function processLikeActivity (options: APProcessorOptions<ActivityLike>) { | 12 | async function processLikeActivity (options: APProcessorOptions<ActivityLike>) { |
13 | const { activity, byActor } = options | 13 | const { activity, byActor } = options |
@@ -22,7 +22,7 @@ export { | |||
22 | 22 | ||
23 | // --------------------------------------------------------------------------- | 23 | // --------------------------------------------------------------------------- |
24 | 24 | ||
25 | async function processLikeVideo (byActor: SignatureActorModel, activity: ActivityLike) { | 25 | async function processLikeVideo (byActor: MActorSignature, activity: ActivityLike) { |
26 | const videoUrl = getAPId(activity.object) | 26 | const videoUrl = getAPId(activity.object) |
27 | 27 | ||
28 | const byAccount = byActor.Account | 28 | const byAccount = byActor.Account |
diff --git a/server/lib/activitypub/process/process-reject.ts b/server/lib/activitypub/process/process-reject.ts index 22e311ceb..00e9afa10 100644 --- a/server/lib/activitypub/process/process-reject.ts +++ b/server/lib/activitypub/process/process-reject.ts | |||
@@ -2,7 +2,7 @@ import { ActivityReject } from '../../../../shared/models/activitypub/activity' | |||
2 | import { sequelizeTypescript } from '../../../initializers' | 2 | import { sequelizeTypescript } from '../../../initializers' |
3 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 3 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
4 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 4 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
5 | import { ActorModelOnly } from '../../../typings/models' | 5 | import { MActor } from '../../../typings/models' |
6 | 6 | ||
7 | async function processRejectActivity (options: APProcessorOptions<ActivityReject>) { | 7 | async function processRejectActivity (options: APProcessorOptions<ActivityReject>) { |
8 | const { byActor: targetActor, inboxActor } = options | 8 | const { byActor: targetActor, inboxActor } = options |
@@ -19,7 +19,7 @@ export { | |||
19 | 19 | ||
20 | // --------------------------------------------------------------------------- | 20 | // --------------------------------------------------------------------------- |
21 | 21 | ||
22 | async function processReject (follower: ActorModelOnly, targetActor: ActorModelOnly) { | 22 | async function processReject (follower: MActor, targetActor: MActor) { |
23 | return sequelizeTypescript.transaction(async t => { | 23 | return sequelizeTypescript.transaction(async t => { |
24 | const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, targetActor.id, t) | 24 | const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, targetActor.id, t) |
25 | 25 | ||
diff --git a/server/lib/activitypub/process/process-undo.ts b/server/lib/activitypub/process/process-undo.ts index c37ee38bb..10643b2e9 100644 --- a/server/lib/activitypub/process/process-undo.ts +++ b/server/lib/activitypub/process/process-undo.ts | |||
@@ -11,7 +11,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos' | |||
11 | import { VideoShareModel } from '../../../models/video/video-share' | 11 | import { VideoShareModel } from '../../../models/video/video-share' |
12 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' | 12 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' |
13 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 13 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
14 | import { SignatureActorModel } from '../../../typings/models' | 14 | import { MActorSignature } from '../../../typings/models' |
15 | 15 | ||
16 | async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) { | 16 | async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) { |
17 | const { activity, byActor } = options | 17 | const { activity, byActor } = options |
@@ -54,7 +54,7 @@ export { | |||
54 | 54 | ||
55 | // --------------------------------------------------------------------------- | 55 | // --------------------------------------------------------------------------- |
56 | 56 | ||
57 | async function processUndoLike (byActor: SignatureActorModel, activity: ActivityUndo) { | 57 | async function processUndoLike (byActor: MActorSignature, activity: ActivityUndo) { |
58 | const likeActivity = activity.object as ActivityLike | 58 | const likeActivity = activity.object as ActivityLike |
59 | 59 | ||
60 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: likeActivity.object }) | 60 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: likeActivity.object }) |
@@ -77,7 +77,7 @@ async function processUndoLike (byActor: SignatureActorModel, activity: Activity | |||
77 | }) | 77 | }) |
78 | } | 78 | } |
79 | 79 | ||
80 | async function processUndoDislike (byActor: SignatureActorModel, activity: ActivityUndo) { | 80 | async function processUndoDislike (byActor: MActorSignature, activity: ActivityUndo) { |
81 | const dislike = activity.object.type === 'Dislike' | 81 | const dislike = activity.object.type === 'Dislike' |
82 | ? activity.object | 82 | ? activity.object |
83 | : activity.object.object as DislikeObject | 83 | : activity.object.object as DislikeObject |
@@ -102,7 +102,7 @@ async function processUndoDislike (byActor: SignatureActorModel, activity: Activ | |||
102 | }) | 102 | }) |
103 | } | 103 | } |
104 | 104 | ||
105 | async function processUndoCacheFile (byActor: SignatureActorModel, activity: ActivityUndo) { | 105 | async function processUndoCacheFile (byActor: MActorSignature, activity: ActivityUndo) { |
106 | const cacheFileObject = activity.object.object as CacheFileObject | 106 | const cacheFileObject = activity.object.object as CacheFileObject |
107 | 107 | ||
108 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFileObject.object }) | 108 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFileObject.object }) |
@@ -127,7 +127,7 @@ async function processUndoCacheFile (byActor: SignatureActorModel, activity: Act | |||
127 | }) | 127 | }) |
128 | } | 128 | } |
129 | 129 | ||
130 | function processUndoFollow (follower: SignatureActorModel, followActivity: ActivityFollow) { | 130 | function processUndoFollow (follower: MActorSignature, followActivity: ActivityFollow) { |
131 | return sequelizeTypescript.transaction(async t => { | 131 | return sequelizeTypescript.transaction(async t => { |
132 | const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t) | 132 | const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t) |
133 | const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t) | 133 | const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t) |
@@ -140,7 +140,7 @@ function processUndoFollow (follower: SignatureActorModel, followActivity: Activ | |||
140 | }) | 140 | }) |
141 | } | 141 | } |
142 | 142 | ||
143 | function processUndoAnnounce (byActor: SignatureActorModel, announceActivity: ActivityAnnounce) { | 143 | function processUndoAnnounce (byActor: MActorSignature, announceActivity: ActivityAnnounce) { |
144 | return sequelizeTypescript.transaction(async t => { | 144 | return sequelizeTypescript.transaction(async t => { |
145 | const share = await VideoShareModel.loadByUrl(announceActivity.id, t) | 145 | const share = await VideoShareModel.loadByUrl(announceActivity.id, t) |
146 | if (!share) throw new Error(`Unknown video share ${announceActivity.id}.`) | 146 | if (!share) throw new Error(`Unknown video share ${announceActivity.id}.`) |
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts index 414f9e375..a47d605d8 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' | |||
15 | import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' | 15 | import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' |
16 | import { createOrUpdateVideoPlaylist } from '../playlist' | 16 | import { createOrUpdateVideoPlaylist } from '../playlist' |
17 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 17 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
18 | import { SignatureActorModel } from '../../../typings/models' | 18 | import { MActorSignature, MAccountIdActor } from '../../../typings/models' |
19 | 19 | ||
20 | async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) { | 20 | async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) { |
21 | const { activity, byActor } = options | 21 | const { activity, byActor } = options |
@@ -53,7 +53,7 @@ export { | |||
53 | 53 | ||
54 | // --------------------------------------------------------------------------- | 54 | // --------------------------------------------------------------------------- |
55 | 55 | ||
56 | async function processUpdateVideo (actor: SignatureActorModel, activity: ActivityUpdate) { | 56 | async function processUpdateVideo (actor: MActorSignature, activity: ActivityUpdate) { |
57 | const videoObject = activity.object as VideoTorrentObject | 57 | const videoObject = activity.object as VideoTorrentObject |
58 | 58 | ||
59 | if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) { | 59 | if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) { |
@@ -61,20 +61,23 @@ async function processUpdateVideo (actor: SignatureActorModel, activity: Activit | |||
61 | return undefined | 61 | return undefined |
62 | } | 62 | } |
63 | 63 | ||
64 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoObject.id, allowRefresh: false }) | 64 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoObject.id, allowRefresh: false, fetchType: 'all' }) |
65 | const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) | 65 | const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) |
66 | 66 | ||
67 | const account = actor.Account as MAccountIdActor | ||
68 | account.Actor = actor | ||
69 | |||
67 | const updateOptions = { | 70 | const updateOptions = { |
68 | video, | 71 | video, |
69 | videoObject, | 72 | videoObject, |
70 | account: actor.Account, | 73 | account, |
71 | channel: channelActor.VideoChannel, | 74 | channel: channelActor.VideoChannel, |
72 | overrideTo: activity.to | 75 | overrideTo: activity.to |
73 | } | 76 | } |
74 | return updateVideoFromAP(updateOptions) | 77 | return updateVideoFromAP(updateOptions) |
75 | } | 78 | } |
76 | 79 | ||
77 | async function processUpdateCacheFile (byActor: SignatureActorModel, activity: ActivityUpdate) { | 80 | async function processUpdateCacheFile (byActor: MActorSignature, activity: ActivityUpdate) { |
78 | const cacheFileObject = activity.object as CacheFileObject | 81 | const cacheFileObject = activity.object as CacheFileObject |
79 | 82 | ||
80 | if (!isCacheFileObjectValid(cacheFileObject)) { | 83 | if (!isCacheFileObjectValid(cacheFileObject)) { |
@@ -150,7 +153,7 @@ async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate) | |||
150 | } | 153 | } |
151 | } | 154 | } |
152 | 155 | ||
153 | async function processUpdatePlaylist (byActor: SignatureActorModel, activity: ActivityUpdate) { | 156 | async function processUpdatePlaylist (byActor: MActorSignature, activity: ActivityUpdate) { |
154 | const playlistObject = activity.object as PlaylistObject | 157 | const playlistObject = activity.object as PlaylistObject |
155 | const byAccount = byActor.Account | 158 | const byAccount = byActor.Account |
156 | 159 | ||
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' | |||
3 | import { Redis } from '../../redis' | 3 | import { Redis } from '../../redis' |
4 | import { ActivityCreate, ActivityView, ViewObject } from '../../../../shared/models/activitypub' | 4 | import { ActivityCreate, ActivityView, ViewObject } from '../../../../shared/models/activitypub' |
5 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 5 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
6 | import { SignatureActorModel } from '../../../typings/models' | 6 | import { MActorSignature } from '../../../typings/models' |
7 | 7 | ||
8 | async function processViewActivity (options: APProcessorOptions<ActivityCreate | ActivityView>) { | 8 | async function processViewActivity (options: APProcessorOptions<ActivityCreate | ActivityView>) { |
9 | const { activity, byActor } = options | 9 | const { activity, byActor } = options |
@@ -18,11 +18,11 @@ export { | |||
18 | 18 | ||
19 | // --------------------------------------------------------------------------- | 19 | // --------------------------------------------------------------------------- |
20 | 20 | ||
21 | async function processCreateView (activity: ActivityView | ActivityCreate, byActor: SignatureActorModel) { | 21 | async function processCreateView (activity: ActivityView | ActivityCreate, byActor: MActorSignature) { |
22 | const videoObject = activity.type === 'View' ? activity.object : (activity.object as ViewObject).object | 22 | const videoObject = activity.type === 'View' ? activity.object : (activity.object as ViewObject).object |
23 | 23 | ||
24 | const options = { | 24 | const options = { |
25 | videoObject: videoObject, | 25 | videoObject, |
26 | fetchType: 'only-video' as 'only-video' | 26 | fetchType: 'only-video' as 'only-video' |
27 | } | 27 | } |
28 | const { video } = await getOrCreateVideoAndAccountAndChannel(options) | 28 | const { video } = await getOrCreateVideoAndAccountAndChannel(options) |
diff --git a/server/lib/activitypub/process/process.ts b/server/lib/activitypub/process/process.ts index d108fe321..c602bf218 100644 --- a/server/lib/activitypub/process/process.ts +++ b/server/lib/activitypub/process/process.ts | |||
@@ -1,7 +1,6 @@ | |||
1 | import { Activity, ActivityType } from '../../../../shared/models/activitypub' | 1 | import { Activity, ActivityType } from '../../../../shared/models/activitypub' |
2 | import { checkUrlsSameHost, getAPId } from '../../../helpers/activitypub' | 2 | import { checkUrlsSameHost, getAPId } from '../../../helpers/activitypub' |
3 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
4 | import { ActorModel } from '../../../models/activitypub/actor' | ||
5 | import { processAcceptActivity } from './process-accept' | 4 | import { processAcceptActivity } from './process-accept' |
6 | import { processAnnounceActivity } from './process-announce' | 5 | import { processAnnounceActivity } from './process-announce' |
7 | import { processCreateActivity } from './process-create' | 6 | import { processCreateActivity } from './process-create' |
@@ -16,7 +15,7 @@ import { processDislikeActivity } from './process-dislike' | |||
16 | import { processFlagActivity } from './process-flag' | 15 | import { processFlagActivity } from './process-flag' |
17 | import { processViewActivity } from './process-view' | 16 | import { processViewActivity } from './process-view' |
18 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 17 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
19 | import { SignatureActorModel } from '../../../typings/models' | 18 | import { MActorDefault, MActorSignature } from '../../../typings/models' |
20 | 19 | ||
21 | const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions<Activity>) => Promise<any> } = { | 20 | const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions<Activity>) => Promise<any> } = { |
22 | Create: processCreateActivity, | 21 | Create: processCreateActivity, |
@@ -36,15 +35,15 @@ const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions<Act | |||
36 | async function processActivities ( | 35 | async function processActivities ( |
37 | activities: Activity[], | 36 | activities: Activity[], |
38 | options: { | 37 | options: { |
39 | signatureActor?: SignatureActorModel | 38 | signatureActor?: MActorSignature |
40 | inboxActor?: ActorModel | 39 | inboxActor?: MActorDefault |
41 | outboxUrl?: string | 40 | outboxUrl?: string |
42 | fromFetch?: boolean | 41 | fromFetch?: boolean |
43 | } = {} | 42 | } = {} |
44 | ) { | 43 | ) { |
45 | const { outboxUrl, signatureActor, inboxActor, fromFetch = false } = options | 44 | const { outboxUrl, signatureActor, inboxActor, fromFetch = false } = options |
46 | 45 | ||
47 | const actorsCache: { [ url: string ]: SignatureActorModel } = {} | 46 | const actorsCache: { [ url: string ]: MActorSignature } = {} |
48 | 47 | ||
49 | for (const activity of activities) { | 48 | for (const activity of activities) { |
50 | if (!signatureActor && [ 'Create', 'Announce', 'Like' ].includes(activity.type) === false) { | 49 | if (!signatureActor && [ 'Create', 'Announce', 'Like' ].includes(activity.type) === false) { |
@@ -75,7 +74,7 @@ async function processActivities ( | |||
75 | } | 74 | } |
76 | 75 | ||
77 | try { | 76 | try { |
78 | await activityProcessor({ activity, byActor, inboxActor: inboxActor, fromFetch }) | 77 | await activityProcessor({ activity, byActor, inboxActor, fromFetch }) |
79 | } catch (err) { | 78 | } catch (err) { |
80 | logger.warn('Cannot process activity %s.', activity.type, { err }) | 79 | logger.warn('Cannot process activity %s.', activity.type, { err }) |
81 | } | 80 | } |
diff --git a/server/lib/activitypub/send/send-accept.ts b/server/lib/activitypub/send/send-accept.ts index 813c42e15..9f0225b64 100644 --- a/server/lib/activitypub/send/send-accept.ts +++ b/server/lib/activitypub/send/send-accept.ts | |||
@@ -3,10 +3,9 @@ import { getActorFollowAcceptActivityPubUrl, getActorFollowActivityPubUrl } from | |||
3 | import { unicastTo } from './utils' | 3 | import { unicastTo } from './utils' |
4 | import { buildFollowActivity } from './send-follow' | 4 | import { buildFollowActivity } from './send-follow' |
5 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
6 | import { ActorFollowModelLight } from '../../../typings/models/actor-follow' | 6 | import { MActor, MActorFollowActors } from '../../../typings/models' |
7 | import { ActorModelOnly } from '../../../typings/models' | ||
8 | 7 | ||
9 | async function sendAccept (actorFollow: ActorFollowModelLight) { | 8 | async function sendAccept (actorFollow: MActorFollowActors) { |
10 | const follower = actorFollow.ActorFollower | 9 | const follower = actorFollow.ActorFollower |
11 | const me = actorFollow.ActorFollowing | 10 | const me = actorFollow.ActorFollowing |
12 | 11 | ||
@@ -34,7 +33,7 @@ export { | |||
34 | 33 | ||
35 | // --------------------------------------------------------------------------- | 34 | // --------------------------------------------------------------------------- |
36 | 35 | ||
37 | function buildAcceptActivity (url: string, byActor: ActorModelOnly, followActivityData: ActivityFollow): ActivityAccept { | 36 | function buildAcceptActivity (url: string, byActor: MActor, followActivityData: ActivityFollow): ActivityAccept { |
38 | return { | 37 | return { |
39 | type: 'Accept', | 38 | type: 'Accept', |
40 | id: url, | 39 | id: url, |
diff --git a/server/lib/activitypub/send/send-announce.ts b/server/lib/activitypub/send/send-announce.ts index 7fe4ca180..a0f33852c 100644 --- a/server/lib/activitypub/send/send-announce.ts +++ b/server/lib/activitypub/send/send-announce.ts | |||
@@ -1,16 +1,15 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub' | 2 | import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub' |
3 | import { VideoModel } from '../../../models/video/video' | ||
4 | import { broadcastToFollowers } from './utils' | 3 | import { broadcastToFollowers } from './utils' |
5 | import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience' | 4 | import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience' |
6 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
7 | import { ActorModelOnly } from '../../../typings/models' | 6 | import { MActorLight, MVideo } from '../../../typings/models' |
8 | import { VideoShareModelOnly } from '../../../typings/models/video-share' | 7 | import { MVideoShare } from '../../../typings/models/video' |
9 | 8 | ||
10 | async function buildAnnounceWithVideoAudience ( | 9 | async function buildAnnounceWithVideoAudience ( |
11 | byActor: ActorModelOnly, | 10 | byActor: MActorLight, |
12 | videoShare: VideoShareModelOnly, | 11 | videoShare: MVideoShare, |
13 | video: VideoModel, | 12 | video: MVideo, |
14 | t: Transaction | 13 | t: Transaction |
15 | ) { | 14 | ) { |
16 | const announcedObject = video.url | 15 | const announcedObject = video.url |
@@ -23,7 +22,7 @@ async function buildAnnounceWithVideoAudience ( | |||
23 | return { activity, actorsInvolvedInVideo } | 22 | return { activity, actorsInvolvedInVideo } |
24 | } | 23 | } |
25 | 24 | ||
26 | async function sendVideoAnnounce (byActor: ActorModelOnly, videoShare: VideoShareModelOnly, video: VideoModel, t: Transaction) { | 25 | async function sendVideoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) { |
27 | const { activity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t) | 26 | const { activity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t) |
28 | 27 | ||
29 | logger.info('Creating job to send announce %s.', videoShare.url) | 28 | logger.info('Creating job to send announce %s.', videoShare.url) |
@@ -32,7 +31,7 @@ async function sendVideoAnnounce (byActor: ActorModelOnly, videoShare: VideoShar | |||
32 | return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, t, followersException) | 31 | return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, t, followersException) |
33 | } | 32 | } |
34 | 33 | ||
35 | function buildAnnounceActivity (url: string, byActor: ActorModelOnly, object: string, audience?: ActivityAudience): ActivityAnnounce { | 34 | function buildAnnounceActivity (url: string, byActor: MActorLight, object: string, audience?: ActivityAudience): ActivityAnnounce { |
36 | if (!audience) audience = getAudience(byActor) | 35 | if (!audience) audience = getAudience(byActor) |
37 | 36 | ||
38 | return audiencify({ | 37 | return audiencify({ |
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts index 9c21149f2..26ec3e948 100644 --- a/server/lib/activitypub/send/send-create.ts +++ b/server/lib/activitypub/send/send-create.ts | |||
@@ -1,19 +1,23 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub' | 2 | import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub' |
3 | import { VideoPrivacy } from '../../../../shared/models/videos' | 3 | import { VideoPrivacy } from '../../../../shared/models/videos' |
4 | import { ActorModel } from '../../../models/activitypub/actor' | ||
5 | import { VideoModel } from '../../../models/video/video' | ||
6 | import { VideoCommentModel } from '../../../models/video/video-comment' | 4 | import { VideoCommentModel } from '../../../models/video/video-comment' |
7 | import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' | 5 | import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' |
8 | import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience' | 6 | import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience' |
9 | import { logger } from '../../../helpers/logger' | 7 | import { logger } from '../../../helpers/logger' |
10 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' | ||
11 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' | ||
12 | import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' | 8 | import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' |
13 | import { getServerActor } from '../../../helpers/utils' | 9 | import { getServerActor } from '../../../helpers/utils' |
14 | import * as Bluebird from 'bluebird' | 10 | import { |
15 | 11 | MActorLight, | |
16 | async function sendCreateVideo (video: VideoModel, t: Transaction) { | 12 | MCommentOwnerVideo, |
13 | MVideoAccountLight, | ||
14 | MVideoAP, | ||
15 | MVideoPlaylistFull, | ||
16 | MVideoRedundancyFileVideo, | ||
17 | MVideoRedundancyStreamingPlaylistVideo | ||
18 | } from '../../../typings/models' | ||
19 | |||
20 | async function sendCreateVideo (video: MVideoAP, t: Transaction) { | ||
17 | if (video.privacy === VideoPrivacy.PRIVATE) return undefined | 21 | if (video.privacy === VideoPrivacy.PRIVATE) return undefined |
18 | 22 | ||
19 | logger.info('Creating job to send video creation of %s.', video.url) | 23 | logger.info('Creating job to send video creation of %s.', video.url) |
@@ -27,7 +31,11 @@ async function sendCreateVideo (video: VideoModel, t: Transaction) { | |||
27 | return broadcastToFollowers(createActivity, byActor, [ byActor ], t) | 31 | return broadcastToFollowers(createActivity, byActor, [ byActor ], t) |
28 | } | 32 | } |
29 | 33 | ||
30 | async function sendCreateCacheFile (byActor: ActorModel, video: VideoModel, fileRedundancy: VideoRedundancyModel) { | 34 | async function sendCreateCacheFile ( |
35 | byActor: MActorLight, | ||
36 | video: MVideoAccountLight, | ||
37 | fileRedundancy: MVideoRedundancyStreamingPlaylistVideo | MVideoRedundancyFileVideo | ||
38 | ) { | ||
31 | logger.info('Creating job to send file cache of %s.', fileRedundancy.url) | 39 | logger.info('Creating job to send file cache of %s.', fileRedundancy.url) |
32 | 40 | ||
33 | return sendVideoRelatedCreateActivity({ | 41 | return sendVideoRelatedCreateActivity({ |
@@ -38,7 +46,7 @@ async function sendCreateCacheFile (byActor: ActorModel, video: VideoModel, file | |||
38 | }) | 46 | }) |
39 | } | 47 | } |
40 | 48 | ||
41 | async function sendCreateVideoPlaylist (playlist: VideoPlaylistModel, t: Transaction) { | 49 | async function sendCreateVideoPlaylist (playlist: MVideoPlaylistFull, t: Transaction) { |
42 | if (playlist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined | 50 | if (playlist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined |
43 | 51 | ||
44 | logger.info('Creating job to send create video playlist of %s.', playlist.url) | 52 | logger.info('Creating job to send create video playlist of %s.', playlist.url) |
@@ -57,7 +65,7 @@ async function sendCreateVideoPlaylist (playlist: VideoPlaylistModel, t: Transac | |||
57 | return broadcastToFollowers(createActivity, byActor, toFollowersOf, t) | 65 | return broadcastToFollowers(createActivity, byActor, toFollowersOf, t) |
58 | } | 66 | } |
59 | 67 | ||
60 | async function sendCreateVideoComment (comment: VideoCommentModel, t: Transaction) { | 68 | async function sendCreateVideoComment (comment: MCommentOwnerVideo, t: Transaction) { |
61 | logger.info('Creating job to send comment %s.', comment.url) | 69 | logger.info('Creating job to send comment %s.', comment.url) |
62 | 70 | ||
63 | const isOrigin = comment.Video.isOwned() | 71 | const isOrigin = comment.Video.isOwned() |
@@ -95,7 +103,7 @@ async function sendCreateVideoComment (comment: VideoCommentModel, t: Transactio | |||
95 | t.afterCommit(() => unicastTo(createActivity, byActor, comment.Video.VideoChannel.Account.Actor.sharedInboxUrl)) | 103 | t.afterCommit(() => unicastTo(createActivity, byActor, comment.Video.VideoChannel.Account.Actor.sharedInboxUrl)) |
96 | } | 104 | } |
97 | 105 | ||
98 | function buildCreateActivity (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityCreate { | 106 | function buildCreateActivity (url: string, byActor: MActorLight, object: any, audience?: ActivityAudience): ActivityCreate { |
99 | if (!audience) audience = getAudience(byActor) | 107 | if (!audience) audience = getAudience(byActor) |
100 | 108 | ||
101 | return audiencify( | 109 | return audiencify( |
@@ -122,8 +130,8 @@ export { | |||
122 | // --------------------------------------------------------------------------- | 130 | // --------------------------------------------------------------------------- |
123 | 131 | ||
124 | async function sendVideoRelatedCreateActivity (options: { | 132 | async function sendVideoRelatedCreateActivity (options: { |
125 | byActor: ActorModel, | 133 | byActor: MActorLight, |
126 | video: VideoModel, | 134 | video: MVideoAccountLight, |
127 | url: string, | 135 | url: string, |
128 | object: any, | 136 | object: any, |
129 | transaction?: Transaction | 137 | transaction?: Transaction |
diff --git a/server/lib/activitypub/send/send-delete.ts b/server/lib/activitypub/send/send-delete.ts index 6c7fb8449..4b1ff8dc5 100644 --- a/server/lib/activitypub/send/send-delete.ts +++ b/server/lib/activitypub/send/send-delete.ts | |||
@@ -1,17 +1,17 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub' | 2 | import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub' |
3 | import { ActorModel } from '../../../models/activitypub/actor' | 3 | import { ActorModel } from '../../../models/activitypub/actor' |
4 | import { VideoModel } from '../../../models/video/video' | ||
5 | import { VideoCommentModel } from '../../../models/video/video-comment' | 4 | import { VideoCommentModel } from '../../../models/video/video-comment' |
6 | import { VideoShareModel } from '../../../models/video/video-share' | 5 | import { VideoShareModel } from '../../../models/video/video-share' |
7 | import { getDeleteActivityPubUrl } from '../url' | 6 | import { getDeleteActivityPubUrl } from '../url' |
8 | import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' | 7 | import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' |
9 | import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience' | 8 | import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience' |
10 | import { logger } from '../../../helpers/logger' | 9 | import { logger } from '../../../helpers/logger' |
11 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' | ||
12 | import { getServerActor } from '../../../helpers/utils' | 10 | import { getServerActor } from '../../../helpers/utils' |
11 | import { MCommentOwnerVideoReply, MVideoAccountLight, MVideoPlaylistFullSummary } from '../../../typings/models/video' | ||
12 | import { MActorUrl } from '../../../typings/models' | ||
13 | 13 | ||
14 | async function sendDeleteVideo (video: VideoModel, transaction: Transaction) { | 14 | async function sendDeleteVideo (video: MVideoAccountLight, transaction: Transaction) { |
15 | logger.info('Creating job to broadcast delete of video %s.', video.url) | 15 | logger.info('Creating job to broadcast delete of video %s.', video.url) |
16 | 16 | ||
17 | const byActor = video.VideoChannel.Account.Actor | 17 | const byActor = video.VideoChannel.Account.Actor |
@@ -42,7 +42,7 @@ async function sendDeleteActor (byActor: ActorModel, t: Transaction) { | |||
42 | return broadcastToFollowers(activity, byActor, actorsInvolved, t) | 42 | return broadcastToFollowers(activity, byActor, actorsInvolved, t) |
43 | } | 43 | } |
44 | 44 | ||
45 | async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Transaction) { | 45 | async function sendDeleteVideoComment (videoComment: MCommentOwnerVideoReply, t: Transaction) { |
46 | logger.info('Creating job to send delete of comment %s.', videoComment.url) | 46 | logger.info('Creating job to send delete of comment %s.', videoComment.url) |
47 | 47 | ||
48 | const isVideoOrigin = videoComment.Video.isOwned() | 48 | const isVideoOrigin = videoComment.Video.isOwned() |
@@ -74,7 +74,7 @@ async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Trans | |||
74 | t.afterCommit(() => unicastTo(activity, byActor, videoComment.Video.VideoChannel.Account.Actor.sharedInboxUrl)) | 74 | t.afterCommit(() => unicastTo(activity, byActor, videoComment.Video.VideoChannel.Account.Actor.sharedInboxUrl)) |
75 | } | 75 | } |
76 | 76 | ||
77 | async function sendDeleteVideoPlaylist (videoPlaylist: VideoPlaylistModel, t: Transaction) { | 77 | async function sendDeleteVideoPlaylist (videoPlaylist: MVideoPlaylistFullSummary, t: Transaction) { |
78 | logger.info('Creating job to send delete of playlist %s.', videoPlaylist.url) | 78 | logger.info('Creating job to send delete of playlist %s.', videoPlaylist.url) |
79 | 79 | ||
80 | const byActor = videoPlaylist.OwnerAccount.Actor | 80 | const byActor = videoPlaylist.OwnerAccount.Actor |
@@ -101,7 +101,7 @@ export { | |||
101 | 101 | ||
102 | // --------------------------------------------------------------------------- | 102 | // --------------------------------------------------------------------------- |
103 | 103 | ||
104 | function buildDeleteActivity (url: string, object: string, byActor: ActorModel, audience?: ActivityAudience): ActivityDelete { | 104 | function buildDeleteActivity (url: string, object: string, byActor: MActorUrl, audience?: ActivityAudience): ActivityDelete { |
105 | const activity = { | 105 | const activity = { |
106 | type: 'Delete' as 'Delete', | 106 | type: 'Delete' as 'Delete', |
107 | id: url, | 107 | id: url, |
diff --git a/server/lib/activitypub/send/send-dislike.ts b/server/lib/activitypub/send/send-dislike.ts index a88436f2c..6e41f241f 100644 --- a/server/lib/activitypub/send/send-dislike.ts +++ b/server/lib/activitypub/send/send-dislike.ts | |||
@@ -1,13 +1,12 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActorModel } from '../../../models/activitypub/actor' | ||
3 | import { VideoModel } from '../../../models/video/video' | ||
4 | import { getVideoDislikeActivityPubUrl } from '../url' | 2 | import { getVideoDislikeActivityPubUrl } from '../url' |
5 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
6 | import { ActivityAudience, ActivityDislike } from '../../../../shared/models/activitypub' | 4 | import { ActivityAudience, ActivityDislike } from '../../../../shared/models/activitypub' |
7 | import { sendVideoRelatedActivity } from './utils' | 5 | import { sendVideoRelatedActivity } from './utils' |
8 | import { audiencify, getAudience } from '../audience' | 6 | import { audiencify, getAudience } from '../audience' |
7 | import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../typings/models' | ||
9 | 8 | ||
10 | async function sendDislike (byActor: ActorModel, video: VideoModel, t: Transaction) { | 9 | async function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { |
11 | logger.info('Creating job to dislike %s.', video.url) | 10 | logger.info('Creating job to dislike %s.', video.url) |
12 | 11 | ||
13 | const activityBuilder = (audience: ActivityAudience) => { | 12 | const activityBuilder = (audience: ActivityAudience) => { |
@@ -19,7 +18,7 @@ async function sendDislike (byActor: ActorModel, video: VideoModel, t: Transacti | |||
19 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) | 18 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) |
20 | } | 19 | } |
21 | 20 | ||
22 | function buildDislikeActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityDislike { | 21 | function buildDislikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityDislike { |
23 | if (!audience) audience = getAudience(byActor) | 22 | if (!audience) audience = getAudience(byActor) |
24 | 23 | ||
25 | return audiencify( | 24 | return audiencify( |
diff --git a/server/lib/activitypub/send/send-flag.ts b/server/lib/activitypub/send/send-flag.ts index 61ee389a6..5ae1614ab 100644 --- a/server/lib/activitypub/send/send-flag.ts +++ b/server/lib/activitypub/send/send-flag.ts | |||
@@ -1,14 +1,13 @@ | |||
1 | import { ActorModel } from '../../../models/activitypub/actor' | ||
2 | import { VideoModel } from '../../../models/video/video' | ||
3 | import { VideoAbuseModel } from '../../../models/video/video-abuse' | ||
4 | import { getVideoAbuseActivityPubUrl } from '../url' | 1 | import { getVideoAbuseActivityPubUrl } from '../url' |
5 | import { unicastTo } from './utils' | 2 | import { unicastTo } from './utils' |
6 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
7 | import { ActivityAudience, ActivityFlag } from '../../../../shared/models/activitypub' | 4 | import { ActivityAudience, ActivityFlag } from '../../../../shared/models/activitypub' |
8 | import { audiencify, getAudience } from '../audience' | 5 | import { audiencify, getAudience } from '../audience' |
9 | import { Transaction } from 'sequelize' | 6 | import { Transaction } from 'sequelize' |
7 | import { MActor, MVideoFullLight } from '../../../typings/models' | ||
8 | import { MVideoAbuseVideo } from '../../../typings/models/video' | ||
10 | 9 | ||
11 | async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel, video: VideoModel, t: Transaction) { | 10 | async function sendVideoAbuse (byActor: MActor, videoAbuse: MVideoAbuseVideo, video: MVideoFullLight, t: Transaction) { |
12 | if (!video.VideoChannel.Account.Actor.serverId) return // Local user | 11 | if (!video.VideoChannel.Account.Actor.serverId) return // Local user |
13 | 12 | ||
14 | const url = getVideoAbuseActivityPubUrl(videoAbuse) | 13 | const url = getVideoAbuseActivityPubUrl(videoAbuse) |
@@ -22,7 +21,7 @@ async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel, | |||
22 | t.afterCommit(() => unicastTo(flagActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)) | 21 | t.afterCommit(() => unicastTo(flagActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)) |
23 | } | 22 | } |
24 | 23 | ||
25 | function buildFlagActivity (url: string, byActor: ActorModel, videoAbuse: VideoAbuseModel, audience: ActivityAudience): ActivityFlag { | 24 | function buildFlagActivity (url: string, byActor: MActor, videoAbuse: MVideoAbuseVideo, audience: ActivityAudience): ActivityFlag { |
26 | if (!audience) audience = getAudience(byActor) | 25 | if (!audience) audience = getAudience(byActor) |
27 | 26 | ||
28 | const activity = Object.assign( | 27 | const activity = Object.assign( |
diff --git a/server/lib/activitypub/send/send-follow.ts b/server/lib/activitypub/send/send-follow.ts index a59ed50cf..ce400d8ff 100644 --- a/server/lib/activitypub/send/send-follow.ts +++ b/server/lib/activitypub/send/send-follow.ts | |||
@@ -1,12 +1,11 @@ | |||
1 | import { ActivityFollow } from '../../../../shared/models/activitypub' | 1 | import { ActivityFollow } from '../../../../shared/models/activitypub' |
2 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | ||
3 | import { getActorFollowActivityPubUrl } from '../url' | 2 | import { getActorFollowActivityPubUrl } from '../url' |
4 | import { unicastTo } from './utils' | 3 | import { unicastTo } from './utils' |
5 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
6 | import { Transaction } from 'sequelize' | 5 | import { Transaction } from 'sequelize' |
7 | import { ActorModelOnly } from '../../../typings/models' | 6 | import { MActor, MActorFollowActors } from '../../../typings/models' |
8 | 7 | ||
9 | function sendFollow (actorFollow: ActorFollowModel, t: Transaction) { | 8 | function sendFollow (actorFollow: MActorFollowActors, t: Transaction) { |
10 | const me = actorFollow.ActorFollower | 9 | const me = actorFollow.ActorFollower |
11 | const following = actorFollow.ActorFollowing | 10 | const following = actorFollow.ActorFollowing |
12 | 11 | ||
@@ -21,7 +20,7 @@ function sendFollow (actorFollow: ActorFollowModel, t: Transaction) { | |||
21 | t.afterCommit(() => unicastTo(data, me, following.inboxUrl)) | 20 | t.afterCommit(() => unicastTo(data, me, following.inboxUrl)) |
22 | } | 21 | } |
23 | 22 | ||
24 | function buildFollowActivity (url: string, byActor: ActorModelOnly, targetActor: ActorModelOnly): ActivityFollow { | 23 | function buildFollowActivity (url: string, byActor: MActor, targetActor: MActor): ActivityFollow { |
25 | return { | 24 | return { |
26 | type: 'Follow', | 25 | type: 'Follow', |
27 | id: url, | 26 | 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 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub' | 2 | import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub' |
3 | import { ActorModel } from '../../../models/activitypub/actor' | ||
4 | import { VideoModel } from '../../../models/video/video' | ||
5 | import { getVideoLikeActivityPubUrl } from '../url' | 3 | import { getVideoLikeActivityPubUrl } from '../url' |
6 | import { sendVideoRelatedActivity } from './utils' | 4 | import { sendVideoRelatedActivity } from './utils' |
7 | import { audiencify, getAudience } from '../audience' | 5 | import { audiencify, getAudience } from '../audience' |
8 | import { logger } from '../../../helpers/logger' | 6 | import { logger } from '../../../helpers/logger' |
7 | import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../typings/models' | ||
9 | 8 | ||
10 | async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction) { | 9 | async function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { |
11 | logger.info('Creating job to like %s.', video.url) | 10 | logger.info('Creating job to like %s.', video.url) |
12 | 11 | ||
13 | const activityBuilder = (audience: ActivityAudience) => { | 12 | const activityBuilder = (audience: ActivityAudience) => { |
@@ -19,7 +18,7 @@ async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction) | |||
19 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) | 18 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) |
20 | } | 19 | } |
21 | 20 | ||
22 | function buildLikeActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityLike { | 21 | function buildLikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityLike { |
23 | if (!audience) audience = getAudience(byActor) | 22 | if (!audience) audience = getAudience(byActor) |
24 | 23 | ||
25 | return audiencify( | 24 | return audiencify( |
diff --git a/server/lib/activitypub/send/send-reject.ts b/server/lib/activitypub/send/send-reject.ts index 63110b433..4258a3c36 100644 --- a/server/lib/activitypub/send/send-reject.ts +++ b/server/lib/activitypub/send/send-reject.ts | |||
@@ -1,12 +1,11 @@ | |||
1 | import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub' | 1 | import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub' |
2 | import { ActorModel } from '../../../models/activitypub/actor' | ||
3 | import { getActorFollowActivityPubUrl, getActorFollowRejectActivityPubUrl } from '../url' | 2 | import { getActorFollowActivityPubUrl, getActorFollowRejectActivityPubUrl } from '../url' |
4 | import { unicastTo } from './utils' | 3 | import { unicastTo } from './utils' |
5 | import { buildFollowActivity } from './send-follow' | 4 | import { buildFollowActivity } from './send-follow' |
6 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
7 | import { SignatureActorModel } from '../../../typings/models' | 6 | import { MActor } from '../../../typings/models' |
8 | 7 | ||
9 | async function sendReject (follower: SignatureActorModel, following: ActorModel) { | 8 | async function sendReject (follower: MActor, following: MActor) { |
10 | if (!follower.serverId) { // This should never happen | 9 | if (!follower.serverId) { // This should never happen |
11 | logger.warn('Do not sending reject to local follower.') | 10 | logger.warn('Do not sending reject to local follower.') |
12 | return | 11 | return |
@@ -31,7 +30,7 @@ export { | |||
31 | 30 | ||
32 | // --------------------------------------------------------------------------- | 31 | // --------------------------------------------------------------------------- |
33 | 32 | ||
34 | function buildRejectActivity (url: string, byActor: ActorModel, followActivityData: ActivityFollow): ActivityReject { | 33 | function buildRejectActivity (url: string, byActor: MActor, followActivityData: ActivityFollow): ActivityReject { |
35 | return { | 34 | return { |
36 | type: 'Reject', | 35 | type: 'Reject', |
37 | id: url, | 36 | id: url, |
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts index 8fcbbac5c..e9ab5b3c5 100644 --- a/server/lib/activitypub/send/send-undo.ts +++ b/server/lib/activitypub/send/send-undo.ts | |||
@@ -2,13 +2,12 @@ import { Transaction } from 'sequelize' | |||
2 | import { | 2 | import { |
3 | ActivityAnnounce, | 3 | ActivityAnnounce, |
4 | ActivityAudience, | 4 | ActivityAudience, |
5 | ActivityCreate, ActivityDislike, | 5 | ActivityCreate, |
6 | ActivityDislike, | ||
6 | ActivityFollow, | 7 | ActivityFollow, |
7 | ActivityLike, | 8 | ActivityLike, |
8 | ActivityUndo | 9 | ActivityUndo |
9 | } from '../../../../shared/models/activitypub' | 10 | } from '../../../../shared/models/activitypub' |
10 | import { ActorModel } from '../../../models/activitypub/actor' | ||
11 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | ||
12 | import { VideoModel } from '../../../models/video/video' | 11 | import { VideoModel } from '../../../models/video/video' |
13 | import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url' | 12 | import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url' |
14 | import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' | 13 | import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' |
@@ -16,13 +15,20 @@ import { audiencify, getAudience } from '../audience' | |||
16 | import { buildCreateActivity } from './send-create' | 15 | import { buildCreateActivity } from './send-create' |
17 | import { buildFollowActivity } from './send-follow' | 16 | import { buildFollowActivity } from './send-follow' |
18 | import { buildLikeActivity } from './send-like' | 17 | import { buildLikeActivity } from './send-like' |
19 | import { VideoShareModel } from '../../../models/video/video-share' | ||
20 | import { buildAnnounceWithVideoAudience } from './send-announce' | 18 | import { buildAnnounceWithVideoAudience } from './send-announce' |
21 | import { logger } from '../../../helpers/logger' | 19 | import { logger } from '../../../helpers/logger' |
22 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' | ||
23 | import { buildDislikeActivity } from './send-dislike' | 20 | import { buildDislikeActivity } from './send-dislike' |
24 | 21 | import { | |
25 | async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) { | 22 | MActor, MActorAudience, |
23 | MActorFollowActors, | ||
24 | MActorLight, | ||
25 | MVideo, | ||
26 | MVideoAccountLight, | ||
27 | MVideoRedundancyVideo, | ||
28 | MVideoShare | ||
29 | } from '../../../typings/models' | ||
30 | |||
31 | async function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) { | ||
26 | const me = actorFollow.ActorFollower | 32 | const me = actorFollow.ActorFollower |
27 | const following = actorFollow.ActorFollowing | 33 | const following = actorFollow.ActorFollowing |
28 | 34 | ||
@@ -40,7 +46,7 @@ async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) { | |||
40 | t.afterCommit(() => unicastTo(undoActivity, me, following.inboxUrl)) | 46 | t.afterCommit(() => unicastTo(undoActivity, me, following.inboxUrl)) |
41 | } | 47 | } |
42 | 48 | ||
43 | async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) { | 49 | async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) { |
44 | logger.info('Creating job to undo announce %s.', videoShare.url) | 50 | logger.info('Creating job to undo announce %s.', videoShare.url) |
45 | 51 | ||
46 | const undoUrl = getUndoActivityPubUrl(videoShare.url) | 52 | const undoUrl = getUndoActivityPubUrl(videoShare.url) |
@@ -52,7 +58,7 @@ async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareMode | |||
52 | return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException) | 58 | return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException) |
53 | } | 59 | } |
54 | 60 | ||
55 | async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transaction) { | 61 | async function sendUndoLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { |
56 | logger.info('Creating job to undo a like of video %s.', video.url) | 62 | logger.info('Creating job to undo a like of video %s.', video.url) |
57 | 63 | ||
58 | const likeUrl = getVideoLikeActivityPubUrl(byActor, video) | 64 | const likeUrl = getVideoLikeActivityPubUrl(byActor, video) |
@@ -61,7 +67,7 @@ async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transact | |||
61 | return sendUndoVideoRelatedActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t }) | 67 | return sendUndoVideoRelatedActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t }) |
62 | } | 68 | } |
63 | 69 | ||
64 | async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Transaction) { | 70 | async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { |
65 | logger.info('Creating job to undo a dislike of video %s.', video.url) | 71 | logger.info('Creating job to undo a dislike of video %s.', video.url) |
66 | 72 | ||
67 | const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video) | 73 | const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video) |
@@ -70,7 +76,7 @@ async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Trans | |||
70 | return sendUndoVideoRelatedActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t }) | 76 | return sendUndoVideoRelatedActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t }) |
71 | } | 77 | } |
72 | 78 | ||
73 | async function sendUndoCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel, t: Transaction) { | 79 | async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedundancyVideo, t: Transaction) { |
74 | logger.info('Creating job to undo cache file %s.', redundancyModel.url) | 80 | logger.info('Creating job to undo cache file %s.', redundancyModel.url) |
75 | 81 | ||
76 | const videoId = redundancyModel.getVideo().id | 82 | const videoId = redundancyModel.getVideo().id |
@@ -94,7 +100,7 @@ export { | |||
94 | 100 | ||
95 | function undoActivityData ( | 101 | function undoActivityData ( |
96 | url: string, | 102 | url: string, |
97 | byActor: ActorModel, | 103 | byActor: MActorAudience, |
98 | object: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce, | 104 | object: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce, |
99 | audience?: ActivityAudience | 105 | audience?: ActivityAudience |
100 | ): ActivityUndo { | 106 | ): ActivityUndo { |
@@ -112,8 +118,8 @@ function undoActivityData ( | |||
112 | } | 118 | } |
113 | 119 | ||
114 | async function sendUndoVideoRelatedActivity (options: { | 120 | async function sendUndoVideoRelatedActivity (options: { |
115 | byActor: ActorModel, | 121 | byActor: MActor, |
116 | video: VideoModel, | 122 | video: MVideoAccountLight, |
117 | url: string, | 123 | url: string, |
118 | activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce, | 124 | activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce, |
119 | transaction: Transaction | 125 | transaction: Transaction |
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts index 5bf092894..37517c2be 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' | |||
2 | import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub' | 2 | import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub' |
3 | import { VideoPrivacy } from '../../../../shared/models/videos' | 3 | import { VideoPrivacy } from '../../../../shared/models/videos' |
4 | import { AccountModel } from '../../../models/account/account' | 4 | import { AccountModel } from '../../../models/account/account' |
5 | import { ActorModel } from '../../../models/activitypub/actor' | ||
6 | import { VideoModel } from '../../../models/video/video' | 5 | import { VideoModel } from '../../../models/video/video' |
7 | import { VideoChannelModel } from '../../../models/video/video-channel' | ||
8 | import { VideoShareModel } from '../../../models/video/video-share' | 6 | import { VideoShareModel } from '../../../models/video/video-share' |
9 | import { getUpdateActivityPubUrl } from '../url' | 7 | import { getUpdateActivityPubUrl } from '../url' |
10 | import { broadcastToFollowers, sendVideoRelatedActivity } from './utils' | 8 | import { broadcastToFollowers, sendVideoRelatedActivity } from './utils' |
11 | import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience' | 9 | import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience' |
12 | import { logger } from '../../../helpers/logger' | 10 | import { logger } from '../../../helpers/logger' |
13 | import { VideoCaptionModel } from '../../../models/video/video-caption' | 11 | import { VideoCaptionModel } from '../../../models/video/video-caption' |
14 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' | ||
15 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' | ||
16 | import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' | 12 | import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' |
17 | import { getServerActor } from '../../../helpers/utils' | 13 | import { getServerActor } from '../../../helpers/utils' |
14 | import { | ||
15 | MAccountDefault, | ||
16 | MActor, | ||
17 | MActorLight, | ||
18 | MChannelDefault, | ||
19 | MVideoAP, | ||
20 | MVideoAPWithoutCaption, | ||
21 | MVideoPlaylistFull, | ||
22 | MVideoRedundancyVideo | ||
23 | } from '../../../typings/models' | ||
24 | |||
25 | async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, t: Transaction, overrodeByActor?: MActor) { | ||
26 | const video = videoArg as MVideoAP | ||
18 | 27 | ||
19 | async function sendUpdateVideo (video: VideoModel, t: Transaction, overrodeByActor?: ActorModel) { | ||
20 | if (video.privacy === VideoPrivacy.PRIVATE) return undefined | 28 | if (video.privacy === VideoPrivacy.PRIVATE) return undefined |
21 | 29 | ||
22 | logger.info('Creating job to update video %s.', video.url) | 30 | logger.info('Creating job to update video %s.', video.url) |
@@ -41,7 +49,7 @@ async function sendUpdateVideo (video: VideoModel, t: Transaction, overrodeByAct | |||
41 | return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t) | 49 | return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t) |
42 | } | 50 | } |
43 | 51 | ||
44 | async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelModel, t: Transaction) { | 52 | async function sendUpdateActor (accountOrChannel: MChannelDefault | MAccountDefault, t: Transaction) { |
45 | const byActor = accountOrChannel.Actor | 53 | const byActor = accountOrChannel.Actor |
46 | 54 | ||
47 | logger.info('Creating job to update actor %s.', byActor.url) | 55 | logger.info('Creating job to update actor %s.', byActor.url) |
@@ -51,7 +59,7 @@ async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelMod | |||
51 | const audience = getAudience(byActor) | 59 | const audience = getAudience(byActor) |
52 | const updateActivity = buildUpdateActivity(url, byActor, accountOrChannelObject, audience) | 60 | const updateActivity = buildUpdateActivity(url, byActor, accountOrChannelObject, audience) |
53 | 61 | ||
54 | let actorsInvolved: ActorModel[] | 62 | let actorsInvolved: MActor[] |
55 | if (accountOrChannel instanceof AccountModel) { | 63 | if (accountOrChannel instanceof AccountModel) { |
56 | // Actors that shared my videos are involved too | 64 | // Actors that shared my videos are involved too |
57 | actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, t) | 65 | actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, t) |
@@ -65,7 +73,7 @@ async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelMod | |||
65 | return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t) | 73 | return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t) |
66 | } | 74 | } |
67 | 75 | ||
68 | async function sendUpdateCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel) { | 76 | async function sendUpdateCacheFile (byActor: MActorLight, redundancyModel: MVideoRedundancyVideo) { |
69 | logger.info('Creating job to update cache file %s.', redundancyModel.url) | 77 | logger.info('Creating job to update cache file %s.', redundancyModel.url) |
70 | 78 | ||
71 | const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.getVideo().id) | 79 | const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.getVideo().id) |
@@ -80,7 +88,7 @@ async function sendUpdateCacheFile (byActor: ActorModel, redundancyModel: VideoR | |||
80 | return sendVideoRelatedActivity(activityBuilder, { byActor, video }) | 88 | return sendVideoRelatedActivity(activityBuilder, { byActor, video }) |
81 | } | 89 | } |
82 | 90 | ||
83 | async function sendUpdateVideoPlaylist (videoPlaylist: VideoPlaylistModel, t: Transaction) { | 91 | async function sendUpdateVideoPlaylist (videoPlaylist: MVideoPlaylistFull, t: Transaction) { |
84 | if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined | 92 | if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined |
85 | 93 | ||
86 | const byActor = videoPlaylist.OwnerAccount.Actor | 94 | const byActor = videoPlaylist.OwnerAccount.Actor |
@@ -113,7 +121,7 @@ export { | |||
113 | 121 | ||
114 | // --------------------------------------------------------------------------- | 122 | // --------------------------------------------------------------------------- |
115 | 123 | ||
116 | function buildUpdateActivity (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityUpdate { | 124 | function buildUpdateActivity (url: string, byActor: MActorLight, object: any, audience?: ActivityAudience): ActivityUpdate { |
117 | if (!audience) audience = getAudience(byActor) | 125 | if (!audience) audience = getAudience(byActor) |
118 | 126 | ||
119 | return audiencify( | 127 | return audiencify( |
@@ -121,8 +129,7 @@ function buildUpdateActivity (url: string, byActor: ActorModel, object: any, aud | |||
121 | type: 'Update' as 'Update', | 129 | type: 'Update' as 'Update', |
122 | id: url, | 130 | id: url, |
123 | actor: byActor.url, | 131 | actor: byActor.url, |
124 | object: audiencify(object, audience | 132 | object: audiencify(object, audience) |
125 | ) | ||
126 | }, | 133 | }, |
127 | audience | 134 | audience |
128 | ) | 135 | ) |
diff --git a/server/lib/activitypub/send/send-view.ts b/server/lib/activitypub/send/send-view.ts index 8ad126be0..8809417f9 100644 --- a/server/lib/activitypub/send/send-view.ts +++ b/server/lib/activitypub/send/send-view.ts | |||
@@ -1,13 +1,13 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub' | 2 | import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub' |
3 | import { ActorModel } from '../../../models/activitypub/actor' | 3 | import { ActorModel } from '../../../models/activitypub/actor' |
4 | import { VideoModel } from '../../../models/video/video' | ||
5 | import { getVideoLikeActivityPubUrl } from '../url' | 4 | import { getVideoLikeActivityPubUrl } from '../url' |
6 | import { sendVideoRelatedActivity } from './utils' | 5 | import { sendVideoRelatedActivity } from './utils' |
7 | import { audiencify, getAudience } from '../audience' | 6 | import { audiencify, getAudience } from '../audience' |
8 | import { logger } from '../../../helpers/logger' | 7 | import { logger } from '../../../helpers/logger' |
8 | import { MActorAudience, MVideoAccountLight, MVideoUrl } from '@server/typings/models' | ||
9 | 9 | ||
10 | async function sendView (byActor: ActorModel, video: VideoModel, t: Transaction) { | 10 | async function sendView (byActor: ActorModel, video: MVideoAccountLight, t: Transaction) { |
11 | logger.info('Creating job to send view of %s.', video.url) | 11 | logger.info('Creating job to send view of %s.', video.url) |
12 | 12 | ||
13 | const activityBuilder = (audience: ActivityAudience) => { | 13 | const activityBuilder = (audience: ActivityAudience) => { |
@@ -19,7 +19,7 @@ async function sendView (byActor: ActorModel, video: VideoModel, t: Transaction) | |||
19 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) | 19 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) |
20 | } | 20 | } |
21 | 21 | ||
22 | function buildViewActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityView { | 22 | function buildViewActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityView { |
23 | if (!audience) audience = getAudience(byActor) | 23 | if (!audience) audience = getAudience(byActor) |
24 | 24 | ||
25 | return audiencify( | 25 | return audiencify( |
diff --git a/server/lib/activitypub/send/utils.ts b/server/lib/activitypub/send/utils.ts index 4f69afb00..8129ab32a 100644 --- a/server/lib/activitypub/send/utils.ts +++ b/server/lib/activitypub/send/utils.ts | |||
@@ -4,15 +4,14 @@ import { logger } from '../../../helpers/logger' | |||
4 | import { ActorModel } from '../../../models/activitypub/actor' | 4 | import { ActorModel } from '../../../models/activitypub/actor' |
5 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 5 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
6 | import { JobQueue } from '../../job-queue' | 6 | import { JobQueue } from '../../job-queue' |
7 | import { VideoModel } from '../../../models/video/video' | ||
8 | import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience' | 7 | import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience' |
9 | import { getServerActor } from '../../../helpers/utils' | 8 | import { getServerActor } from '../../../helpers/utils' |
10 | import { afterCommitIfTransaction } from '../../../helpers/database-utils' | 9 | import { afterCommitIfTransaction } from '../../../helpers/database-utils' |
11 | import { ActorFollowerException, ActorModelId, ActorModelOnly } from '../../../typings/models' | 10 | import { MActorFollowerException, MActor, MActorId, MActorLight, MVideo, MVideoAccountLight } from '../../../typings/models' |
12 | 11 | ||
13 | async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { | 12 | async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { |
14 | byActor: ActorModelOnly, | 13 | byActor: MActorLight, |
15 | video: VideoModel, | 14 | video: MVideoAccountLight, |
16 | transaction?: Transaction | 15 | transaction?: Transaction |
17 | }) { | 16 | }) { |
18 | const { byActor, video, transaction } = options | 17 | const { byActor, video, transaction } = options |
@@ -41,8 +40,8 @@ async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAud | |||
41 | async function forwardVideoRelatedActivity ( | 40 | async function forwardVideoRelatedActivity ( |
42 | activity: Activity, | 41 | activity: Activity, |
43 | t: Transaction, | 42 | t: Transaction, |
44 | followersException: ActorFollowerException[] = [], | 43 | followersException: MActorFollowerException[] = [], |
45 | video: VideoModel | 44 | video: MVideo |
46 | ) { | 45 | ) { |
47 | // Mastodon does not add our announces in audience, so we forward to them manually | 46 | // Mastodon does not add our announces in audience, so we forward to them manually |
48 | const additionalActors = await getActorsInvolvedInVideo(video, t) | 47 | const additionalActors = await getActorsInvolvedInVideo(video, t) |
@@ -54,7 +53,7 @@ async function forwardVideoRelatedActivity ( | |||
54 | async function forwardActivity ( | 53 | async function forwardActivity ( |
55 | activity: Activity, | 54 | activity: Activity, |
56 | t: Transaction, | 55 | t: Transaction, |
57 | followersException: ActorFollowerException[] = [], | 56 | followersException: MActorFollowerException[] = [], |
58 | additionalFollowerUrls: string[] = [] | 57 | additionalFollowerUrls: string[] = [] |
59 | ) { | 58 | ) { |
60 | logger.info('Forwarding activity %s.', activity.id) | 59 | logger.info('Forwarding activity %s.', activity.id) |
@@ -88,10 +87,10 @@ async function forwardActivity ( | |||
88 | 87 | ||
89 | async function broadcastToFollowers ( | 88 | async function broadcastToFollowers ( |
90 | data: any, | 89 | data: any, |
91 | byActor: ActorModelId, | 90 | byActor: MActorId, |
92 | toFollowersOf: ActorModelId[], | 91 | toFollowersOf: MActorId[], |
93 | t: Transaction, | 92 | t: Transaction, |
94 | actorsException: ActorFollowerException[] = [] | 93 | actorsException: MActorFollowerException[] = [] |
95 | ) { | 94 | ) { |
96 | const uris = await computeFollowerUris(toFollowersOf, actorsException, t) | 95 | const uris = await computeFollowerUris(toFollowersOf, actorsException, t) |
97 | 96 | ||
@@ -100,16 +99,16 @@ async function broadcastToFollowers ( | |||
100 | 99 | ||
101 | async function broadcastToActors ( | 100 | async function broadcastToActors ( |
102 | data: any, | 101 | data: any, |
103 | byActor: ActorModelId, | 102 | byActor: MActorId, |
104 | toActors: ActorModelOnly[], | 103 | toActors: MActor[], |
105 | t?: Transaction, | 104 | t?: Transaction, |
106 | actorsException: ActorFollowerException[] = [] | 105 | actorsException: MActorFollowerException[] = [] |
107 | ) { | 106 | ) { |
108 | const uris = await computeUris(toActors, actorsException) | 107 | const uris = await computeUris(toActors, actorsException) |
109 | return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor)) | 108 | return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor)) |
110 | } | 109 | } |
111 | 110 | ||
112 | function broadcastTo (uris: string[], data: any, byActor: ActorModelId) { | 111 | function broadcastTo (uris: string[], data: any, byActor: MActorId) { |
113 | if (uris.length === 0) return undefined | 112 | if (uris.length === 0) return undefined |
114 | 113 | ||
115 | logger.debug('Creating broadcast job.', { uris }) | 114 | logger.debug('Creating broadcast job.', { uris }) |
@@ -123,7 +122,7 @@ function broadcastTo (uris: string[], data: any, byActor: ActorModelId) { | |||
123 | return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload }) | 122 | return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload }) |
124 | } | 123 | } |
125 | 124 | ||
126 | function unicastTo (data: any, byActor: ActorModelId, toActorUrl: string) { | 125 | function unicastTo (data: any, byActor: MActorId, toActorUrl: string) { |
127 | logger.debug('Creating unicast job.', { uri: toActorUrl }) | 126 | logger.debug('Creating unicast job.', { uri: toActorUrl }) |
128 | 127 | ||
129 | const payload = { | 128 | const payload = { |
@@ -148,7 +147,7 @@ export { | |||
148 | 147 | ||
149 | // --------------------------------------------------------------------------- | 148 | // --------------------------------------------------------------------------- |
150 | 149 | ||
151 | async function computeFollowerUris (toFollowersOf: ActorModelId[], actorsException: ActorFollowerException[], t: Transaction) { | 150 | async function computeFollowerUris (toFollowersOf: MActorId[], actorsException: MActorFollowerException[], t: Transaction) { |
152 | const toActorFollowerIds = toFollowersOf.map(a => a.id) | 151 | const toActorFollowerIds = toFollowersOf.map(a => a.id) |
153 | 152 | ||
154 | const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t) | 153 | const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t) |
@@ -157,7 +156,7 @@ async function computeFollowerUris (toFollowersOf: ActorModelId[], actorsExcepti | |||
157 | return result.data.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1) | 156 | return result.data.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1) |
158 | } | 157 | } |
159 | 158 | ||
160 | async function computeUris (toActors: ActorModelOnly[], actorsException: ActorFollowerException[] = []) { | 159 | async function computeUris (toActors: MActor[], actorsException: MActorFollowerException[] = []) { |
161 | const serverActor = await getServerActor() | 160 | const serverActor = await getServerActor() |
162 | const targetUrls = toActors | 161 | const targetUrls = toActors |
163 | .filter(a => a.id !== serverActor.id) // Don't send to ourselves | 162 | .filter(a => a.id !== serverActor.id) // Don't send to ourselves |
@@ -170,7 +169,7 @@ async function computeUris (toActors: ActorModelOnly[], actorsException: ActorFo | |||
170 | .filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1) | 169 | .filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1) |
171 | } | 170 | } |
172 | 171 | ||
173 | async function buildSharedInboxesException (actorsException: ActorFollowerException[]) { | 172 | async function buildSharedInboxesException (actorsException: MActorFollowerException[]) { |
174 | const serverActor = await getServerActor() | 173 | const serverActor = await getServerActor() |
175 | 174 | ||
176 | return actorsException | 175 | return actorsException |
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts index 7f38402b6..fdca9bed7 100644 --- a/server/lib/activitypub/share.ts +++ b/server/lib/activitypub/share.ts | |||
@@ -1,19 +1,18 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { VideoPrivacy } from '../../../shared/models/videos' | 2 | import { VideoPrivacy } from '../../../shared/models/videos' |
3 | import { getServerActor } from '../../helpers/utils' | 3 | import { getServerActor } from '../../helpers/utils' |
4 | import { VideoModel } from '../../models/video/video' | ||
5 | import { VideoShareModel } from '../../models/video/video-share' | 4 | import { VideoShareModel } from '../../models/video/video-share' |
6 | import { sendUndoAnnounce, sendVideoAnnounce } from './send' | 5 | import { sendUndoAnnounce, sendVideoAnnounce } from './send' |
7 | import { getVideoAnnounceActivityPubUrl } from './url' | 6 | import { getVideoAnnounceActivityPubUrl } from './url' |
8 | import { VideoChannelModel } from '../../models/video/video-channel' | ||
9 | import * as Bluebird from 'bluebird' | 7 | import * as Bluebird from 'bluebird' |
10 | import { doRequest } from '../../helpers/requests' | 8 | import { doRequest } from '../../helpers/requests' |
11 | import { getOrCreateActorAndServerAndModel } from './actor' | 9 | import { getOrCreateActorAndServerAndModel } from './actor' |
12 | import { logger } from '../../helpers/logger' | 10 | import { logger } from '../../helpers/logger' |
13 | import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' | 11 | import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' |
14 | import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' | 12 | import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' |
13 | import { MChannelActor, MChannelActorLight, MVideo, MVideoAccountLight, MVideoId } from '../../typings/models/video' | ||
15 | 14 | ||
16 | async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) { | 15 | async function shareVideoByServerAndChannel (video: MVideoAccountLight, t: Transaction) { |
17 | if (video.privacy === VideoPrivacy.PRIVATE) return undefined | 16 | if (video.privacy === VideoPrivacy.PRIVATE) return undefined |
18 | 17 | ||
19 | return Promise.all([ | 18 | return Promise.all([ |
@@ -22,7 +21,11 @@ async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) | |||
22 | ]) | 21 | ]) |
23 | } | 22 | } |
24 | 23 | ||
25 | async function changeVideoChannelShare (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) { | 24 | async function changeVideoChannelShare ( |
25 | video: MVideoAccountLight, | ||
26 | oldVideoChannel: MChannelActorLight, | ||
27 | t: Transaction | ||
28 | ) { | ||
26 | logger.info('Updating video channel of video %s: %s -> %s.', video.uuid, oldVideoChannel.name, video.VideoChannel.name) | 29 | logger.info('Updating video channel of video %s: %s -> %s.', video.uuid, oldVideoChannel.name, video.VideoChannel.name) |
27 | 30 | ||
28 | await undoShareByVideoChannel(video, oldVideoChannel, t) | 31 | await undoShareByVideoChannel(video, oldVideoChannel, t) |
@@ -30,7 +33,7 @@ async function changeVideoChannelShare (video: VideoModel, oldVideoChannel: Vide | |||
30 | await shareByVideoChannel(video, t) | 33 | await shareByVideoChannel(video, t) |
31 | } | 34 | } |
32 | 35 | ||
33 | async function addVideoShares (shareUrls: string[], instance: VideoModel) { | 36 | async function addVideoShares (shareUrls: string[], video: MVideoId) { |
34 | await Bluebird.map(shareUrls, async shareUrl => { | 37 | await Bluebird.map(shareUrls, async shareUrl => { |
35 | try { | 38 | try { |
36 | // Fetch url | 39 | // Fetch url |
@@ -50,7 +53,7 @@ async function addVideoShares (shareUrls: string[], instance: VideoModel) { | |||
50 | 53 | ||
51 | const entry = { | 54 | const entry = { |
52 | actorId: actor.id, | 55 | actorId: actor.id, |
53 | videoId: instance.id, | 56 | videoId: video.id, |
54 | url: shareUrl | 57 | url: shareUrl |
55 | } | 58 | } |
56 | 59 | ||
@@ -69,7 +72,7 @@ export { | |||
69 | 72 | ||
70 | // --------------------------------------------------------------------------- | 73 | // --------------------------------------------------------------------------- |
71 | 74 | ||
72 | async function shareByServer (video: VideoModel, t: Transaction) { | 75 | async function shareByServer (video: MVideo, t: Transaction) { |
73 | const serverActor = await getServerActor() | 76 | const serverActor = await getServerActor() |
74 | 77 | ||
75 | const serverShareUrl = getVideoAnnounceActivityPubUrl(serverActor, video) | 78 | const serverShareUrl = getVideoAnnounceActivityPubUrl(serverActor, video) |
@@ -88,7 +91,7 @@ async function shareByServer (video: VideoModel, t: Transaction) { | |||
88 | return sendVideoAnnounce(serverActor, serverShare, video, t) | 91 | return sendVideoAnnounce(serverActor, serverShare, video, t) |
89 | } | 92 | } |
90 | 93 | ||
91 | async function shareByVideoChannel (video: VideoModel, t: Transaction) { | 94 | async function shareByVideoChannel (video: MVideoAccountLight, t: Transaction) { |
92 | const videoChannelShareUrl = getVideoAnnounceActivityPubUrl(video.VideoChannel.Actor, video) | 95 | const videoChannelShareUrl = getVideoAnnounceActivityPubUrl(video.VideoChannel.Actor, video) |
93 | const [ videoChannelShare ] = await VideoShareModel.findOrCreate({ | 96 | const [ videoChannelShare ] = await VideoShareModel.findOrCreate({ |
94 | defaults: { | 97 | defaults: { |
@@ -105,7 +108,7 @@ async function shareByVideoChannel (video: VideoModel, t: Transaction) { | |||
105 | return sendVideoAnnounce(video.VideoChannel.Actor, videoChannelShare, video, t) | 108 | return sendVideoAnnounce(video.VideoChannel.Actor, videoChannelShare, video, t) |
106 | } | 109 | } |
107 | 110 | ||
108 | async function undoShareByVideoChannel (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) { | 111 | async function undoShareByVideoChannel (video: MVideo, oldVideoChannel: MChannelActorLight, t: Transaction) { |
109 | // Load old share | 112 | // Load old share |
110 | const oldShare = await VideoShareModel.load(oldVideoChannel.actorId, video.id, t) | 113 | const oldShare = await VideoShareModel.load(oldVideoChannel.actorId, video.id, t) |
111 | if (!oldShare) return new Error('Cannot find old video channel share ' + oldVideoChannel.actorId + ' for video ' + video.id) | 114 | if (!oldShare) return new Error('Cannot find old video channel share ' + oldVideoChannel.actorId + ' for video ' + video.id) |
diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts index dfcb3c668..6290af34b 100644 --- a/server/lib/activitypub/url.ts +++ b/server/lib/activitypub/url.ts | |||
@@ -1,36 +1,42 @@ | |||
1 | import { WEBSERVER } from '../../initializers/constants' | 1 | import { WEBSERVER } from '../../initializers/constants' |
2 | import { VideoModel } from '../../models/video/video' | 2 | import { |
3 | import { VideoAbuseModel } from '../../models/video/video-abuse' | 3 | MActor, |
4 | import { VideoCommentModel } from '../../models/video/video-comment' | 4 | MActorFollowActors, |
5 | import { VideoFileModel } from '../../models/video/video-file' | 5 | MActorId, |
6 | import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' | 6 | MActorUrl, |
7 | import { VideoPlaylistModel } from '../../models/video/video-playlist' | 7 | MCommentId, |
8 | import { ActorModelOnly, ActorModelUrl } from '../../typings/models' | 8 | MVideoAbuseId, |
9 | import { ActorFollowModelLight } from '../../typings/models/actor-follow' | 9 | MVideoId, |
10 | 10 | MVideoUrl, | |
11 | function getVideoActivityPubUrl (video: VideoModel) { | 11 | MVideoUUID |
12 | } from '../../typings/models' | ||
13 | import { MVideoPlaylist, MVideoPlaylistUUID } from '../../typings/models/video/video-playlist' | ||
14 | import { MVideoFileVideoUUID } from '../../typings/models/video/video-file' | ||
15 | import { MStreamingPlaylist } from '../../typings/models/video/video-streaming-playlist' | ||
16 | |||
17 | function getVideoActivityPubUrl (video: MVideoUUID) { | ||
12 | return WEBSERVER.URL + '/videos/watch/' + video.uuid | 18 | return WEBSERVER.URL + '/videos/watch/' + video.uuid |
13 | } | 19 | } |
14 | 20 | ||
15 | function getVideoPlaylistActivityPubUrl (videoPlaylist: VideoPlaylistModel) { | 21 | function getVideoPlaylistActivityPubUrl (videoPlaylist: MVideoPlaylist) { |
16 | return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid | 22 | return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid |
17 | } | 23 | } |
18 | 24 | ||
19 | function getVideoPlaylistElementActivityPubUrl (videoPlaylist: VideoPlaylistModel, video: VideoModel) { | 25 | function getVideoPlaylistElementActivityPubUrl (videoPlaylist: MVideoPlaylistUUID, video: MVideoUUID) { |
20 | return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid + '/' + video.uuid | 26 | return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid + '/' + video.uuid |
21 | } | 27 | } |
22 | 28 | ||
23 | function getVideoCacheFileActivityPubUrl (videoFile: VideoFileModel) { | 29 | function getVideoCacheFileActivityPubUrl (videoFile: MVideoFileVideoUUID) { |
24 | const suffixFPS = videoFile.fps && videoFile.fps !== -1 ? '-' + videoFile.fps : '' | 30 | const suffixFPS = videoFile.fps && videoFile.fps !== -1 ? '-' + videoFile.fps : '' |
25 | 31 | ||
26 | return `${WEBSERVER.URL}/redundancy/videos/${videoFile.Video.uuid}/${videoFile.resolution}${suffixFPS}` | 32 | return `${WEBSERVER.URL}/redundancy/videos/${videoFile.Video.uuid}/${videoFile.resolution}${suffixFPS}` |
27 | } | 33 | } |
28 | 34 | ||
29 | function getVideoCacheStreamingPlaylistActivityPubUrl (video: VideoModel, playlist: VideoStreamingPlaylistModel) { | 35 | function getVideoCacheStreamingPlaylistActivityPubUrl (video: MVideoUUID, playlist: MStreamingPlaylist) { |
30 | return `${WEBSERVER.URL}/redundancy/streaming-playlists/${playlist.getStringType()}/${video.uuid}` | 36 | return `${WEBSERVER.URL}/redundancy/streaming-playlists/${playlist.getStringType()}/${video.uuid}` |
31 | } | 37 | } |
32 | 38 | ||
33 | function getVideoCommentActivityPubUrl (video: VideoModel, videoComment: VideoCommentModel) { | 39 | function getVideoCommentActivityPubUrl (video: MVideoUUID, videoComment: MCommentId) { |
34 | return WEBSERVER.URL + '/videos/watch/' + video.uuid + '/comments/' + videoComment.id | 40 | return WEBSERVER.URL + '/videos/watch/' + video.uuid + '/comments/' + videoComment.id |
35 | } | 41 | } |
36 | 42 | ||
@@ -42,54 +48,54 @@ function getAccountActivityPubUrl (accountName: string) { | |||
42 | return WEBSERVER.URL + '/accounts/' + accountName | 48 | return WEBSERVER.URL + '/accounts/' + accountName |
43 | } | 49 | } |
44 | 50 | ||
45 | function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseModel) { | 51 | function getVideoAbuseActivityPubUrl (videoAbuse: MVideoAbuseId) { |
46 | return WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id | 52 | return WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id |
47 | } | 53 | } |
48 | 54 | ||
49 | function getVideoViewActivityPubUrl (byActor: ActorModelUrl, video: VideoModel) { | 55 | function getVideoViewActivityPubUrl (byActor: MActorUrl, video: MVideoId) { |
50 | return byActor.url + '/views/videos/' + video.id + '/' + new Date().toISOString() | 56 | return byActor.url + '/views/videos/' + video.id + '/' + new Date().toISOString() |
51 | } | 57 | } |
52 | 58 | ||
53 | function getVideoLikeActivityPubUrl (byActor: ActorModelUrl, video: VideoModel | { id: number }) { | 59 | function getVideoLikeActivityPubUrl (byActor: MActorUrl, video: MVideoId) { |
54 | return byActor.url + '/likes/' + video.id | 60 | return byActor.url + '/likes/' + video.id |
55 | } | 61 | } |
56 | 62 | ||
57 | function getVideoDislikeActivityPubUrl (byActor: ActorModelUrl, video: VideoModel | { id: number }) { | 63 | function getVideoDislikeActivityPubUrl (byActor: MActorUrl, video: MVideoId) { |
58 | return byActor.url + '/dislikes/' + video.id | 64 | return byActor.url + '/dislikes/' + video.id |
59 | } | 65 | } |
60 | 66 | ||
61 | function getVideoSharesActivityPubUrl (video: VideoModel) { | 67 | function getVideoSharesActivityPubUrl (video: MVideoUrl) { |
62 | return video.url + '/announces' | 68 | return video.url + '/announces' |
63 | } | 69 | } |
64 | 70 | ||
65 | function getVideoCommentsActivityPubUrl (video: VideoModel) { | 71 | function getVideoCommentsActivityPubUrl (video: MVideoUrl) { |
66 | return video.url + '/comments' | 72 | return video.url + '/comments' |
67 | } | 73 | } |
68 | 74 | ||
69 | function getVideoLikesActivityPubUrl (video: VideoModel) { | 75 | function getVideoLikesActivityPubUrl (video: MVideoUrl) { |
70 | return video.url + '/likes' | 76 | return video.url + '/likes' |
71 | } | 77 | } |
72 | 78 | ||
73 | function getVideoDislikesActivityPubUrl (video: VideoModel) { | 79 | function getVideoDislikesActivityPubUrl (video: MVideoUrl) { |
74 | return video.url + '/dislikes' | 80 | return video.url + '/dislikes' |
75 | } | 81 | } |
76 | 82 | ||
77 | function getActorFollowActivityPubUrl (follower: ActorModelOnly, following: ActorModelOnly) { | 83 | function getActorFollowActivityPubUrl (follower: MActor, following: MActorId) { |
78 | return follower.url + '/follows/' + following.id | 84 | return follower.url + '/follows/' + following.id |
79 | } | 85 | } |
80 | 86 | ||
81 | function getActorFollowAcceptActivityPubUrl (actorFollow: ActorFollowModelLight) { | 87 | function getActorFollowAcceptActivityPubUrl (actorFollow: MActorFollowActors) { |
82 | const follower = actorFollow.ActorFollower | 88 | const follower = actorFollow.ActorFollower |
83 | const me = actorFollow.ActorFollowing | 89 | const me = actorFollow.ActorFollowing |
84 | 90 | ||
85 | return follower.url + '/accepts/follows/' + me.id | 91 | return follower.url + '/accepts/follows/' + me.id |
86 | } | 92 | } |
87 | 93 | ||
88 | function getActorFollowRejectActivityPubUrl (follower: ActorModelOnly, following: ActorModelOnly) { | 94 | function getActorFollowRejectActivityPubUrl (follower: MActorUrl, following: MActorId) { |
89 | return follower.url + '/rejects/follows/' + following.id | 95 | return follower.url + '/rejects/follows/' + following.id |
90 | } | 96 | } |
91 | 97 | ||
92 | function getVideoAnnounceActivityPubUrl (byActor: ActorModelOnly, video: VideoModel) { | 98 | function getVideoAnnounceActivityPubUrl (byActor: MActorId, video: MVideoUrl) { |
93 | return video.url + '/announces/' + byActor.id | 99 | return video.url + '/announces/' + byActor.id |
94 | } | 100 | } |
95 | 101 | ||
diff --git a/server/lib/activitypub/video-comments.ts b/server/lib/activitypub/video-comments.ts index 8d2c1ade3..3e8306fa4 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 | |||
2 | import { logger } from '../../helpers/logger' | 2 | import { logger } from '../../helpers/logger' |
3 | import { doRequest } from '../../helpers/requests' | 3 | import { doRequest } from '../../helpers/requests' |
4 | import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' | 4 | import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' |
5 | import { VideoModel } from '../../models/video/video' | ||
6 | import { VideoCommentModel } from '../../models/video/video-comment' | 5 | import { VideoCommentModel } from '../../models/video/video-comment' |
7 | import { getOrCreateActorAndServerAndModel } from './actor' | 6 | import { getOrCreateActorAndServerAndModel } from './actor' |
8 | import { getOrCreateVideoAndAccountAndChannel } from './videos' | 7 | import { getOrCreateVideoAndAccountAndChannel } from './videos' |
9 | import * as Bluebird from 'bluebird' | 8 | import * as Bluebird from 'bluebird' |
10 | import { checkUrlsSameHost } from '../../helpers/activitypub' | 9 | import { checkUrlsSameHost } from '../../helpers/activitypub' |
10 | import { MCommentOwner, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFiles } from '../../typings/models/video' | ||
11 | 11 | ||
12 | type ResolveThreadParams = { | 12 | type ResolveThreadParams = { |
13 | url: string, | 13 | url: string, |
14 | comments?: VideoCommentModel[], | 14 | comments?: MCommentOwner[], |
15 | isVideo?: boolean, | 15 | isVideo?: boolean, |
16 | commentCreated?: boolean | 16 | commentCreated?: boolean |
17 | } | 17 | } |
18 | type ResolveThreadResult = Promise<{ video: VideoModel, comment: VideoCommentModel, commentCreated: boolean }> | 18 | type ResolveThreadResult = Promise<{ video: MVideoAccountLightBlacklistAllFiles, comment: MCommentOwnerVideo, commentCreated: boolean }> |
19 | 19 | ||
20 | async function addVideoComments (commentUrls: string[]) { | 20 | async function addVideoComments (commentUrls: string[]) { |
21 | return Bluebird.map(commentUrls, commentUrl => { | 21 | return Bluebird.map(commentUrls, commentUrl => { |
@@ -85,9 +85,9 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) { | |||
85 | const syncParam = { likes: true, dislikes: true, shares: true, comments: false, thumbnail: true, refreshVideo: false } | 85 | const syncParam = { likes: true, dislikes: true, shares: true, comments: false, thumbnail: true, refreshVideo: false } |
86 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: url, syncParam }) | 86 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: url, syncParam }) |
87 | 87 | ||
88 | let resultComment: VideoCommentModel | 88 | let resultComment: MCommentOwnerVideo |
89 | if (comments.length !== 0) { | 89 | if (comments.length !== 0) { |
90 | const firstReply = comments[ comments.length - 1 ] | 90 | const firstReply = comments[ comments.length - 1 ] as MCommentOwnerVideo |
91 | firstReply.inReplyToCommentId = null | 91 | firstReply.inReplyToCommentId = null |
92 | firstReply.originCommentId = null | 92 | firstReply.originCommentId = null |
93 | firstReply.videoId = video.id | 93 | firstReply.videoId = video.id |
@@ -97,7 +97,7 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) { | |||
97 | comments[comments.length - 1] = await firstReply.save() | 97 | comments[comments.length - 1] = await firstReply.save() |
98 | 98 | ||
99 | for (let i = comments.length - 2; i >= 0; i--) { | 99 | for (let i = comments.length - 2; i >= 0; i--) { |
100 | const comment = comments[ i ] | 100 | const comment = comments[ i ] as MCommentOwnerVideo |
101 | comment.originCommentId = firstReply.id | 101 | comment.originCommentId = firstReply.id |
102 | comment.inReplyToCommentId = comments[ i + 1 ].id | 102 | comment.inReplyToCommentId = comments[ i + 1 ].id |
103 | comment.videoId = video.id | 103 | comment.videoId = video.id |
@@ -107,7 +107,7 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) { | |||
107 | comments[i] = await comment.save() | 107 | comments[i] = await comment.save() |
108 | } | 108 | } |
109 | 109 | ||
110 | resultComment = comments[0] | 110 | resultComment = comments[0] as MCommentOwnerVideo |
111 | } | 111 | } |
112 | 112 | ||
113 | return { video, comment: resultComment, commentCreated } | 113 | return { video, comment: resultComment, commentCreated } |
@@ -151,7 +151,7 @@ async function resolveParentComment (params: ResolveThreadParams) { | |||
151 | originCommentId: null, | 151 | originCommentId: null, |
152 | createdAt: new Date(body.published), | 152 | createdAt: new Date(body.published), |
153 | updatedAt: new Date(body.updated) | 153 | updatedAt: new Date(body.updated) |
154 | }) | 154 | }) as MCommentOwner |
155 | comment.Account = actor.Account | 155 | comment.Account = actor.Account |
156 | 156 | ||
157 | return resolveThread({ | 157 | return resolveThread({ |
diff --git a/server/lib/activitypub/video-rates.ts b/server/lib/activitypub/video-rates.ts index cda5b2981..6bd46bb58 100644 --- a/server/lib/activitypub/video-rates.ts +++ b/server/lib/activitypub/video-rates.ts | |||
@@ -1,6 +1,4 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { AccountModel } from '../../models/account/account' | ||
3 | import { VideoModel } from '../../models/video/video' | ||
4 | import { sendLike, sendUndoDislike, sendUndoLike } from './send' | 2 | import { sendLike, sendUndoDislike, sendUndoLike } from './send' |
5 | import { VideoRateType } from '../../../shared/models/videos' | 3 | import { VideoRateType } from '../../../shared/models/videos' |
6 | import * as Bluebird from 'bluebird' | 4 | import * as Bluebird from 'bluebird' |
@@ -10,11 +8,11 @@ import { logger } from '../../helpers/logger' | |||
10 | import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' | 8 | import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' |
11 | import { doRequest } from '../../helpers/requests' | 9 | import { doRequest } from '../../helpers/requests' |
12 | import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' | 10 | import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' |
13 | import { ActorModel } from '../../models/activitypub/actor' | ||
14 | import { getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from './url' | 11 | import { getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from './url' |
15 | import { sendDislike } from './send/send-dislike' | 12 | import { sendDislike } from './send/send-dislike' |
13 | import { MAccountActor, MActorUrl, MVideo, MVideoAccountLight, MVideoId } from '../../typings/models' | ||
16 | 14 | ||
17 | async function createRates (ratesUrl: string[], video: VideoModel, rate: VideoRateType) { | 15 | async function createRates (ratesUrl: string[], video: MVideo, rate: VideoRateType) { |
18 | let rateCounts = 0 | 16 | let rateCounts = 0 |
19 | 17 | ||
20 | await Bluebird.map(ratesUrl, async rateUrl => { | 18 | await Bluebird.map(ratesUrl, async rateUrl => { |
@@ -64,11 +62,13 @@ async function createRates (ratesUrl: string[], video: VideoModel, rate: VideoRa | |||
64 | return | 62 | return |
65 | } | 63 | } |
66 | 64 | ||
67 | async function sendVideoRateChange (account: AccountModel, | 65 | async function sendVideoRateChange ( |
68 | video: VideoModel, | 66 | account: MAccountActor, |
69 | likes: number, | 67 | video: MVideoAccountLight, |
70 | dislikes: number, | 68 | likes: number, |
71 | t: Transaction) { | 69 | dislikes: number, |
70 | t: Transaction | ||
71 | ) { | ||
72 | const actor = account.Actor | 72 | const actor = account.Actor |
73 | 73 | ||
74 | // Keep the order: first we undo and then we create | 74 | // Keep the order: first we undo and then we create |
@@ -84,8 +84,10 @@ async function sendVideoRateChange (account: AccountModel, | |||
84 | if (dislikes > 0) await sendDislike(actor, video, t) | 84 | if (dislikes > 0) await sendDislike(actor, video, t) |
85 | } | 85 | } |
86 | 86 | ||
87 | function getRateUrl (rateType: VideoRateType, actor: ActorModel, video: VideoModel) { | 87 | function getRateUrl (rateType: VideoRateType, actor: MActorUrl, video: MVideoId) { |
88 | return rateType === 'like' ? getVideoLikeActivityPubUrl(actor, video) : getVideoDislikeActivityPubUrl(actor, video) | 88 | return rateType === 'like' |
89 | ? getVideoLikeActivityPubUrl(actor, video) | ||
90 | : getVideoDislikeActivityPubUrl(actor, video) | ||
89 | } | 91 | } |
90 | 92 | ||
91 | export { | 93 | export { |
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts index 3a8451a32..c318978fd 100644 --- a/server/lib/activitypub/videos.ts +++ b/server/lib/activitypub/videos.ts | |||
@@ -24,7 +24,6 @@ import { | |||
24 | REMOTE_SCHEME, | 24 | REMOTE_SCHEME, |
25 | STATIC_PATHS | 25 | STATIC_PATHS |
26 | } from '../../initializers/constants' | 26 | } from '../../initializers/constants' |
27 | import { ActorModel } from '../../models/activitypub/actor' | ||
28 | import { TagModel } from '../../models/video/tag' | 27 | import { TagModel } from '../../models/video/tag' |
29 | import { VideoModel } from '../../models/video/video' | 28 | import { VideoModel } from '../../models/video/video' |
30 | import { VideoFileModel } from '../../models/video/video-file' | 29 | import { VideoFileModel } from '../../models/video/video-file' |
@@ -38,7 +37,6 @@ import { JobQueue } from '../job-queue' | |||
38 | import { ActivitypubHttpFetcherPayload } from '../job-queue/handlers/activitypub-http-fetcher' | 37 | import { ActivitypubHttpFetcherPayload } from '../job-queue/handlers/activitypub-http-fetcher' |
39 | import { createRates } from './video-rates' | 38 | import { createRates } from './video-rates' |
40 | import { addVideoShares, shareVideoByServerAndChannel } from './share' | 39 | import { addVideoShares, shareVideoByServerAndChannel } from './share' |
41 | import { AccountModel } from '../../models/account/account' | ||
42 | import { fetchVideoByUrl, VideoFetchByUrlType } from '../../helpers/video' | 40 | import { fetchVideoByUrl, VideoFetchByUrlType } from '../../helpers/video' |
43 | import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' | 41 | import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' |
44 | import { Notifier } from '../notifier' | 42 | import { Notifier } from '../notifier' |
@@ -49,15 +47,31 @@ import { VideoShareModel } from '../../models/video/video-share' | |||
49 | import { VideoCommentModel } from '../../models/video/video-comment' | 47 | import { VideoCommentModel } from '../../models/video/video-comment' |
50 | import { sequelizeTypescript } from '../../initializers/database' | 48 | import { sequelizeTypescript } from '../../initializers/database' |
51 | import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '../thumbnail' | 49 | import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '../thumbnail' |
52 | import { ThumbnailModel } from '../../models/video/thumbnail' | ||
53 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' | 50 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' |
54 | import { join } from 'path' | 51 | import { join } from 'path' |
55 | import { FilteredModelAttributes } from '../../typings/sequelize' | 52 | import { FilteredModelAttributes } from '../../typings/sequelize' |
56 | import { autoBlacklistVideoIfNeeded } from '../video-blacklist' | 53 | import { autoBlacklistVideoIfNeeded } from '../video-blacklist' |
57 | import { ActorFollowScoreCache } from '../files-cache' | 54 | import { ActorFollowScoreCache } from '../files-cache' |
58 | import { AccountModelIdActor, VideoChannelModelId, VideoChannelModelIdActor } from '../../typings/models' | 55 | import { |
56 | MAccountIdActor, | ||
57 | MChannelAccountLight, | ||
58 | MChannelDefault, | ||
59 | MChannelId, | ||
60 | MVideo, | ||
61 | MVideoAccountLight, | ||
62 | MVideoAccountLightBlacklistAllFiles, | ||
63 | MVideoAP, | ||
64 | MVideoAPWithoutCaption, | ||
65 | MVideoFile, | ||
66 | MVideoFullLight, | ||
67 | MVideoId, | ||
68 | MVideoThumbnail | ||
69 | } from '../../typings/models' | ||
70 | import { MThumbnail } from '../../typings/models/video/thumbnail' | ||
71 | |||
72 | async function federateVideoIfNeeded (videoArg: MVideoAPWithoutCaption, isNewVideo: boolean, transaction?: sequelize.Transaction) { | ||
73 | const video = videoArg as MVideoAP | ||
59 | 74 | ||
60 | async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) { | ||
61 | if ( | 75 | if ( |
62 | // Check this is not a blacklisted video, or unfederated blacklisted video | 76 | // Check this is not a blacklisted video, or unfederated blacklisted video |
63 | (video.isBlacklisted() === false || (isNewVideo === false && video.VideoBlacklist.unfederated === false)) && | 77 | (video.isBlacklisted() === false || (isNewVideo === false && video.VideoBlacklist.unfederated === false)) && |
@@ -102,7 +116,7 @@ async function fetchRemoteVideo (videoUrl: string): Promise<{ response: request. | |||
102 | return { response, videoObject: body } | 116 | return { response, videoObject: body } |
103 | } | 117 | } |
104 | 118 | ||
105 | async function fetchRemoteVideoDescription (video: VideoModel) { | 119 | async function fetchRemoteVideoDescription (video: MVideoAccountLight) { |
106 | const host = video.VideoChannel.Account.Actor.Server.host | 120 | const host = video.VideoChannel.Account.Actor.Server.host |
107 | const path = video.getDescriptionAPIPath() | 121 | const path = video.getDescriptionAPIPath() |
108 | const options = { | 122 | const options = { |
@@ -114,14 +128,14 @@ async function fetchRemoteVideoDescription (video: VideoModel) { | |||
114 | return body.description ? body.description : '' | 128 | return body.description ? body.description : '' |
115 | } | 129 | } |
116 | 130 | ||
117 | function fetchRemoteVideoStaticFile (video: VideoModel, path: string, destPath: string) { | 131 | function fetchRemoteVideoStaticFile (video: MVideoAccountLight, path: string, destPath: string) { |
118 | const url = buildRemoteBaseUrl(video, path) | 132 | const url = buildRemoteBaseUrl(video, path) |
119 | 133 | ||
120 | // We need to provide a callback, if no we could have an uncaught exception | 134 | // We need to provide a callback, if no we could have an uncaught exception |
121 | return doRequestAndSaveToFile({ uri: url }, destPath) | 135 | return doRequestAndSaveToFile({ uri: url }, destPath) |
122 | } | 136 | } |
123 | 137 | ||
124 | function buildRemoteBaseUrl (video: VideoModel, path: string) { | 138 | function buildRemoteBaseUrl (video: MVideoAccountLight, path: string) { |
125 | const host = video.VideoChannel.Account.Actor.Server.host | 139 | const host = video.VideoChannel.Account.Actor.Server.host |
126 | 140 | ||
127 | return REMOTE_SCHEME.HTTP + '://' + host + path | 141 | return REMOTE_SCHEME.HTTP + '://' + host + path |
@@ -146,7 +160,7 @@ type SyncParam = { | |||
146 | thumbnail: boolean | 160 | thumbnail: boolean |
147 | refreshVideo?: boolean | 161 | refreshVideo?: boolean |
148 | } | 162 | } |
149 | async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) { | 163 | async function syncVideoExternalAttributes (video: MVideo, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) { |
150 | logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid) | 164 | logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid) |
151 | 165 | ||
152 | const jobPayloads: ActivitypubHttpFetcherPayload[] = [] | 166 | const jobPayloads: ActivitypubHttpFetcherPayload[] = [] |
@@ -194,12 +208,24 @@ async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: Vid | |||
194 | await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })) | 208 | await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })) |
195 | } | 209 | } |
196 | 210 | ||
211 | function getOrCreateVideoAndAccountAndChannel (options: { | ||
212 | videoObject: { id: string } | string, | ||
213 | syncParam?: SyncParam, | ||
214 | fetchType?: 'all', | ||
215 | allowRefresh?: boolean | ||
216 | }): Promise<{ video: MVideoAccountLightBlacklistAllFiles, created: boolean, autoBlacklisted?: boolean }> | ||
217 | function getOrCreateVideoAndAccountAndChannel (options: { | ||
218 | videoObject: { id: string } | string, | ||
219 | syncParam?: SyncParam, | ||
220 | fetchType?: VideoFetchByUrlType, | ||
221 | allowRefresh?: boolean | ||
222 | }): Promise<{ video: MVideoAccountLightBlacklistAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }> | ||
197 | async function getOrCreateVideoAndAccountAndChannel (options: { | 223 | async function getOrCreateVideoAndAccountAndChannel (options: { |
198 | videoObject: { id: string } | string, | 224 | videoObject: { id: string } | string, |
199 | syncParam?: SyncParam, | 225 | syncParam?: SyncParam, |
200 | fetchType?: VideoFetchByUrlType, | 226 | fetchType?: VideoFetchByUrlType, |
201 | allowRefresh?: boolean // true by default | 227 | allowRefresh?: boolean // true by default |
202 | }) { | 228 | }): Promise<{ video: MVideoAccountLightBlacklistAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }> { |
203 | // Default params | 229 | // Default params |
204 | const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } | 230 | const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } |
205 | const fetchType = options.fetchType || 'all' | 231 | const fetchType = options.fetchType || 'all' |
@@ -227,8 +253,9 @@ async function getOrCreateVideoAndAccountAndChannel (options: { | |||
227 | const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl) | 253 | const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl) |
228 | if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl) | 254 | if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl) |
229 | 255 | ||
230 | const channelActor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo) | 256 | const actor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo) |
231 | const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, channelActor, syncParam.thumbnail) | 257 | const videoChannel = actor.VideoChannel |
258 | const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, videoChannel, syncParam.thumbnail) | ||
232 | 259 | ||
233 | await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam) | 260 | await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam) |
234 | 261 | ||
@@ -236,22 +263,22 @@ async function getOrCreateVideoAndAccountAndChannel (options: { | |||
236 | } | 263 | } |
237 | 264 | ||
238 | async function updateVideoFromAP (options: { | 265 | async function updateVideoFromAP (options: { |
239 | video: VideoModel, | 266 | video: MVideoAccountLightBlacklistAllFiles, |
240 | videoObject: VideoTorrentObject, | 267 | videoObject: VideoTorrentObject, |
241 | account: AccountModelIdActor, | 268 | account: MAccountIdActor, |
242 | channel: VideoChannelModelIdActor, | 269 | channel: MChannelDefault, |
243 | overrideTo?: string[] | 270 | overrideTo?: string[] |
244 | }) { | 271 | }) { |
245 | const { video, videoObject, account, channel, overrideTo } = options | 272 | const { video, videoObject, account, channel, overrideTo } = options |
246 | 273 | ||
247 | logger.debug('Updating remote video "%s".', options.videoObject.uuid) | 274 | logger.debug('Updating remote video "%s".', options.videoObject.uuid, { account, channel }) |
248 | 275 | ||
249 | let videoFieldsSave: any | 276 | let videoFieldsSave: any |
250 | const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE | 277 | const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE |
251 | const wasUnlistedVideo = video.privacy === VideoPrivacy.UNLISTED | 278 | const wasUnlistedVideo = video.privacy === VideoPrivacy.UNLISTED |
252 | 279 | ||
253 | try { | 280 | try { |
254 | let thumbnailModel: ThumbnailModel | 281 | let thumbnailModel: MThumbnail |
255 | 282 | ||
256 | try { | 283 | try { |
257 | thumbnailModel = await createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) | 284 | thumbnailModel = await createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) |
@@ -259,7 +286,7 @@ async function updateVideoFromAP (options: { | |||
259 | logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err }) | 286 | logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err }) |
260 | } | 287 | } |
261 | 288 | ||
262 | await sequelizeTypescript.transaction(async t => { | 289 | const videoUpdated = await sequelizeTypescript.transaction(async t => { |
263 | const sequelizeOptions = { transaction: t } | 290 | const sequelizeOptions = { transaction: t } |
264 | 291 | ||
265 | videoFieldsSave = video.toJSON() | 292 | videoFieldsSave = video.toJSON() |
@@ -293,21 +320,21 @@ async function updateVideoFromAP (options: { | |||
293 | video.channelId = videoData.channelId | 320 | video.channelId = videoData.channelId |
294 | video.views = videoData.views | 321 | video.views = videoData.views |
295 | 322 | ||
296 | await video.save(sequelizeOptions) | 323 | const videoUpdated = await video.save(sequelizeOptions) as MVideoFullLight |
297 | 324 | ||
298 | if (thumbnailModel) await video.addAndSaveThumbnail(thumbnailModel, t) | 325 | if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel, t) |
299 | 326 | ||
300 | // FIXME: use icon URL instead | 327 | // FIXME: use icon URL instead |
301 | const previewUrl = buildRemoteBaseUrl(video, join(STATIC_PATHS.PREVIEWS, video.getPreview().filename)) | 328 | const previewUrl = buildRemoteBaseUrl(videoUpdated, join(STATIC_PATHS.PREVIEWS, videoUpdated.getPreview().filename)) |
302 | const previewModel = createPlaceholderThumbnail(previewUrl, video, ThumbnailType.PREVIEW, PREVIEWS_SIZE) | 329 | const previewModel = createPlaceholderThumbnail(previewUrl, video, ThumbnailType.PREVIEW, PREVIEWS_SIZE) |
303 | await video.addAndSaveThumbnail(previewModel, t) | 330 | await videoUpdated.addAndSaveThumbnail(previewModel, t) |
304 | 331 | ||
305 | { | 332 | { |
306 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(video, videoObject) | 333 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoUpdated, videoObject) |
307 | const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a)) | 334 | const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a)) |
308 | 335 | ||
309 | // Remove video files that do not exist anymore | 336 | // Remove video files that do not exist anymore |
310 | const destroyTasks = video.VideoFiles | 337 | const destroyTasks = videoUpdated.VideoFiles |
311 | .filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f))) | 338 | .filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f))) |
312 | .map(f => f.destroy(sequelizeOptions)) | 339 | .map(f => f.destroy(sequelizeOptions)) |
313 | await Promise.all(destroyTasks) | 340 | await Promise.all(destroyTasks) |
@@ -318,15 +345,15 @@ async function updateVideoFromAP (options: { | |||
318 | .then(([ file ]) => file) | 345 | .then(([ file ]) => file) |
319 | }) | 346 | }) |
320 | 347 | ||
321 | video.VideoFiles = await Promise.all(upsertTasks) | 348 | videoUpdated.VideoFiles = await Promise.all(upsertTasks) |
322 | } | 349 | } |
323 | 350 | ||
324 | { | 351 | { |
325 | const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(video, videoObject, video.VideoFiles) | 352 | const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(videoUpdated, videoObject, videoUpdated.VideoFiles) |
326 | const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) | 353 | const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) |
327 | 354 | ||
328 | // Remove video files that do not exist anymore | 355 | // Remove video files that do not exist anymore |
329 | const destroyTasks = video.VideoStreamingPlaylists | 356 | const destroyTasks = videoUpdated.VideoStreamingPlaylists |
330 | .filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f))) | 357 | .filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f))) |
331 | .map(f => f.destroy(sequelizeOptions)) | 358 | .map(f => f.destroy(sequelizeOptions)) |
332 | await Promise.all(destroyTasks) | 359 | await Promise.all(destroyTasks) |
@@ -337,38 +364,42 @@ async function updateVideoFromAP (options: { | |||
337 | .then(([ streamingPlaylist ]) => streamingPlaylist) | 364 | .then(([ streamingPlaylist ]) => streamingPlaylist) |
338 | }) | 365 | }) |
339 | 366 | ||
340 | video.VideoStreamingPlaylists = await Promise.all(upsertTasks) | 367 | videoUpdated.VideoStreamingPlaylists = await Promise.all(upsertTasks) |
341 | } | 368 | } |
342 | 369 | ||
343 | { | 370 | { |
344 | // Update Tags | 371 | // Update Tags |
345 | const tags = videoObject.tag.map(tag => tag.name) | 372 | const tags = videoObject.tag.map(tag => tag.name) |
346 | const tagInstances = await TagModel.findOrCreateTags(tags, t) | 373 | const tagInstances = await TagModel.findOrCreateTags(tags, t) |
347 | await video.$set('Tags', tagInstances, sequelizeOptions) | 374 | await videoUpdated.$set('Tags', tagInstances, sequelizeOptions) |
348 | } | 375 | } |
349 | 376 | ||
350 | { | 377 | { |
351 | // Update captions | 378 | // Update captions |
352 | await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(video.id, t) | 379 | await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(videoUpdated.id, t) |
353 | 380 | ||
354 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { | 381 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { |
355 | return VideoCaptionModel.insertOrReplaceLanguage(video.id, c.identifier, t) | 382 | return VideoCaptionModel.insertOrReplaceLanguage(videoUpdated.id, c.identifier, t) |
356 | }) | 383 | }) |
357 | video.VideoCaptions = await Promise.all(videoCaptionsPromises) | 384 | await Promise.all(videoCaptionsPromises) |
358 | } | 385 | } |
386 | |||
387 | return videoUpdated | ||
359 | }) | 388 | }) |
360 | 389 | ||
361 | await autoBlacklistVideoIfNeeded({ | 390 | await autoBlacklistVideoIfNeeded({ |
362 | video, | 391 | video: videoUpdated, |
363 | user: undefined, | 392 | user: undefined, |
364 | isRemote: true, | 393 | isRemote: true, |
365 | isNew: false, | 394 | isNew: false, |
366 | transaction: undefined | 395 | transaction: undefined |
367 | }) | 396 | }) |
368 | 397 | ||
369 | if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(video) // Notify our users? | 398 | if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated) // Notify our users? |
370 | 399 | ||
371 | logger.info('Remote video with uuid %s updated', videoObject.uuid) | 400 | logger.info('Remote video with uuid %s updated', videoObject.uuid) |
401 | |||
402 | return videoUpdated | ||
372 | } catch (err) { | 403 | } catch (err) { |
373 | if (video !== undefined && videoFieldsSave !== undefined) { | 404 | if (video !== undefined && videoFieldsSave !== undefined) { |
374 | resetSequelizeInstance(video, videoFieldsSave) | 405 | resetSequelizeInstance(video, videoFieldsSave) |
@@ -381,15 +412,15 @@ async function updateVideoFromAP (options: { | |||
381 | } | 412 | } |
382 | 413 | ||
383 | async function refreshVideoIfNeeded (options: { | 414 | async function refreshVideoIfNeeded (options: { |
384 | video: VideoModel, | 415 | video: MVideoThumbnail, |
385 | fetchedType: VideoFetchByUrlType, | 416 | fetchedType: VideoFetchByUrlType, |
386 | syncParam: SyncParam | 417 | syncParam: SyncParam |
387 | }): Promise<VideoModel> { | 418 | }): Promise<MVideoThumbnail> { |
388 | if (!options.video.isOutdated()) return options.video | 419 | if (!options.video.isOutdated()) return options.video |
389 | 420 | ||
390 | // We need more attributes if the argument video was fetched with not enough joints | 421 | // We need more attributes if the argument video was fetched with not enough joints |
391 | const video = options.fetchedType === 'all' | 422 | const video = options.fetchedType === 'all' |
392 | ? options.video | 423 | ? options.video as MVideoAccountLightBlacklistAllFiles |
393 | : await VideoModel.loadByUrlAndPopulateAccount(options.video.url) | 424 | : await VideoModel.loadByUrlAndPopulateAccount(options.video.url) |
394 | 425 | ||
395 | try { | 426 | try { |
@@ -410,12 +441,11 @@ async function refreshVideoIfNeeded (options: { | |||
410 | } | 441 | } |
411 | 442 | ||
412 | const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) | 443 | const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) |
413 | const account = await AccountModel.load(channelActor.VideoChannel.accountId) | ||
414 | 444 | ||
415 | const updateOptions = { | 445 | const updateOptions = { |
416 | video, | 446 | video, |
417 | videoObject, | 447 | videoObject, |
418 | account, | 448 | account: channelActor.VideoChannel.Account, |
419 | channel: channelActor.VideoChannel | 449 | channel: channelActor.VideoChannel |
420 | } | 450 | } |
421 | await retryTransactionWrapper(updateVideoFromAP, updateOptions) | 451 | await retryTransactionWrapper(updateVideoFromAP, updateOptions) |
@@ -467,15 +497,15 @@ function isAPPlaylistSegmentHashesUrlObject (tag: any): tag is ActivityPlaylistS | |||
467 | return tag.name === 'sha256' && tag.type === 'Link' && urlMediaType === 'application/json' | 497 | return tag.name === 'sha256' && tag.type === 'Link' && urlMediaType === 'application/json' |
468 | } | 498 | } |
469 | 499 | ||
470 | async function createVideo (videoObject: VideoTorrentObject, channelActor: ActorModel, waitThumbnail = false) { | 500 | async function createVideo (videoObject: VideoTorrentObject, channel: MChannelAccountLight, waitThumbnail = false) { |
471 | logger.debug('Adding remote video %s.', videoObject.id) | 501 | logger.debug('Adding remote video %s.', videoObject.id) |
472 | 502 | ||
473 | const videoData = await videoActivityObjectToDBAttributes(channelActor.VideoChannel, videoObject, videoObject.to) | 503 | const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, videoObject.to) |
474 | const video = VideoModel.build(videoData) | 504 | const video = VideoModel.build(videoData) as MVideoThumbnail |
475 | 505 | ||
476 | const promiseThumbnail = createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) | 506 | const promiseThumbnail = createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) |
477 | 507 | ||
478 | let thumbnailModel: ThumbnailModel | 508 | let thumbnailModel: MThumbnail |
479 | if (waitThumbnail === true) { | 509 | if (waitThumbnail === true) { |
480 | thumbnailModel = await promiseThumbnail | 510 | thumbnailModel = await promiseThumbnail |
481 | } | 511 | } |
@@ -483,8 +513,8 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor | |||
483 | const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => { | 513 | const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => { |
484 | const sequelizeOptions = { transaction: t } | 514 | const sequelizeOptions = { transaction: t } |
485 | 515 | ||
486 | const videoCreated = await video.save(sequelizeOptions) | 516 | const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight |
487 | videoCreated.VideoChannel = channelActor.VideoChannel | 517 | videoCreated.VideoChannel = channel |
488 | 518 | ||
489 | if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) | 519 | if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) |
490 | 520 | ||
@@ -517,15 +547,14 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor | |||
517 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { | 547 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { |
518 | return VideoCaptionModel.insertOrReplaceLanguage(videoCreated.id, c.identifier, t) | 548 | return VideoCaptionModel.insertOrReplaceLanguage(videoCreated.id, c.identifier, t) |
519 | }) | 549 | }) |
520 | const captions = await Promise.all(videoCaptionsPromises) | 550 | await Promise.all(videoCaptionsPromises) |
521 | 551 | ||
522 | video.VideoFiles = videoFiles | 552 | videoCreated.VideoFiles = videoFiles |
523 | video.VideoStreamingPlaylists = streamingPlaylists | 553 | videoCreated.VideoStreamingPlaylists = streamingPlaylists |
524 | video.Tags = tagInstances | 554 | videoCreated.Tags = tagInstances |
525 | video.VideoCaptions = captions | ||
526 | 555 | ||
527 | const autoBlacklisted = await autoBlacklistVideoIfNeeded({ | 556 | const autoBlacklisted = await autoBlacklistVideoIfNeeded({ |
528 | video, | 557 | video: videoCreated, |
529 | user: undefined, | 558 | user: undefined, |
530 | isRemote: true, | 559 | isRemote: true, |
531 | isNew: true, | 560 | isNew: true, |
@@ -548,11 +577,7 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor | |||
548 | return { autoBlacklisted, videoCreated } | 577 | return { autoBlacklisted, videoCreated } |
549 | } | 578 | } |
550 | 579 | ||
551 | async function videoActivityObjectToDBAttributes ( | 580 | async function videoActivityObjectToDBAttributes (videoChannel: MChannelId, videoObject: VideoTorrentObject, to: string[] = []) { |
552 | videoChannel: VideoChannelModelId, | ||
553 | videoObject: VideoTorrentObject, | ||
554 | to: string[] = [] | ||
555 | ) { | ||
556 | const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED | 581 | const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED |
557 | const duration = videoObject.duration.replace(/[^\d]+/, '') | 582 | const duration = videoObject.duration.replace(/[^\d]+/, '') |
558 | 583 | ||
@@ -603,7 +628,7 @@ async function videoActivityObjectToDBAttributes ( | |||
603 | } | 628 | } |
604 | } | 629 | } |
605 | 630 | ||
606 | function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: VideoTorrentObject) { | 631 | function videoFileActivityUrlToDBAttributes (video: MVideo, videoObject: VideoTorrentObject) { |
607 | const fileUrls = videoObject.url.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[] | 632 | const fileUrls = videoObject.url.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[] |
608 | 633 | ||
609 | if (fileUrls.length === 0) { | 634 | if (fileUrls.length === 0) { |
@@ -641,7 +666,7 @@ function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: Vid | |||
641 | return attributes | 666 | return attributes |
642 | } | 667 | } |
643 | 668 | ||
644 | function streamingPlaylistActivityUrlToDBAttributes (video: VideoModel, videoObject: VideoTorrentObject, videoFiles: VideoFileModel[]) { | 669 | function streamingPlaylistActivityUrlToDBAttributes (video: MVideoId, videoObject: VideoTorrentObject, videoFiles: MVideoFile[]) { |
645 | const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[] | 670 | const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[] |
646 | if (playlistUrls.length === 0) return [] | 671 | if (playlistUrls.length === 0) return [] |
647 | 672 | ||
diff --git a/server/lib/avatar.ts b/server/lib/avatar.ts index 1b38e6cb5..ad4cdd3ab 100644 --- a/server/lib/avatar.ts +++ b/server/lib/avatar.ts | |||
@@ -3,8 +3,6 @@ import { sendUpdateActor } from './activitypub/send' | |||
3 | import { AVATARS_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants' | 3 | import { AVATARS_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants' |
4 | import { updateActorAvatarInstance } from './activitypub' | 4 | import { updateActorAvatarInstance } from './activitypub' |
5 | import { processImage } from '../helpers/image-utils' | 5 | import { processImage } from '../helpers/image-utils' |
6 | import { AccountModel } from '../models/account/account' | ||
7 | import { VideoChannelModel } from '../models/video/video-channel' | ||
8 | import { extname, join } from 'path' | 6 | import { extname, join } from 'path' |
9 | import { retryTransactionWrapper } from '../helpers/database-utils' | 7 | import { retryTransactionWrapper } from '../helpers/database-utils' |
10 | import * as uuidv4 from 'uuid/v4' | 8 | import * as uuidv4 from 'uuid/v4' |
@@ -13,8 +11,12 @@ import { sequelizeTypescript } from '../initializers/database' | |||
13 | import * as LRUCache from 'lru-cache' | 11 | import * as LRUCache from 'lru-cache' |
14 | import { queue } from 'async' | 12 | import { queue } from 'async' |
15 | import { downloadImage } from '../helpers/requests' | 13 | import { downloadImage } from '../helpers/requests' |
14 | import { MAccountDefault, MChannelDefault } from '../typings/models' | ||
16 | 15 | ||
17 | async function updateActorAvatarFile (avatarPhysicalFile: Express.Multer.File, accountOrChannel: AccountModel | VideoChannelModel) { | 16 | async function updateActorAvatarFile ( |
17 | avatarPhysicalFile: Express.Multer.File, | ||
18 | accountOrChannel: MAccountDefault | MChannelDefault | ||
19 | ) { | ||
18 | const extension = extname(avatarPhysicalFile.filename) | 20 | const extension = extname(avatarPhysicalFile.filename) |
19 | const avatarName = uuidv4() + extension | 21 | const avatarName = uuidv4() + extension |
20 | const destination = join(CONFIG.STORAGE.AVATARS_DIR, avatarName) | 22 | const destination = join(CONFIG.STORAGE.AVATARS_DIR, avatarName) |
diff --git a/server/lib/blocklist.ts b/server/lib/blocklist.ts index 1633e500c..28c69b46e 100644 --- a/server/lib/blocklist.ts +++ b/server/lib/blocklist.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { sequelizeTypescript } from '../initializers' | 1 | import { sequelizeTypescript } from '../initializers' |
2 | import { AccountBlocklistModel } from '../models/account/account-blocklist' | 2 | import { AccountBlocklistModel } from '../models/account/account-blocklist' |
3 | import { ServerBlocklistModel } from '../models/server/server-blocklist' | 3 | import { ServerBlocklistModel } from '../models/server/server-blocklist' |
4 | import { MAccountBlocklist, MServerBlocklist } from '@server/typings/models' | ||
4 | 5 | ||
5 | function addAccountInBlocklist (byAccountId: number, targetAccountId: number) { | 6 | function addAccountInBlocklist (byAccountId: number, targetAccountId: number) { |
6 | return sequelizeTypescript.transaction(async t => { | 7 | return sequelizeTypescript.transaction(async t => { |
@@ -20,13 +21,13 @@ function addServerInBlocklist (byAccountId: number, targetServerId: number) { | |||
20 | }) | 21 | }) |
21 | } | 22 | } |
22 | 23 | ||
23 | function removeAccountFromBlocklist (accountBlock: AccountBlocklistModel) { | 24 | function removeAccountFromBlocklist (accountBlock: MAccountBlocklist) { |
24 | return sequelizeTypescript.transaction(async t => { | 25 | return sequelizeTypescript.transaction(async t => { |
25 | return accountBlock.destroy({ transaction: t }) | 26 | return accountBlock.destroy({ transaction: t }) |
26 | }) | 27 | }) |
27 | } | 28 | } |
28 | 29 | ||
29 | function removeServerFromBlocklist (serverBlock: ServerBlocklistModel) { | 30 | function removeServerFromBlocklist (serverBlock: MServerBlocklist) { |
30 | return sequelizeTypescript.transaction(async t => { | 31 | return sequelizeTypescript.transaction(async t => { |
31 | return serverBlock.destroy({ transaction: t }) | 32 | return serverBlock.destroy({ transaction: t }) |
32 | }) | 33 | }) |
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts index 8841dd2ac..a1f4ae858 100644 --- a/server/lib/client-html.ts +++ b/server/lib/client-html.ts | |||
@@ -13,6 +13,7 @@ import { VideoChannelModel } from '../models/video/video-channel' | |||
13 | import * as Bluebird from 'bluebird' | 13 | import * as Bluebird from 'bluebird' |
14 | import { CONFIG } from '../initializers/config' | 14 | import { CONFIG } from '../initializers/config' |
15 | import { logger } from '../helpers/logger' | 15 | import { logger } from '../helpers/logger' |
16 | import { MAccountActor, MChannelActor, MVideo } from '../typings/models' | ||
16 | 17 | ||
17 | export class ClientHtml { | 18 | export class ClientHtml { |
18 | 19 | ||
@@ -41,11 +42,11 @@ export class ClientHtml { | |||
41 | 42 | ||
42 | const [ html, video ] = await Promise.all([ | 43 | const [ html, video ] = await Promise.all([ |
43 | ClientHtml.getIndexHTML(req, res), | 44 | ClientHtml.getIndexHTML(req, res), |
44 | VideoModel.load(videoId) | 45 | VideoModel.loadWithBlacklist(videoId) |
45 | ]) | 46 | ]) |
46 | 47 | ||
47 | // Let Angular application handle errors | 48 | // Let Angular application handle errors |
48 | if (!video || video.privacy === VideoPrivacy.PRIVATE) { | 49 | if (!video || video.privacy === VideoPrivacy.PRIVATE || video.VideoBlacklist) { |
49 | return ClientHtml.getIndexHTML(req, res) | 50 | return ClientHtml.getIndexHTML(req, res) |
50 | } | 51 | } |
51 | 52 | ||
@@ -65,7 +66,7 @@ export class ClientHtml { | |||
65 | } | 66 | } |
66 | 67 | ||
67 | private static async getAccountOrChannelHTMLPage ( | 68 | private static async getAccountOrChannelHTMLPage ( |
68 | loader: () => Bluebird<AccountModel | VideoChannelModel>, | 69 | loader: () => Bluebird<MAccountActor | MChannelActor>, |
69 | req: express.Request, | 70 | req: express.Request, |
70 | res: express.Response | 71 | res: express.Response |
71 | ) { | 72 | ) { |
@@ -157,7 +158,7 @@ export class ClientHtml { | |||
157 | return htmlStringPage.replace('</head>', linkTag + '</head>') | 158 | return htmlStringPage.replace('</head>', linkTag + '</head>') |
158 | } | 159 | } |
159 | 160 | ||
160 | private static addVideoOpenGraphAndOEmbedTags (htmlStringPage: string, video: VideoModel) { | 161 | private static addVideoOpenGraphAndOEmbedTags (htmlStringPage: string, video: MVideo) { |
161 | const previewUrl = WEBSERVER.URL + video.getPreviewStaticPath() | 162 | const previewUrl = WEBSERVER.URL + video.getPreviewStaticPath() |
162 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() | 163 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() |
163 | 164 | ||
@@ -236,7 +237,7 @@ export class ClientHtml { | |||
236 | return this.addOpenGraphAndOEmbedTags(htmlStringPage, tagsString) | 237 | return this.addOpenGraphAndOEmbedTags(htmlStringPage, tagsString) |
237 | } | 238 | } |
238 | 239 | ||
239 | private static addAccountOrChannelMetaTags (htmlStringPage: string, entity: AccountModel | VideoChannelModel) { | 240 | private static addAccountOrChannelMetaTags (htmlStringPage: string, entity: MAccountActor | MChannelActor) { |
240 | // SEO, use origin account or channel URL | 241 | // SEO, use origin account or channel URL |
241 | const metaTags = `<link rel="canonical" href="${entity.Actor.url}" />` | 242 | const metaTags = `<link rel="canonical" href="${entity.Actor.url}" />` |
242 | 243 | ||
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts index 10e7d0479..bd3d4f252 100644 --- a/server/lib/emailer.ts +++ b/server/lib/emailer.ts | |||
@@ -2,17 +2,20 @@ import { createTransport, Transporter } from 'nodemailer' | |||
2 | import { isTestInstance } from '../helpers/core-utils' | 2 | import { isTestInstance } from '../helpers/core-utils' |
3 | import { bunyanLogger, logger } from '../helpers/logger' | 3 | import { bunyanLogger, logger } from '../helpers/logger' |
4 | import { CONFIG } from '../initializers/config' | 4 | import { CONFIG } from '../initializers/config' |
5 | import { UserModel } from '../models/account/user' | ||
6 | import { VideoModel } from '../models/video/video' | ||
7 | import { JobQueue } from './job-queue' | 5 | import { JobQueue } from './job-queue' |
8 | import { EmailPayload } from './job-queue/handlers/email' | 6 | import { EmailPayload } from './job-queue/handlers/email' |
9 | import { readFileSync } from 'fs-extra' | 7 | import { readFileSync } from 'fs-extra' |
10 | import { VideoCommentModel } from '../models/video/video-comment' | ||
11 | import { VideoAbuseModel } from '../models/video/video-abuse' | ||
12 | import { VideoBlacklistModel } from '../models/video/video-blacklist' | ||
13 | import { VideoImportModel } from '../models/video/video-import' | ||
14 | import { ActorFollowModel } from '../models/activitypub/actor-follow' | ||
15 | import { WEBSERVER } from '../initializers/constants' | 8 | import { WEBSERVER } from '../initializers/constants' |
9 | import { | ||
10 | MCommentOwnerVideo, | ||
11 | MVideo, | ||
12 | MVideoAbuseVideo, | ||
13 | MVideoAccountLight, | ||
14 | MVideoBlacklistLightVideo, | ||
15 | MVideoBlacklistVideo | ||
16 | } from '../typings/models/video' | ||
17 | import { MActorFollowActors, MActorFollowFull, MUser } from '../typings/models' | ||
18 | import { MVideoImport, MVideoImportVideo } from '@server/typings/models/video/video-import' | ||
16 | 19 | ||
17 | type SendEmailOptions = { | 20 | type SendEmailOptions = { |
18 | to: string[] | 21 | to: string[] |
@@ -90,7 +93,7 @@ class Emailer { | |||
90 | } | 93 | } |
91 | } | 94 | } |
92 | 95 | ||
93 | addNewVideoFromSubscriberNotification (to: string[], video: VideoModel) { | 96 | addNewVideoFromSubscriberNotification (to: string[], video: MVideoAccountLight) { |
94 | const channelName = video.VideoChannel.getDisplayName() | 97 | const channelName = video.VideoChannel.getDisplayName() |
95 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() | 98 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() |
96 | 99 | ||
@@ -111,7 +114,7 @@ class Emailer { | |||
111 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 114 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
112 | } | 115 | } |
113 | 116 | ||
114 | addNewFollowNotification (to: string[], actorFollow: ActorFollowModel, followType: 'account' | 'channel') { | 117 | addNewFollowNotification (to: string[], actorFollow: MActorFollowFull, followType: 'account' | 'channel') { |
115 | const followerName = actorFollow.ActorFollower.Account.getDisplayName() | 118 | const followerName = actorFollow.ActorFollower.Account.getDisplayName() |
116 | const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName() | 119 | const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName() |
117 | 120 | ||
@@ -130,7 +133,7 @@ class Emailer { | |||
130 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 133 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
131 | } | 134 | } |
132 | 135 | ||
133 | addNewInstanceFollowerNotification (to: string[], actorFollow: ActorFollowModel) { | 136 | addNewInstanceFollowerNotification (to: string[], actorFollow: MActorFollowActors) { |
134 | const awaitingApproval = actorFollow.state === 'pending' ? ' awaiting manual approval.' : '' | 137 | const awaitingApproval = actorFollow.state === 'pending' ? ' awaiting manual approval.' : '' |
135 | 138 | ||
136 | const text = `Hi dear admin,\n\n` + | 139 | const text = `Hi dear admin,\n\n` + |
@@ -148,7 +151,23 @@ class Emailer { | |||
148 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 151 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
149 | } | 152 | } |
150 | 153 | ||
151 | myVideoPublishedNotification (to: string[], video: VideoModel) { | 154 | addAutoInstanceFollowingNotification (to: string[], actorFollow: MActorFollowActors) { |
155 | const text = `Hi dear admin,\n\n` + | ||
156 | `Your instance automatically followed a new instance: ${actorFollow.ActorFollowing.url}` + | ||
157 | `\n\n` + | ||
158 | `Cheers,\n` + | ||
159 | `${CONFIG.EMAIL.BODY.SIGNATURE}` | ||
160 | |||
161 | const emailPayload: EmailPayload = { | ||
162 | to, | ||
163 | subject: CONFIG.EMAIL.SUBJECT.PREFIX + 'Auto instance following', | ||
164 | text | ||
165 | } | ||
166 | |||
167 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | ||
168 | } | ||
169 | |||
170 | myVideoPublishedNotification (to: string[], video: MVideo) { | ||
152 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() | 171 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() |
153 | 172 | ||
154 | const text = `Hi dear user,\n\n` + | 173 | const text = `Hi dear user,\n\n` + |
@@ -168,7 +187,7 @@ class Emailer { | |||
168 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 187 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
169 | } | 188 | } |
170 | 189 | ||
171 | myVideoImportSuccessNotification (to: string[], videoImport: VideoImportModel) { | 190 | myVideoImportSuccessNotification (to: string[], videoImport: MVideoImportVideo) { |
172 | const videoUrl = WEBSERVER.URL + videoImport.Video.getWatchStaticPath() | 191 | const videoUrl = WEBSERVER.URL + videoImport.Video.getWatchStaticPath() |
173 | 192 | ||
174 | const text = `Hi dear user,\n\n` + | 193 | const text = `Hi dear user,\n\n` + |
@@ -188,7 +207,7 @@ class Emailer { | |||
188 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 207 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
189 | } | 208 | } |
190 | 209 | ||
191 | myVideoImportErrorNotification (to: string[], videoImport: VideoImportModel) { | 210 | myVideoImportErrorNotification (to: string[], videoImport: MVideoImport) { |
192 | const importUrl = WEBSERVER.URL + '/my-account/video-imports' | 211 | const importUrl = WEBSERVER.URL + '/my-account/video-imports' |
193 | 212 | ||
194 | const text = `Hi dear user,\n\n` + | 213 | const text = `Hi dear user,\n\n` + |
@@ -208,7 +227,7 @@ class Emailer { | |||
208 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 227 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
209 | } | 228 | } |
210 | 229 | ||
211 | addNewCommentOnMyVideoNotification (to: string[], comment: VideoCommentModel) { | 230 | addNewCommentOnMyVideoNotification (to: string[], comment: MCommentOwnerVideo) { |
212 | const accountName = comment.Account.getDisplayName() | 231 | const accountName = comment.Account.getDisplayName() |
213 | const video = comment.Video | 232 | const video = comment.Video |
214 | const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() | 233 | const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() |
@@ -230,7 +249,7 @@ class Emailer { | |||
230 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 249 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
231 | } | 250 | } |
232 | 251 | ||
233 | addNewCommentMentionNotification (to: string[], comment: VideoCommentModel) { | 252 | addNewCommentMentionNotification (to: string[], comment: MCommentOwnerVideo) { |
234 | const accountName = comment.Account.getDisplayName() | 253 | const accountName = comment.Account.getDisplayName() |
235 | const video = comment.Video | 254 | const video = comment.Video |
236 | const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() | 255 | const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() |
@@ -252,7 +271,7 @@ class Emailer { | |||
252 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 271 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
253 | } | 272 | } |
254 | 273 | ||
255 | addVideoAbuseModeratorsNotification (to: string[], videoAbuse: VideoAbuseModel) { | 274 | addVideoAbuseModeratorsNotification (to: string[], videoAbuse: MVideoAbuseVideo) { |
256 | const videoUrl = WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath() | 275 | const videoUrl = WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath() |
257 | 276 | ||
258 | const text = `Hi,\n\n` + | 277 | const text = `Hi,\n\n` + |
@@ -269,9 +288,9 @@ class Emailer { | |||
269 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 288 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
270 | } | 289 | } |
271 | 290 | ||
272 | addVideoAutoBlacklistModeratorsNotification (to: string[], video: VideoModel) { | 291 | addVideoAutoBlacklistModeratorsNotification (to: string[], videoBlacklist: MVideoBlacklistLightVideo) { |
273 | const VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list' | 292 | const VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list' |
274 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() | 293 | const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() |
275 | 294 | ||
276 | const text = `Hi,\n\n` + | 295 | const text = `Hi,\n\n` + |
277 | `A recently added video was auto-blacklisted and requires moderator review before publishing.` + | 296 | `A recently added video was auto-blacklisted and requires moderator review before publishing.` + |
@@ -292,7 +311,7 @@ class Emailer { | |||
292 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 311 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
293 | } | 312 | } |
294 | 313 | ||
295 | addNewUserRegistrationNotification (to: string[], user: UserModel) { | 314 | addNewUserRegistrationNotification (to: string[], user: MUser) { |
296 | const text = `Hi,\n\n` + | 315 | const text = `Hi,\n\n` + |
297 | `User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` + | 316 | `User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` + |
298 | `Cheers,\n` + | 317 | `Cheers,\n` + |
@@ -307,7 +326,7 @@ class Emailer { | |||
307 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 326 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
308 | } | 327 | } |
309 | 328 | ||
310 | addVideoBlacklistNotification (to: string[], videoBlacklist: VideoBlacklistModel) { | 329 | addVideoBlacklistNotification (to: string[], videoBlacklist: MVideoBlacklistVideo) { |
311 | const videoName = videoBlacklist.Video.name | 330 | const videoName = videoBlacklist.Video.name |
312 | const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() | 331 | const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() |
313 | 332 | ||
@@ -329,7 +348,7 @@ class Emailer { | |||
329 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 348 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
330 | } | 349 | } |
331 | 350 | ||
332 | addVideoUnblacklistNotification (to: string[], video: VideoModel) { | 351 | addVideoUnblacklistNotification (to: string[], video: MVideo) { |
333 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() | 352 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() |
334 | 353 | ||
335 | const text = 'Hi,\n\n' + | 354 | const text = 'Hi,\n\n' + |
@@ -381,7 +400,7 @@ class Emailer { | |||
381 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 400 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
382 | } | 401 | } |
383 | 402 | ||
384 | addUserBlockJob (user: UserModel, blocked: boolean, reason?: string) { | 403 | addUserBlockJob (user: MUser, blocked: boolean, reason?: string) { |
385 | const reasonString = reason ? ` for the following reason: ${reason}` : '' | 404 | const reasonString = reason ? ` for the following reason: ${reason}` : '' |
386 | const blockedWord = blocked ? 'blocked' : 'unblocked' | 405 | const blockedWord = blocked ? 'blocked' : 'unblocked' |
387 | const blockedString = `Your account ${user.username} on ${WEBSERVER.HOST} has been ${blockedWord}${reasonString}.` | 406 | 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 @@ | |||
1 | import { VideoModel } from '../models/video/video' | ||
2 | import { basename, dirname, join } from 'path' | 1 | import { basename, dirname, join } from 'path' |
3 | import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from '../initializers/constants' | 2 | import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from '../initializers/constants' |
4 | import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, writeFile } from 'fs-extra' | 3 | import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, writeFile } from 'fs-extra' |
@@ -12,6 +11,7 @@ import { flatten, uniq } from 'lodash' | |||
12 | import { VideoFileModel } from '../models/video/video-file' | 11 | import { VideoFileModel } from '../models/video/video-file' |
13 | import { CONFIG } from '../initializers/config' | 12 | import { CONFIG } from '../initializers/config' |
14 | import { sequelizeTypescript } from '../initializers/database' | 13 | import { sequelizeTypescript } from '../initializers/database' |
14 | import { MVideoWithFile } from '@server/typings/models' | ||
15 | 15 | ||
16 | async function updateStreamingPlaylistsInfohashesIfNeeded () { | 16 | async function updateStreamingPlaylistsInfohashesIfNeeded () { |
17 | const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion() | 17 | const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion() |
@@ -28,7 +28,7 @@ async function updateStreamingPlaylistsInfohashesIfNeeded () { | |||
28 | } | 28 | } |
29 | } | 29 | } |
30 | 30 | ||
31 | async function updateMasterHLSPlaylist (video: VideoModel) { | 31 | async function updateMasterHLSPlaylist (video: MVideoWithFile) { |
32 | const directory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) | 32 | const directory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) |
33 | const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ] | 33 | const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ] |
34 | const masterPlaylistPath = join(directory, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename()) | 34 | const masterPlaylistPath = join(directory, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename()) |
@@ -55,7 +55,7 @@ async function updateMasterHLSPlaylist (video: VideoModel) { | |||
55 | await writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n') | 55 | await writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n') |
56 | } | 56 | } |
57 | 57 | ||
58 | async function updateSha256Segments (video: VideoModel) { | 58 | async function updateSha256Segments (video: MVideoWithFile) { |
59 | const json: { [filename: string]: { [range: string]: string } } = {} | 59 | const json: { [filename: string]: { [range: string]: string } } = {} |
60 | 60 | ||
61 | const playlistDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) | 61 | const playlistDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) |
diff --git a/server/lib/job-queue/handlers/activitypub-follow.ts b/server/lib/job-queue/handlers/activitypub-follow.ts index 4ae66cd01..af7c8a838 100644 --- a/server/lib/job-queue/handlers/activitypub-follow.ts +++ b/server/lib/job-queue/handlers/activitypub-follow.ts | |||
@@ -10,11 +10,13 @@ import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | |||
10 | import { ActorModel } from '../../../models/activitypub/actor' | 10 | import { ActorModel } from '../../../models/activitypub/actor' |
11 | import { Notifier } from '../../notifier' | 11 | import { Notifier } from '../../notifier' |
12 | import { sequelizeTypescript } from '../../../initializers/database' | 12 | import { sequelizeTypescript } from '../../../initializers/database' |
13 | import { MActor, MActorFollowActors, MActorFull } from '../../../typings/models' | ||
13 | 14 | ||
14 | export type ActivitypubFollowPayload = { | 15 | export type ActivitypubFollowPayload = { |
15 | followerActorId: number | 16 | followerActorId: number |
16 | name: string | 17 | name: string |
17 | host: string | 18 | host: string |
19 | isAutoFollow?: boolean | ||
18 | } | 20 | } |
19 | 21 | ||
20 | async function processActivityPubFollow (job: Bull.Job) { | 22 | async function processActivityPubFollow (job: Bull.Job) { |
@@ -23,18 +25,18 @@ async function processActivityPubFollow (job: Bull.Job) { | |||
23 | 25 | ||
24 | logger.info('Processing ActivityPub follow in job %d.', job.id) | 26 | logger.info('Processing ActivityPub follow in job %d.', job.id) |
25 | 27 | ||
26 | let targetActor: ActorModel | 28 | let targetActor: MActorFull |
27 | if (!host || host === WEBSERVER.HOST) { | 29 | if (!host || host === WEBSERVER.HOST) { |
28 | targetActor = await ActorModel.loadLocalByName(payload.name) | 30 | targetActor = await ActorModel.loadLocalByName(payload.name) |
29 | } else { | 31 | } else { |
30 | const sanitizedHost = sanitizeHost(host, REMOTE_SCHEME.HTTP) | 32 | const sanitizedHost = sanitizeHost(host, REMOTE_SCHEME.HTTP) |
31 | const actorUrl = await loadActorUrlOrGetFromWebfinger(payload.name + '@' + sanitizedHost) | 33 | const actorUrl = await loadActorUrlOrGetFromWebfinger(payload.name + '@' + sanitizedHost) |
32 | targetActor = await getOrCreateActorAndServerAndModel(actorUrl) | 34 | targetActor = await getOrCreateActorAndServerAndModel(actorUrl, 'all') |
33 | } | 35 | } |
34 | 36 | ||
35 | const fromActor = await ActorModel.load(payload.followerActorId) | 37 | const fromActor = await ActorModel.load(payload.followerActorId) |
36 | 38 | ||
37 | return retryTransactionWrapper(follow, fromActor, targetActor) | 39 | return retryTransactionWrapper(follow, fromActor, targetActor, payload.isAutoFollow) |
38 | } | 40 | } |
39 | // --------------------------------------------------------------------------- | 41 | // --------------------------------------------------------------------------- |
40 | 42 | ||
@@ -44,7 +46,7 @@ export { | |||
44 | 46 | ||
45 | // --------------------------------------------------------------------------- | 47 | // --------------------------------------------------------------------------- |
46 | 48 | ||
47 | async function follow (fromActor: ActorModel, targetActor: ActorModel) { | 49 | async function follow (fromActor: MActor, targetActor: MActorFull, isAutoFollow = false) { |
48 | if (fromActor.id === targetActor.id) { | 50 | if (fromActor.id === targetActor.id) { |
49 | throw new Error('Follower is the same than target actor.') | 51 | throw new Error('Follower is the same than target actor.') |
50 | } | 52 | } |
@@ -53,7 +55,7 @@ async function follow (fromActor: ActorModel, targetActor: ActorModel) { | |||
53 | const state = !fromActor.serverId && !targetActor.serverId ? 'accepted' : 'pending' | 55 | const state = !fromActor.serverId && !targetActor.serverId ? 'accepted' : 'pending' |
54 | 56 | ||
55 | const actorFollow = await sequelizeTypescript.transaction(async t => { | 57 | const actorFollow = await sequelizeTypescript.transaction(async t => { |
56 | const [ actorFollow ] = await ActorFollowModel.findOrCreate({ | 58 | const [ actorFollow ] = await ActorFollowModel.findOrCreate<MActorFollowActors>({ |
57 | where: { | 59 | where: { |
58 | actorId: fromActor.id, | 60 | actorId: fromActor.id, |
59 | targetActorId: targetActor.id | 61 | targetActorId: targetActor.id |
@@ -74,5 +76,15 @@ async function follow (fromActor: ActorModel, targetActor: ActorModel) { | |||
74 | return actorFollow | 76 | return actorFollow |
75 | }) | 77 | }) |
76 | 78 | ||
77 | if (actorFollow.state === 'accepted') Notifier.Instance.notifyOfNewUserFollow(actorFollow) | 79 | const followerFull = await ActorModel.loadFull(fromActor.id) |
80 | |||
81 | const actorFollowFull = Object.assign(actorFollow, { | ||
82 | ActorFollowing: targetActor, | ||
83 | ActorFollower: followerFull | ||
84 | }) | ||
85 | |||
86 | if (actorFollow.state === 'accepted') Notifier.Instance.notifyOfNewUserFollow(actorFollowFull) | ||
87 | if (isAutoFollow === true) Notifier.Instance.notifyOfAutoInstanceFollowing(actorFollowFull) | ||
88 | |||
89 | return actorFollow | ||
78 | } | 90 | } |
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' | |||
11 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | 11 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
12 | import { VideoShareModel } from '../../../models/video/video-share' | 12 | import { VideoShareModel } from '../../../models/video/video-share' |
13 | import { VideoCommentModel } from '../../../models/video/video-comment' | 13 | import { VideoCommentModel } from '../../../models/video/video-comment' |
14 | import { MAccountDefault, MVideoFullLight } from '../../../typings/models' | ||
14 | 15 | ||
15 | type FetchType = 'activity' | 'video-likes' | 'video-dislikes' | 'video-shares' | 'video-comments' | 'account-playlists' | 16 | type FetchType = 'activity' | 'video-likes' | 'video-dislikes' | 'video-shares' | 'video-comments' | 'account-playlists' |
16 | 17 | ||
@@ -26,10 +27,10 @@ async function processActivityPubHttpFetcher (job: Bull.Job) { | |||
26 | 27 | ||
27 | const payload = job.data as ActivitypubHttpFetcherPayload | 28 | const payload = job.data as ActivitypubHttpFetcherPayload |
28 | 29 | ||
29 | let video: VideoModel | 30 | let video: MVideoFullLight |
30 | if (payload.videoId) video = await VideoModel.loadAndPopulateAccountAndServerAndTags(payload.videoId) | 31 | if (payload.videoId) video = await VideoModel.loadAndPopulateAccountAndServerAndTags(payload.videoId) |
31 | 32 | ||
32 | let account: AccountModel | 33 | let account: MAccountDefault |
33 | if (payload.accountId) account = await AccountModel.load(payload.accountId) | 34 | if (payload.accountId) account = await AccountModel.load(payload.accountId) |
34 | 35 | ||
35 | const fetcherType: { [ id in FetchType ]: (items: any[]) => Promise<any> } = { | 36 | const fetcherType: { [ id in FetchType ]: (items: any[]) => Promise<any> } = { |
diff --git a/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts b/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts index cdee1f6fd..d3bde6e6a 100644 --- a/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts +++ b/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts | |||
@@ -3,6 +3,7 @@ import { getServerActor } from '../../../../helpers/utils' | |||
3 | import { ActorModel } from '../../../../models/activitypub/actor' | 3 | import { ActorModel } from '../../../../models/activitypub/actor' |
4 | import { sha256 } from '../../../../helpers/core-utils' | 4 | import { sha256 } from '../../../../helpers/core-utils' |
5 | import { HTTP_SIGNATURE } from '../../../../initializers/constants' | 5 | import { HTTP_SIGNATURE } from '../../../../initializers/constants' |
6 | import { MActor } from '../../../../typings/models' | ||
6 | 7 | ||
7 | type Payload = { body: any, signatureActorId?: number } | 8 | type Payload = { body: any, signatureActorId?: number } |
8 | 9 | ||
@@ -19,7 +20,8 @@ async function computeBody (payload: Payload) { | |||
19 | } | 20 | } |
20 | 21 | ||
21 | async function buildSignedRequestOptions (payload: Payload) { | 22 | async function buildSignedRequestOptions (payload: Payload) { |
22 | let actor: ActorModel | null | 23 | let actor: MActor | null |
24 | |||
23 | if (payload.signatureActorId) { | 25 | if (payload.signatureActorId) { |
24 | actor = await ActorModel.load(payload.signatureActorId) | 26 | actor = await ActorModel.load(payload.signatureActorId) |
25 | if (!actor) throw new Error('Unknown signature actor id.') | 27 | if (!actor) throw new Error('Unknown signature actor id.') |
diff --git a/server/lib/job-queue/handlers/video-file-import.ts b/server/lib/job-queue/handlers/video-file-import.ts index 8cacb0ef3..5c5b7dccb 100644 --- a/server/lib/job-queue/handlers/video-file-import.ts +++ b/server/lib/job-queue/handlers/video-file-import.ts | |||
@@ -6,6 +6,7 @@ import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg | |||
6 | import { copy, stat } from 'fs-extra' | 6 | import { copy, stat } from 'fs-extra' |
7 | import { VideoFileModel } from '../../../models/video/video-file' | 7 | import { VideoFileModel } from '../../../models/video/video-file' |
8 | import { extname } from 'path' | 8 | import { extname } from 'path' |
9 | import { MVideoFile, MVideoWithFile } from '@server/typings/models' | ||
9 | 10 | ||
10 | export type VideoFileImportPayload = { | 11 | export type VideoFileImportPayload = { |
11 | videoUUID: string, | 12 | videoUUID: string, |
@@ -37,7 +38,7 @@ export { | |||
37 | 38 | ||
38 | // --------------------------------------------------------------------------- | 39 | // --------------------------------------------------------------------------- |
39 | 40 | ||
40 | async function updateVideoFile (video: VideoModel, inputFilePath: string) { | 41 | async function updateVideoFile (video: MVideoWithFile, inputFilePath: string) { |
41 | const { videoFileResolution } = await getVideoFileResolution(inputFilePath) | 42 | const { videoFileResolution } = await getVideoFileResolution(inputFilePath) |
42 | const { size } = await stat(inputFilePath) | 43 | const { size } = await stat(inputFilePath) |
43 | const fps = await getVideoFileFPS(inputFilePath) | 44 | const fps = await getVideoFileFPS(inputFilePath) |
@@ -48,7 +49,7 @@ async function updateVideoFile (video: VideoModel, inputFilePath: string) { | |||
48 | size, | 49 | size, |
49 | fps, | 50 | fps, |
50 | videoId: video.id | 51 | videoId: video.id |
51 | }) | 52 | }) as MVideoFile |
52 | 53 | ||
53 | const currentVideoFile = video.VideoFiles.find(videoFile => videoFile.resolution === updatedVideoFile.resolution) | 54 | const currentVideoFile = video.VideoFiles.find(videoFile => videoFile.resolution === updatedVideoFile.resolution) |
54 | 55 | ||
@@ -60,9 +61,9 @@ async function updateVideoFile (video: VideoModel, inputFilePath: string) { | |||
60 | video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile) | 61 | video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile) |
61 | 62 | ||
62 | // Update the database | 63 | // Update the database |
63 | currentVideoFile.set('extname', updatedVideoFile.extname) | 64 | currentVideoFile.extname = updatedVideoFile.extname |
64 | currentVideoFile.set('size', updatedVideoFile.size) | 65 | currentVideoFile.size = updatedVideoFile.size |
65 | currentVideoFile.set('fps', updatedVideoFile.fps) | 66 | currentVideoFile.fps = updatedVideoFile.fps |
66 | 67 | ||
67 | updatedVideoFile = currentVideoFile | 68 | updatedVideoFile = currentVideoFile |
68 | } | 69 | } |
diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts index 13b741180..93a3e9d90 100644 --- a/server/lib/job-queue/handlers/video-import.ts +++ b/server/lib/job-queue/handlers/video-import.ts | |||
@@ -17,9 +17,11 @@ import { move, remove, stat } from 'fs-extra' | |||
17 | import { Notifier } from '../../notifier' | 17 | import { Notifier } from '../../notifier' |
18 | import { CONFIG } from '../../../initializers/config' | 18 | import { CONFIG } from '../../../initializers/config' |
19 | import { sequelizeTypescript } from '../../../initializers/database' | 19 | import { sequelizeTypescript } from '../../../initializers/database' |
20 | import { ThumbnailModel } from '../../../models/video/thumbnail' | ||
21 | import { createVideoMiniatureFromUrl, generateVideoMiniature } from '../../thumbnail' | 20 | import { createVideoMiniatureFromUrl, generateVideoMiniature } from '../../thumbnail' |
22 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' | 21 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' |
22 | import { MThumbnail } from '../../../typings/models/video/thumbnail' | ||
23 | import { MVideoImportDefault, MVideoImportDefaultFiles, MVideoImportVideo } from '@server/typings/models/video/video-import' | ||
24 | import { MVideoBlacklistVideo, MVideoBlacklist } from '@server/typings/models' | ||
23 | 25 | ||
24 | type VideoImportYoutubeDLPayload = { | 26 | type VideoImportYoutubeDLPayload = { |
25 | type: 'youtube-dl' | 27 | type: 'youtube-dl' |
@@ -110,7 +112,7 @@ type ProcessFileOptions = { | |||
110 | generateThumbnail: boolean | 112 | generateThumbnail: boolean |
111 | generatePreview: boolean | 113 | generatePreview: boolean |
112 | } | 114 | } |
113 | async function processFile (downloader: () => Promise<string>, videoImport: VideoImportModel, options: ProcessFileOptions) { | 115 | async function processFile (downloader: () => Promise<string>, videoImport: MVideoImportDefault, options: ProcessFileOptions) { |
114 | let tempVideoPath: string | 116 | let tempVideoPath: string |
115 | let videoDestFile: string | 117 | let videoDestFile: string |
116 | let videoFile: VideoFileModel | 118 | let videoFile: VideoFileModel |
@@ -139,41 +141,44 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide | |||
139 | videoId: videoImport.videoId | 141 | videoId: videoImport.videoId |
140 | } | 142 | } |
141 | videoFile = new VideoFileModel(videoFileData) | 143 | videoFile = new VideoFileModel(videoFileData) |
144 | |||
145 | const videoWithFiles = Object.assign(videoImport.Video, { VideoFiles: [ videoFile ] }) | ||
142 | // To clean files if the import fails | 146 | // To clean files if the import fails |
143 | videoImport.Video.VideoFiles = [ videoFile ] | 147 | const videoImportWithFiles: MVideoImportDefaultFiles = Object.assign(videoImport, { Video: videoWithFiles }) |
144 | 148 | ||
145 | // Move file | 149 | // Move file |
146 | videoDestFile = join(CONFIG.STORAGE.VIDEOS_DIR, videoImport.Video.getVideoFilename(videoFile)) | 150 | videoDestFile = join(CONFIG.STORAGE.VIDEOS_DIR, videoImportWithFiles.Video.getVideoFilename(videoFile)) |
147 | await move(tempVideoPath, videoDestFile) | 151 | await move(tempVideoPath, videoDestFile) |
148 | tempVideoPath = null // This path is not used anymore | 152 | tempVideoPath = null // This path is not used anymore |
149 | 153 | ||
150 | // Process thumbnail | 154 | // Process thumbnail |
151 | let thumbnailModel: ThumbnailModel | 155 | let thumbnailModel: MThumbnail |
152 | if (options.downloadThumbnail && options.thumbnailUrl) { | 156 | if (options.downloadThumbnail && options.thumbnailUrl) { |
153 | thumbnailModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.MINIATURE) | 157 | thumbnailModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImportWithFiles.Video, ThumbnailType.MINIATURE) |
154 | } else if (options.generateThumbnail || options.downloadThumbnail) { | 158 | } else if (options.generateThumbnail || options.downloadThumbnail) { |
155 | thumbnailModel = await generateVideoMiniature(videoImport.Video, videoFile, ThumbnailType.MINIATURE) | 159 | thumbnailModel = await generateVideoMiniature(videoImportWithFiles.Video, videoFile, ThumbnailType.MINIATURE) |
156 | } | 160 | } |
157 | 161 | ||
158 | // Process preview | 162 | // Process preview |
159 | let previewModel: ThumbnailModel | 163 | let previewModel: MThumbnail |
160 | if (options.downloadPreview && options.thumbnailUrl) { | 164 | if (options.downloadPreview && options.thumbnailUrl) { |
161 | previewModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.PREVIEW) | 165 | previewModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImportWithFiles.Video, ThumbnailType.PREVIEW) |
162 | } else if (options.generatePreview || options.downloadPreview) { | 166 | } else if (options.generatePreview || options.downloadPreview) { |
163 | previewModel = await generateVideoMiniature(videoImport.Video, videoFile, ThumbnailType.PREVIEW) | 167 | previewModel = await generateVideoMiniature(videoImportWithFiles.Video, videoFile, ThumbnailType.PREVIEW) |
164 | } | 168 | } |
165 | 169 | ||
166 | // Create torrent | 170 | // Create torrent |
167 | await videoImport.Video.createTorrentAndSetInfoHash(videoFile) | 171 | await videoImportWithFiles.Video.createTorrentAndSetInfoHash(videoFile) |
172 | |||
173 | const { videoImportUpdated, video } = await sequelizeTypescript.transaction(async t => { | ||
174 | const videoImportToUpdate = videoImportWithFiles as MVideoImportVideo | ||
168 | 175 | ||
169 | const videoImportUpdated: VideoImportModel = await sequelizeTypescript.transaction(async t => { | ||
170 | // Refresh video | 176 | // Refresh video |
171 | const video = await VideoModel.load(videoImport.videoId, t) | 177 | const video = await VideoModel.load(videoImportToUpdate.videoId, t) |
172 | if (!video) throw new Error('Video linked to import ' + videoImport.videoId + ' does not exist anymore.') | 178 | if (!video) throw new Error('Video linked to import ' + videoImportToUpdate.videoId + ' does not exist anymore.') |
173 | videoImport.Video = video | ||
174 | 179 | ||
175 | const videoFileCreated = await videoFile.save({ transaction: t }) | 180 | const videoFileCreated = await videoFile.save({ transaction: t }) |
176 | video.VideoFiles = [ videoFileCreated ] | 181 | videoImportToUpdate.Video = Object.assign(video, { VideoFiles: [ videoFileCreated ] }) |
177 | 182 | ||
178 | // Update video DB object | 183 | // Update video DB object |
179 | video.duration = duration | 184 | video.duration = duration |
@@ -188,25 +193,27 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide | |||
188 | await federateVideoIfNeeded(videoForFederation, true, t) | 193 | await federateVideoIfNeeded(videoForFederation, true, t) |
189 | 194 | ||
190 | // Update video import object | 195 | // Update video import object |
191 | videoImport.state = VideoImportState.SUCCESS | 196 | videoImportToUpdate.state = VideoImportState.SUCCESS |
192 | const videoImportUpdated = await videoImport.save({ transaction: t }) | 197 | const videoImportUpdated = await videoImportToUpdate.save({ transaction: t }) as MVideoImportVideo |
198 | videoImportUpdated.Video = video | ||
193 | 199 | ||
194 | logger.info('Video %s imported.', video.uuid) | 200 | logger.info('Video %s imported.', video.uuid) |
195 | 201 | ||
196 | videoImportUpdated.Video = videoForFederation | 202 | return { videoImportUpdated, video: videoForFederation } |
197 | return videoImportUpdated | ||
198 | }) | 203 | }) |
199 | 204 | ||
200 | Notifier.Instance.notifyOnFinishedVideoImport(videoImportUpdated, true) | 205 | Notifier.Instance.notifyOnFinishedVideoImport(videoImportUpdated, true) |
201 | 206 | ||
202 | if (videoImportUpdated.Video.isBlacklisted()) { | 207 | if (video.isBlacklisted()) { |
203 | Notifier.Instance.notifyOnVideoAutoBlacklist(videoImportUpdated.Video) | 208 | const videoBlacklist = Object.assign(video.VideoBlacklist, { Video: video }) |
209 | |||
210 | Notifier.Instance.notifyOnVideoAutoBlacklist(videoBlacklist) | ||
204 | } else { | 211 | } else { |
205 | Notifier.Instance.notifyOnNewVideoIfNeeded(videoImportUpdated.Video) | 212 | Notifier.Instance.notifyOnNewVideoIfNeeded(video) |
206 | } | 213 | } |
207 | 214 | ||
208 | // Create transcoding jobs? | 215 | // Create transcoding jobs? |
209 | if (videoImportUpdated.Video.state === VideoState.TO_TRANSCODE) { | 216 | if (video.state === VideoState.TO_TRANSCODE) { |
210 | // Put uuid because we don't have id auto incremented for now | 217 | // Put uuid because we don't have id auto incremented for now |
211 | const dataInput = { | 218 | const dataInput = { |
212 | type: 'optimize' as 'optimize', | 219 | 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' | |||
11 | import { generateHlsPlaylist, optimizeVideofile, transcodeOriginalVideofile, mergeAudioVideofile } from '../../video-transcoding' | 11 | import { generateHlsPlaylist, optimizeVideofile, transcodeOriginalVideofile, mergeAudioVideofile } from '../../video-transcoding' |
12 | import { Notifier } from '../../notifier' | 12 | import { Notifier } from '../../notifier' |
13 | import { CONFIG } from '../../../initializers/config' | 13 | import { CONFIG } from '../../../initializers/config' |
14 | import { MVideoUUID, MVideoWithFile } from '@server/typings/models' | ||
14 | 15 | ||
15 | interface BaseTranscodingPayload { | 16 | interface BaseTranscodingPayload { |
16 | videoUUID: string | 17 | videoUUID: string |
@@ -73,7 +74,7 @@ async function processVideoTranscoding (job: Bull.Job) { | |||
73 | return video | 74 | return video |
74 | } | 75 | } |
75 | 76 | ||
76 | async function onHlsPlaylistGenerationSuccess (video: VideoModel) { | 77 | async function onHlsPlaylistGenerationSuccess (video: MVideoUUID) { |
77 | if (video === undefined) return undefined | 78 | if (video === undefined) return undefined |
78 | 79 | ||
79 | await sequelizeTypescript.transaction(async t => { | 80 | await sequelizeTypescript.transaction(async t => { |
@@ -87,7 +88,7 @@ async function onHlsPlaylistGenerationSuccess (video: VideoModel) { | |||
87 | }) | 88 | }) |
88 | } | 89 | } |
89 | 90 | ||
90 | async function publishNewResolutionIfNeeded (video: VideoModel, payload?: NewResolutionTranscodingPayload | MergeAudioTranscodingPayload) { | 91 | async function publishNewResolutionIfNeeded (video: MVideoUUID, payload?: NewResolutionTranscodingPayload | MergeAudioTranscodingPayload) { |
91 | const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => { | 92 | const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => { |
92 | // Maybe the video changed in database, refresh it | 93 | // Maybe the video changed in database, refresh it |
93 | let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t) | 94 | let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t) |
@@ -119,7 +120,7 @@ async function publishNewResolutionIfNeeded (video: VideoModel, payload?: NewRes | |||
119 | await createHlsJobIfEnabled(payload) | 120 | await createHlsJobIfEnabled(payload) |
120 | } | 121 | } |
121 | 122 | ||
122 | async function onVideoFileOptimizerSuccess (videoArg: VideoModel, payload: OptimizeTranscodingPayload) { | 123 | async function onVideoFileOptimizerSuccess (videoArg: MVideoWithFile, payload: OptimizeTranscodingPayload) { |
123 | if (videoArg === undefined) return undefined | 124 | if (videoArg === undefined) return undefined |
124 | 125 | ||
125 | // Outside the transaction (IO on disk) | 126 | // Outside the transaction (IO on disk) |
diff --git a/server/lib/notifier.ts b/server/lib/notifier.ts index a7dfb0979..b7cc2607d 100644 --- a/server/lib/notifier.ts +++ b/server/lib/notifier.ts | |||
@@ -1,20 +1,30 @@ | |||
1 | import { UserNotificationSettingValue, UserNotificationType, UserRight } from '../../shared/models/users' | 1 | import { UserNotificationSettingValue, UserNotificationType, UserRight } from '../../shared/models/users' |
2 | import { logger } from '../helpers/logger' | 2 | import { logger } from '../helpers/logger' |
3 | import { VideoModel } from '../models/video/video' | ||
4 | import { Emailer } from './emailer' | 3 | import { Emailer } from './emailer' |
5 | import { UserNotificationModel } from '../models/account/user-notification' | 4 | import { UserNotificationModel } from '../models/account/user-notification' |
6 | import { VideoCommentModel } from '../models/video/video-comment' | ||
7 | import { UserModel } from '../models/account/user' | 5 | import { UserModel } from '../models/account/user' |
8 | import { PeerTubeSocket } from './peertube-socket' | 6 | import { PeerTubeSocket } from './peertube-socket' |
9 | import { CONFIG } from '../initializers/config' | 7 | import { CONFIG } from '../initializers/config' |
10 | import { VideoPrivacy, VideoState } from '../../shared/models/videos' | 8 | import { VideoPrivacy, VideoState } from '../../shared/models/videos' |
11 | import { VideoAbuseModel } from '../models/video/video-abuse' | ||
12 | import { VideoBlacklistModel } from '../models/video/video-blacklist' | ||
13 | import * as Bluebird from 'bluebird' | 9 | import * as Bluebird from 'bluebird' |
14 | import { VideoImportModel } from '../models/video/video-import' | ||
15 | import { AccountBlocklistModel } from '../models/account/account-blocklist' | 10 | import { AccountBlocklistModel } from '../models/account/account-blocklist' |
16 | import { ActorFollowModel } from '../models/activitypub/actor-follow' | 11 | import { |
17 | import { AccountModel } from '../models/account/account' | 12 | MCommentOwnerVideo, |
13 | MVideoAbuseVideo, | ||
14 | MVideoAccountLight, | ||
15 | MVideoBlacklistLightVideo, | ||
16 | MVideoBlacklistVideo, | ||
17 | MVideoFullLight | ||
18 | } from '../typings/models/video' | ||
19 | import { | ||
20 | MUser, | ||
21 | MUserDefault, | ||
22 | MUserNotifSettingAccount, | ||
23 | MUserWithNotificationSetting, | ||
24 | UserNotificationModelForApi | ||
25 | } from '@server/typings/models/user' | ||
26 | import { MActorFollowFull } from '../typings/models' | ||
27 | import { MVideoImportVideo } from '@server/typings/models/video/video-import' | ||
18 | 28 | ||
19 | class Notifier { | 29 | class Notifier { |
20 | 30 | ||
@@ -22,7 +32,7 @@ class Notifier { | |||
22 | 32 | ||
23 | private constructor () {} | 33 | private constructor () {} |
24 | 34 | ||
25 | notifyOnNewVideoIfNeeded (video: VideoModel): void { | 35 | notifyOnNewVideoIfNeeded (video: MVideoAccountLight): void { |
26 | // Only notify on public and published videos which are not blacklisted | 36 | // Only notify on public and published videos which are not blacklisted |
27 | if (video.privacy !== VideoPrivacy.PUBLIC || video.state !== VideoState.PUBLISHED || video.isBlacklisted()) return | 37 | if (video.privacy !== VideoPrivacy.PUBLIC || video.state !== VideoState.PUBLISHED || video.isBlacklisted()) return |
28 | 38 | ||
@@ -30,7 +40,7 @@ class Notifier { | |||
30 | .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err })) | 40 | .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err })) |
31 | } | 41 | } |
32 | 42 | ||
33 | notifyOnVideoPublishedAfterTranscoding (video: VideoModel): void { | 43 | notifyOnVideoPublishedAfterTranscoding (video: MVideoFullLight): void { |
34 | // don't notify if didn't wait for transcoding or video is still blacklisted/waiting for scheduled update | 44 | // don't notify if didn't wait for transcoding or video is still blacklisted/waiting for scheduled update |
35 | if (!video.waitTranscoding || video.VideoBlacklist || video.ScheduleVideoUpdate) return | 45 | if (!video.waitTranscoding || video.VideoBlacklist || video.ScheduleVideoUpdate) return |
36 | 46 | ||
@@ -38,7 +48,7 @@ class Notifier { | |||
38 | .catch(err => logger.error('Cannot notify owner that its video %s has been published after transcoding.', video.url, { err })) | 48 | .catch(err => logger.error('Cannot notify owner that its video %s has been published after transcoding.', video.url, { err })) |
39 | } | 49 | } |
40 | 50 | ||
41 | notifyOnVideoPublishedAfterScheduledUpdate (video: VideoModel): void { | 51 | notifyOnVideoPublishedAfterScheduledUpdate (video: MVideoFullLight): void { |
42 | // don't notify if video is still blacklisted or waiting for transcoding | 52 | // don't notify if video is still blacklisted or waiting for transcoding |
43 | if (video.VideoBlacklist || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return | 53 | if (video.VideoBlacklist || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return |
44 | 54 | ||
@@ -46,7 +56,7 @@ class Notifier { | |||
46 | .catch(err => logger.error('Cannot notify owner that its video %s has been published after scheduled update.', video.url, { err })) | 56 | .catch(err => logger.error('Cannot notify owner that its video %s has been published after scheduled update.', video.url, { err })) |
47 | } | 57 | } |
48 | 58 | ||
49 | notifyOnVideoPublishedAfterRemovedFromAutoBlacklist (video: VideoModel): void { | 59 | notifyOnVideoPublishedAfterRemovedFromAutoBlacklist (video: MVideoFullLight): void { |
50 | // don't notify if video is still waiting for transcoding or scheduled update | 60 | // don't notify if video is still waiting for transcoding or scheduled update |
51 | if (video.ScheduleVideoUpdate || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return | 61 | if (video.ScheduleVideoUpdate || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return |
52 | 62 | ||
@@ -54,7 +64,7 @@ class Notifier { | |||
54 | .catch(err => logger.error('Cannot notify owner that its video %s has been published after removed from auto-blacklist.', video.url, { err })) // tslint:disable-line:max-line-length | 64 | .catch(err => logger.error('Cannot notify owner that its video %s has been published after removed from auto-blacklist.', video.url, { err })) // tslint:disable-line:max-line-length |
55 | } | 65 | } |
56 | 66 | ||
57 | notifyOnNewComment (comment: VideoCommentModel): void { | 67 | notifyOnNewComment (comment: MCommentOwnerVideo): void { |
58 | this.notifyVideoOwnerOfNewComment(comment) | 68 | this.notifyVideoOwnerOfNewComment(comment) |
59 | .catch(err => logger.error('Cannot notify video owner of new comment %s.', comment.url, { err })) | 69 | .catch(err => logger.error('Cannot notify video owner of new comment %s.', comment.url, { err })) |
60 | 70 | ||
@@ -62,37 +72,37 @@ class Notifier { | |||
62 | .catch(err => logger.error('Cannot notify mentions of comment %s.', comment.url, { err })) | 72 | .catch(err => logger.error('Cannot notify mentions of comment %s.', comment.url, { err })) |
63 | } | 73 | } |
64 | 74 | ||
65 | notifyOnNewVideoAbuse (videoAbuse: VideoAbuseModel): void { | 75 | notifyOnNewVideoAbuse (videoAbuse: MVideoAbuseVideo): void { |
66 | this.notifyModeratorsOfNewVideoAbuse(videoAbuse) | 76 | this.notifyModeratorsOfNewVideoAbuse(videoAbuse) |
67 | .catch(err => logger.error('Cannot notify of new video abuse of video %s.', videoAbuse.Video.url, { err })) | 77 | .catch(err => logger.error('Cannot notify of new video abuse of video %s.', videoAbuse.Video.url, { err })) |
68 | } | 78 | } |
69 | 79 | ||
70 | notifyOnVideoAutoBlacklist (video: VideoModel): void { | 80 | notifyOnVideoAutoBlacklist (videoBlacklist: MVideoBlacklistLightVideo): void { |
71 | this.notifyModeratorsOfVideoAutoBlacklist(video) | 81 | this.notifyModeratorsOfVideoAutoBlacklist(videoBlacklist) |
72 | .catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', video.url, { err })) | 82 | .catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', videoBlacklist.Video.url, { err })) |
73 | } | 83 | } |
74 | 84 | ||
75 | notifyOnVideoBlacklist (videoBlacklist: VideoBlacklistModel): void { | 85 | notifyOnVideoBlacklist (videoBlacklist: MVideoBlacklistVideo): void { |
76 | this.notifyVideoOwnerOfBlacklist(videoBlacklist) | 86 | this.notifyVideoOwnerOfBlacklist(videoBlacklist) |
77 | .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err })) | 87 | .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err })) |
78 | } | 88 | } |
79 | 89 | ||
80 | notifyOnVideoUnblacklist (video: VideoModel): void { | 90 | notifyOnVideoUnblacklist (video: MVideoFullLight): void { |
81 | this.notifyVideoOwnerOfUnblacklist(video) | 91 | this.notifyVideoOwnerOfUnblacklist(video) |
82 | .catch(err => logger.error('Cannot notify video owner of unblacklist of %s.', video.url, { err })) | 92 | .catch(err => logger.error('Cannot notify video owner of unblacklist of %s.', video.url, { err })) |
83 | } | 93 | } |
84 | 94 | ||
85 | notifyOnFinishedVideoImport (videoImport: VideoImportModel, success: boolean): void { | 95 | notifyOnFinishedVideoImport (videoImport: MVideoImportVideo, success: boolean): void { |
86 | this.notifyOwnerVideoImportIsFinished(videoImport, success) | 96 | this.notifyOwnerVideoImportIsFinished(videoImport, success) |
87 | .catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err })) | 97 | .catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err })) |
88 | } | 98 | } |
89 | 99 | ||
90 | notifyOnNewUserRegistration (user: UserModel): void { | 100 | notifyOnNewUserRegistration (user: MUserDefault): void { |
91 | this.notifyModeratorsOfNewUserRegistration(user) | 101 | this.notifyModeratorsOfNewUserRegistration(user) |
92 | .catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err })) | 102 | .catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err })) |
93 | } | 103 | } |
94 | 104 | ||
95 | notifyOfNewUserFollow (actorFollow: ActorFollowModel): void { | 105 | notifyOfNewUserFollow (actorFollow: MActorFollowFull): void { |
96 | this.notifyUserOfNewActorFollow(actorFollow) | 106 | this.notifyUserOfNewActorFollow(actorFollow) |
97 | .catch(err => { | 107 | .catch(err => { |
98 | logger.error( | 108 | logger.error( |
@@ -104,25 +114,32 @@ class Notifier { | |||
104 | }) | 114 | }) |
105 | } | 115 | } |
106 | 116 | ||
107 | notifyOfNewInstanceFollow (actorFollow: ActorFollowModel): void { | 117 | notifyOfNewInstanceFollow (actorFollow: MActorFollowFull): void { |
108 | this.notifyAdminsOfNewInstanceFollow(actorFollow) | 118 | this.notifyAdminsOfNewInstanceFollow(actorFollow) |
109 | .catch(err => { | 119 | .catch(err => { |
110 | logger.error('Cannot notify administrators of new follower %s.', actorFollow.ActorFollower.url, { err }) | 120 | logger.error('Cannot notify administrators of new follower %s.', actorFollow.ActorFollower.url, { err }) |
111 | }) | 121 | }) |
112 | } | 122 | } |
113 | 123 | ||
114 | private async notifySubscribersOfNewVideo (video: VideoModel) { | 124 | notifyOfAutoInstanceFollowing (actorFollow: MActorFollowFull): void { |
125 | this.notifyAdminsOfAutoInstanceFollowing(actorFollow) | ||
126 | .catch(err => { | ||
127 | logger.error('Cannot notify administrators of auto instance following %s.', actorFollow.ActorFollowing.url, { err }) | ||
128 | }) | ||
129 | } | ||
130 | |||
131 | private async notifySubscribersOfNewVideo (video: MVideoAccountLight) { | ||
115 | // List all followers that are users | 132 | // List all followers that are users |
116 | const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId) | 133 | const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId) |
117 | 134 | ||
118 | logger.info('Notifying %d users of new video %s.', users.length, video.url) | 135 | logger.info('Notifying %d users of new video %s.', users.length, video.url) |
119 | 136 | ||
120 | function settingGetter (user: UserModel) { | 137 | function settingGetter (user: MUserWithNotificationSetting) { |
121 | return user.NotificationSetting.newVideoFromSubscription | 138 | return user.NotificationSetting.newVideoFromSubscription |
122 | } | 139 | } |
123 | 140 | ||
124 | async function notificationCreator (user: UserModel) { | 141 | async function notificationCreator (user: MUserWithNotificationSetting) { |
125 | const notification = await UserNotificationModel.create({ | 142 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
126 | type: UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION, | 143 | type: UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION, |
127 | userId: user.id, | 144 | userId: user.id, |
128 | videoId: video.id | 145 | videoId: video.id |
@@ -139,7 +156,7 @@ class Notifier { | |||
139 | return this.notify({ users, settingGetter, notificationCreator, emailSender }) | 156 | return this.notify({ users, settingGetter, notificationCreator, emailSender }) |
140 | } | 157 | } |
141 | 158 | ||
142 | private async notifyVideoOwnerOfNewComment (comment: VideoCommentModel) { | 159 | private async notifyVideoOwnerOfNewComment (comment: MCommentOwnerVideo) { |
143 | if (comment.Video.isOwned() === false) return | 160 | if (comment.Video.isOwned() === false) return |
144 | 161 | ||
145 | const user = await UserModel.loadByVideoId(comment.videoId) | 162 | const user = await UserModel.loadByVideoId(comment.videoId) |
@@ -152,12 +169,12 @@ class Notifier { | |||
152 | 169 | ||
153 | logger.info('Notifying user %s of new comment %s.', user.username, comment.url) | 170 | logger.info('Notifying user %s of new comment %s.', user.username, comment.url) |
154 | 171 | ||
155 | function settingGetter (user: UserModel) { | 172 | function settingGetter (user: MUserWithNotificationSetting) { |
156 | return user.NotificationSetting.newCommentOnMyVideo | 173 | return user.NotificationSetting.newCommentOnMyVideo |
157 | } | 174 | } |
158 | 175 | ||
159 | async function notificationCreator (user: UserModel) { | 176 | async function notificationCreator (user: MUserWithNotificationSetting) { |
160 | const notification = await UserNotificationModel.create({ | 177 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
161 | type: UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, | 178 | type: UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, |
162 | userId: user.id, | 179 | userId: user.id, |
163 | commentId: comment.id | 180 | commentId: comment.id |
@@ -174,7 +191,7 @@ class Notifier { | |||
174 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | 191 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) |
175 | } | 192 | } |
176 | 193 | ||
177 | private async notifyOfCommentMention (comment: VideoCommentModel) { | 194 | private async notifyOfCommentMention (comment: MCommentOwnerVideo) { |
178 | const extractedUsernames = comment.extractMentions() | 195 | const extractedUsernames = comment.extractMentions() |
179 | logger.debug( | 196 | logger.debug( |
180 | 'Extracted %d username from comment %s.', extractedUsernames.length, comment.url, | 197 | 'Extracted %d username from comment %s.', extractedUsernames.length, comment.url, |
@@ -197,14 +214,14 @@ class Notifier { | |||
197 | 214 | ||
198 | logger.info('Notifying %d users of new comment %s.', users.length, comment.url) | 215 | logger.info('Notifying %d users of new comment %s.', users.length, comment.url) |
199 | 216 | ||
200 | function settingGetter (user: UserModel) { | 217 | function settingGetter (user: MUserNotifSettingAccount) { |
201 | if (accountMutedHash[user.Account.id] === true) return UserNotificationSettingValue.NONE | 218 | if (accountMutedHash[user.Account.id] === true) return UserNotificationSettingValue.NONE |
202 | 219 | ||
203 | return user.NotificationSetting.commentMention | 220 | return user.NotificationSetting.commentMention |
204 | } | 221 | } |
205 | 222 | ||
206 | async function notificationCreator (user: UserModel) { | 223 | async function notificationCreator (user: MUserNotifSettingAccount) { |
207 | const notification = await UserNotificationModel.create({ | 224 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
208 | type: UserNotificationType.COMMENT_MENTION, | 225 | type: UserNotificationType.COMMENT_MENTION, |
209 | userId: user.id, | 226 | userId: user.id, |
210 | commentId: comment.id | 227 | commentId: comment.id |
@@ -221,7 +238,7 @@ class Notifier { | |||
221 | return this.notify({ users, settingGetter, notificationCreator, emailSender }) | 238 | return this.notify({ users, settingGetter, notificationCreator, emailSender }) |
222 | } | 239 | } |
223 | 240 | ||
224 | private async notifyUserOfNewActorFollow (actorFollow: ActorFollowModel) { | 241 | private async notifyUserOfNewActorFollow (actorFollow: MActorFollowFull) { |
225 | if (actorFollow.ActorFollowing.isOwned() === false) return | 242 | if (actorFollow.ActorFollowing.isOwned() === false) return |
226 | 243 | ||
227 | // Account follows one of our account? | 244 | // Account follows one of our account? |
@@ -236,9 +253,6 @@ class Notifier { | |||
236 | 253 | ||
237 | if (!user) return | 254 | if (!user) return |
238 | 255 | ||
239 | if (!actorFollow.ActorFollower.Account || !actorFollow.ActorFollower.Account.name) { | ||
240 | actorFollow.ActorFollower.Account = await actorFollow.ActorFollower.$get('Account') as AccountModel | ||
241 | } | ||
242 | const followerAccount = actorFollow.ActorFollower.Account | 256 | const followerAccount = actorFollow.ActorFollower.Account |
243 | 257 | ||
244 | const accountMuted = await AccountBlocklistModel.isAccountMutedBy(user.Account.id, followerAccount.id) | 258 | const accountMuted = await AccountBlocklistModel.isAccountMutedBy(user.Account.id, followerAccount.id) |
@@ -246,12 +260,12 @@ class Notifier { | |||
246 | 260 | ||
247 | logger.info('Notifying user %s of new follower: %s.', user.username, followerAccount.getDisplayName()) | 261 | logger.info('Notifying user %s of new follower: %s.', user.username, followerAccount.getDisplayName()) |
248 | 262 | ||
249 | function settingGetter (user: UserModel) { | 263 | function settingGetter (user: MUserWithNotificationSetting) { |
250 | return user.NotificationSetting.newFollow | 264 | return user.NotificationSetting.newFollow |
251 | } | 265 | } |
252 | 266 | ||
253 | async function notificationCreator (user: UserModel) { | 267 | async function notificationCreator (user: MUserWithNotificationSetting) { |
254 | const notification = await UserNotificationModel.create({ | 268 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
255 | type: UserNotificationType.NEW_FOLLOW, | 269 | type: UserNotificationType.NEW_FOLLOW, |
256 | userId: user.id, | 270 | userId: user.id, |
257 | actorFollowId: actorFollow.id | 271 | actorFollowId: actorFollow.id |
@@ -268,17 +282,17 @@ class Notifier { | |||
268 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | 282 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) |
269 | } | 283 | } |
270 | 284 | ||
271 | private async notifyAdminsOfNewInstanceFollow (actorFollow: ActorFollowModel) { | 285 | private async notifyAdminsOfNewInstanceFollow (actorFollow: MActorFollowFull) { |
272 | const admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW) | 286 | const admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW) |
273 | 287 | ||
274 | logger.info('Notifying %d administrators of new instance follower: %s.', admins.length, actorFollow.ActorFollower.url) | 288 | logger.info('Notifying %d administrators of new instance follower: %s.', admins.length, actorFollow.ActorFollower.url) |
275 | 289 | ||
276 | function settingGetter (user: UserModel) { | 290 | function settingGetter (user: MUserWithNotificationSetting) { |
277 | return user.NotificationSetting.newInstanceFollower | 291 | return user.NotificationSetting.newInstanceFollower |
278 | } | 292 | } |
279 | 293 | ||
280 | async function notificationCreator (user: UserModel) { | 294 | async function notificationCreator (user: MUserWithNotificationSetting) { |
281 | const notification = await UserNotificationModel.create({ | 295 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
282 | type: UserNotificationType.NEW_INSTANCE_FOLLOWER, | 296 | type: UserNotificationType.NEW_INSTANCE_FOLLOWER, |
283 | userId: user.id, | 297 | userId: user.id, |
284 | actorFollowId: actorFollow.id | 298 | actorFollowId: actorFollow.id |
@@ -295,18 +309,45 @@ class Notifier { | |||
295 | return this.notify({ users: admins, settingGetter, notificationCreator, emailSender }) | 309 | return this.notify({ users: admins, settingGetter, notificationCreator, emailSender }) |
296 | } | 310 | } |
297 | 311 | ||
298 | private async notifyModeratorsOfNewVideoAbuse (videoAbuse: VideoAbuseModel) { | 312 | private async notifyAdminsOfAutoInstanceFollowing (actorFollow: MActorFollowFull) { |
313 | const admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW) | ||
314 | |||
315 | logger.info('Notifying %d administrators of auto instance following: %s.', admins.length, actorFollow.ActorFollowing.url) | ||
316 | |||
317 | function settingGetter (user: MUserWithNotificationSetting) { | ||
318 | return user.NotificationSetting.autoInstanceFollowing | ||
319 | } | ||
320 | |||
321 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
322 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
323 | type: UserNotificationType.AUTO_INSTANCE_FOLLOWING, | ||
324 | userId: user.id, | ||
325 | actorFollowId: actorFollow.id | ||
326 | }) | ||
327 | notification.ActorFollow = actorFollow | ||
328 | |||
329 | return notification | ||
330 | } | ||
331 | |||
332 | function emailSender (emails: string[]) { | ||
333 | return Emailer.Instance.addAutoInstanceFollowingNotification(emails, actorFollow) | ||
334 | } | ||
335 | |||
336 | return this.notify({ users: admins, settingGetter, notificationCreator, emailSender }) | ||
337 | } | ||
338 | |||
339 | private async notifyModeratorsOfNewVideoAbuse (videoAbuse: MVideoAbuseVideo) { | ||
299 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_ABUSES) | 340 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_ABUSES) |
300 | if (moderators.length === 0) return | 341 | if (moderators.length === 0) return |
301 | 342 | ||
302 | logger.info('Notifying %s user/moderators of new video abuse %s.', moderators.length, videoAbuse.Video.url) | 343 | logger.info('Notifying %s user/moderators of new video abuse %s.', moderators.length, videoAbuse.Video.url) |
303 | 344 | ||
304 | function settingGetter (user: UserModel) { | 345 | function settingGetter (user: MUserWithNotificationSetting) { |
305 | return user.NotificationSetting.videoAbuseAsModerator | 346 | return user.NotificationSetting.videoAbuseAsModerator |
306 | } | 347 | } |
307 | 348 | ||
308 | async function notificationCreator (user: UserModel) { | 349 | async function notificationCreator (user: MUserWithNotificationSetting) { |
309 | const notification = await UserNotificationModel.create({ | 350 | const notification: UserNotificationModelForApi = await UserNotificationModel.create<UserNotificationModelForApi>({ |
310 | type: UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS, | 351 | type: UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS, |
311 | userId: user.id, | 352 | userId: user.id, |
312 | videoAbuseId: videoAbuse.id | 353 | videoAbuseId: videoAbuse.id |
@@ -323,46 +364,46 @@ class Notifier { | |||
323 | return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) | 364 | return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) |
324 | } | 365 | } |
325 | 366 | ||
326 | private async notifyModeratorsOfVideoAutoBlacklist (video: VideoModel) { | 367 | private async notifyModeratorsOfVideoAutoBlacklist (videoBlacklist: MVideoBlacklistLightVideo) { |
327 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST) | 368 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST) |
328 | if (moderators.length === 0) return | 369 | if (moderators.length === 0) return |
329 | 370 | ||
330 | logger.info('Notifying %s moderators of video auto-blacklist %s.', moderators.length, video.url) | 371 | logger.info('Notifying %s moderators of video auto-blacklist %s.', moderators.length, videoBlacklist.Video.url) |
331 | 372 | ||
332 | function settingGetter (user: UserModel) { | 373 | function settingGetter (user: MUserWithNotificationSetting) { |
333 | return user.NotificationSetting.videoAutoBlacklistAsModerator | 374 | return user.NotificationSetting.videoAutoBlacklistAsModerator |
334 | } | 375 | } |
335 | async function notificationCreator (user: UserModel) { | ||
336 | 376 | ||
337 | const notification = await UserNotificationModel.create({ | 377 | async function notificationCreator (user: MUserWithNotificationSetting) { |
378 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
338 | type: UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS, | 379 | type: UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS, |
339 | userId: user.id, | 380 | userId: user.id, |
340 | videoId: video.id | 381 | videoBlacklistId: videoBlacklist.id |
341 | }) | 382 | }) |
342 | notification.Video = video | 383 | notification.VideoBlacklist = videoBlacklist |
343 | 384 | ||
344 | return notification | 385 | return notification |
345 | } | 386 | } |
346 | 387 | ||
347 | function emailSender (emails: string[]) { | 388 | function emailSender (emails: string[]) { |
348 | return Emailer.Instance.addVideoAutoBlacklistModeratorsNotification(emails, video) | 389 | return Emailer.Instance.addVideoAutoBlacklistModeratorsNotification(emails, videoBlacklist) |
349 | } | 390 | } |
350 | 391 | ||
351 | return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) | 392 | return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) |
352 | } | 393 | } |
353 | 394 | ||
354 | private async notifyVideoOwnerOfBlacklist (videoBlacklist: VideoBlacklistModel) { | 395 | private async notifyVideoOwnerOfBlacklist (videoBlacklist: MVideoBlacklistVideo) { |
355 | const user = await UserModel.loadByVideoId(videoBlacklist.videoId) | 396 | const user = await UserModel.loadByVideoId(videoBlacklist.videoId) |
356 | if (!user) return | 397 | if (!user) return |
357 | 398 | ||
358 | logger.info('Notifying user %s that its video %s has been blacklisted.', user.username, videoBlacklist.Video.url) | 399 | logger.info('Notifying user %s that its video %s has been blacklisted.', user.username, videoBlacklist.Video.url) |
359 | 400 | ||
360 | function settingGetter (user: UserModel) { | 401 | function settingGetter (user: MUserWithNotificationSetting) { |
361 | return user.NotificationSetting.blacklistOnMyVideo | 402 | return user.NotificationSetting.blacklistOnMyVideo |
362 | } | 403 | } |
363 | 404 | ||
364 | async function notificationCreator (user: UserModel) { | 405 | async function notificationCreator (user: MUserWithNotificationSetting) { |
365 | const notification = await UserNotificationModel.create({ | 406 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
366 | type: UserNotificationType.BLACKLIST_ON_MY_VIDEO, | 407 | type: UserNotificationType.BLACKLIST_ON_MY_VIDEO, |
367 | userId: user.id, | 408 | userId: user.id, |
368 | videoBlacklistId: videoBlacklist.id | 409 | videoBlacklistId: videoBlacklist.id |
@@ -379,18 +420,18 @@ class Notifier { | |||
379 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | 420 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) |
380 | } | 421 | } |
381 | 422 | ||
382 | private async notifyVideoOwnerOfUnblacklist (video: VideoModel) { | 423 | private async notifyVideoOwnerOfUnblacklist (video: MVideoFullLight) { |
383 | const user = await UserModel.loadByVideoId(video.id) | 424 | const user = await UserModel.loadByVideoId(video.id) |
384 | if (!user) return | 425 | if (!user) return |
385 | 426 | ||
386 | logger.info('Notifying user %s that its video %s has been unblacklisted.', user.username, video.url) | 427 | logger.info('Notifying user %s that its video %s has been unblacklisted.', user.username, video.url) |
387 | 428 | ||
388 | function settingGetter (user: UserModel) { | 429 | function settingGetter (user: MUserWithNotificationSetting) { |
389 | return user.NotificationSetting.blacklistOnMyVideo | 430 | return user.NotificationSetting.blacklistOnMyVideo |
390 | } | 431 | } |
391 | 432 | ||
392 | async function notificationCreator (user: UserModel) { | 433 | async function notificationCreator (user: MUserWithNotificationSetting) { |
393 | const notification = await UserNotificationModel.create({ | 434 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
394 | type: UserNotificationType.UNBLACKLIST_ON_MY_VIDEO, | 435 | type: UserNotificationType.UNBLACKLIST_ON_MY_VIDEO, |
395 | userId: user.id, | 436 | userId: user.id, |
396 | videoId: video.id | 437 | videoId: video.id |
@@ -407,18 +448,18 @@ class Notifier { | |||
407 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | 448 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) |
408 | } | 449 | } |
409 | 450 | ||
410 | private async notifyOwnedVideoHasBeenPublished (video: VideoModel) { | 451 | private async notifyOwnedVideoHasBeenPublished (video: MVideoFullLight) { |
411 | const user = await UserModel.loadByVideoId(video.id) | 452 | const user = await UserModel.loadByVideoId(video.id) |
412 | if (!user) return | 453 | if (!user) return |
413 | 454 | ||
414 | logger.info('Notifying user %s of the publication of its video %s.', user.username, video.url) | 455 | logger.info('Notifying user %s of the publication of its video %s.', user.username, video.url) |
415 | 456 | ||
416 | function settingGetter (user: UserModel) { | 457 | function settingGetter (user: MUserWithNotificationSetting) { |
417 | return user.NotificationSetting.myVideoPublished | 458 | return user.NotificationSetting.myVideoPublished |
418 | } | 459 | } |
419 | 460 | ||
420 | async function notificationCreator (user: UserModel) { | 461 | async function notificationCreator (user: MUserWithNotificationSetting) { |
421 | const notification = await UserNotificationModel.create({ | 462 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
422 | type: UserNotificationType.MY_VIDEO_PUBLISHED, | 463 | type: UserNotificationType.MY_VIDEO_PUBLISHED, |
423 | userId: user.id, | 464 | userId: user.id, |
424 | videoId: video.id | 465 | videoId: video.id |
@@ -435,18 +476,18 @@ class Notifier { | |||
435 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | 476 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) |
436 | } | 477 | } |
437 | 478 | ||
438 | private async notifyOwnerVideoImportIsFinished (videoImport: VideoImportModel, success: boolean) { | 479 | private async notifyOwnerVideoImportIsFinished (videoImport: MVideoImportVideo, success: boolean) { |
439 | const user = await UserModel.loadByVideoImportId(videoImport.id) | 480 | const user = await UserModel.loadByVideoImportId(videoImport.id) |
440 | if (!user) return | 481 | if (!user) return |
441 | 482 | ||
442 | logger.info('Notifying user %s its video import %s is finished.', user.username, videoImport.getTargetIdentifier()) | 483 | logger.info('Notifying user %s its video import %s is finished.', user.username, videoImport.getTargetIdentifier()) |
443 | 484 | ||
444 | function settingGetter (user: UserModel) { | 485 | function settingGetter (user: MUserWithNotificationSetting) { |
445 | return user.NotificationSetting.myVideoImportFinished | 486 | return user.NotificationSetting.myVideoImportFinished |
446 | } | 487 | } |
447 | 488 | ||
448 | async function notificationCreator (user: UserModel) { | 489 | async function notificationCreator (user: MUserWithNotificationSetting) { |
449 | const notification = await UserNotificationModel.create({ | 490 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
450 | type: success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR, | 491 | type: success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR, |
451 | userId: user.id, | 492 | userId: user.id, |
452 | videoImportId: videoImport.id | 493 | videoImportId: videoImport.id |
@@ -465,21 +506,21 @@ class Notifier { | |||
465 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | 506 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) |
466 | } | 507 | } |
467 | 508 | ||
468 | private async notifyModeratorsOfNewUserRegistration (registeredUser: UserModel) { | 509 | private async notifyModeratorsOfNewUserRegistration (registeredUser: MUserDefault) { |
469 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS) | 510 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS) |
470 | if (moderators.length === 0) return | 511 | if (moderators.length === 0) return |
471 | 512 | ||
472 | logger.info( | 513 | logger.info( |
473 | 'Notifying %s moderators of new user registration of %s.', | 514 | 'Notifying %s moderators of new user registration of %s.', |
474 | moderators.length, registeredUser.Account.Actor.preferredUsername | 515 | moderators.length, registeredUser.username |
475 | ) | 516 | ) |
476 | 517 | ||
477 | function settingGetter (user: UserModel) { | 518 | function settingGetter (user: MUserWithNotificationSetting) { |
478 | return user.NotificationSetting.newUserRegistration | 519 | return user.NotificationSetting.newUserRegistration |
479 | } | 520 | } |
480 | 521 | ||
481 | async function notificationCreator (user: UserModel) { | 522 | async function notificationCreator (user: MUserWithNotificationSetting) { |
482 | const notification = await UserNotificationModel.create({ | 523 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
483 | type: UserNotificationType.NEW_USER_REGISTRATION, | 524 | type: UserNotificationType.NEW_USER_REGISTRATION, |
484 | userId: user.id, | 525 | userId: user.id, |
485 | accountId: registeredUser.Account.id | 526 | accountId: registeredUser.Account.id |
@@ -496,11 +537,11 @@ class Notifier { | |||
496 | return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) | 537 | return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) |
497 | } | 538 | } |
498 | 539 | ||
499 | private async notify (options: { | 540 | private async notify <T extends MUserWithNotificationSetting> (options: { |
500 | users: UserModel[], | 541 | users: T[], |
501 | notificationCreator: (user: UserModel) => Promise<UserNotificationModel>, | 542 | notificationCreator: (user: T) => Promise<UserNotificationModelForApi>, |
502 | emailSender: (emails: string[]) => Promise<any> | Bluebird<any>, | 543 | emailSender: (emails: string[]) => Promise<any> | Bluebird<any>, |
503 | settingGetter: (user: UserModel) => UserNotificationSettingValue | 544 | settingGetter: (user: T) => UserNotificationSettingValue |
504 | }) { | 545 | }) { |
505 | const emails: string[] = [] | 546 | const emails: string[] = [] |
506 | 547 | ||
@@ -521,7 +562,7 @@ class Notifier { | |||
521 | } | 562 | } |
522 | } | 563 | } |
523 | 564 | ||
524 | private isEmailEnabled (user: UserModel, value: UserNotificationSettingValue) { | 565 | private isEmailEnabled (user: MUser, value: UserNotificationSettingValue) { |
525 | if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION === true && user.emailVerified === false) return false | 566 | if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION === true && user.emailVerified === false) return false |
526 | 567 | ||
527 | return value & UserNotificationSettingValue.EMAIL | 568 | 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' | |||
8 | import { Transaction } from 'sequelize' | 8 | import { Transaction } from 'sequelize' |
9 | import { CONFIG } from '../initializers/config' | 9 | import { CONFIG } from '../initializers/config' |
10 | import * as LRUCache from 'lru-cache' | 10 | import * as LRUCache from 'lru-cache' |
11 | import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token' | ||
11 | 12 | ||
12 | type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date } | 13 | type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date } |
13 | 14 | ||
14 | const accessTokenCache = new LRUCache<string, OAuthTokenModel>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE }) | 15 | const accessTokenCache = new LRUCache<string, MOAuthTokenUser>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE }) |
15 | const userHavingToken = new LRUCache<number, string>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE }) | 16 | const userHavingToken = new LRUCache<number, string>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE }) |
16 | 17 | ||
17 | // --------------------------------------------------------------------------- | 18 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/peertube-socket.ts b/server/lib/peertube-socket.ts index 17748fd18..26ced351f 100644 --- a/server/lib/peertube-socket.ts +++ b/server/lib/peertube-socket.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | import * as SocketIO from 'socket.io' | 1 | import * as SocketIO from 'socket.io' |
2 | import { authenticateSocket } from '../middlewares' | 2 | import { authenticateSocket } from '../middlewares' |
3 | import { UserNotificationModel } from '../models/account/user-notification' | ||
4 | import { logger } from '../helpers/logger' | 3 | import { logger } from '../helpers/logger' |
5 | import { Server } from 'http' | 4 | import { Server } from 'http' |
5 | import { UserNotificationModelForApi } from '@server/typings/models/user' | ||
6 | 6 | ||
7 | class PeerTubeSocket { | 7 | class PeerTubeSocket { |
8 | 8 | ||
@@ -34,13 +34,14 @@ class PeerTubeSocket { | |||
34 | }) | 34 | }) |
35 | } | 35 | } |
36 | 36 | ||
37 | sendNotification (userId: number, notification: UserNotificationModel) { | 37 | sendNotification (userId: number, notification: UserNotificationModelForApi) { |
38 | const sockets = this.userNotificationSockets[userId] | 38 | const sockets = this.userNotificationSockets[userId] |
39 | 39 | ||
40 | if (!sockets) return | 40 | if (!sockets) return |
41 | 41 | ||
42 | const notificationMessage = notification.toFormattedJSON() | ||
42 | for (const socket of sockets) { | 43 | for (const socket of sockets) { |
43 | socket.emit('new-notification', notification.toFormattedJSON()) | 44 | socket.emit('new-notification', notificationMessage) |
44 | } | 45 | } |
45 | } | 46 | } |
46 | 47 | ||
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' | |||
2 | import { sendUndoCacheFile } from './activitypub/send' | 2 | import { sendUndoCacheFile } from './activitypub/send' |
3 | import { Transaction } from 'sequelize' | 3 | import { Transaction } from 'sequelize' |
4 | import { getServerActor } from '../helpers/utils' | 4 | import { getServerActor } from '../helpers/utils' |
5 | import { MVideoRedundancyVideo } from '@server/typings/models' | ||
5 | 6 | ||
6 | async function removeVideoRedundancy (videoRedundancy: VideoRedundancyModel, t?: Transaction) { | 7 | async function removeVideoRedundancy (videoRedundancy: MVideoRedundancyVideo, t?: Transaction) { |
7 | const serverActor = await getServerActor() | 8 | const serverActor = await getServerActor() |
8 | 9 | ||
9 | // Local cache, send undo to remote instances | 10 | // Local cache, send undo to remote instances |
diff --git a/server/lib/schedulers/auto-follow-index-instances.ts b/server/lib/schedulers/auto-follow-index-instances.ts new file mode 100644 index 000000000..ef11fc87f --- /dev/null +++ b/server/lib/schedulers/auto-follow-index-instances.ts | |||
@@ -0,0 +1,72 @@ | |||
1 | import { logger } from '../../helpers/logger' | ||
2 | import { AbstractScheduler } from './abstract-scheduler' | ||
3 | import { INSTANCES_INDEX, SCHEDULER_INTERVALS_MS, SERVER_ACTOR_NAME } from '../../initializers/constants' | ||
4 | import { CONFIG } from '../../initializers/config' | ||
5 | import { chunk } from 'lodash' | ||
6 | import { doRequest } from '@server/helpers/requests' | ||
7 | import { ActorFollowModel } from '@server/models/activitypub/actor-follow' | ||
8 | import { JobQueue } from '@server/lib/job-queue' | ||
9 | import { getServerActor } from '@server/helpers/utils' | ||
10 | |||
11 | export class AutoFollowIndexInstances extends AbstractScheduler { | ||
12 | |||
13 | private static instance: AbstractScheduler | ||
14 | |||
15 | protected schedulerIntervalMs = SCHEDULER_INTERVALS_MS.autoFollowIndexInstances | ||
16 | |||
17 | private lastCheck: Date | ||
18 | |||
19 | private constructor () { | ||
20 | super() | ||
21 | } | ||
22 | |||
23 | protected async internalExecute () { | ||
24 | return this.autoFollow() | ||
25 | } | ||
26 | |||
27 | private async autoFollow () { | ||
28 | if (CONFIG.FOLLOWINGS.INSTANCE.AUTO_FOLLOW_INDEX.ENABLED === false) return | ||
29 | |||
30 | const indexUrl = CONFIG.FOLLOWINGS.INSTANCE.AUTO_FOLLOW_INDEX.INDEX_URL | ||
31 | |||
32 | logger.info('Auto follow instances of index %s.', indexUrl) | ||
33 | |||
34 | try { | ||
35 | const serverActor = await getServerActor() | ||
36 | |||
37 | const uri = indexUrl + INSTANCES_INDEX.HOSTS_PATH | ||
38 | |||
39 | const qs = this.lastCheck ? { since: this.lastCheck.toISOString() } : {} | ||
40 | this.lastCheck = new Date() | ||
41 | |||
42 | const { body } = await doRequest({ uri, qs, json: true }) | ||
43 | |||
44 | const hosts: string[] = body.data.map(o => o.host) | ||
45 | const chunks = chunk(hosts, 20) | ||
46 | |||
47 | for (const chunk of chunks) { | ||
48 | const unfollowedHosts = await ActorFollowModel.keepUnfollowedInstance(chunk) | ||
49 | |||
50 | for (const unfollowedHost of unfollowedHosts) { | ||
51 | const payload = { | ||
52 | host: unfollowedHost, | ||
53 | name: SERVER_ACTOR_NAME, | ||
54 | followerActorId: serverActor.id, | ||
55 | isAutoFollow: true | ||
56 | } | ||
57 | |||
58 | await JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) | ||
59 | .catch(err => logger.error('Cannot create follow job for %s.', unfollowedHost, err)) | ||
60 | } | ||
61 | } | ||
62 | |||
63 | } catch (err) { | ||
64 | logger.error('Cannot auto follow hosts of index %s.', indexUrl, { err }) | ||
65 | } | ||
66 | |||
67 | } | ||
68 | |||
69 | static get Instance () { | ||
70 | return this.instance || (this.instance = new this()) | ||
71 | } | ||
72 | } | ||
diff --git a/server/lib/schedulers/videos-redundancy-scheduler.ts b/server/lib/schedulers/videos-redundancy-scheduler.ts index 5f4aad66e..1e30f6ebc 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 } | |||
3 | import { logger } from '../../helpers/logger' | 3 | import { logger } from '../../helpers/logger' |
4 | import { VideosRedundancy } from '../../../shared/models/redundancy' | 4 | import { VideosRedundancy } from '../../../shared/models/redundancy' |
5 | import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' | 5 | import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' |
6 | import { VideoFileModel } from '../../models/video/video-file' | ||
7 | import { downloadWebTorrentVideo } from '../../helpers/webtorrent' | 6 | import { downloadWebTorrentVideo } from '../../helpers/webtorrent' |
8 | import { join } from 'path' | 7 | import { join } from 'path' |
9 | import { move } from 'fs-extra' | 8 | import { move } from 'fs-extra' |
@@ -12,16 +11,31 @@ import { sendCreateCacheFile, sendUpdateCacheFile } from '../activitypub/send' | |||
12 | import { getVideoCacheFileActivityPubUrl, getVideoCacheStreamingPlaylistActivityPubUrl } from '../activitypub/url' | 11 | import { getVideoCacheFileActivityPubUrl, getVideoCacheStreamingPlaylistActivityPubUrl } from '../activitypub/url' |
13 | import { removeVideoRedundancy } from '../redundancy' | 12 | import { removeVideoRedundancy } from '../redundancy' |
14 | import { getOrCreateVideoAndAccountAndChannel } from '../activitypub' | 13 | import { getOrCreateVideoAndAccountAndChannel } from '../activitypub' |
15 | import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' | ||
16 | import { VideoModel } from '../../models/video/video' | ||
17 | import { downloadPlaylistSegments } from '../hls' | 14 | import { downloadPlaylistSegments } from '../hls' |
18 | import { CONFIG } from '../../initializers/config' | 15 | import { CONFIG } from '../../initializers/config' |
16 | import { | ||
17 | MStreamingPlaylist, | ||
18 | MStreamingPlaylistVideo, | ||
19 | MVideoAccountLight, | ||
20 | MVideoFile, | ||
21 | MVideoFileVideo, | ||
22 | MVideoRedundancyFileVideo, | ||
23 | MVideoRedundancyStreamingPlaylistVideo, | ||
24 | MVideoRedundancyVideo, | ||
25 | MVideoWithAllFiles | ||
26 | } from '@server/typings/models' | ||
19 | 27 | ||
20 | type CandidateToDuplicate = { | 28 | type CandidateToDuplicate = { |
21 | redundancy: VideosRedundancy, | 29 | redundancy: VideosRedundancy, |
22 | video: VideoModel, | 30 | video: MVideoWithAllFiles, |
23 | files: VideoFileModel[], | 31 | files: MVideoFile[], |
24 | streamingPlaylists: VideoStreamingPlaylistModel[] | 32 | streamingPlaylists: MStreamingPlaylist[] |
33 | } | ||
34 | |||
35 | function isMVideoRedundancyFileVideo ( | ||
36 | o: MVideoRedundancyFileVideo | MVideoRedundancyStreamingPlaylistVideo | ||
37 | ): o is MVideoRedundancyFileVideo { | ||
38 | return !!(o as MVideoRedundancyFileVideo).VideoFile | ||
25 | } | 39 | } |
26 | 40 | ||
27 | export class VideosRedundancyScheduler extends AbstractScheduler { | 41 | export class VideosRedundancyScheduler extends AbstractScheduler { |
@@ -102,7 +116,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
102 | } | 116 | } |
103 | } | 117 | } |
104 | 118 | ||
105 | private async extendsRedundancy (redundancyModel: VideoRedundancyModel) { | 119 | private async extendsRedundancy (redundancyModel: MVideoRedundancyVideo) { |
106 | const redundancy = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES.find(s => s.strategy === redundancyModel.strategy) | 120 | const redundancy = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES.find(s => s.strategy === redundancyModel.strategy) |
107 | // Redundancy strategy disabled, remove our redundancy instead of extending expiration | 121 | // Redundancy strategy disabled, remove our redundancy instead of extending expiration |
108 | if (!redundancy) { | 122 | if (!redundancy) { |
@@ -172,7 +186,8 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
172 | } | 186 | } |
173 | } | 187 | } |
174 | 188 | ||
175 | private async createVideoFileRedundancy (redundancy: VideosRedundancy, video: VideoModel, file: VideoFileModel) { | 189 | private async createVideoFileRedundancy (redundancy: VideosRedundancy, video: MVideoAccountLight, fileArg: MVideoFile) { |
190 | const file = fileArg as MVideoFileVideo | ||
176 | file.Video = video | 191 | file.Video = video |
177 | 192 | ||
178 | const serverActor = await getServerActor() | 193 | const serverActor = await getServerActor() |
@@ -187,7 +202,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
187 | const destPath = join(CONFIG.STORAGE.REDUNDANCY_DIR, video.getVideoFilename(file)) | 202 | const destPath = join(CONFIG.STORAGE.REDUNDANCY_DIR, video.getVideoFilename(file)) |
188 | await move(tmpPath, destPath, { overwrite: true }) | 203 | await move(tmpPath, destPath, { overwrite: true }) |
189 | 204 | ||
190 | const createdModel = await VideoRedundancyModel.create({ | 205 | const createdModel: MVideoRedundancyFileVideo = await VideoRedundancyModel.create({ |
191 | expiresOn: this.buildNewExpiration(redundancy.minLifetime), | 206 | expiresOn: this.buildNewExpiration(redundancy.minLifetime), |
192 | url: getVideoCacheFileActivityPubUrl(file), | 207 | url: getVideoCacheFileActivityPubUrl(file), |
193 | fileUrl: video.getVideoRedundancyUrl(file, WEBSERVER.URL), | 208 | fileUrl: video.getVideoRedundancyUrl(file, WEBSERVER.URL), |
@@ -203,7 +218,12 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
203 | logger.info('Duplicated %s - %d -> %s.', video.url, file.resolution, createdModel.url) | 218 | logger.info('Duplicated %s - %d -> %s.', video.url, file.resolution, createdModel.url) |
204 | } | 219 | } |
205 | 220 | ||
206 | private async createStreamingPlaylistRedundancy (redundancy: VideosRedundancy, video: VideoModel, playlist: VideoStreamingPlaylistModel) { | 221 | private async createStreamingPlaylistRedundancy ( |
222 | redundancy: VideosRedundancy, | ||
223 | video: MVideoAccountLight, | ||
224 | playlistArg: MStreamingPlaylist | ||
225 | ) { | ||
226 | const playlist = playlistArg as MStreamingPlaylistVideo | ||
207 | playlist.Video = video | 227 | playlist.Video = video |
208 | 228 | ||
209 | const serverActor = await getServerActor() | 229 | const serverActor = await getServerActor() |
@@ -213,7 +233,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
213 | const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid) | 233 | const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid) |
214 | await downloadPlaylistSegments(playlist.playlistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT) | 234 | await downloadPlaylistSegments(playlist.playlistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT) |
215 | 235 | ||
216 | const createdModel = await VideoRedundancyModel.create({ | 236 | const createdModel: MVideoRedundancyStreamingPlaylistVideo = await VideoRedundancyModel.create({ |
217 | expiresOn: this.buildNewExpiration(redundancy.minLifetime), | 237 | expiresOn: this.buildNewExpiration(redundancy.minLifetime), |
218 | url: getVideoCacheStreamingPlaylistActivityPubUrl(video, playlist), | 238 | url: getVideoCacheStreamingPlaylistActivityPubUrl(video, playlist), |
219 | fileUrl: playlist.getVideoRedundancyUrl(WEBSERVER.URL), | 239 | fileUrl: playlist.getVideoRedundancyUrl(WEBSERVER.URL), |
@@ -229,7 +249,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
229 | logger.info('Duplicated playlist %s -> %s.', playlist.playlistUrl, createdModel.url) | 249 | logger.info('Duplicated playlist %s -> %s.', playlist.playlistUrl, createdModel.url) |
230 | } | 250 | } |
231 | 251 | ||
232 | private async extendsExpirationOf (redundancy: VideoRedundancyModel, expiresAfterMs: number) { | 252 | private async extendsExpirationOf (redundancy: MVideoRedundancyVideo, expiresAfterMs: number) { |
233 | logger.info('Extending expiration of %s.', redundancy.url) | 253 | logger.info('Extending expiration of %s.', redundancy.url) |
234 | 254 | ||
235 | const serverActor = await getServerActor() | 255 | const serverActor = await getServerActor() |
@@ -243,7 +263,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
243 | private async purgeCacheIfNeeded (candidateToDuplicate: CandidateToDuplicate) { | 263 | private async purgeCacheIfNeeded (candidateToDuplicate: CandidateToDuplicate) { |
244 | while (await this.isTooHeavy(candidateToDuplicate)) { | 264 | while (await this.isTooHeavy(candidateToDuplicate)) { |
245 | const redundancy = candidateToDuplicate.redundancy | 265 | const redundancy = candidateToDuplicate.redundancy |
246 | const toDelete = await VideoRedundancyModel.loadOldestLocalThatAlreadyExpired(redundancy.strategy, redundancy.minLifetime) | 266 | const toDelete = await VideoRedundancyModel.loadOldestLocalExpired(redundancy.strategy, redundancy.minLifetime) |
247 | if (!toDelete) return | 267 | if (!toDelete) return |
248 | 268 | ||
249 | await removeVideoRedundancy(toDelete) | 269 | await removeVideoRedundancy(toDelete) |
@@ -263,19 +283,18 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
263 | return new Date(Date.now() + expiresAfterMs) | 283 | return new Date(Date.now() + expiresAfterMs) |
264 | } | 284 | } |
265 | 285 | ||
266 | private buildEntryLogId (object: VideoRedundancyModel) { | 286 | private buildEntryLogId (object: MVideoRedundancyFileVideo | MVideoRedundancyStreamingPlaylistVideo) { |
267 | if (object.VideoFile) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}` | 287 | if (isMVideoRedundancyFileVideo(object)) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}` |
268 | 288 | ||
269 | return `${object.VideoStreamingPlaylist.playlistUrl}` | 289 | return `${object.VideoStreamingPlaylist.playlistUrl}` |
270 | } | 290 | } |
271 | 291 | ||
272 | private getTotalFileSizes (files: VideoFileModel[], playlists: VideoStreamingPlaylistModel[]) { | 292 | private getTotalFileSizes (files: MVideoFile[], playlists: MStreamingPlaylist[]) { |
273 | const fileReducer = (previous: number, current: VideoFileModel) => previous + current.size | 293 | const fileReducer = (previous: number, current: MVideoFile) => previous + current.size |
274 | 294 | ||
275 | const totalSize = files.reduce(fileReducer, 0) | 295 | const totalSize = files.reduce(fileReducer, 0) |
276 | if (playlists.length === 0) return totalSize | ||
277 | 296 | ||
278 | return totalSize * playlists.length | 297 | return totalSize + (totalSize * playlists.length) |
279 | } | 298 | } |
280 | 299 | ||
281 | private async loadAndRefreshVideo (videoUrl: string) { | 300 | private async loadAndRefreshVideo (videoUrl: string) { |
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 @@ | |||
1 | import { VideoFileModel } from '../models/video/video-file' | ||
2 | import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils' | 1 | import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils' |
3 | import { CONFIG } from '../initializers/config' | 2 | import { CONFIG } from '../initializers/config' |
4 | import { PREVIEWS_SIZE, THUMBNAILS_SIZE, ASSETS_PATH } from '../initializers/constants' | 3 | import { ASSETS_PATH, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '../initializers/constants' |
5 | import { VideoModel } from '../models/video/video' | ||
6 | import { ThumbnailModel } from '../models/video/thumbnail' | 4 | import { ThumbnailModel } from '../models/video/thumbnail' |
7 | import { ThumbnailType } from '../../shared/models/videos/thumbnail.type' | 5 | import { ThumbnailType } from '../../shared/models/videos/thumbnail.type' |
8 | import { processImage } from '../helpers/image-utils' | 6 | import { processImage } from '../helpers/image-utils' |
9 | import { join } from 'path' | 7 | import { join } from 'path' |
10 | import { downloadImage } from '../helpers/requests' | 8 | import { downloadImage } from '../helpers/requests' |
11 | import { VideoPlaylistModel } from '../models/video/video-playlist' | 9 | import { MVideoPlaylistThumbnail } from '../typings/models/video/video-playlist' |
10 | import { MVideoFile, MVideoThumbnail } from '../typings/models' | ||
11 | import { MThumbnail } from '../typings/models/video/thumbnail' | ||
12 | 12 | ||
13 | type ImageSize = { height: number, width: number } | 13 | type ImageSize = { height: number, width: number } |
14 | 14 | ||
15 | function createPlaylistMiniatureFromExisting ( | 15 | function createPlaylistMiniatureFromExisting ( |
16 | inputPath: string, | 16 | inputPath: string, |
17 | playlist: VideoPlaylistModel, | 17 | playlist: MVideoPlaylistThumbnail, |
18 | automaticallyGenerated: boolean, | 18 | automaticallyGenerated: boolean, |
19 | keepOriginal = false, | 19 | keepOriginal = false, |
20 | size?: ImageSize | 20 | size?: ImageSize |
@@ -26,7 +26,7 @@ function createPlaylistMiniatureFromExisting ( | |||
26 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail }) | 26 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail }) |
27 | } | 27 | } |
28 | 28 | ||
29 | function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: VideoPlaylistModel, size?: ImageSize) { | 29 | function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: MVideoPlaylistThumbnail, size?: ImageSize) { |
30 | const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromPlaylist(playlist, size) | 30 | const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromPlaylist(playlist, size) |
31 | const type = ThumbnailType.MINIATURE | 31 | const type = ThumbnailType.MINIATURE |
32 | 32 | ||
@@ -34,7 +34,7 @@ function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: VideoPlaylis | |||
34 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl }) | 34 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl }) |
35 | } | 35 | } |
36 | 36 | ||
37 | function createVideoMiniatureFromUrl (fileUrl: string, video: VideoModel, type: ThumbnailType, size?: ImageSize) { | 37 | function createVideoMiniatureFromUrl (fileUrl: string, video: MVideoThumbnail, type: ThumbnailType, size?: ImageSize) { |
38 | const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size) | 38 | const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size) |
39 | const thumbnailCreator = () => downloadImage(fileUrl, basePath, filename, { width, height }) | 39 | const thumbnailCreator = () => downloadImage(fileUrl, basePath, filename, { width, height }) |
40 | 40 | ||
@@ -43,7 +43,7 @@ function createVideoMiniatureFromUrl (fileUrl: string, video: VideoModel, type: | |||
43 | 43 | ||
44 | function createVideoMiniatureFromExisting ( | 44 | function createVideoMiniatureFromExisting ( |
45 | inputPath: string, | 45 | inputPath: string, |
46 | video: VideoModel, | 46 | video: MVideoThumbnail, |
47 | type: ThumbnailType, | 47 | type: ThumbnailType, |
48 | automaticallyGenerated: boolean, | 48 | automaticallyGenerated: boolean, |
49 | size?: ImageSize | 49 | size?: ImageSize |
@@ -54,7 +54,7 @@ function createVideoMiniatureFromExisting ( | |||
54 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail }) | 54 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail }) |
55 | } | 55 | } |
56 | 56 | ||
57 | function generateVideoMiniature (video: VideoModel, videoFile: VideoFileModel, type: ThumbnailType) { | 57 | function generateVideoMiniature (video: MVideoThumbnail, videoFile: MVideoFile, type: ThumbnailType) { |
58 | const input = video.getVideoFilePath(videoFile) | 58 | const input = video.getVideoFilePath(videoFile) |
59 | 59 | ||
60 | const { filename, basePath, height, width, existingThumbnail, outputPath } = buildMetadataFromVideo(video, type) | 60 | const { filename, basePath, height, width, existingThumbnail, outputPath } = buildMetadataFromVideo(video, type) |
@@ -65,7 +65,7 @@ function generateVideoMiniature (video: VideoModel, videoFile: VideoFileModel, t | |||
65 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated: true, existingThumbnail }) | 65 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated: true, existingThumbnail }) |
66 | } | 66 | } |
67 | 67 | ||
68 | function createPlaceholderThumbnail (fileUrl: string, video: VideoModel, type: ThumbnailType, size: ImageSize) { | 68 | function createPlaceholderThumbnail (fileUrl: string, video: MVideoThumbnail, type: ThumbnailType, size: ImageSize) { |
69 | const { filename, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size) | 69 | const { filename, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size) |
70 | 70 | ||
71 | const thumbnail = existingThumbnail ? existingThumbnail : new ThumbnailModel() | 71 | const thumbnail = existingThumbnail ? existingThumbnail : new ThumbnailModel() |
@@ -90,7 +90,7 @@ export { | |||
90 | createPlaylistMiniatureFromExisting | 90 | createPlaylistMiniatureFromExisting |
91 | } | 91 | } |
92 | 92 | ||
93 | function buildMetadataFromPlaylist (playlist: VideoPlaylistModel, size: ImageSize) { | 93 | function buildMetadataFromPlaylist (playlist: MVideoPlaylistThumbnail, size: ImageSize) { |
94 | const filename = playlist.generateThumbnailName() | 94 | const filename = playlist.generateThumbnailName() |
95 | const basePath = CONFIG.STORAGE.THUMBNAILS_DIR | 95 | const basePath = CONFIG.STORAGE.THUMBNAILS_DIR |
96 | 96 | ||
@@ -104,7 +104,7 @@ function buildMetadataFromPlaylist (playlist: VideoPlaylistModel, size: ImageSiz | |||
104 | } | 104 | } |
105 | } | 105 | } |
106 | 106 | ||
107 | function buildMetadataFromVideo (video: VideoModel, type: ThumbnailType, size?: ImageSize) { | 107 | function buildMetadataFromVideo (video: MVideoThumbnail, type: ThumbnailType, size?: ImageSize) { |
108 | const existingThumbnail = Array.isArray(video.Thumbnails) | 108 | const existingThumbnail = Array.isArray(video.Thumbnails) |
109 | ? video.Thumbnails.find(t => t.type === type) | 109 | ? video.Thumbnails.find(t => t.type === type) |
110 | : undefined | 110 | : undefined |
@@ -148,7 +148,7 @@ async function createThumbnailFromFunction (parameters: { | |||
148 | type: ThumbnailType, | 148 | type: ThumbnailType, |
149 | automaticallyGenerated?: boolean, | 149 | automaticallyGenerated?: boolean, |
150 | fileUrl?: string, | 150 | fileUrl?: string, |
151 | existingThumbnail?: ThumbnailModel | 151 | existingThumbnail?: MThumbnail |
152 | }) { | 152 | }) { |
153 | const { thumbnailCreator, filename, width, height, type, existingThumbnail, automaticallyGenerated = null, fileUrl = null } = parameters | 153 | const { thumbnailCreator, filename, width, height, type, existingThumbnail, automaticallyGenerated = null, fileUrl = null } = parameters |
154 | 154 | ||
diff --git a/server/lib/user.ts b/server/lib/user.ts index 0e4007770..c45438d95 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts | |||
@@ -2,10 +2,8 @@ import * as uuidv4 from 'uuid/v4' | |||
2 | import { ActivityPubActorType } from '../../shared/models/activitypub' | 2 | import { ActivityPubActorType } from '../../shared/models/activitypub' |
3 | import { SERVER_ACTOR_NAME, WEBSERVER } from '../initializers/constants' | 3 | import { SERVER_ACTOR_NAME, WEBSERVER } from '../initializers/constants' |
4 | import { AccountModel } from '../models/account/account' | 4 | import { AccountModel } from '../models/account/account' |
5 | import { UserModel } from '../models/account/user' | ||
6 | import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub' | 5 | import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub' |
7 | import { createVideoChannel } from './video-channel' | 6 | import { createLocalVideoChannel } from './video-channel' |
8 | import { VideoChannelModel } from '../models/video/video-channel' | ||
9 | import { ActorModel } from '../models/activitypub/actor' | 7 | import { ActorModel } from '../models/activitypub/actor' |
10 | import { UserNotificationSettingModel } from '../models/account/user-notification-setting' | 8 | import { UserNotificationSettingModel } from '../models/account/user-notification-setting' |
11 | import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users' | 9 | import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users' |
@@ -14,14 +12,17 @@ import { sequelizeTypescript } from '../initializers/database' | |||
14 | import { Transaction } from 'sequelize/types' | 12 | import { Transaction } from 'sequelize/types' |
15 | import { Redis } from './redis' | 13 | import { Redis } from './redis' |
16 | import { Emailer } from './emailer' | 14 | import { Emailer } from './emailer' |
15 | import { MAccountDefault, MActorDefault, MChannelActor } from '../typings/models' | ||
16 | import { MUser, MUserDefault, MUserId } from '../typings/models/user' | ||
17 | 17 | ||
18 | type ChannelNames = { name: string, displayName: string } | 18 | type ChannelNames = { name: string, displayName: string } |
19 | |||
19 | async function createUserAccountAndChannelAndPlaylist (parameters: { | 20 | async function createUserAccountAndChannelAndPlaylist (parameters: { |
20 | userToCreate: UserModel, | 21 | userToCreate: MUser, |
21 | userDisplayName?: string, | 22 | userDisplayName?: string, |
22 | channelNames?: ChannelNames, | 23 | channelNames?: ChannelNames, |
23 | validateUser?: boolean | 24 | validateUser?: boolean |
24 | }) { | 25 | }): Promise<{ user: MUserDefault, account: MAccountDefault, videoChannel: MChannelActor }> { |
25 | const { userToCreate, userDisplayName, channelNames, validateUser = true } = parameters | 26 | const { userToCreate, userDisplayName, channelNames, validateUser = true } = parameters |
26 | 27 | ||
27 | const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => { | 28 | const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => { |
@@ -30,7 +31,7 @@ async function createUserAccountAndChannelAndPlaylist (parameters: { | |||
30 | validate: validateUser | 31 | validate: validateUser |
31 | } | 32 | } |
32 | 33 | ||
33 | const userCreated = await userToCreate.save(userOptions) | 34 | const userCreated: MUserDefault = await userToCreate.save(userOptions) |
34 | userCreated.NotificationSetting = await createDefaultUserNotificationSettings(userCreated, t) | 35 | userCreated.NotificationSetting = await createDefaultUserNotificationSettings(userCreated, t) |
35 | 36 | ||
36 | const accountCreated = await createLocalAccountWithoutKeys({ | 37 | const accountCreated = await createLocalAccountWithoutKeys({ |
@@ -43,22 +44,22 @@ async function createUserAccountAndChannelAndPlaylist (parameters: { | |||
43 | userCreated.Account = accountCreated | 44 | userCreated.Account = accountCreated |
44 | 45 | ||
45 | const channelAttributes = await buildChannelAttributes(userCreated, channelNames) | 46 | const channelAttributes = await buildChannelAttributes(userCreated, channelNames) |
46 | const videoChannel = await createVideoChannel(channelAttributes, accountCreated, t) | 47 | const videoChannel = await createLocalVideoChannel(channelAttributes, accountCreated, t) |
47 | 48 | ||
48 | const videoPlaylist = await createWatchLaterPlaylist(accountCreated, t) | 49 | const videoPlaylist = await createWatchLaterPlaylist(accountCreated, t) |
49 | 50 | ||
50 | return { user: userCreated, account: accountCreated, videoChannel, videoPlaylist } | 51 | return { user: userCreated, account: accountCreated, videoChannel, videoPlaylist } |
51 | }) | 52 | }) |
52 | 53 | ||
53 | const [ accountKeys, channelKeys ] = await Promise.all([ | 54 | const [ accountActorWithKeys, channelActorWithKeys ] = await Promise.all([ |
54 | setAsyncActorKeys(account.Actor), | 55 | setAsyncActorKeys(account.Actor), |
55 | setAsyncActorKeys(videoChannel.Actor) | 56 | setAsyncActorKeys(videoChannel.Actor) |
56 | ]) | 57 | ]) |
57 | 58 | ||
58 | account.Actor = accountKeys | 59 | account.Actor = accountActorWithKeys |
59 | videoChannel.Actor = channelKeys | 60 | videoChannel.Actor = channelActorWithKeys |
60 | 61 | ||
61 | return { user, account, videoChannel } as { user: UserModel, account: AccountModel, videoChannel: VideoChannelModel } | 62 | return { user, account, videoChannel } |
62 | } | 63 | } |
63 | 64 | ||
64 | async function createLocalAccountWithoutKeys (parameters: { | 65 | async function createLocalAccountWithoutKeys (parameters: { |
@@ -73,7 +74,7 @@ async function createLocalAccountWithoutKeys (parameters: { | |||
73 | const url = getAccountActivityPubUrl(name) | 74 | const url = getAccountActivityPubUrl(name) |
74 | 75 | ||
75 | const actorInstance = buildActorInstance(type, url, name) | 76 | const actorInstance = buildActorInstance(type, url, name) |
76 | const actorInstanceCreated = await actorInstance.save({ transaction: t }) | 77 | const actorInstanceCreated: MActorDefault = await actorInstance.save({ transaction: t }) |
77 | 78 | ||
78 | const accountInstance = new AccountModel({ | 79 | const accountInstance = new AccountModel({ |
79 | name: displayName || name, | 80 | name: displayName || name, |
@@ -82,7 +83,7 @@ async function createLocalAccountWithoutKeys (parameters: { | |||
82 | actorId: actorInstanceCreated.id | 83 | actorId: actorInstanceCreated.id |
83 | }) | 84 | }) |
84 | 85 | ||
85 | const accountInstanceCreated = await accountInstance.save({ transaction: t }) | 86 | const accountInstanceCreated: MAccountDefault = await accountInstance.save({ transaction: t }) |
86 | accountInstanceCreated.Actor = actorInstanceCreated | 87 | accountInstanceCreated.Actor = actorInstanceCreated |
87 | 88 | ||
88 | return accountInstanceCreated | 89 | return accountInstanceCreated |
@@ -102,7 +103,7 @@ async function createApplicationActor (applicationId: number) { | |||
102 | return accountCreated | 103 | return accountCreated |
103 | } | 104 | } |
104 | 105 | ||
105 | async function sendVerifyUserEmail (user: UserModel, isPendingEmail = false) { | 106 | async function sendVerifyUserEmail (user: MUser, isPendingEmail = false) { |
106 | const verificationString = await Redis.Instance.setVerifyEmailVerificationString(user.id) | 107 | const verificationString = await Redis.Instance.setVerifyEmailVerificationString(user.id) |
107 | let url = WEBSERVER.URL + '/verify-account/email?userId=' + user.id + '&verificationString=' + verificationString | 108 | let url = WEBSERVER.URL + '/verify-account/email?userId=' + user.id + '&verificationString=' + verificationString |
108 | 109 | ||
@@ -124,7 +125,7 @@ export { | |||
124 | 125 | ||
125 | // --------------------------------------------------------------------------- | 126 | // --------------------------------------------------------------------------- |
126 | 127 | ||
127 | function createDefaultUserNotificationSettings (user: UserModel, t: Transaction | undefined) { | 128 | function createDefaultUserNotificationSettings (user: MUserId, t: Transaction | undefined) { |
128 | const values: UserNotificationSetting & { userId: number } = { | 129 | const values: UserNotificationSetting & { userId: number } = { |
129 | userId: user.id, | 130 | userId: user.id, |
130 | newVideoFromSubscription: UserNotificationSettingValue.WEB, | 131 | newVideoFromSubscription: UserNotificationSettingValue.WEB, |
@@ -137,13 +138,14 @@ function createDefaultUserNotificationSettings (user: UserModel, t: Transaction | |||
137 | newUserRegistration: UserNotificationSettingValue.WEB, | 138 | newUserRegistration: UserNotificationSettingValue.WEB, |
138 | commentMention: UserNotificationSettingValue.WEB, | 139 | commentMention: UserNotificationSettingValue.WEB, |
139 | newFollow: UserNotificationSettingValue.WEB, | 140 | newFollow: UserNotificationSettingValue.WEB, |
140 | newInstanceFollower: UserNotificationSettingValue.WEB | 141 | newInstanceFollower: UserNotificationSettingValue.WEB, |
142 | autoInstanceFollowing: UserNotificationSettingValue.WEB | ||
141 | } | 143 | } |
142 | 144 | ||
143 | return UserNotificationSettingModel.create(values, { transaction: t }) | 145 | return UserNotificationSettingModel.create(values, { transaction: t }) |
144 | } | 146 | } |
145 | 147 | ||
146 | async function buildChannelAttributes (user: UserModel, channelNames?: ChannelNames) { | 148 | async function buildChannelAttributes (user: MUser, channelNames?: ChannelNames) { |
147 | if (channelNames) return channelNames | 149 | if (channelNames) return channelNames |
148 | 150 | ||
149 | let channelName = user.username + '_channel' | 151 | let channelName = user.username + '_channel' |
diff --git a/server/lib/video-blacklist.ts b/server/lib/video-blacklist.ts index bdaecd8e2..1dd45b76d 100644 --- a/server/lib/video-blacklist.ts +++ b/server/lib/video-blacklist.ts | |||
@@ -2,16 +2,15 @@ import { Transaction } from 'sequelize' | |||
2 | import { CONFIG } from '../initializers/config' | 2 | import { CONFIG } from '../initializers/config' |
3 | import { UserRight, VideoBlacklistType } from '../../shared/models' | 3 | import { UserRight, VideoBlacklistType } from '../../shared/models' |
4 | import { VideoBlacklistModel } from '../models/video/video-blacklist' | 4 | import { VideoBlacklistModel } from '../models/video/video-blacklist' |
5 | import { UserModel } from '../models/account/user' | ||
6 | import { VideoModel } from '../models/video/video' | ||
7 | import { logger } from '../helpers/logger' | 5 | import { logger } from '../helpers/logger' |
8 | import { UserAdminFlag } from '../../shared/models/users/user-flag.model' | 6 | import { UserAdminFlag } from '../../shared/models/users/user-flag.model' |
9 | import { Hooks } from './plugins/hooks' | 7 | import { Hooks } from './plugins/hooks' |
10 | import { Notifier } from './notifier' | 8 | import { Notifier } from './notifier' |
9 | import { MUser, MVideoBlacklistVideo, MVideoWithBlacklistLight } from '@server/typings/models' | ||
11 | 10 | ||
12 | async function autoBlacklistVideoIfNeeded (parameters: { | 11 | async function autoBlacklistVideoIfNeeded (parameters: { |
13 | video: VideoModel, | 12 | video: MVideoWithBlacklistLight, |
14 | user?: UserModel, | 13 | user?: MUser, |
15 | isRemote: boolean, | 14 | isRemote: boolean, |
16 | isNew: boolean, | 15 | isNew: boolean, |
17 | notify?: boolean, | 16 | notify?: boolean, |
@@ -32,7 +31,7 @@ async function autoBlacklistVideoIfNeeded (parameters: { | |||
32 | reason: 'Auto-blacklisted. Moderator review required.', | 31 | reason: 'Auto-blacklisted. Moderator review required.', |
33 | type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED | 32 | type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED |
34 | } | 33 | } |
35 | const [ videoBlacklist ] = await VideoBlacklistModel.findOrCreate({ | 34 | const [ videoBlacklist ] = await VideoBlacklistModel.findOrCreate<MVideoBlacklistVideo>({ |
36 | where: { | 35 | where: { |
37 | videoId: video.id | 36 | videoId: video.id |
38 | }, | 37 | }, |
@@ -41,7 +40,9 @@ async function autoBlacklistVideoIfNeeded (parameters: { | |||
41 | }) | 40 | }) |
42 | video.VideoBlacklist = videoBlacklist | 41 | video.VideoBlacklist = videoBlacklist |
43 | 42 | ||
44 | if (notify) Notifier.Instance.notifyOnVideoAutoBlacklist(video) | 43 | videoBlacklist.Video = video |
44 | |||
45 | if (notify) Notifier.Instance.notifyOnVideoAutoBlacklist(videoBlacklist) | ||
45 | 46 | ||
46 | logger.info('Video %s auto-blacklisted.', video.uuid) | 47 | logger.info('Video %s auto-blacklisted.', video.uuid) |
47 | 48 | ||
@@ -49,10 +50,10 @@ async function autoBlacklistVideoIfNeeded (parameters: { | |||
49 | } | 50 | } |
50 | 51 | ||
51 | async function autoBlacklistNeeded (parameters: { | 52 | async function autoBlacklistNeeded (parameters: { |
52 | video: VideoModel, | 53 | video: MVideoWithBlacklistLight, |
53 | isRemote: boolean, | 54 | isRemote: boolean, |
54 | isNew: boolean, | 55 | isNew: boolean, |
55 | user?: UserModel | 56 | user?: MUser |
56 | }) { | 57 | }) { |
57 | const { user, video, isRemote, isNew } = parameters | 58 | const { user, video, isRemote, isNew } = parameters |
58 | 59 | ||
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts index ee0482c36..41eab456b 100644 --- a/server/lib/video-channel.ts +++ b/server/lib/video-channel.ts | |||
@@ -1,12 +1,19 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import * as uuidv4 from 'uuid/v4' | 2 | import * as uuidv4 from 'uuid/v4' |
3 | import { VideoChannelCreate } from '../../shared/models' | 3 | import { VideoChannelCreate } from '../../shared/models' |
4 | import { AccountModel } from '../models/account/account' | ||
5 | import { VideoChannelModel } from '../models/video/video-channel' | 4 | import { VideoChannelModel } from '../models/video/video-channel' |
6 | import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub' | 5 | import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub' |
7 | import { VideoModel } from '../models/video/video' | 6 | import { VideoModel } from '../models/video/video' |
7 | import { MAccountId, MChannelDefault, MChannelId } from '../typings/models' | ||
8 | 8 | ||
9 | async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) { | 9 | type CustomVideoChannelModelAccount <T extends MAccountId> = MChannelDefault & |
10 | { Account?: T } | ||
11 | |||
12 | async function createLocalVideoChannel <T extends MAccountId> ( | ||
13 | videoChannelInfo: VideoChannelCreate, | ||
14 | account: T, | ||
15 | t: Sequelize.Transaction | ||
16 | ): Promise<CustomVideoChannelModelAccount<T>> { | ||
10 | const uuid = uuidv4() | 17 | const uuid = uuidv4() |
11 | const url = getVideoChannelActivityPubUrl(videoChannelInfo.name) | 18 | const url = getVideoChannelActivityPubUrl(videoChannelInfo.name) |
12 | const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid) | 19 | const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid) |
@@ -21,10 +28,10 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account | |||
21 | actorId: actorInstanceCreated.id | 28 | actorId: actorInstanceCreated.id |
22 | } | 29 | } |
23 | 30 | ||
24 | const videoChannel = VideoChannelModel.build(videoChannelData) | 31 | const videoChannel = new VideoChannelModel(videoChannelData) |
25 | 32 | ||
26 | const options = { transaction: t } | 33 | const options = { transaction: t } |
27 | const videoChannelCreated = await videoChannel.save(options) | 34 | const videoChannelCreated: CustomVideoChannelModelAccount<T> = await videoChannel.save(options) as MChannelDefault |
28 | 35 | ||
29 | // Do not forget to add Account/Actor information to the created video channel | 36 | // Do not forget to add Account/Actor information to the created video channel |
30 | videoChannelCreated.Account = account | 37 | videoChannelCreated.Account = account |
@@ -34,7 +41,7 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account | |||
34 | return videoChannelCreated | 41 | return videoChannelCreated |
35 | } | 42 | } |
36 | 43 | ||
37 | async function federateAllVideosOfChannel (videoChannel: VideoChannelModel) { | 44 | async function federateAllVideosOfChannel (videoChannel: MChannelId) { |
38 | const videoIds = await VideoModel.getAllIdsFromChannel(videoChannel) | 45 | const videoIds = await VideoModel.getAllIdsFromChannel(videoChannel) |
39 | 46 | ||
40 | for (const videoId of videoIds) { | 47 | for (const videoId of videoIds) { |
@@ -47,6 +54,6 @@ async function federateAllVideosOfChannel (videoChannel: VideoChannelModel) { | |||
47 | // --------------------------------------------------------------------------- | 54 | // --------------------------------------------------------------------------- |
48 | 55 | ||
49 | export { | 56 | export { |
50 | createVideoChannel, | 57 | createLocalVideoChannel, |
51 | federateAllVideosOfChannel | 58 | federateAllVideosOfChannel |
52 | } | 59 | } |
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 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { ResultList } from '../../shared/models' | 2 | import { ResultList } from '../../shared/models' |
3 | import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model' | 3 | import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model' |
4 | import { AccountModel } from '../models/account/account' | ||
5 | import { VideoModel } from '../models/video/video' | ||
6 | import { VideoCommentModel } from '../models/video/video-comment' | 4 | import { VideoCommentModel } from '../models/video/video-comment' |
7 | import { getVideoCommentActivityPubUrl } from './activitypub' | 5 | import { getVideoCommentActivityPubUrl } from './activitypub' |
8 | import { sendCreateVideoComment } from './activitypub/send' | 6 | import { sendCreateVideoComment } from './activitypub/send' |
7 | import { MAccountDefault, MComment, MCommentOwnerVideoReply, MVideoFullLight } from '../typings/models' | ||
9 | 8 | ||
10 | async function createVideoComment (obj: { | 9 | async function createVideoComment (obj: { |
11 | text: string, | 10 | text: string, |
12 | inReplyToComment: VideoCommentModel | null, | 11 | inReplyToComment: MComment | null, |
13 | video: VideoModel | 12 | video: MVideoFullLight, |
14 | account: AccountModel | 13 | account: MAccountDefault |
15 | }, t: Sequelize.Transaction) { | 14 | }, t: Sequelize.Transaction) { |
16 | let originCommentId: number | null = null | 15 | let originCommentId: number | null = null |
17 | let inReplyToCommentId: number | null = null | 16 | let inReplyToCommentId: number | null = null |
@@ -32,7 +31,7 @@ async function createVideoComment (obj: { | |||
32 | 31 | ||
33 | comment.url = getVideoCommentActivityPubUrl(obj.video, comment) | 32 | comment.url = getVideoCommentActivityPubUrl(obj.video, comment) |
34 | 33 | ||
35 | const savedComment = await comment.save({ transaction: t }) | 34 | const savedComment: MCommentOwnerVideoReply = await comment.save({ transaction: t }) |
36 | savedComment.InReplyToVideoComment = obj.inReplyToComment | 35 | savedComment.InReplyToVideoComment = obj.inReplyToComment |
37 | savedComment.Video = obj.video | 36 | savedComment.Video = obj.video |
38 | savedComment.Account = obj.account | 37 | savedComment.Account = obj.account |
diff --git a/server/lib/video-playlist.ts b/server/lib/video-playlist.ts index 6e214e60f..29b70cfda 100644 --- a/server/lib/video-playlist.ts +++ b/server/lib/video-playlist.ts | |||
@@ -1,12 +1,13 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { AccountModel } from '../models/account/account' | ||
3 | import { VideoPlaylistModel } from '../models/video/video-playlist' | 2 | import { VideoPlaylistModel } from '../models/video/video-playlist' |
4 | import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model' | 3 | import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model' |
5 | import { getVideoPlaylistActivityPubUrl } from './activitypub' | 4 | import { getVideoPlaylistActivityPubUrl } from './activitypub' |
6 | import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model' | 5 | import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model' |
6 | import { MAccount } from '../typings/models' | ||
7 | import { MVideoPlaylistOwner } from '../typings/models/video/video-playlist' | ||
7 | 8 | ||
8 | async function createWatchLaterPlaylist (account: AccountModel, t: Sequelize.Transaction) { | 9 | async function createWatchLaterPlaylist (account: MAccount, t: Sequelize.Transaction) { |
9 | const videoPlaylist = new VideoPlaylistModel({ | 10 | const videoPlaylist: MVideoPlaylistOwner = new VideoPlaylistModel({ |
10 | name: 'Watch later', | 11 | name: 'Watch later', |
11 | privacy: VideoPlaylistPrivacy.PRIVATE, | 12 | privacy: VideoPlaylistPrivacy.PRIVATE, |
12 | type: VideoPlaylistType.WATCH_LATER, | 13 | type: VideoPlaylistType.WATCH_LATER, |
diff --git a/server/lib/video-transcoding.ts b/server/lib/video-transcoding.ts index ba6b29163..a204c0c63 100644 --- a/server/lib/video-transcoding.ts +++ b/server/lib/video-transcoding.ts | |||
@@ -5,16 +5,16 @@ import { ensureDir, move, remove, stat } from 'fs-extra' | |||
5 | import { logger } from '../helpers/logger' | 5 | import { logger } from '../helpers/logger' |
6 | import { VideoResolution } from '../../shared/models/videos' | 6 | import { VideoResolution } from '../../shared/models/videos' |
7 | import { VideoFileModel } from '../models/video/video-file' | 7 | import { VideoFileModel } from '../models/video/video-file' |
8 | import { VideoModel } from '../models/video/video' | ||
9 | import { updateMasterHLSPlaylist, updateSha256Segments } from './hls' | 8 | import { updateMasterHLSPlaylist, updateSha256Segments } from './hls' |
10 | import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' | 9 | import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' |
11 | import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' | 10 | import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' |
12 | import { CONFIG } from '../initializers/config' | 11 | import { CONFIG } from '../initializers/config' |
12 | import { MVideoFile, MVideoWithFile, MVideoWithFileThumbnail } from '@server/typings/models' | ||
13 | 13 | ||
14 | /** | 14 | /** |
15 | * Optimize the original video file and replace it. The resolution is not changed. | 15 | * Optimize the original video file and replace it. The resolution is not changed. |
16 | */ | 16 | */ |
17 | async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFileModel) { | 17 | async function optimizeVideofile (video: MVideoWithFile, inputVideoFileArg?: MVideoFile) { |
18 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR | 18 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR |
19 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR | 19 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR |
20 | const newExtname = '.mp4' | 20 | const newExtname = '.mp4' |
@@ -57,7 +57,7 @@ async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFi | |||
57 | /** | 57 | /** |
58 | * Transcode the original video file to a lower resolution. | 58 | * Transcode the original video file to a lower resolution. |
59 | */ | 59 | */ |
60 | async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoResolution, isPortrait: boolean) { | 60 | async function transcodeOriginalVideofile (video: MVideoWithFile, resolution: VideoResolution, isPortrait: boolean) { |
61 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR | 61 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR |
62 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR | 62 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR |
63 | const extname = '.mp4' | 63 | const extname = '.mp4' |
@@ -87,7 +87,7 @@ async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoR | |||
87 | return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath) | 87 | return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath) |
88 | } | 88 | } |
89 | 89 | ||
90 | async function mergeAudioVideofile (video: VideoModel, resolution: VideoResolution) { | 90 | async function mergeAudioVideofile (video: MVideoWithFileThumbnail, resolution: VideoResolution) { |
91 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR | 91 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR |
92 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR | 92 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR |
93 | const newExtname = '.mp4' | 93 | const newExtname = '.mp4' |
@@ -117,7 +117,7 @@ async function mergeAudioVideofile (video: VideoModel, resolution: VideoResoluti | |||
117 | return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) | 117 | return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) |
118 | } | 118 | } |
119 | 119 | ||
120 | async function generateHlsPlaylist (video: VideoModel, resolution: VideoResolution, isPortraitMode: boolean) { | 120 | async function generateHlsPlaylist (video: MVideoWithFile, resolution: VideoResolution, isPortraitMode: boolean) { |
121 | const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) | 121 | const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) |
122 | await ensureDir(join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)) | 122 | await ensureDir(join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)) |
123 | 123 | ||
@@ -165,14 +165,14 @@ export { | |||
165 | 165 | ||
166 | // --------------------------------------------------------------------------- | 166 | // --------------------------------------------------------------------------- |
167 | 167 | ||
168 | async function onVideoFileTranscoding (video: VideoModel, videoFile: VideoFileModel, transcodingPath: string, outputPath: string) { | 168 | async function onVideoFileTranscoding (video: MVideoWithFile, videoFile: MVideoFile, transcodingPath: string, outputPath: string) { |
169 | const stats = await stat(transcodingPath) | 169 | const stats = await stat(transcodingPath) |
170 | const fps = await getVideoFileFPS(transcodingPath) | 170 | const fps = await getVideoFileFPS(transcodingPath) |
171 | 171 | ||
172 | await move(transcodingPath, outputPath) | 172 | await move(transcodingPath, outputPath) |
173 | 173 | ||
174 | videoFile.set('size', stats.size) | 174 | videoFile.size = stats.size |
175 | videoFile.set('fps', fps) | 175 | videoFile.fps = fps |
176 | 176 | ||
177 | await video.createTorrentAndSetInfoHash(videoFile) | 177 | await video.createTorrentAndSetInfoHash(videoFile) |
178 | 178 | ||
diff --git a/server/middlewares/activitypub.ts b/server/middlewares/activitypub.ts index b1e5b5236..bea213d27 100644 --- a/server/middlewares/activitypub.ts +++ b/server/middlewares/activitypub.ts | |||
@@ -101,6 +101,8 @@ async function checkJsonLDSignature (req: Request, res: Response) { | |||
101 | const verified = await isJsonLDSignatureVerified(actor, req.body) | 101 | const verified = await isJsonLDSignatureVerified(actor, req.body) |
102 | 102 | ||
103 | if (verified !== true) { | 103 | if (verified !== true) { |
104 | logger.warn('Signature not verified.', req.body) | ||
105 | |||
104 | res.sendStatus(403) | 106 | res.sendStatus(403) |
105 | return false | 107 | return false |
106 | } | 108 | } |
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' | |||
10 | import { ActorModel } from '../../models/activitypub/actor' | 10 | import { ActorModel } from '../../models/activitypub/actor' |
11 | import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' | 11 | import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' |
12 | import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' | 12 | import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' |
13 | import { MActorFollowActorsDefault } from '@server/typings/models' | ||
13 | 14 | ||
14 | const followValidator = [ | 15 | const followValidator = [ |
15 | body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'), | 16 | body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'), |
@@ -65,7 +66,7 @@ const getFollowerValidator = [ | |||
65 | 66 | ||
66 | if (areValidationErrors(req, res)) return | 67 | if (areValidationErrors(req, res)) return |
67 | 68 | ||
68 | let follow: ActorFollowModel | 69 | let follow: MActorFollowActorsDefault |
69 | try { | 70 | try { |
70 | const actorUrl = await loadActorUrlOrGetFromWebfinger(req.params.nameWithHost) | 71 | const actorUrl = await loadActorUrlOrGetFromWebfinger(req.params.nameWithHost) |
71 | const actor = await ActorModel.loadByUrl(actorUrl) | 72 | const actor = await ActorModel.loadByUrl(actorUrl) |
diff --git a/server/middlewares/validators/redundancy.ts b/server/middlewares/validators/redundancy.ts index 1fdac0e4e..e65d3b8d3 100644 --- a/server/middlewares/validators/redundancy.ts +++ b/server/middlewares/validators/redundancy.ts | |||
@@ -24,7 +24,7 @@ const videoFileRedundancyGetValidator = [ | |||
24 | if (areValidationErrors(req, res)) return | 24 | if (areValidationErrors(req, res)) return |
25 | if (!await doesVideoExist(req.params.videoId, res)) return | 25 | if (!await doesVideoExist(req.params.videoId, res)) return |
26 | 26 | ||
27 | const video = res.locals.video | 27 | const video = res.locals.videoAll |
28 | const videoFile = video.VideoFiles.find(f => { | 28 | const videoFile = video.VideoFiles.find(f => { |
29 | return f.resolution === req.params.resolution && (!req.params.fps || f.fps === req.params.fps) | 29 | return f.resolution === req.params.resolution && (!req.params.fps || f.fps === req.params.fps) |
30 | }) | 30 | }) |
@@ -50,7 +50,7 @@ const videoPlaylistRedundancyGetValidator = [ | |||
50 | if (areValidationErrors(req, res)) return | 50 | if (areValidationErrors(req, res)) return |
51 | if (!await doesVideoExist(req.params.videoId, res)) return | 51 | if (!await doesVideoExist(req.params.videoId, res)) return |
52 | 52 | ||
53 | const video = res.locals.video | 53 | const video = res.locals.videoAll |
54 | const videoStreamingPlaylist = video.VideoStreamingPlaylists.find(p => p === req.params.streamingPlaylistType) | 54 | const videoStreamingPlaylist = video.VideoStreamingPlaylists.find(p => p === req.params.streamingPlaylistType) |
55 | 55 | ||
56 | if (!videoStreamingPlaylist) return res.status(404).json({ error: 'Video playlist not found.' }) | 56 | if (!videoStreamingPlaylist) return res.status(404).json({ error: 'Video playlist not found.' }) |
diff --git a/server/middlewares/validators/user-notifications.ts b/server/middlewares/validators/user-notifications.ts index 308b32655..fbfcb0a4c 100644 --- a/server/middlewares/validators/user-notifications.ts +++ b/server/middlewares/validators/user-notifications.ts | |||
@@ -43,6 +43,8 @@ const updateNotificationSettingsValidator = [ | |||
43 | .custom(isUserNotificationSettingValid).withMessage('Should have a valid new user registration notification setting'), | 43 | .custom(isUserNotificationSettingValid).withMessage('Should have a valid new user registration notification setting'), |
44 | body('newInstanceFollower') | 44 | body('newInstanceFollower') |
45 | .custom(isUserNotificationSettingValid).withMessage('Should have a valid new instance follower notification setting'), | 45 | .custom(isUserNotificationSettingValid).withMessage('Should have a valid new instance follower notification setting'), |
46 | body('autoInstanceFollowing') | ||
47 | .custom(isUserNotificationSettingValid).withMessage('Should have a valid new instance following notification setting'), | ||
46 | 48 | ||
47 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | 49 | (req: express.Request, res: express.Response, next: express.NextFunction) => { |
48 | logger.debug('Checking updateNotificationSettingsValidator parameters', { parameters: req.body }) | 50 | logger.debug('Checking updateNotificationSettingsValidator parameters', { parameters: req.body }) |
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts index 8ee2ec1f5..544db76d7 100644 --- a/server/middlewares/validators/users.ts +++ b/server/middlewares/validators/users.ts | |||
@@ -4,6 +4,7 @@ import { body, param } from 'express-validator' | |||
4 | import { omit } from 'lodash' | 4 | import { omit } from 'lodash' |
5 | import { isIdOrUUIDValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' | 5 | import { isIdOrUUIDValid, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' |
6 | import { | 6 | import { |
7 | isNoInstanceConfigWarningModal, isNoWelcomeModal, | ||
7 | isUserAdminFlagsValid, | 8 | isUserAdminFlagsValid, |
8 | isUserAutoPlayVideoValid, | 9 | isUserAutoPlayVideoValid, |
9 | isUserBlockedReasonValid, | 10 | isUserBlockedReasonValid, |
@@ -31,6 +32,7 @@ import { isThemeNameValid } from '../../helpers/custom-validators/plugins' | |||
31 | import { isThemeRegistered } from '../../lib/plugins/theme-utils' | 32 | import { isThemeRegistered } from '../../lib/plugins/theme-utils' |
32 | import { doesVideoExist } from '../../helpers/middlewares' | 33 | import { doesVideoExist } from '../../helpers/middlewares' |
33 | import { UserRole } from '../../../shared/models/users' | 34 | import { UserRole } from '../../../shared/models/users' |
35 | import { MUserDefault } from '@server/typings/models' | ||
34 | 36 | ||
35 | const usersAddValidator = [ | 37 | const usersAddValidator = [ |
36 | body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'), | 38 | body('username').custom(isUserUsernameValid).withMessage('Should have a valid username (lowercase alphanumeric characters)'), |
@@ -215,6 +217,12 @@ const usersUpdateMeValidator = [ | |||
215 | body('theme') | 217 | body('theme') |
216 | .optional() | 218 | .optional() |
217 | .custom(v => isThemeNameValid(v) && isThemeRegistered(v)).withMessage('Should have a valid theme'), | 219 | .custom(v => isThemeNameValid(v) && isThemeRegistered(v)).withMessage('Should have a valid theme'), |
220 | body('noInstanceConfigWarningModal') | ||
221 | .optional() | ||
222 | .custom(v => isNoInstanceConfigWarningModal(v)).withMessage('Should have a valid noInstanceConfigWarningModal boolean'), | ||
223 | body('noWelcomeModal') | ||
224 | .optional() | ||
225 | .custom(v => isNoWelcomeModal(v)).withMessage('Should have a valid noWelcomeModal boolean'), | ||
218 | 226 | ||
219 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 227 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
220 | logger.debug('Checking usersUpdateMe parameters', { parameters: omit(req.body, 'password') }) | 228 | logger.debug('Checking usersUpdateMe parameters', { parameters: omit(req.body, 'password') }) |
@@ -462,7 +470,7 @@ async function checkUserNameOrEmailDoesNotAlreadyExist (username: string, email: | |||
462 | return true | 470 | return true |
463 | } | 471 | } |
464 | 472 | ||
465 | async function checkUserExist (finder: () => Bluebird<UserModel>, res: express.Response, abortResponse = true) { | 473 | async function checkUserExist (finder: () => Bluebird<MUserDefault>, res: express.Response, abortResponse = true) { |
466 | const user = await finder() | 474 | const user = await finder() |
467 | 475 | ||
468 | if (!user) { | 476 | if (!user) { |
diff --git a/server/middlewares/validators/videos/video-abuses.ts b/server/middlewares/validators/videos/video-abuses.ts index e27d91bb1..a4aef4024 100644 --- a/server/middlewares/validators/videos/video-abuses.ts +++ b/server/middlewares/validators/videos/video-abuses.ts | |||
@@ -33,7 +33,7 @@ const videoAbuseGetValidator = [ | |||
33 | 33 | ||
34 | if (areValidationErrors(req, res)) return | 34 | if (areValidationErrors(req, res)) return |
35 | if (!await doesVideoExist(req.params.videoId, res)) return | 35 | if (!await doesVideoExist(req.params.videoId, res)) return |
36 | if (!await doesVideoAbuseExist(req.params.id, res.locals.video.id, res)) return | 36 | if (!await doesVideoAbuseExist(req.params.id, res.locals.videoAll.id, res)) return |
37 | 37 | ||
38 | return next() | 38 | return next() |
39 | } | 39 | } |
@@ -54,7 +54,7 @@ const videoAbuseUpdateValidator = [ | |||
54 | 54 | ||
55 | if (areValidationErrors(req, res)) return | 55 | if (areValidationErrors(req, res)) return |
56 | if (!await doesVideoExist(req.params.videoId, res)) return | 56 | if (!await doesVideoExist(req.params.videoId, res)) return |
57 | if (!await doesVideoAbuseExist(req.params.id, res.locals.video.id, res)) return | 57 | if (!await doesVideoAbuseExist(req.params.id, res.locals.videoAll.id, res)) return |
58 | 58 | ||
59 | return next() | 59 | return next() |
60 | } | 60 | } |
diff --git a/server/middlewares/validators/videos/video-blacklist.ts b/server/middlewares/validators/videos/video-blacklist.ts index 3e8c5b30c..5440e57e7 100644 --- a/server/middlewares/validators/videos/video-blacklist.ts +++ b/server/middlewares/validators/videos/video-blacklist.ts | |||
@@ -14,7 +14,7 @@ const videosBlacklistRemoveValidator = [ | |||
14 | 14 | ||
15 | if (areValidationErrors(req, res)) return | 15 | if (areValidationErrors(req, res)) return |
16 | if (!await doesVideoExist(req.params.videoId, res)) return | 16 | if (!await doesVideoExist(req.params.videoId, res)) return |
17 | if (!await doesVideoBlacklistExist(res.locals.video.id, res)) return | 17 | if (!await doesVideoBlacklistExist(res.locals.videoAll.id, res)) return |
18 | 18 | ||
19 | return next() | 19 | return next() |
20 | } | 20 | } |
@@ -36,7 +36,7 @@ const videosBlacklistAddValidator = [ | |||
36 | if (areValidationErrors(req, res)) return | 36 | if (areValidationErrors(req, res)) return |
37 | if (!await doesVideoExist(req.params.videoId, res)) return | 37 | if (!await doesVideoExist(req.params.videoId, res)) return |
38 | 38 | ||
39 | const video = res.locals.video | 39 | const video = res.locals.videoAll |
40 | if (req.body.unfederate === true && video.remote === true) { | 40 | if (req.body.unfederate === true && video.remote === true) { |
41 | return res | 41 | return res |
42 | .status(409) | 42 | .status(409) |
@@ -59,7 +59,7 @@ const videosBlacklistUpdateValidator = [ | |||
59 | 59 | ||
60 | if (areValidationErrors(req, res)) return | 60 | if (areValidationErrors(req, res)) return |
61 | if (!await doesVideoExist(req.params.videoId, res)) return | 61 | if (!await doesVideoExist(req.params.videoId, res)) return |
62 | if (!await doesVideoBlacklistExist(res.locals.video.id, res)) return | 62 | if (!await doesVideoBlacklistExist(res.locals.videoAll.id, res)) return |
63 | 63 | ||
64 | return next() | 64 | return next() |
65 | } | 65 | } |
diff --git a/server/middlewares/validators/videos/video-captions.ts b/server/middlewares/validators/videos/video-captions.ts index f5610222a..2fb1da5ce 100644 --- a/server/middlewares/validators/videos/video-captions.ts +++ b/server/middlewares/validators/videos/video-captions.ts | |||
@@ -26,7 +26,7 @@ const addVideoCaptionValidator = [ | |||
26 | 26 | ||
27 | // Check if the user who did the request is able to update the video | 27 | // Check if the user who did the request is able to update the video |
28 | const user = res.locals.oauth.token.User | 28 | const user = res.locals.oauth.token.User |
29 | if (!checkUserCanManageVideo(user, res.locals.video, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req) | 29 | if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req) |
30 | 30 | ||
31 | return next() | 31 | return next() |
32 | } | 32 | } |
@@ -41,11 +41,11 @@ const deleteVideoCaptionValidator = [ | |||
41 | 41 | ||
42 | if (areValidationErrors(req, res)) return | 42 | if (areValidationErrors(req, res)) return |
43 | if (!await doesVideoExist(req.params.videoId, res)) return | 43 | if (!await doesVideoExist(req.params.videoId, res)) return |
44 | if (!await doesVideoCaptionExist(res.locals.video, req.params.captionLanguage, res)) return | 44 | if (!await doesVideoCaptionExist(res.locals.videoAll, req.params.captionLanguage, res)) return |
45 | 45 | ||
46 | // Check if the user who did the request is able to update the video | 46 | // Check if the user who did the request is able to update the video |
47 | const user = res.locals.oauth.token.User | 47 | const user = res.locals.oauth.token.User |
48 | if (!checkUserCanManageVideo(user, res.locals.video, UserRight.UPDATE_ANY_VIDEO, res)) return | 48 | if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.UPDATE_ANY_VIDEO, res)) return |
49 | 49 | ||
50 | return next() | 50 | return next() |
51 | } | 51 | } |
diff --git a/server/middlewares/validators/videos/video-channels.ts b/server/middlewares/validators/videos/video-channels.ts index 3ee5064fc..d21274527 100644 --- a/server/middlewares/validators/videos/video-channels.ts +++ b/server/middlewares/validators/videos/video-channels.ts | |||
@@ -7,13 +7,13 @@ import { | |||
7 | isVideoChannelSupportValid | 7 | isVideoChannelSupportValid |
8 | } from '../../../helpers/custom-validators/video-channels' | 8 | } from '../../../helpers/custom-validators/video-channels' |
9 | import { logger } from '../../../helpers/logger' | 9 | import { logger } from '../../../helpers/logger' |
10 | import { UserModel } from '../../../models/account/user' | ||
11 | import { VideoChannelModel } from '../../../models/video/video-channel' | 10 | import { VideoChannelModel } from '../../../models/video/video-channel' |
12 | import { areValidationErrors } from '../utils' | 11 | import { areValidationErrors } from '../utils' |
13 | import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor' | 12 | import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor' |
14 | import { ActorModel } from '../../../models/activitypub/actor' | 13 | import { ActorModel } from '../../../models/activitypub/actor' |
15 | import { isBooleanValid } from '../../../helpers/custom-validators/misc' | 14 | import { isBooleanValid } from '../../../helpers/custom-validators/misc' |
16 | import { doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../../../helpers/middlewares' | 15 | import { doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../../../helpers/middlewares' |
16 | import { MChannelAccountDefault, MUser } from '@server/typings/models' | ||
17 | 17 | ||
18 | const videoChannelsAddValidator = [ | 18 | const videoChannelsAddValidator = [ |
19 | body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), | 19 | body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), |
@@ -131,7 +131,7 @@ export { | |||
131 | 131 | ||
132 | // --------------------------------------------------------------------------- | 132 | // --------------------------------------------------------------------------- |
133 | 133 | ||
134 | function checkUserCanDeleteVideoChannel (user: UserModel, videoChannel: VideoChannelModel, res: express.Response) { | 134 | function checkUserCanDeleteVideoChannel (user: MUser, videoChannel: MChannelAccountDefault, res: express.Response) { |
135 | if (videoChannel.Actor.isOwned() === false) { | 135 | if (videoChannel.Actor.isOwned() === false) { |
136 | res.status(403) | 136 | res.status(403) |
137 | .json({ error: 'Cannot remove video channel of another server.' }) | 137 | .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' | |||
4 | import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc' | 4 | import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc' |
5 | import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments' | 5 | import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments' |
6 | import { logger } from '../../../helpers/logger' | 6 | import { logger } from '../../../helpers/logger' |
7 | import { UserModel } from '../../../models/account/user' | ||
8 | import { VideoModel } from '../../../models/video/video' | ||
9 | import { VideoCommentModel } from '../../../models/video/video-comment' | 7 | import { VideoCommentModel } from '../../../models/video/video-comment' |
10 | import { areValidationErrors } from '../utils' | 8 | import { areValidationErrors } from '../utils' |
11 | import { Hooks } from '../../../lib/plugins/hooks' | 9 | import { Hooks } from '../../../lib/plugins/hooks' |
12 | import { isLocalVideoThreadAccepted, isLocalVideoCommentReplyAccepted, AcceptResult } from '../../../lib/moderation' | 10 | import { AcceptResult, isLocalVideoCommentReplyAccepted, isLocalVideoThreadAccepted } from '../../../lib/moderation' |
13 | import { doesVideoExist } from '../../../helpers/middlewares' | 11 | import { doesVideoExist } from '../../../helpers/middlewares' |
12 | import { MCommentOwner, MVideo, MVideoFullLight, MVideoId } from '../../../typings/models/video' | ||
13 | import { MUser } from '@server/typings/models' | ||
14 | 14 | ||
15 | const listVideoCommentThreadsValidator = [ | 15 | const listVideoCommentThreadsValidator = [ |
16 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), | 16 | param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'), |
@@ -34,7 +34,7 @@ const listVideoThreadCommentsValidator = [ | |||
34 | 34 | ||
35 | if (areValidationErrors(req, res)) return | 35 | if (areValidationErrors(req, res)) return |
36 | if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return | 36 | if (!await doesVideoExist(req.params.videoId, res, 'only-video')) return |
37 | if (!await doesVideoCommentThreadExist(req.params.threadId, res.locals.video, res)) return | 37 | if (!await doesVideoCommentThreadExist(req.params.threadId, res.locals.onlyVideo, res)) return |
38 | 38 | ||
39 | return next() | 39 | return next() |
40 | } | 40 | } |
@@ -49,8 +49,8 @@ const addVideoCommentThreadValidator = [ | |||
49 | 49 | ||
50 | if (areValidationErrors(req, res)) return | 50 | if (areValidationErrors(req, res)) return |
51 | if (!await doesVideoExist(req.params.videoId, res)) return | 51 | if (!await doesVideoExist(req.params.videoId, res)) return |
52 | if (!isVideoCommentsEnabled(res.locals.video, res)) return | 52 | if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return |
53 | if (!await isVideoCommentAccepted(req, res, false)) return | 53 | if (!await isVideoCommentAccepted(req, res, res.locals.videoAll,false)) return |
54 | 54 | ||
55 | return next() | 55 | return next() |
56 | } | 56 | } |
@@ -66,9 +66,9 @@ const addVideoCommentReplyValidator = [ | |||
66 | 66 | ||
67 | if (areValidationErrors(req, res)) return | 67 | if (areValidationErrors(req, res)) return |
68 | if (!await doesVideoExist(req.params.videoId, res)) return | 68 | if (!await doesVideoExist(req.params.videoId, res)) return |
69 | if (!isVideoCommentsEnabled(res.locals.video, res)) return | 69 | if (!isVideoCommentsEnabled(res.locals.videoAll, res)) return |
70 | if (!await doesVideoCommentExist(req.params.commentId, res.locals.video, res)) return | 70 | if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return |
71 | if (!await isVideoCommentAccepted(req, res, true)) return | 71 | if (!await isVideoCommentAccepted(req, res, res.locals.videoAll, true)) return |
72 | 72 | ||
73 | return next() | 73 | return next() |
74 | } | 74 | } |
@@ -83,7 +83,7 @@ const videoCommentGetValidator = [ | |||
83 | 83 | ||
84 | if (areValidationErrors(req, res)) return | 84 | if (areValidationErrors(req, res)) return |
85 | if (!await doesVideoExist(req.params.videoId, res, 'id')) return | 85 | if (!await doesVideoExist(req.params.videoId, res, 'id')) return |
86 | if (!await doesVideoCommentExist(req.params.commentId, res.locals.video, res)) return | 86 | if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoId, res)) return |
87 | 87 | ||
88 | return next() | 88 | return next() |
89 | } | 89 | } |
@@ -98,10 +98,10 @@ const removeVideoCommentValidator = [ | |||
98 | 98 | ||
99 | if (areValidationErrors(req, res)) return | 99 | if (areValidationErrors(req, res)) return |
100 | if (!await doesVideoExist(req.params.videoId, res)) return | 100 | if (!await doesVideoExist(req.params.videoId, res)) return |
101 | if (!await doesVideoCommentExist(req.params.commentId, res.locals.video, res)) return | 101 | if (!await doesVideoCommentExist(req.params.commentId, res.locals.videoAll, res)) return |
102 | 102 | ||
103 | // Check if the user who did the request is able to delete the video | 103 | // Check if the user who did the request is able to delete the video |
104 | if (!checkUserCanDeleteVideoComment(res.locals.oauth.token.User, res.locals.videoComment, res)) return | 104 | if (!checkUserCanDeleteVideoComment(res.locals.oauth.token.User, res.locals.videoCommentFull, res)) return |
105 | 105 | ||
106 | return next() | 106 | return next() |
107 | } | 107 | } |
@@ -120,7 +120,7 @@ export { | |||
120 | 120 | ||
121 | // --------------------------------------------------------------------------- | 121 | // --------------------------------------------------------------------------- |
122 | 122 | ||
123 | async function doesVideoCommentThreadExist (id: number, video: VideoModel, res: express.Response) { | 123 | async function doesVideoCommentThreadExist (id: number, video: MVideoId, res: express.Response) { |
124 | const videoComment = await VideoCommentModel.loadById(id) | 124 | const videoComment = await VideoCommentModel.loadById(id) |
125 | 125 | ||
126 | if (!videoComment) { | 126 | if (!videoComment) { |
@@ -151,7 +151,7 @@ async function doesVideoCommentThreadExist (id: number, video: VideoModel, res: | |||
151 | return true | 151 | return true |
152 | } | 152 | } |
153 | 153 | ||
154 | async function doesVideoCommentExist (id: number, video: VideoModel, res: express.Response) { | 154 | async function doesVideoCommentExist (id: number, video: MVideoId, res: express.Response) { |
155 | const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id) | 155 | const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id) |
156 | 156 | ||
157 | if (!videoComment) { | 157 | if (!videoComment) { |
@@ -170,11 +170,11 @@ async function doesVideoCommentExist (id: number, video: VideoModel, res: expres | |||
170 | return false | 170 | return false |
171 | } | 171 | } |
172 | 172 | ||
173 | res.locals.videoComment = videoComment | 173 | res.locals.videoCommentFull = videoComment |
174 | return true | 174 | return true |
175 | } | 175 | } |
176 | 176 | ||
177 | function isVideoCommentsEnabled (video: VideoModel, res: express.Response) { | 177 | function isVideoCommentsEnabled (video: MVideo, res: express.Response) { |
178 | if (video.commentsEnabled !== true) { | 178 | if (video.commentsEnabled !== true) { |
179 | res.status(409) | 179 | res.status(409) |
180 | .json({ error: 'Video comments are disabled for this video.' }) | 180 | .json({ error: 'Video comments are disabled for this video.' }) |
@@ -186,7 +186,7 @@ function isVideoCommentsEnabled (video: VideoModel, res: express.Response) { | |||
186 | return true | 186 | return true |
187 | } | 187 | } |
188 | 188 | ||
189 | function checkUserCanDeleteVideoComment (user: UserModel, videoComment: VideoCommentModel, res: express.Response) { | 189 | function checkUserCanDeleteVideoComment (user: MUser, videoComment: MCommentOwner, res: express.Response) { |
190 | const account = videoComment.Account | 190 | const account = videoComment.Account |
191 | if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) === false && account.userId !== user.id) { | 191 | if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) === false && account.userId !== user.id) { |
192 | res.status(403) | 192 | res.status(403) |
@@ -198,9 +198,9 @@ function checkUserCanDeleteVideoComment (user: UserModel, videoComment: VideoCom | |||
198 | return true | 198 | return true |
199 | } | 199 | } |
200 | 200 | ||
201 | async function isVideoCommentAccepted (req: express.Request, res: express.Response, isReply: boolean) { | 201 | async function isVideoCommentAccepted (req: express.Request, res: express.Response, video: MVideoFullLight, isReply: boolean) { |
202 | const acceptParameters = { | 202 | const acceptParameters = { |
203 | video: res.locals.video, | 203 | video, |
204 | commentBody: req.body, | 204 | commentBody: req.body, |
205 | user: res.locals.oauth.token.User | 205 | user: res.locals.oauth.token.User |
206 | } | 206 | } |
@@ -208,7 +208,7 @@ async function isVideoCommentAccepted (req: express.Request, res: express.Respon | |||
208 | let acceptedResult: AcceptResult | 208 | let acceptedResult: AcceptResult |
209 | 209 | ||
210 | if (isReply) { | 210 | if (isReply) { |
211 | const acceptReplyParameters = Object.assign(acceptParameters, { parentComment: res.locals.videoComment }) | 211 | const acceptReplyParameters = Object.assign(acceptParameters, { parentComment: res.locals.videoCommentFull }) |
212 | 212 | ||
213 | acceptedResult = await Hooks.wrapFun( | 213 | acceptedResult = await Hooks.wrapFun( |
214 | isLocalVideoCommentReplyAccepted, | 214 | isLocalVideoCommentReplyAccepted, |
diff --git a/server/middlewares/validators/videos/video-playlists.ts b/server/middlewares/validators/videos/video-playlists.ts index 5823795be..27ee62b1f 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' | |||
2 | import { body, param, query, ValidationChain } from 'express-validator' | 2 | import { body, param, query, ValidationChain } from 'express-validator' |
3 | import { UserRight, VideoPlaylistCreate, VideoPlaylistUpdate } from '../../../../shared' | 3 | import { UserRight, VideoPlaylistCreate, VideoPlaylistUpdate } from '../../../../shared' |
4 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
5 | import { UserModel } from '../../../models/account/user' | ||
6 | import { areValidationErrors } from '../utils' | 5 | import { areValidationErrors } from '../utils' |
7 | import { isVideoImage } from '../../../helpers/custom-validators/videos' | 6 | import { isVideoImage } from '../../../helpers/custom-validators/videos' |
8 | import { CONSTRAINTS_FIELDS } from '../../../initializers/constants' | 7 | import { CONSTRAINTS_FIELDS } from '../../../initializers/constants' |
@@ -22,13 +21,14 @@ import { | |||
22 | isVideoPlaylistTimestampValid, | 21 | isVideoPlaylistTimestampValid, |
23 | isVideoPlaylistTypeValid | 22 | isVideoPlaylistTypeValid |
24 | } from '../../../helpers/custom-validators/video-playlists' | 23 | } from '../../../helpers/custom-validators/video-playlists' |
25 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' | ||
26 | import { cleanUpReqFiles } from '../../../helpers/express-utils' | 24 | import { cleanUpReqFiles } from '../../../helpers/express-utils' |
27 | import { VideoPlaylistElementModel } from '../../../models/video/video-playlist-element' | 25 | import { VideoPlaylistElementModel } from '../../../models/video/video-playlist-element' |
28 | import { authenticatePromiseIfNeeded } from '../../oauth' | 26 | import { authenticatePromiseIfNeeded } from '../../oauth' |
29 | import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' | 27 | import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' |
30 | import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model' | 28 | import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model' |
31 | import { doesVideoChannelIdExist, doesVideoExist, doesVideoPlaylistExist } from '../../../helpers/middlewares' | 29 | import { doesVideoChannelIdExist, doesVideoExist, doesVideoPlaylistExist, VideoPlaylistFetchType } from '../../../helpers/middlewares' |
30 | import { MVideoPlaylist } from '../../../typings/models/video/video-playlist' | ||
31 | import { MUserAccountId } from '@server/typings/models' | ||
32 | 32 | ||
33 | const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([ | 33 | const videoPlaylistsAddValidator = getCommonPlaylistEditAttributes().concat([ |
34 | body('displayName') | 34 | body('displayName') |
@@ -67,9 +67,9 @@ const videoPlaylistsUpdateValidator = getCommonPlaylistEditAttributes().concat([ | |||
67 | 67 | ||
68 | if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return cleanUpReqFiles(req) | 68 | if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return cleanUpReqFiles(req) |
69 | 69 | ||
70 | const videoPlaylist = res.locals.videoPlaylist | 70 | const videoPlaylist = getPlaylist(res) |
71 | 71 | ||
72 | if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) { | 72 | if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) { |
73 | return cleanUpReqFiles(req) | 73 | return cleanUpReqFiles(req) |
74 | } | 74 | } |
75 | 75 | ||
@@ -110,13 +110,13 @@ const videoPlaylistsDeleteValidator = [ | |||
110 | 110 | ||
111 | if (!await doesVideoPlaylistExist(req.params.playlistId, res)) return | 111 | if (!await doesVideoPlaylistExist(req.params.playlistId, res)) return |
112 | 112 | ||
113 | const videoPlaylist = res.locals.videoPlaylist | 113 | const videoPlaylist = getPlaylist(res) |
114 | if (videoPlaylist.type === VideoPlaylistType.WATCH_LATER) { | 114 | if (videoPlaylist.type === VideoPlaylistType.WATCH_LATER) { |
115 | return res.status(400) | 115 | return res.status(400) |
116 | .json({ error: 'Cannot delete a watch later playlist.' }) | 116 | .json({ error: 'Cannot delete a watch later playlist.' }) |
117 | } | 117 | } |
118 | 118 | ||
119 | if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) { | 119 | if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.REMOVE_ANY_VIDEO_PLAYLIST, res)) { |
120 | return | 120 | return |
121 | } | 121 | } |
122 | 122 | ||
@@ -124,45 +124,47 @@ const videoPlaylistsDeleteValidator = [ | |||
124 | } | 124 | } |
125 | ] | 125 | ] |
126 | 126 | ||
127 | const videoPlaylistsGetValidator = [ | 127 | const videoPlaylistsGetValidator = (fetchType: VideoPlaylistFetchType) => { |
128 | param('playlistId') | 128 | return [ |
129 | .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), | 129 | param('playlistId') |
130 | .custom(isIdOrUUIDValid).withMessage('Should have a valid playlist id/uuid'), | ||
130 | 131 | ||
131 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 132 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
132 | logger.debug('Checking videoPlaylistsGetValidator parameters', { parameters: req.params }) | 133 | logger.debug('Checking videoPlaylistsGetValidator parameters', { parameters: req.params }) |
133 | 134 | ||
134 | if (areValidationErrors(req, res)) return | 135 | if (areValidationErrors(req, res)) return |
135 | 136 | ||
136 | if (!await doesVideoPlaylistExist(req.params.playlistId, res)) return | 137 | if (!await doesVideoPlaylistExist(req.params.playlistId, res, fetchType)) return |
137 | 138 | ||
138 | const videoPlaylist = res.locals.videoPlaylist | 139 | const videoPlaylist = res.locals.videoPlaylistFull || res.locals.videoPlaylistSummary |
139 | 140 | ||
140 | // Video is unlisted, check we used the uuid to fetch it | 141 | // Video is unlisted, check we used the uuid to fetch it |
141 | if (videoPlaylist.privacy === VideoPlaylistPrivacy.UNLISTED) { | 142 | if (videoPlaylist.privacy === VideoPlaylistPrivacy.UNLISTED) { |
142 | if (isUUIDValid(req.params.playlistId)) return next() | 143 | if (isUUIDValid(req.params.playlistId)) return next() |
143 | 144 | ||
144 | return res.status(404).end() | 145 | return res.status(404).end() |
145 | } | 146 | } |
147 | |||
148 | if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) { | ||
149 | await authenticatePromiseIfNeeded(req, res) | ||
146 | 150 | ||
147 | if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) { | 151 | const user = res.locals.oauth ? res.locals.oauth.token.User : null |
148 | await authenticatePromiseIfNeeded(req, res) | ||
149 | 152 | ||
150 | const user = res.locals.oauth ? res.locals.oauth.token.User : null | 153 | if ( |
154 | !user || | ||
155 | (videoPlaylist.OwnerAccount.id !== user.Account.id && !user.hasRight(UserRight.UPDATE_ANY_VIDEO_PLAYLIST)) | ||
156 | ) { | ||
157 | return res.status(403) | ||
158 | .json({ error: 'Cannot get this private video playlist.' }) | ||
159 | } | ||
151 | 160 | ||
152 | if ( | 161 | return next() |
153 | !user || | ||
154 | (videoPlaylist.OwnerAccount.id !== user.Account.id && !user.hasRight(UserRight.UPDATE_ANY_VIDEO_PLAYLIST)) | ||
155 | ) { | ||
156 | return res.status(403) | ||
157 | .json({ error: 'Cannot get this private video playlist.' }) | ||
158 | } | 162 | } |
159 | 163 | ||
160 | return next() | 164 | return next() |
161 | } | 165 | } |
162 | 166 | ] | |
163 | return next() | 167 | } |
164 | } | ||
165 | ] | ||
166 | 168 | ||
167 | const videoPlaylistsAddVideoValidator = [ | 169 | const videoPlaylistsAddVideoValidator = [ |
168 | param('playlistId') | 170 | param('playlistId') |
@@ -184,8 +186,8 @@ const videoPlaylistsAddVideoValidator = [ | |||
184 | if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return | 186 | if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return |
185 | if (!await doesVideoExist(req.body.videoId, res, 'only-video')) return | 187 | if (!await doesVideoExist(req.body.videoId, res, 'only-video')) return |
186 | 188 | ||
187 | const videoPlaylist = res.locals.videoPlaylist | 189 | const videoPlaylist = getPlaylist(res) |
188 | const video = res.locals.video | 190 | const video = res.locals.onlyVideo |
189 | 191 | ||
190 | const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndVideo(videoPlaylist.id, video.id) | 192 | const videoPlaylistElement = await VideoPlaylistElementModel.loadByPlaylistAndVideo(videoPlaylist.id, video.id) |
191 | if (videoPlaylistElement) { | 193 | if (videoPlaylistElement) { |
@@ -196,7 +198,7 @@ const videoPlaylistsAddVideoValidator = [ | |||
196 | return | 198 | return |
197 | } | 199 | } |
198 | 200 | ||
199 | if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, res.locals.videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) { | 201 | if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) { |
200 | return | 202 | return |
201 | } | 203 | } |
202 | 204 | ||
@@ -223,7 +225,7 @@ const videoPlaylistsUpdateOrRemoveVideoValidator = [ | |||
223 | 225 | ||
224 | if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return | 226 | if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return |
225 | 227 | ||
226 | const videoPlaylist = res.locals.videoPlaylist | 228 | const videoPlaylist = getPlaylist(res) |
227 | 229 | ||
228 | const videoPlaylistElement = await VideoPlaylistElementModel.loadById(req.params.playlistElementId) | 230 | const videoPlaylistElement = await VideoPlaylistElementModel.loadById(req.params.playlistElementId) |
229 | if (!videoPlaylistElement) { | 231 | if (!videoPlaylistElement) { |
@@ -265,7 +267,7 @@ const videoPlaylistElementAPGetValidator = [ | |||
265 | return res.status(403).end() | 267 | return res.status(403).end() |
266 | } | 268 | } |
267 | 269 | ||
268 | res.locals.videoPlaylistElement = videoPlaylistElement | 270 | res.locals.videoPlaylistElementAP = videoPlaylistElement |
269 | 271 | ||
270 | return next() | 272 | return next() |
271 | } | 273 | } |
@@ -289,7 +291,7 @@ const videoPlaylistsReorderVideosValidator = [ | |||
289 | 291 | ||
290 | if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return | 292 | if (!await doesVideoPlaylistExist(req.params.playlistId, res, 'all')) return |
291 | 293 | ||
292 | const videoPlaylist = res.locals.videoPlaylist | 294 | const videoPlaylist = getPlaylist(res) |
293 | if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) return | 295 | if (!checkUserCanManageVideoPlaylist(res.locals.oauth.token.User, videoPlaylist, UserRight.UPDATE_ANY_VIDEO_PLAYLIST, res)) return |
294 | 296 | ||
295 | const nextPosition = await VideoPlaylistElementModel.getNextPositionOf(videoPlaylist.id) | 297 | const nextPosition = await VideoPlaylistElementModel.getNextPositionOf(videoPlaylist.id) |
@@ -388,7 +390,7 @@ function getCommonPlaylistEditAttributes () { | |||
388 | ] as (ValidationChain | express.Handler)[] | 390 | ] as (ValidationChain | express.Handler)[] |
389 | } | 391 | } |
390 | 392 | ||
391 | function checkUserCanManageVideoPlaylist (user: UserModel, videoPlaylist: VideoPlaylistModel, right: UserRight, res: express.Response) { | 393 | function checkUserCanManageVideoPlaylist (user: MUserAccountId, videoPlaylist: MVideoPlaylist, right: UserRight, res: express.Response) { |
392 | if (videoPlaylist.isOwned() === false) { | 394 | if (videoPlaylist.isOwned() === false) { |
393 | res.status(403) | 395 | res.status(403) |
394 | .json({ error: 'Cannot manage video playlist of another server.' }) | 396 | .json({ error: 'Cannot manage video playlist of another server.' }) |
@@ -410,3 +412,7 @@ function checkUserCanManageVideoPlaylist (user: UserModel, videoPlaylist: VideoP | |||
410 | 412 | ||
411 | return true | 413 | return true |
412 | } | 414 | } |
415 | |||
416 | function getPlaylist (res: express.Response) { | ||
417 | return res.locals.videoPlaylistFull || res.locals.videoPlaylistSummary | ||
418 | } | ||
diff --git a/server/middlewares/validators/videos/video-shares.ts b/server/middlewares/validators/videos/video-shares.ts index ace62be5c..20fc96243 100644 --- a/server/middlewares/validators/videos/video-shares.ts +++ b/server/middlewares/validators/videos/video-shares.ts | |||
@@ -16,7 +16,7 @@ const videosShareValidator = [ | |||
16 | if (areValidationErrors(req, res)) return | 16 | if (areValidationErrors(req, res)) return |
17 | if (!await doesVideoExist(req.params.id, res)) return | 17 | if (!await doesVideoExist(req.params.id, res)) return |
18 | 18 | ||
19 | const video = res.locals.video | 19 | const video = res.locals.videoAll |
20 | 20 | ||
21 | const share = await VideoShareModel.load(req.params.actorId, video.id) | 21 | const share = await VideoShareModel.load(req.params.actorId, video.id) |
22 | if (!share) { | 22 | if (!share) { |
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts index af06f3c62..1449903b7 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' | |||
37 | import { checkUserCanTerminateOwnershipChange, doesChangeVideoOwnershipExist } from '../../../helpers/custom-validators/video-ownership' | 37 | import { checkUserCanTerminateOwnershipChange, doesChangeVideoOwnershipExist } from '../../../helpers/custom-validators/video-ownership' |
38 | import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/video-change-ownership-accept.model' | 38 | import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/video-change-ownership-accept.model' |
39 | import { AccountModel } from '../../../models/account/account' | 39 | import { AccountModel } from '../../../models/account/account' |
40 | import { VideoFetchType } from '../../../helpers/video' | ||
41 | import { isNSFWQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search' | 40 | import { isNSFWQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search' |
42 | import { getServerActor } from '../../../helpers/utils' | 41 | import { getServerActor } from '../../../helpers/utils' |
43 | import { CONFIG } from '../../../initializers/config' | 42 | import { CONFIG } from '../../../initializers/config' |
44 | import { isLocalVideoAccepted } from '../../../lib/moderation' | 43 | import { isLocalVideoAccepted } from '../../../lib/moderation' |
45 | import { Hooks } from '../../../lib/plugins/hooks' | 44 | import { Hooks } from '../../../lib/plugins/hooks' |
46 | import { checkUserCanManageVideo, doesVideoChannelOfAccountExist, doesVideoExist } from '../../../helpers/middlewares' | 45 | import { checkUserCanManageVideo, doesVideoChannelOfAccountExist, doesVideoExist } from '../../../helpers/middlewares' |
46 | import { MVideoFullLight } from '@server/typings/models' | ||
47 | import { getVideoWithAttributes } from '../../../helpers/video' | ||
47 | 48 | ||
48 | const videosAddValidator = getCommonVideoEditAttributes().concat([ | 49 | const videosAddValidator = getCommonVideoEditAttributes().concat([ |
49 | body('videofile') | 50 | body('videofile') |
@@ -113,7 +114,7 @@ const videosUpdateValidator = getCommonVideoEditAttributes().concat([ | |||
113 | 114 | ||
114 | // Check if the user who did the request is able to update the video | 115 | // Check if the user who did the request is able to update the video |
115 | const user = res.locals.oauth.token.User | 116 | const user = res.locals.oauth.token.User |
116 | if (!checkUserCanManageVideo(user, res.locals.video, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req) | 117 | if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.UPDATE_ANY_VIDEO, res)) return cleanUpReqFiles(req) |
117 | 118 | ||
118 | if (req.body.channelId && !await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) | 119 | if (req.body.channelId && !await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) |
119 | 120 | ||
@@ -122,7 +123,7 @@ const videosUpdateValidator = getCommonVideoEditAttributes().concat([ | |||
122 | ]) | 123 | ]) |
123 | 124 | ||
124 | async function checkVideoFollowConstraints (req: express.Request, res: express.Response, next: express.NextFunction) { | 125 | async function checkVideoFollowConstraints (req: express.Request, res: express.Response, next: express.NextFunction) { |
125 | const video = res.locals.video | 126 | const video = getVideoWithAttributes(res) |
126 | 127 | ||
127 | // Anybody can watch local videos | 128 | // Anybody can watch local videos |
128 | if (video.isOwned() === true) return next() | 129 | if (video.isOwned() === true) return next() |
@@ -146,7 +147,7 @@ async function checkVideoFollowConstraints (req: express.Request, res: express.R | |||
146 | }) | 147 | }) |
147 | } | 148 | } |
148 | 149 | ||
149 | const videosCustomGetValidator = (fetchType: VideoFetchType) => { | 150 | const videosCustomGetValidator = (fetchType: 'all' | 'only-video' | 'only-video-with-rights') => { |
150 | return [ | 151 | return [ |
151 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | 152 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), |
152 | 153 | ||
@@ -156,10 +157,11 @@ const videosCustomGetValidator = (fetchType: VideoFetchType) => { | |||
156 | if (areValidationErrors(req, res)) return | 157 | if (areValidationErrors(req, res)) return |
157 | if (!await doesVideoExist(req.params.id, res, fetchType)) return | 158 | if (!await doesVideoExist(req.params.id, res, fetchType)) return |
158 | 159 | ||
159 | const video = res.locals.video | 160 | const video = getVideoWithAttributes(res) |
161 | const videoAll = video as MVideoFullLight | ||
160 | 162 | ||
161 | // Video private or blacklisted | 163 | // Video private or blacklisted |
162 | if (video.privacy === VideoPrivacy.PRIVATE || video.VideoBlacklist) { | 164 | if (video.privacy === VideoPrivacy.PRIVATE || videoAll.VideoBlacklist) { |
163 | await authenticatePromiseIfNeeded(req, res) | 165 | await authenticatePromiseIfNeeded(req, res) |
164 | 166 | ||
165 | const user = res.locals.oauth ? res.locals.oauth.token.User : null | 167 | const user = res.locals.oauth ? res.locals.oauth.token.User : null |
@@ -167,7 +169,7 @@ const videosCustomGetValidator = (fetchType: VideoFetchType) => { | |||
167 | // Only the owner or a user that have blacklist rights can see the video | 169 | // Only the owner or a user that have blacklist rights can see the video |
168 | if ( | 170 | if ( |
169 | !user || | 171 | !user || |
170 | (video.VideoChannel.Account.userId !== user.id && !user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)) | 172 | (videoAll.VideoChannel && videoAll.VideoChannel.Account.userId !== user.id && !user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST)) |
171 | ) { | 173 | ) { |
172 | return res.status(403) | 174 | return res.status(403) |
173 | .json({ error: 'Cannot get this private or blacklisted video.' }) | 175 | .json({ error: 'Cannot get this private or blacklisted video.' }) |
@@ -202,7 +204,7 @@ const videosRemoveValidator = [ | |||
202 | if (!await doesVideoExist(req.params.id, res)) return | 204 | if (!await doesVideoExist(req.params.id, res)) return |
203 | 205 | ||
204 | // Check if the user who did the request is able to delete the video | 206 | // Check if the user who did the request is able to delete the video |
205 | if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.video, UserRight.REMOVE_ANY_VIDEO, res)) return | 207 | if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.videoAll, UserRight.REMOVE_ANY_VIDEO, res)) return |
206 | 208 | ||
207 | return next() | 209 | return next() |
208 | } | 210 | } |
@@ -218,7 +220,7 @@ const videosChangeOwnershipValidator = [ | |||
218 | if (!await doesVideoExist(req.params.videoId, res)) return | 220 | if (!await doesVideoExist(req.params.videoId, res)) return |
219 | 221 | ||
220 | // Check if the user who did the request is able to change the ownership of the video | 222 | // Check if the user who did the request is able to change the ownership of the video |
221 | if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.video, UserRight.CHANGE_VIDEO_OWNERSHIP, res)) return | 223 | if (!checkUserCanManageVideo(res.locals.oauth.token.User, res.locals.videoAll, UserRight.CHANGE_VIDEO_OWNERSHIP, res)) return |
222 | 224 | ||
223 | const nextOwner = await AccountModel.loadLocalByName(req.body.username) | 225 | const nextOwner = await AccountModel.loadLocalByName(req.body.username) |
224 | if (!nextOwner) { | 226 | if (!nextOwner) { |
diff --git a/server/middlewares/validators/webfinger.ts b/server/middlewares/validators/webfinger.ts index d7cfe17f0..d50e6527f 100644 --- a/server/middlewares/validators/webfinger.ts +++ b/server/middlewares/validators/webfinger.ts | |||
@@ -18,6 +18,7 @@ const webfingerValidator = [ | |||
18 | const nameWithHost = getHostWithPort(req.query.resource.substr(5)) | 18 | const nameWithHost = getHostWithPort(req.query.resource.substr(5)) |
19 | const [ name ] = nameWithHost.split('@') | 19 | const [ name ] = nameWithHost.split('@') |
20 | 20 | ||
21 | // FIXME: we don't need the full actor | ||
21 | const actor = await ActorModel.loadLocalByName(name) | 22 | const actor = await ActorModel.loadLocalByName(name) |
22 | if (!actor) { | 23 | if (!actor) { |
23 | return res.status(404) | 24 | return res.status(404) |
@@ -25,7 +26,7 @@ const webfingerValidator = [ | |||
25 | .end() | 26 | .end() |
26 | } | 27 | } |
27 | 28 | ||
28 | res.locals.actor = actor | 29 | res.locals.actorFull = actor |
29 | return next() | 30 | return next() |
30 | } | 31 | } |
31 | ] | 32 | ] |
diff --git a/server/models/account/account-blocklist.ts b/server/models/account/account-blocklist.ts index d5746ad76..8bcaca828 100644 --- a/server/models/account/account-blocklist.ts +++ b/server/models/account/account-blocklist.ts | |||
@@ -3,6 +3,8 @@ import { AccountModel } from './account' | |||
3 | import { getSort } from '../utils' | 3 | import { getSort } from '../utils' |
4 | import { AccountBlock } from '../../../shared/models/blocklist' | 4 | import { AccountBlock } from '../../../shared/models/blocklist' |
5 | import { Op } from 'sequelize' | 5 | import { Op } from 'sequelize' |
6 | import * as Bluebird from 'bluebird' | ||
7 | import { MAccountBlocklist, MAccountBlocklistAccounts, MAccountBlocklistFormattable } from '@server/typings/models' | ||
6 | 8 | ||
7 | enum ScopeNames { | 9 | enum ScopeNames { |
8 | WITH_ACCOUNTS = 'WITH_ACCOUNTS' | 10 | WITH_ACCOUNTS = 'WITH_ACCOUNTS' |
@@ -103,7 +105,7 @@ export class AccountBlocklistModel extends Model<AccountBlocklistModel> { | |||
103 | }) | 105 | }) |
104 | } | 106 | } |
105 | 107 | ||
106 | static loadByAccountAndTarget (accountId: number, targetAccountId: number) { | 108 | static loadByAccountAndTarget (accountId: number, targetAccountId: number): Bluebird<MAccountBlocklist> { |
107 | const query = { | 109 | const query = { |
108 | where: { | 110 | where: { |
109 | accountId, | 111 | accountId, |
@@ -126,13 +128,13 @@ export class AccountBlocklistModel extends Model<AccountBlocklistModel> { | |||
126 | 128 | ||
127 | return AccountBlocklistModel | 129 | return AccountBlocklistModel |
128 | .scope([ ScopeNames.WITH_ACCOUNTS ]) | 130 | .scope([ ScopeNames.WITH_ACCOUNTS ]) |
129 | .findAndCountAll(query) | 131 | .findAndCountAll<MAccountBlocklistAccounts>(query) |
130 | .then(({ rows, count }) => { | 132 | .then(({ rows, count }) => { |
131 | return { total: count, data: rows } | 133 | return { total: count, data: rows } |
132 | }) | 134 | }) |
133 | } | 135 | } |
134 | 136 | ||
135 | toFormattedJSON (): AccountBlock { | 137 | toFormattedJSON (this: MAccountBlocklistFormattable): AccountBlock { |
136 | return { | 138 | return { |
137 | byAccount: this.ByAccount.toFormattedJSON(), | 139 | byAccount: this.ByAccount.toFormattedJSON(), |
138 | blockedAccount: this.BlockedAccount.toFormattedJSON(), | 140 | blockedAccount: this.BlockedAccount.toFormattedJSON(), |
diff --git a/server/models/account/account-video-rate.ts b/server/models/account/account-video-rate.ts index 4bd8114cf..a6edbeee8 100644 --- a/server/models/account/account-video-rate.ts +++ b/server/models/account/account-video-rate.ts | |||
@@ -10,6 +10,13 @@ import { buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils' | |||
10 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 10 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
11 | import { AccountVideoRate } from '../../../shared' | 11 | import { AccountVideoRate } from '../../../shared' |
12 | import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel' | 12 | import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel' |
13 | import * as Bluebird from 'bluebird' | ||
14 | import { | ||
15 | MAccountVideoRate, | ||
16 | MAccountVideoRateAccountUrl, | ||
17 | MAccountVideoRateAccountVideo, | ||
18 | MAccountVideoRateFormattable | ||
19 | } from '@server/typings/models/video/video-rate' | ||
13 | 20 | ||
14 | /* | 21 | /* |
15 | Account rates per video. | 22 | Account rates per video. |
@@ -77,7 +84,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> { | |||
77 | }) | 84 | }) |
78 | Account: AccountModel | 85 | Account: AccountModel |
79 | 86 | ||
80 | static load (accountId: number, videoId: number, transaction?: Transaction) { | 87 | static load (accountId: number, videoId: number, transaction?: Transaction): Bluebird<MAccountVideoRate> { |
81 | const options: FindOptions = { | 88 | const options: FindOptions = { |
82 | where: { | 89 | where: { |
83 | accountId, | 90 | accountId, |
@@ -89,7 +96,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> { | |||
89 | return AccountVideoRateModel.findOne(options) | 96 | return AccountVideoRateModel.findOne(options) |
90 | } | 97 | } |
91 | 98 | ||
92 | static loadByAccountAndVideoOrUrl (accountId: number, videoId: number, url: string, transaction?: Transaction) { | 99 | static loadByAccountAndVideoOrUrl (accountId: number, videoId: number, url: string, t?: Transaction): Bluebird<MAccountVideoRate> { |
93 | const options: FindOptions = { | 100 | const options: FindOptions = { |
94 | where: { | 101 | where: { |
95 | [ Op.or]: [ | 102 | [ Op.or]: [ |
@@ -103,7 +110,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> { | |||
103 | ] | 110 | ] |
104 | } | 111 | } |
105 | } | 112 | } |
106 | if (transaction) options.transaction = transaction | 113 | if (t) options.transaction = t |
107 | 114 | ||
108 | return AccountVideoRateModel.findOne(options) | 115 | return AccountVideoRateModel.findOne(options) |
109 | } | 116 | } |
@@ -140,7 +147,12 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> { | |||
140 | return AccountVideoRateModel.findAndCountAll(query) | 147 | return AccountVideoRateModel.findAndCountAll(query) |
141 | } | 148 | } |
142 | 149 | ||
143 | static loadLocalAndPopulateVideo (rateType: VideoRateType, accountName: string, videoId: number, transaction?: Transaction) { | 150 | static loadLocalAndPopulateVideo ( |
151 | rateType: VideoRateType, | ||
152 | accountName: string, | ||
153 | videoId: number, | ||
154 | t?: Transaction | ||
155 | ): Bluebird<MAccountVideoRateAccountVideo> { | ||
144 | const options: FindOptions = { | 156 | const options: FindOptions = { |
145 | where: { | 157 | where: { |
146 | videoId, | 158 | videoId, |
@@ -152,7 +164,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> { | |||
152 | required: true, | 164 | required: true, |
153 | include: [ | 165 | include: [ |
154 | { | 166 | { |
155 | attributes: [ 'id', 'url', 'preferredUsername' ], | 167 | attributes: [ 'id', 'url', 'followersUrl', 'preferredUsername' ], |
156 | model: ActorModel.unscoped(), | 168 | model: ActorModel.unscoped(), |
157 | required: true, | 169 | required: true, |
158 | where: { | 170 | where: { |
@@ -167,7 +179,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> { | |||
167 | } | 179 | } |
168 | ] | 180 | ] |
169 | } | 181 | } |
170 | if (transaction) options.transaction = transaction | 182 | if (t) options.transaction = t |
171 | 183 | ||
172 | return AccountVideoRateModel.findOne(options) | 184 | return AccountVideoRateModel.findOne(options) |
173 | } | 185 | } |
@@ -208,7 +220,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> { | |||
208 | ] | 220 | ] |
209 | } | 221 | } |
210 | 222 | ||
211 | return AccountVideoRateModel.findAndCountAll(query) | 223 | return AccountVideoRateModel.findAndCountAll<MAccountVideoRateAccountUrl>(query) |
212 | } | 224 | } |
213 | 225 | ||
214 | static cleanOldRatesOf (videoId: number, type: VideoRateType, beforeUpdatedAt: Date) { | 226 | static cleanOldRatesOf (videoId: number, type: VideoRateType, beforeUpdatedAt: Date) { |
@@ -241,7 +253,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> { | |||
241 | }) | 253 | }) |
242 | } | 254 | } |
243 | 255 | ||
244 | toFormattedJSON (): AccountVideoRate { | 256 | toFormattedJSON (this: MAccountVideoRateFormattable): AccountVideoRate { |
245 | return { | 257 | return { |
246 | video: this.Video.toFormattedJSON(), | 258 | video: this.Video.toFormattedJSON(), |
247 | rating: this.type | 259 | rating: this.type |
diff --git a/server/models/account/account.ts b/server/models/account/account.ts index 4dc412301..ba1094536 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts | |||
@@ -3,7 +3,8 @@ import { | |||
3 | BeforeDestroy, | 3 | BeforeDestroy, |
4 | BelongsTo, | 4 | BelongsTo, |
5 | Column, | 5 | Column, |
6 | CreatedAt, DataType, | 6 | CreatedAt, |
7 | DataType, | ||
7 | Default, | 8 | Default, |
8 | DefaultScope, | 9 | DefaultScope, |
9 | ForeignKey, | 10 | ForeignKey, |
@@ -31,6 +32,8 @@ import { FindOptions, IncludeOptions, Op, Transaction, WhereOptions } from 'sequ | |||
31 | import { AccountBlocklistModel } from './account-blocklist' | 32 | import { AccountBlocklistModel } from './account-blocklist' |
32 | import { ServerBlocklistModel } from '../server/server-blocklist' | 33 | import { ServerBlocklistModel } from '../server/server-blocklist' |
33 | import { ActorFollowModel } from '../activitypub/actor-follow' | 34 | import { ActorFollowModel } from '../activitypub/actor-follow' |
35 | import { MAccountActor, MAccountDefault, MAccountSummaryFormattable, MAccountFormattable, MAccountAP } from '../../typings/models' | ||
36 | import * as Bluebird from 'bluebird' | ||
34 | 37 | ||
35 | export enum ScopeNames { | 38 | export enum ScopeNames { |
36 | SUMMARY = 'SUMMARY' | 39 | SUMMARY = 'SUMMARY' |
@@ -229,11 +232,11 @@ export class AccountModel extends Model<AccountModel> { | |||
229 | return undefined | 232 | return undefined |
230 | } | 233 | } |
231 | 234 | ||
232 | static load (id: number, transaction?: Transaction) { | 235 | static load (id: number, transaction?: Transaction): Bluebird<MAccountDefault> { |
233 | return AccountModel.findByPk(id, { transaction }) | 236 | return AccountModel.findByPk(id, { transaction }) |
234 | } | 237 | } |
235 | 238 | ||
236 | static loadByNameWithHost (nameWithHost: string) { | 239 | static loadByNameWithHost (nameWithHost: string): Bluebird<MAccountDefault> { |
237 | const [ accountName, host ] = nameWithHost.split('@') | 240 | const [ accountName, host ] = nameWithHost.split('@') |
238 | 241 | ||
239 | if (!host || host === WEBSERVER.HOST) return AccountModel.loadLocalByName(accountName) | 242 | if (!host || host === WEBSERVER.HOST) return AccountModel.loadLocalByName(accountName) |
@@ -241,7 +244,7 @@ export class AccountModel extends Model<AccountModel> { | |||
241 | return AccountModel.loadByNameAndHost(accountName, host) | 244 | return AccountModel.loadByNameAndHost(accountName, host) |
242 | } | 245 | } |
243 | 246 | ||
244 | static loadLocalByName (name: string) { | 247 | static loadLocalByName (name: string): Bluebird<MAccountDefault> { |
245 | const query = { | 248 | const query = { |
246 | where: { | 249 | where: { |
247 | [ Op.or ]: [ | 250 | [ Op.or ]: [ |
@@ -271,7 +274,7 @@ export class AccountModel extends Model<AccountModel> { | |||
271 | return AccountModel.findOne(query) | 274 | return AccountModel.findOne(query) |
272 | } | 275 | } |
273 | 276 | ||
274 | static loadByNameAndHost (name: string, host: string) { | 277 | static loadByNameAndHost (name: string, host: string): Bluebird<MAccountDefault> { |
275 | const query = { | 278 | const query = { |
276 | include: [ | 279 | include: [ |
277 | { | 280 | { |
@@ -296,7 +299,7 @@ export class AccountModel extends Model<AccountModel> { | |||
296 | return AccountModel.findOne(query) | 299 | return AccountModel.findOne(query) |
297 | } | 300 | } |
298 | 301 | ||
299 | static loadByUrl (url: string, transaction?: Transaction) { | 302 | static loadByUrl (url: string, transaction?: Transaction): Bluebird<MAccountDefault> { |
300 | const query = { | 303 | const query = { |
301 | include: [ | 304 | include: [ |
302 | { | 305 | { |
@@ -329,7 +332,7 @@ export class AccountModel extends Model<AccountModel> { | |||
329 | }) | 332 | }) |
330 | } | 333 | } |
331 | 334 | ||
332 | static listLocalsForSitemap (sort: string) { | 335 | static listLocalsForSitemap (sort: string): Bluebird<MAccountActor[]> { |
333 | const query = { | 336 | const query = { |
334 | attributes: [ ], | 337 | attributes: [ ], |
335 | offset: 0, | 338 | offset: 0, |
@@ -350,7 +353,7 @@ export class AccountModel extends Model<AccountModel> { | |||
350 | .findAll(query) | 353 | .findAll(query) |
351 | } | 354 | } |
352 | 355 | ||
353 | toFormattedJSON (): Account { | 356 | toFormattedJSON (this: MAccountFormattable): Account { |
354 | const actor = this.Actor.toFormattedJSON() | 357 | const actor = this.Actor.toFormattedJSON() |
355 | const account = { | 358 | const account = { |
356 | id: this.id, | 359 | id: this.id, |
@@ -364,8 +367,8 @@ export class AccountModel extends Model<AccountModel> { | |||
364 | return Object.assign(actor, account) | 367 | return Object.assign(actor, account) |
365 | } | 368 | } |
366 | 369 | ||
367 | toFormattedSummaryJSON (): AccountSummary { | 370 | toFormattedSummaryJSON (this: MAccountSummaryFormattable): AccountSummary { |
368 | const actor = this.Actor.toFormattedJSON() | 371 | const actor = this.Actor.toFormattedSummaryJSON() |
369 | 372 | ||
370 | return { | 373 | return { |
371 | id: this.id, | 374 | id: this.id, |
@@ -377,8 +380,8 @@ export class AccountModel extends Model<AccountModel> { | |||
377 | } | 380 | } |
378 | } | 381 | } |
379 | 382 | ||
380 | toActivityPubObject () { | 383 | toActivityPubObject (this: MAccountAP) { |
381 | const obj = this.Actor.toActivityPubObject(this.name, 'Account') | 384 | const obj = this.Actor.toActivityPubObject(this.name) |
382 | 385 | ||
383 | return Object.assign(obj, { | 386 | return Object.assign(obj, { |
384 | summary: this.description | 387 | summary: this.description |
diff --git a/server/models/account/user-notification-setting.ts b/server/models/account/user-notification-setting.ts index c2fbc6d23..dc69a17fd 100644 --- a/server/models/account/user-notification-setting.ts +++ b/server/models/account/user-notification-setting.ts | |||
@@ -17,6 +17,7 @@ import { UserModel } from './user' | |||
17 | import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications' | 17 | import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications' |
18 | import { UserNotificationSetting, UserNotificationSettingValue } from '../../../shared/models/users/user-notification-setting.model' | 18 | import { UserNotificationSetting, UserNotificationSettingValue } from '../../../shared/models/users/user-notification-setting.model' |
19 | import { clearCacheByUserId } from '../../lib/oauth-model' | 19 | import { clearCacheByUserId } from '../../lib/oauth-model' |
20 | import { MNotificationSettingFormattable } from '@server/typings/models' | ||
20 | 21 | ||
21 | @Table({ | 22 | @Table({ |
22 | tableName: 'userNotificationSetting', | 23 | tableName: 'userNotificationSetting', |
@@ -113,6 +114,15 @@ export class UserNotificationSettingModel extends Model<UserNotificationSettingM | |||
113 | @AllowNull(false) | 114 | @AllowNull(false) |
114 | @Default(null) | 115 | @Default(null) |
115 | @Is( | 116 | @Is( |
117 | 'UserNotificationSettingNewInstanceFollower', | ||
118 | value => throwIfNotValid(value, isUserNotificationSettingValid, 'autoInstanceFollowing') | ||
119 | ) | ||
120 | @Column | ||
121 | autoInstanceFollowing: UserNotificationSettingValue | ||
122 | |||
123 | @AllowNull(false) | ||
124 | @Default(null) | ||
125 | @Is( | ||
116 | 'UserNotificationSettingNewFollow', | 126 | 'UserNotificationSettingNewFollow', |
117 | value => throwIfNotValid(value, isUserNotificationSettingValid, 'newFollow') | 127 | value => throwIfNotValid(value, isUserNotificationSettingValid, 'newFollow') |
118 | ) | 128 | ) |
@@ -152,7 +162,7 @@ export class UserNotificationSettingModel extends Model<UserNotificationSettingM | |||
152 | return clearCacheByUserId(instance.userId) | 162 | return clearCacheByUserId(instance.userId) |
153 | } | 163 | } |
154 | 164 | ||
155 | toFormattedJSON (): UserNotificationSetting { | 165 | toFormattedJSON (this: MNotificationSettingFormattable): UserNotificationSetting { |
156 | return { | 166 | return { |
157 | newCommentOnMyVideo: this.newCommentOnMyVideo, | 167 | newCommentOnMyVideo: this.newCommentOnMyVideo, |
158 | newVideoFromSubscription: this.newVideoFromSubscription, | 168 | newVideoFromSubscription: this.newVideoFromSubscription, |
@@ -164,7 +174,8 @@ export class UserNotificationSettingModel extends Model<UserNotificationSettingM | |||
164 | newUserRegistration: this.newUserRegistration, | 174 | newUserRegistration: this.newUserRegistration, |
165 | commentMention: this.commentMention, | 175 | commentMention: this.commentMention, |
166 | newFollow: this.newFollow, | 176 | newFollow: this.newFollow, |
167 | newInstanceFollower: this.newInstanceFollower | 177 | newInstanceFollower: this.newInstanceFollower, |
178 | autoInstanceFollowing: this.autoInstanceFollowing | ||
168 | } | 179 | } |
169 | } | 180 | } |
170 | } | 181 | } |
diff --git a/server/models/account/user-notification.ts b/server/models/account/user-notification.ts index f38cd7e78..ccb81b891 100644 --- a/server/models/account/user-notification.ts +++ b/server/models/account/user-notification.ts | |||
@@ -16,6 +16,7 @@ import { ActorModel } from '../activitypub/actor' | |||
16 | import { ActorFollowModel } from '../activitypub/actor-follow' | 16 | import { ActorFollowModel } from '../activitypub/actor-follow' |
17 | import { AvatarModel } from '../avatar/avatar' | 17 | import { AvatarModel } from '../avatar/avatar' |
18 | import { ServerModel } from '../server/server' | 18 | import { ServerModel } from '../server/server' |
19 | import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/typings/models/user' | ||
19 | 20 | ||
20 | enum ScopeNames { | 21 | enum ScopeNames { |
21 | WITH_ALL = 'WITH_ALL' | 22 | WITH_ALL = 'WITH_ALL' |
@@ -134,13 +135,18 @@ function buildAccountInclude (required: boolean, withActor = false) { | |||
134 | ] | 135 | ] |
135 | }, | 136 | }, |
136 | { | 137 | { |
137 | attributes: [ 'preferredUsername' ], | 138 | attributes: [ 'preferredUsername', 'type' ], |
138 | model: ActorModel.unscoped(), | 139 | model: ActorModel.unscoped(), |
139 | required: true, | 140 | required: true, |
140 | as: 'ActorFollowing', | 141 | as: 'ActorFollowing', |
141 | include: [ | 142 | include: [ |
142 | buildChannelInclude(false), | 143 | buildChannelInclude(false), |
143 | buildAccountInclude(false) | 144 | buildAccountInclude(false), |
145 | { | ||
146 | attributes: [ 'host' ], | ||
147 | model: ServerModel.unscoped(), | ||
148 | required: false | ||
149 | } | ||
144 | ] | 150 | ] |
145 | } | 151 | } |
146 | ] | 152 | ] |
@@ -371,7 +377,7 @@ export class UserNotificationModel extends Model<UserNotificationModel> { | |||
371 | return UserNotificationModel.update({ read: true }, query) | 377 | return UserNotificationModel.update({ read: true }, query) |
372 | } | 378 | } |
373 | 379 | ||
374 | toFormattedJSON (): UserNotification { | 380 | toFormattedJSON (this: UserNotificationModelForApi): UserNotification { |
375 | const video = this.Video | 381 | const video = this.Video |
376 | ? Object.assign(this.formatVideo(this.Video),{ channel: this.formatActor(this.Video.VideoChannel) }) | 382 | ? Object.assign(this.formatVideo(this.Video),{ channel: this.formatActor(this.Video.VideoChannel) }) |
377 | : undefined | 383 | : undefined |
@@ -403,6 +409,11 @@ export class UserNotificationModel extends Model<UserNotificationModel> { | |||
403 | 409 | ||
404 | const account = this.Account ? this.formatActor(this.Account) : undefined | 410 | const account = this.Account ? this.formatActor(this.Account) : undefined |
405 | 411 | ||
412 | const actorFollowingType = { | ||
413 | Application: 'instance' as 'instance', | ||
414 | Group: 'channel' as 'channel', | ||
415 | Person: 'account' as 'account' | ||
416 | } | ||
406 | const actorFollow = this.ActorFollow ? { | 417 | const actorFollow = this.ActorFollow ? { |
407 | id: this.ActorFollow.id, | 418 | id: this.ActorFollow.id, |
408 | state: this.ActorFollow.state, | 419 | state: this.ActorFollow.state, |
@@ -414,9 +425,10 @@ export class UserNotificationModel extends Model<UserNotificationModel> { | |||
414 | host: this.ActorFollow.ActorFollower.getHost() | 425 | host: this.ActorFollow.ActorFollower.getHost() |
415 | }, | 426 | }, |
416 | following: { | 427 | following: { |
417 | type: this.ActorFollow.ActorFollowing.VideoChannel ? 'channel' as 'channel' : 'account' as 'account', | 428 | type: actorFollowingType[this.ActorFollow.ActorFollowing.type], |
418 | displayName: (this.ActorFollow.ActorFollowing.VideoChannel || this.ActorFollow.ActorFollowing.Account).getDisplayName(), | 429 | displayName: (this.ActorFollow.ActorFollowing.VideoChannel || this.ActorFollow.ActorFollowing.Account).getDisplayName(), |
419 | name: this.ActorFollow.ActorFollowing.preferredUsername | 430 | name: this.ActorFollow.ActorFollowing.preferredUsername, |
431 | host: this.ActorFollow.ActorFollowing.getHost() | ||
420 | } | 432 | } |
421 | } : undefined | 433 | } : undefined |
422 | 434 | ||
@@ -436,7 +448,7 @@ export class UserNotificationModel extends Model<UserNotificationModel> { | |||
436 | } | 448 | } |
437 | } | 449 | } |
438 | 450 | ||
439 | private formatVideo (video: VideoModel) { | 451 | formatVideo (this: UserNotificationModelForApi, video: UserNotificationIncludes.VideoInclude) { |
440 | return { | 452 | return { |
441 | id: video.id, | 453 | id: video.id, |
442 | uuid: video.uuid, | 454 | uuid: video.uuid, |
@@ -444,7 +456,10 @@ export class UserNotificationModel extends Model<UserNotificationModel> { | |||
444 | } | 456 | } |
445 | } | 457 | } |
446 | 458 | ||
447 | private formatActor (accountOrChannel: AccountModel | VideoChannelModel) { | 459 | formatActor ( |
460 | this: UserNotificationModelForApi, | ||
461 | accountOrChannel: UserNotificationIncludes.AccountIncludeActor | UserNotificationIncludes.VideoChannelIncludeActor | ||
462 | ) { | ||
448 | const avatar = accountOrChannel.Actor.Avatar | 463 | const avatar = accountOrChannel.Actor.Avatar |
449 | ? { path: accountOrChannel.Actor.Avatar.getStaticPath() } | 464 | ? { path: accountOrChannel.Actor.Avatar.getStaticPath() } |
450 | : undefined | 465 | : 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 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, IsInt, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, IsInt, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { VideoModel } from '../video/video' | 2 | import { VideoModel } from '../video/video' |
3 | import { UserModel } from './user' | 3 | import { UserModel } from './user' |
4 | import { Transaction, Op, DestroyOptions } from 'sequelize' | 4 | import { DestroyOptions, Op, Transaction } from 'sequelize' |
5 | import { MUserAccountId, MUserId } from '@server/typings/models' | ||
5 | 6 | ||
6 | @Table({ | 7 | @Table({ |
7 | tableName: 'userVideoHistory', | 8 | tableName: 'userVideoHistory', |
@@ -54,7 +55,7 @@ export class UserVideoHistoryModel extends Model<UserVideoHistoryModel> { | |||
54 | }) | 55 | }) |
55 | User: UserModel | 56 | User: UserModel |
56 | 57 | ||
57 | static listForApi (user: UserModel, start: number, count: number) { | 58 | static listForApi (user: MUserAccountId, start: number, count: number) { |
58 | return VideoModel.listForApi({ | 59 | return VideoModel.listForApi({ |
59 | start, | 60 | start, |
60 | count, | 61 | count, |
@@ -67,7 +68,7 @@ export class UserVideoHistoryModel extends Model<UserVideoHistoryModel> { | |||
67 | }) | 68 | }) |
68 | } | 69 | } |
69 | 70 | ||
70 | static removeUserHistoryBefore (user: UserModel, beforeDate: string, t: Transaction) { | 71 | static removeUserHistoryBefore (user: MUserId, beforeDate: string, t: Transaction) { |
71 | const query: DestroyOptions = { | 72 | const query: DestroyOptions = { |
72 | where: { | 73 | where: { |
73 | userId: user.id | 74 | userId: user.id |
diff --git a/server/models/account/user.ts b/server/models/account/user.ts index 0041bf577..451e1fd6b 100644 --- a/server/models/account/user.ts +++ b/server/models/account/user.ts | |||
@@ -22,6 +22,7 @@ import { | |||
22 | import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared' | 22 | import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared' |
23 | import { User, UserRole } from '../../../shared/models/users' | 23 | import { User, UserRole } from '../../../shared/models/users' |
24 | import { | 24 | import { |
25 | isNoInstanceConfigWarningModal, | ||
25 | isUserAdminFlagsValid, | 26 | isUserAdminFlagsValid, |
26 | isUserAutoPlayVideoValid, | 27 | isUserAutoPlayVideoValid, |
27 | isUserBlockedReasonValid, | 28 | isUserBlockedReasonValid, |
@@ -35,7 +36,8 @@ import { | |||
35 | isUserVideoQuotaDailyValid, | 36 | isUserVideoQuotaDailyValid, |
36 | isUserVideoQuotaValid, | 37 | isUserVideoQuotaValid, |
37 | isUserVideosHistoryEnabledValid, | 38 | isUserVideosHistoryEnabledValid, |
38 | isUserWebTorrentEnabledValid | 39 | isUserWebTorrentEnabledValid, |
40 | isNoWelcomeModal | ||
39 | } from '../../helpers/custom-validators/users' | 41 | } from '../../helpers/custom-validators/users' |
40 | import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto' | 42 | import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto' |
41 | import { OAuthTokenModel } from '../oauth/oauth-token' | 43 | import { OAuthTokenModel } from '../oauth/oauth-token' |
@@ -54,6 +56,14 @@ import { VideoImportModel } from '../video/video-import' | |||
54 | import { UserAdminFlag } from '../../../shared/models/users/user-flag.model' | 56 | import { UserAdminFlag } from '../../../shared/models/users/user-flag.model' |
55 | import { isThemeNameValid } from '../../helpers/custom-validators/plugins' | 57 | import { isThemeNameValid } from '../../helpers/custom-validators/plugins' |
56 | import { getThemeOrDefault } from '../../lib/plugins/theme-utils' | 58 | import { getThemeOrDefault } from '../../lib/plugins/theme-utils' |
59 | import * as Bluebird from 'bluebird' | ||
60 | import { | ||
61 | MUserDefault, | ||
62 | MUserFormattable, | ||
63 | MUserId, | ||
64 | MUserNotifSettingChannelDefault, | ||
65 | MUserWithNotificationSetting | ||
66 | } from '@server/typings/models' | ||
57 | 67 | ||
58 | enum ScopeNames { | 68 | enum ScopeNames { |
59 | WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL' | 69 | WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL' |
@@ -195,6 +205,24 @@ export class UserModel extends Model<UserModel> { | |||
195 | @Column | 205 | @Column |
196 | theme: string | 206 | theme: string |
197 | 207 | ||
208 | @AllowNull(false) | ||
209 | @Default(false) | ||
210 | @Is( | ||
211 | 'UserNoInstanceConfigWarningModal', | ||
212 | value => throwIfNotValid(value, isNoInstanceConfigWarningModal, 'no instance config warning modal') | ||
213 | ) | ||
214 | @Column | ||
215 | noInstanceConfigWarningModal: boolean | ||
216 | |||
217 | @AllowNull(false) | ||
218 | @Default(false) | ||
219 | @Is( | ||
220 | 'UserNoInstanceConfigWarningModal', | ||
221 | value => throwIfNotValid(value, isNoWelcomeModal, 'no welcome modal') | ||
222 | ) | ||
223 | @Column | ||
224 | noWelcomeModal: boolean | ||
225 | |||
198 | @CreatedAt | 226 | @CreatedAt |
199 | createdAt: Date | 227 | createdAt: Date |
200 | 228 | ||
@@ -303,7 +331,7 @@ export class UserModel extends Model<UserModel> { | |||
303 | }) | 331 | }) |
304 | } | 332 | } |
305 | 333 | ||
306 | static listWithRight (right: UserRight) { | 334 | static listWithRight (right: UserRight): Bluebird<MUserDefault[]> { |
307 | const roles = Object.keys(USER_ROLE_LABELS) | 335 | const roles = Object.keys(USER_ROLE_LABELS) |
308 | .map(k => parseInt(k, 10) as UserRole) | 336 | .map(k => parseInt(k, 10) as UserRole) |
309 | .filter(role => hasUserRight(role, right)) | 337 | .filter(role => hasUserRight(role, right)) |
@@ -319,7 +347,7 @@ export class UserModel extends Model<UserModel> { | |||
319 | return UserModel.findAll(query) | 347 | return UserModel.findAll(query) |
320 | } | 348 | } |
321 | 349 | ||
322 | static listUserSubscribersOf (actorId: number) { | 350 | static listUserSubscribersOf (actorId: number): Bluebird<MUserWithNotificationSetting[]> { |
323 | const query = { | 351 | const query = { |
324 | include: [ | 352 | include: [ |
325 | { | 353 | { |
@@ -358,7 +386,7 @@ export class UserModel extends Model<UserModel> { | |||
358 | return UserModel.unscoped().findAll(query) | 386 | return UserModel.unscoped().findAll(query) |
359 | } | 387 | } |
360 | 388 | ||
361 | static listByUsernames (usernames: string[]) { | 389 | static listByUsernames (usernames: string[]): Bluebird<MUserDefault[]> { |
362 | const query = { | 390 | const query = { |
363 | where: { | 391 | where: { |
364 | username: usernames | 392 | username: usernames |
@@ -368,11 +396,11 @@ export class UserModel extends Model<UserModel> { | |||
368 | return UserModel.findAll(query) | 396 | return UserModel.findAll(query) |
369 | } | 397 | } |
370 | 398 | ||
371 | static loadById (id: number) { | 399 | static loadById (id: number): Bluebird<MUserDefault> { |
372 | return UserModel.findByPk(id) | 400 | return UserModel.findByPk(id) |
373 | } | 401 | } |
374 | 402 | ||
375 | static loadByUsername (username: string) { | 403 | static loadByUsername (username: string): Bluebird<MUserDefault> { |
376 | const query = { | 404 | const query = { |
377 | where: { | 405 | where: { |
378 | username: { [ Op.iLike ]: username } | 406 | username: { [ Op.iLike ]: username } |
@@ -382,7 +410,7 @@ export class UserModel extends Model<UserModel> { | |||
382 | return UserModel.findOne(query) | 410 | return UserModel.findOne(query) |
383 | } | 411 | } |
384 | 412 | ||
385 | static loadByUsernameAndPopulateChannels (username: string) { | 413 | static loadByUsernameAndPopulateChannels (username: string): Bluebird<MUserNotifSettingChannelDefault> { |
386 | const query = { | 414 | const query = { |
387 | where: { | 415 | where: { |
388 | username: { [ Op.iLike ]: username } | 416 | username: { [ Op.iLike ]: username } |
@@ -392,7 +420,7 @@ export class UserModel extends Model<UserModel> { | |||
392 | return UserModel.scope(ScopeNames.WITH_VIDEO_CHANNEL).findOne(query) | 420 | return UserModel.scope(ScopeNames.WITH_VIDEO_CHANNEL).findOne(query) |
393 | } | 421 | } |
394 | 422 | ||
395 | static loadByEmail (email: string) { | 423 | static loadByEmail (email: string): Bluebird<MUserDefault> { |
396 | const query = { | 424 | const query = { |
397 | where: { | 425 | where: { |
398 | 426 | ||
@@ -402,7 +430,7 @@ export class UserModel extends Model<UserModel> { | |||
402 | return UserModel.findOne(query) | 430 | return UserModel.findOne(query) |
403 | } | 431 | } |
404 | 432 | ||
405 | static loadByUsernameOrEmail (username: string, email?: string) { | 433 | static loadByUsernameOrEmail (username: string, email?: string): Bluebird<MUserDefault> { |
406 | if (!email) email = username | 434 | if (!email) email = username |
407 | 435 | ||
408 | const query = { | 436 | const query = { |
@@ -414,7 +442,7 @@ export class UserModel extends Model<UserModel> { | |||
414 | return UserModel.findOne(query) | 442 | return UserModel.findOne(query) |
415 | } | 443 | } |
416 | 444 | ||
417 | static loadByVideoId (videoId: number) { | 445 | static loadByVideoId (videoId: number): Bluebird<MUserDefault> { |
418 | const query = { | 446 | const query = { |
419 | include: [ | 447 | include: [ |
420 | { | 448 | { |
@@ -445,7 +473,7 @@ export class UserModel extends Model<UserModel> { | |||
445 | return UserModel.findOne(query) | 473 | return UserModel.findOne(query) |
446 | } | 474 | } |
447 | 475 | ||
448 | static loadByVideoImportId (videoImportId: number) { | 476 | static loadByVideoImportId (videoImportId: number): Bluebird<MUserDefault> { |
449 | const query = { | 477 | const query = { |
450 | include: [ | 478 | include: [ |
451 | { | 479 | { |
@@ -462,7 +490,7 @@ export class UserModel extends Model<UserModel> { | |||
462 | return UserModel.findOne(query) | 490 | return UserModel.findOne(query) |
463 | } | 491 | } |
464 | 492 | ||
465 | static loadByChannelActorId (videoChannelActorId: number) { | 493 | static loadByChannelActorId (videoChannelActorId: number): Bluebird<MUserDefault> { |
466 | const query = { | 494 | const query = { |
467 | include: [ | 495 | include: [ |
468 | { | 496 | { |
@@ -486,7 +514,7 @@ export class UserModel extends Model<UserModel> { | |||
486 | return UserModel.findOne(query) | 514 | return UserModel.findOne(query) |
487 | } | 515 | } |
488 | 516 | ||
489 | static loadByAccountActorId (accountActorId: number) { | 517 | static loadByAccountActorId (accountActorId: number): Bluebird<MUserDefault> { |
490 | const query = { | 518 | const query = { |
491 | include: [ | 519 | include: [ |
492 | { | 520 | { |
@@ -503,7 +531,7 @@ export class UserModel extends Model<UserModel> { | |||
503 | return UserModel.findOne(query) | 531 | return UserModel.findOne(query) |
504 | } | 532 | } |
505 | 533 | ||
506 | static getOriginalVideoFileTotalFromUser (user: UserModel) { | 534 | static getOriginalVideoFileTotalFromUser (user: MUserId) { |
507 | // Don't use sequelize because we need to use a sub query | 535 | // Don't use sequelize because we need to use a sub query |
508 | const query = UserModel.generateUserQuotaBaseSQL() | 536 | const query = UserModel.generateUserQuotaBaseSQL() |
509 | 537 | ||
@@ -511,7 +539,7 @@ export class UserModel extends Model<UserModel> { | |||
511 | } | 539 | } |
512 | 540 | ||
513 | // Returns cumulative size of all video files uploaded in the last 24 hours. | 541 | // Returns cumulative size of all video files uploaded in the last 24 hours. |
514 | static getOriginalVideoFileTotalDailyFromUser (user: UserModel) { | 542 | static getOriginalVideoFileTotalDailyFromUser (user: MUserId) { |
515 | // Don't use sequelize because we need to use a sub query | 543 | // Don't use sequelize because we need to use a sub query |
516 | const query = UserModel.generateUserQuotaBaseSQL('"video"."createdAt" > now() - interval \'24 hours\'') | 544 | const query = UserModel.generateUserQuotaBaseSQL('"video"."createdAt" > now() - interval \'24 hours\'') |
517 | 545 | ||
@@ -552,38 +580,52 @@ export class UserModel extends Model<UserModel> { | |||
552 | return comparePassword(password, this.password) | 580 | return comparePassword(password, this.password) |
553 | } | 581 | } |
554 | 582 | ||
555 | toFormattedJSON (parameters: { withAdminFlags?: boolean } = {}): User { | 583 | toFormattedJSON (this: MUserFormattable, parameters: { withAdminFlags?: boolean } = {}): User { |
556 | const videoQuotaUsed = this.get('videoQuotaUsed') | 584 | const videoQuotaUsed = this.get('videoQuotaUsed') |
557 | const videoQuotaUsedDaily = this.get('videoQuotaUsedDaily') | 585 | const videoQuotaUsedDaily = this.get('videoQuotaUsedDaily') |
558 | 586 | ||
559 | const json = { | 587 | const json: User = { |
560 | id: this.id, | 588 | id: this.id, |
561 | username: this.username, | 589 | username: this.username, |
562 | email: this.email, | 590 | email: this.email, |
591 | theme: getThemeOrDefault(this.theme, DEFAULT_USER_THEME_NAME), | ||
592 | |||
563 | pendingEmail: this.pendingEmail, | 593 | pendingEmail: this.pendingEmail, |
564 | emailVerified: this.emailVerified, | 594 | emailVerified: this.emailVerified, |
595 | |||
565 | nsfwPolicy: this.nsfwPolicy, | 596 | nsfwPolicy: this.nsfwPolicy, |
566 | webTorrentEnabled: this.webTorrentEnabled, | 597 | webTorrentEnabled: this.webTorrentEnabled, |
567 | videosHistoryEnabled: this.videosHistoryEnabled, | 598 | videosHistoryEnabled: this.videosHistoryEnabled, |
568 | autoPlayVideo: this.autoPlayVideo, | 599 | autoPlayVideo: this.autoPlayVideo, |
569 | videoLanguages: this.videoLanguages, | 600 | videoLanguages: this.videoLanguages, |
601 | |||
570 | role: this.role, | 602 | role: this.role, |
571 | theme: getThemeOrDefault(this.theme, DEFAULT_USER_THEME_NAME), | ||
572 | roleLabel: USER_ROLE_LABELS[ this.role ], | 603 | roleLabel: USER_ROLE_LABELS[ this.role ], |
604 | |||
573 | videoQuota: this.videoQuota, | 605 | videoQuota: this.videoQuota, |
574 | videoQuotaDaily: this.videoQuotaDaily, | 606 | videoQuotaDaily: this.videoQuotaDaily, |
575 | createdAt: this.createdAt, | 607 | videoQuotaUsed: videoQuotaUsed !== undefined |
608 | ? parseInt(videoQuotaUsed + '', 10) | ||
609 | : undefined, | ||
610 | videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined | ||
611 | ? parseInt(videoQuotaUsedDaily + '', 10) | ||
612 | : undefined, | ||
613 | |||
614 | noInstanceConfigWarningModal: this.noInstanceConfigWarningModal, | ||
615 | noWelcomeModal: this.noWelcomeModal, | ||
616 | |||
576 | blocked: this.blocked, | 617 | blocked: this.blocked, |
577 | blockedReason: this.blockedReason, | 618 | blockedReason: this.blockedReason, |
619 | |||
578 | account: this.Account.toFormattedJSON(), | 620 | account: this.Account.toFormattedJSON(), |
579 | notificationSettings: this.NotificationSetting ? this.NotificationSetting.toFormattedJSON() : undefined, | 621 | |
622 | notificationSettings: this.NotificationSetting | ||
623 | ? this.NotificationSetting.toFormattedJSON() | ||
624 | : undefined, | ||
625 | |||
580 | videoChannels: [], | 626 | videoChannels: [], |
581 | videoQuotaUsed: videoQuotaUsed !== undefined | 627 | |
582 | ? parseInt(videoQuotaUsed + '', 10) | 628 | createdAt: this.createdAt |
583 | : undefined, | ||
584 | videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined | ||
585 | ? parseInt(videoQuotaUsedDaily + '', 10) | ||
586 | : undefined | ||
587 | } | 629 | } |
588 | 630 | ||
589 | if (parameters.withAdminFlags) { | 631 | if (parameters.withAdminFlags) { |
diff --git a/server/models/activitypub/actor-follow.ts b/server/models/activitypub/actor-follow.ts index 51b09e09b..8498692f0 100644 --- a/server/models/activitypub/actor-follow.ts +++ b/server/models/activitypub/actor-follow.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import { values } from 'lodash' | 2 | import { values, difference } from 'lodash' |
3 | import { | 3 | import { |
4 | AfterCreate, | 4 | AfterCreate, |
5 | AfterDestroy, | 5 | AfterDestroy, |
@@ -21,13 +21,20 @@ import { FollowState } from '../../../shared/models/actors' | |||
21 | import { ActorFollow } from '../../../shared/models/actors/follow.model' | 21 | import { ActorFollow } from '../../../shared/models/actors/follow.model' |
22 | import { logger } from '../../helpers/logger' | 22 | import { logger } from '../../helpers/logger' |
23 | import { getServerActor } from '../../helpers/utils' | 23 | import { getServerActor } from '../../helpers/utils' |
24 | import { ACTOR_FOLLOW_SCORE, FOLLOW_STATES } from '../../initializers/constants' | 24 | import { ACTOR_FOLLOW_SCORE, FOLLOW_STATES, SERVER_ACTOR_NAME } from '../../initializers/constants' |
25 | import { ServerModel } from '../server/server' | 25 | import { ServerModel } from '../server/server' |
26 | import { createSafeIn, getSort } from '../utils' | 26 | import { createSafeIn, getSort } from '../utils' |
27 | import { ActorModel, unusedActorAttributesForAPI } from './actor' | 27 | import { ActorModel, unusedActorAttributesForAPI } from './actor' |
28 | import { VideoChannelModel } from '../video/video-channel' | 28 | import { VideoChannelModel } from '../video/video-channel' |
29 | import { AccountModel } from '../account/account' | 29 | import { AccountModel } from '../account/account' |
30 | import { IncludeOptions, Op, Transaction, QueryTypes } from 'sequelize' | 30 | import { IncludeOptions, Op, QueryTypes, Transaction } from 'sequelize' |
31 | import { | ||
32 | MActorFollowActorsDefault, | ||
33 | MActorFollowActorsDefaultSubscription, | ||
34 | MActorFollowFollowingHost, | ||
35 | MActorFollowFormattable, | ||
36 | MActorFollowSubscriptions | ||
37 | } from '@server/typings/models' | ||
31 | 38 | ||
32 | @Table({ | 39 | @Table({ |
33 | tableName: 'actorFollow', | 40 | tableName: 'actorFollow', |
@@ -143,7 +150,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> { | |||
143 | if (numberOfActorFollowsRemoved) logger.info('Removed bad %d actor follows.', numberOfActorFollowsRemoved) | 150 | if (numberOfActorFollowsRemoved) logger.info('Removed bad %d actor follows.', numberOfActorFollowsRemoved) |
144 | } | 151 | } |
145 | 152 | ||
146 | static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Transaction) { | 153 | static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Transaction): Bluebird<MActorFollowActorsDefault> { |
147 | const query = { | 154 | const query = { |
148 | where: { | 155 | where: { |
149 | actorId, | 156 | actorId, |
@@ -167,7 +174,12 @@ export class ActorFollowModel extends Model<ActorFollowModel> { | |||
167 | return ActorFollowModel.findOne(query) | 174 | return ActorFollowModel.findOne(query) |
168 | } | 175 | } |
169 | 176 | ||
170 | static loadByActorAndTargetNameAndHostForAPI (actorId: number, targetName: string, targetHost: string, t?: Transaction) { | 177 | static loadByActorAndTargetNameAndHostForAPI ( |
178 | actorId: number, | ||
179 | targetName: string, | ||
180 | targetHost: string, | ||
181 | t?: Transaction | ||
182 | ): Bluebird<MActorFollowActorsDefaultSubscription> { | ||
171 | const actorFollowingPartInclude: IncludeOptions = { | 183 | const actorFollowingPartInclude: IncludeOptions = { |
172 | model: ActorModel, | 184 | model: ActorModel, |
173 | required: true, | 185 | required: true, |
@@ -220,7 +232,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> { | |||
220 | }) | 232 | }) |
221 | } | 233 | } |
222 | 234 | ||
223 | static listSubscribedIn (actorId: number, targets: { name: string, host?: string }[]) { | 235 | static listSubscribedIn (actorId: number, targets: { name: string, host?: string }[]): Bluebird<MActorFollowFollowingHost[]> { |
224 | const whereTab = targets | 236 | const whereTab = targets |
225 | .map(t => { | 237 | .map(t => { |
226 | if (t.host) { | 238 | if (t.host) { |
@@ -314,7 +326,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> { | |||
314 | ] | 326 | ] |
315 | } | 327 | } |
316 | 328 | ||
317 | return ActorFollowModel.findAndCountAll(query) | 329 | return ActorFollowModel.findAndCountAll<MActorFollowActorsDefault>(query) |
318 | .then(({ rows, count }) => { | 330 | .then(({ rows, count }) => { |
319 | return { | 331 | return { |
320 | data: rows, | 332 | data: rows, |
@@ -357,7 +369,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> { | |||
357 | ] | 369 | ] |
358 | } | 370 | } |
359 | 371 | ||
360 | return ActorFollowModel.findAndCountAll(query) | 372 | return ActorFollowModel.findAndCountAll<MActorFollowActorsDefault>(query) |
361 | .then(({ rows, count }) => { | 373 | .then(({ rows, count }) => { |
362 | return { | 374 | return { |
363 | data: rows, | 375 | data: rows, |
@@ -414,7 +426,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> { | |||
414 | ] | 426 | ] |
415 | } | 427 | } |
416 | 428 | ||
417 | return ActorFollowModel.findAndCountAll(query) | 429 | return ActorFollowModel.findAndCountAll<MActorFollowSubscriptions>(query) |
418 | .then(({ rows, count }) => { | 430 | .then(({ rows, count }) => { |
419 | return { | 431 | return { |
420 | data: rows.map(r => r.ActorFollowing.VideoChannel), | 432 | data: rows.map(r => r.ActorFollowing.VideoChannel), |
@@ -423,6 +435,45 @@ export class ActorFollowModel extends Model<ActorFollowModel> { | |||
423 | }) | 435 | }) |
424 | } | 436 | } |
425 | 437 | ||
438 | static async keepUnfollowedInstance (hosts: string[]) { | ||
439 | const followerId = (await getServerActor()).id | ||
440 | |||
441 | const query = { | ||
442 | attributes: [ 'id' ], | ||
443 | where: { | ||
444 | actorId: followerId | ||
445 | }, | ||
446 | include: [ | ||
447 | { | ||
448 | attributes: [ 'id' ], | ||
449 | model: ActorModel.unscoped(), | ||
450 | required: true, | ||
451 | as: 'ActorFollowing', | ||
452 | where: { | ||
453 | preferredUsername: SERVER_ACTOR_NAME | ||
454 | }, | ||
455 | include: [ | ||
456 | { | ||
457 | attributes: [ 'host' ], | ||
458 | model: ServerModel.unscoped(), | ||
459 | required: true, | ||
460 | where: { | ||
461 | host: { | ||
462 | [Op.in]: hosts | ||
463 | } | ||
464 | } | ||
465 | } | ||
466 | ] | ||
467 | } | ||
468 | ] | ||
469 | } | ||
470 | |||
471 | const res = await ActorFollowModel.findAll(query) | ||
472 | const followedHosts = res.map(row => row.ActorFollowing.Server.host) | ||
473 | |||
474 | return difference(hosts, followedHosts) | ||
475 | } | ||
476 | |||
426 | static listAcceptedFollowerUrlsForAP (actorIds: number[], t: Transaction, start?: number, count?: number) { | 477 | static listAcceptedFollowerUrlsForAP (actorIds: number[], t: Transaction, start?: number, count?: number) { |
427 | return ActorFollowModel.createListAcceptedFollowForApiQuery('followers', actorIds, t, start, count) | 478 | return ActorFollowModel.createListAcceptedFollowForApiQuery('followers', actorIds, t, start, count) |
428 | } | 479 | } |
@@ -569,7 +620,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> { | |||
569 | return ActorFollowModel.findAll(query) | 620 | return ActorFollowModel.findAll(query) |
570 | } | 621 | } |
571 | 622 | ||
572 | toFormattedJSON (): ActorFollow { | 623 | toFormattedJSON (this: MActorFollowFormattable): ActorFollow { |
573 | const follower = this.ActorFollower.toFormattedJSON() | 624 | const follower = this.ActorFollower.toFormattedJSON() |
574 | const following = this.ActorFollowing.toFormattedJSON() | 625 | const following = this.ActorFollowing.toFormattedJSON() |
575 | 626 | ||
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts index 9cc53f78a..05de1905d 100644 --- a/server/models/activitypub/actor.ts +++ b/server/models/activitypub/actor.ts | |||
@@ -36,6 +36,17 @@ import { isOutdated, throwIfNotValid } from '../utils' | |||
36 | import { VideoChannelModel } from '../video/video-channel' | 36 | import { VideoChannelModel } from '../video/video-channel' |
37 | import { ActorFollowModel } from './actor-follow' | 37 | import { ActorFollowModel } from './actor-follow' |
38 | import { VideoModel } from '../video/video' | 38 | import { VideoModel } from '../video/video' |
39 | import { | ||
40 | MActor, | ||
41 | MActorAccountChannelId, | ||
42 | MActorAP, | ||
43 | MActorFormattable, | ||
44 | MActorFull, | ||
45 | MActorHost, | ||
46 | MActorServer, | ||
47 | MActorSummaryFormattable | ||
48 | } from '../../typings/models' | ||
49 | import * as Bluebird from 'bluebird' | ||
39 | 50 | ||
40 | enum ScopeNames { | 51 | enum ScopeNames { |
41 | FULL = 'FULL' | 52 | FULL = 'FULL' |
@@ -163,8 +174,8 @@ export class ActorModel extends Model<ActorModel> { | |||
163 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max)) | 174 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max)) |
164 | inboxUrl: string | 175 | inboxUrl: string |
165 | 176 | ||
166 | @AllowNull(false) | 177 | @AllowNull(true) |
167 | @Is('ActorOutboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'outbox url')) | 178 | @Is('ActorOutboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'outbox url', true)) |
168 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max)) | 179 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max)) |
169 | outboxUrl: string | 180 | outboxUrl: string |
170 | 181 | ||
@@ -173,13 +184,13 @@ export class ActorModel extends Model<ActorModel> { | |||
173 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max)) | 184 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max)) |
174 | sharedInboxUrl: string | 185 | sharedInboxUrl: string |
175 | 186 | ||
176 | @AllowNull(false) | 187 | @AllowNull(true) |
177 | @Is('ActorFollowersUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'followers url')) | 188 | @Is('ActorFollowersUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'followers url', true)) |
178 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max)) | 189 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max)) |
179 | followersUrl: string | 190 | followersUrl: string |
180 | 191 | ||
181 | @AllowNull(false) | 192 | @AllowNull(true) |
182 | @Is('ActorFollowingUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'following url')) | 193 | @Is('ActorFollowingUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'following url', true)) |
183 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max)) | 194 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max)) |
184 | followingUrl: string | 195 | followingUrl: string |
185 | 196 | ||
@@ -252,11 +263,15 @@ export class ActorModel extends Model<ActorModel> { | |||
252 | }) | 263 | }) |
253 | VideoChannel: VideoChannelModel | 264 | VideoChannel: VideoChannelModel |
254 | 265 | ||
255 | static load (id: number) { | 266 | static load (id: number): Bluebird<MActor> { |
256 | return ActorModel.unscoped().findByPk(id) | 267 | return ActorModel.unscoped().findByPk(id) |
257 | } | 268 | } |
258 | 269 | ||
259 | static loadAccountActorByVideoId (videoId: number, transaction: Sequelize.Transaction) { | 270 | static loadFull (id: number): Bluebird<MActorFull> { |
271 | return ActorModel.scope(ScopeNames.FULL).findByPk(id) | ||
272 | } | ||
273 | |||
274 | static loadFromAccountByVideoId (videoId: number, transaction: Sequelize.Transaction): Bluebird<MActor> { | ||
260 | const query = { | 275 | const query = { |
261 | include: [ | 276 | include: [ |
262 | { | 277 | { |
@@ -300,7 +315,7 @@ export class ActorModel extends Model<ActorModel> { | |||
300 | .then(a => !!a) | 315 | .then(a => !!a) |
301 | } | 316 | } |
302 | 317 | ||
303 | static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction) { | 318 | static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction): Bluebird<MActorFull[]> { |
304 | const query = { | 319 | const query = { |
305 | where: { | 320 | where: { |
306 | followersUrl: { | 321 | followersUrl: { |
@@ -313,7 +328,7 @@ export class ActorModel extends Model<ActorModel> { | |||
313 | return ActorModel.scope(ScopeNames.FULL).findAll(query) | 328 | return ActorModel.scope(ScopeNames.FULL).findAll(query) |
314 | } | 329 | } |
315 | 330 | ||
316 | static loadLocalByName (preferredUsername: string, transaction?: Sequelize.Transaction) { | 331 | static loadLocalByName (preferredUsername: string, transaction?: Sequelize.Transaction): Bluebird<MActorFull> { |
317 | const query = { | 332 | const query = { |
318 | where: { | 333 | where: { |
319 | preferredUsername, | 334 | preferredUsername, |
@@ -325,7 +340,7 @@ export class ActorModel extends Model<ActorModel> { | |||
325 | return ActorModel.scope(ScopeNames.FULL).findOne(query) | 340 | return ActorModel.scope(ScopeNames.FULL).findOne(query) |
326 | } | 341 | } |
327 | 342 | ||
328 | static loadByNameAndHost (preferredUsername: string, host: string) { | 343 | static loadByNameAndHost (preferredUsername: string, host: string): Bluebird<MActorFull> { |
329 | const query = { | 344 | const query = { |
330 | where: { | 345 | where: { |
331 | preferredUsername | 346 | preferredUsername |
@@ -344,7 +359,7 @@ export class ActorModel extends Model<ActorModel> { | |||
344 | return ActorModel.scope(ScopeNames.FULL).findOne(query) | 359 | return ActorModel.scope(ScopeNames.FULL).findOne(query) |
345 | } | 360 | } |
346 | 361 | ||
347 | static loadByUrl (url: string, transaction?: Sequelize.Transaction) { | 362 | static loadByUrl (url: string, transaction?: Sequelize.Transaction): Bluebird<MActorAccountChannelId> { |
348 | const query = { | 363 | const query = { |
349 | where: { | 364 | where: { |
350 | url | 365 | url |
@@ -367,7 +382,7 @@ export class ActorModel extends Model<ActorModel> { | |||
367 | return ActorModel.unscoped().findOne(query) | 382 | return ActorModel.unscoped().findOne(query) |
368 | } | 383 | } |
369 | 384 | ||
370 | static loadByUrlAndPopulateAccountAndChannel (url: string, transaction?: Sequelize.Transaction) { | 385 | static loadByUrlAndPopulateAccountAndChannel (url: string, transaction?: Sequelize.Transaction): Bluebird<MActorFull> { |
371 | const query = { | 386 | const query = { |
372 | where: { | 387 | where: { |
373 | url | 388 | url |
@@ -387,35 +402,35 @@ export class ActorModel extends Model<ActorModel> { | |||
387 | }) | 402 | }) |
388 | } | 403 | } |
389 | 404 | ||
390 | toFormattedJSON () { | 405 | toFormattedSummaryJSON (this: MActorSummaryFormattable) { |
391 | let avatar: Avatar = null | 406 | let avatar: Avatar = null |
392 | if (this.Avatar) { | 407 | if (this.Avatar) { |
393 | avatar = this.Avatar.toFormattedJSON() | 408 | avatar = this.Avatar.toFormattedJSON() |
394 | } | 409 | } |
395 | 410 | ||
396 | return { | 411 | return { |
397 | id: this.id, | ||
398 | url: this.url, | 412 | url: this.url, |
399 | name: this.preferredUsername, | 413 | name: this.preferredUsername, |
400 | host: this.getHost(), | 414 | host: this.getHost(), |
415 | avatar | ||
416 | } | ||
417 | } | ||
418 | |||
419 | toFormattedJSON (this: MActorFormattable) { | ||
420 | const base = this.toFormattedSummaryJSON() | ||
421 | |||
422 | return Object.assign(base, { | ||
423 | id: this.id, | ||
401 | hostRedundancyAllowed: this.getRedundancyAllowed(), | 424 | hostRedundancyAllowed: this.getRedundancyAllowed(), |
402 | followingCount: this.followingCount, | 425 | followingCount: this.followingCount, |
403 | followersCount: this.followersCount, | 426 | followersCount: this.followersCount, |
404 | avatar, | ||
405 | createdAt: this.createdAt, | 427 | createdAt: this.createdAt, |
406 | updatedAt: this.updatedAt | 428 | updatedAt: this.updatedAt |
407 | } | 429 | }) |
408 | } | 430 | } |
409 | 431 | ||
410 | toActivityPubObject (name: string, type: 'Account' | 'Application' | 'VideoChannel') { | 432 | toActivityPubObject (this: MActorAP, name: string) { |
411 | let activityPubType | 433 | let activityPubType |
412 | if (type === 'Account') { | ||
413 | activityPubType = 'Person' as 'Person' | ||
414 | } else if (type === 'Application') { | ||
415 | activityPubType = 'Application' as 'Application' | ||
416 | } else { // VideoChannel | ||
417 | activityPubType = 'Group' as 'Group' | ||
418 | } | ||
419 | 434 | ||
420 | let icon = undefined | 435 | let icon = undefined |
421 | if (this.avatarId) { | 436 | if (this.avatarId) { |
@@ -428,7 +443,7 @@ export class ActorModel extends Model<ActorModel> { | |||
428 | } | 443 | } |
429 | 444 | ||
430 | const json = { | 445 | const json = { |
431 | type: activityPubType, | 446 | type: this.type, |
432 | id: this.url, | 447 | id: this.url, |
433 | following: this.getFollowingUrl(), | 448 | following: this.getFollowingUrl(), |
434 | followers: this.getFollowersUrl(), | 449 | followers: this.getFollowersUrl(), |
@@ -494,7 +509,7 @@ export class ActorModel extends Model<ActorModel> { | |||
494 | return this.serverId === null | 509 | return this.serverId === null |
495 | } | 510 | } |
496 | 511 | ||
497 | getWebfingerUrl () { | 512 | getWebfingerUrl (this: MActorServer) { |
498 | return 'acct:' + this.preferredUsername + '@' + this.getHost() | 513 | return 'acct:' + this.preferredUsername + '@' + this.getHost() |
499 | } | 514 | } |
500 | 515 | ||
@@ -502,7 +517,7 @@ export class ActorModel extends Model<ActorModel> { | |||
502 | return this.Server ? `${this.preferredUsername}@${this.Server.host}` : this.preferredUsername | 517 | return this.Server ? `${this.preferredUsername}@${this.Server.host}` : this.preferredUsername |
503 | } | 518 | } |
504 | 519 | ||
505 | getHost () { | 520 | getHost (this: MActorHost) { |
506 | return this.Server ? this.Server.host : WEBSERVER.HOST | 521 | return this.Server ? this.Server.host : WEBSERVER.HOST |
507 | } | 522 | } |
508 | 523 | ||
diff --git a/server/models/avatar/avatar.ts b/server/models/avatar/avatar.ts index b40144592..950e4b181 100644 --- a/server/models/avatar/avatar.ts +++ b/server/models/avatar/avatar.ts | |||
@@ -7,6 +7,7 @@ import { remove } from 'fs-extra' | |||
7 | import { CONFIG } from '../../initializers/config' | 7 | import { CONFIG } from '../../initializers/config' |
8 | import { throwIfNotValid } from '../utils' | 8 | import { throwIfNotValid } from '../utils' |
9 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 9 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
10 | import { MAvatarFormattable } from '@server/typings/models' | ||
10 | 11 | ||
11 | @Table({ | 12 | @Table({ |
12 | tableName: 'avatar', | 13 | tableName: 'avatar', |
@@ -57,7 +58,7 @@ export class AvatarModel extends Model<AvatarModel> { | |||
57 | return AvatarModel.findOne(query) | 58 | return AvatarModel.findOne(query) |
58 | } | 59 | } |
59 | 60 | ||
60 | toFormattedJSON (): Avatar { | 61 | toFormattedJSON (this: MAvatarFormattable): Avatar { |
61 | return { | 62 | return { |
62 | path: this.getStaticPath(), | 63 | path: this.getStaticPath(), |
63 | createdAt: this.createdAt, | 64 | createdAt: this.createdAt, |
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' | |||
18 | import { AccountModel } from '../account/account' | 18 | import { AccountModel } from '../account/account' |
19 | import { ActorModel } from '../activitypub/actor' | 19 | import { ActorModel } from '../activitypub/actor' |
20 | import { clearCacheByToken } from '../../lib/oauth-model' | 20 | import { clearCacheByToken } from '../../lib/oauth-model' |
21 | import * as Bluebird from 'bluebird' | ||
22 | import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token' | ||
21 | 23 | ||
22 | export type OAuthTokenInfo = { | 24 | export type OAuthTokenInfo = { |
23 | refreshToken: string | 25 | refreshToken: string |
@@ -160,7 +162,7 @@ export class OAuthTokenModel extends Model<OAuthTokenModel> { | |||
160 | }) | 162 | }) |
161 | } | 163 | } |
162 | 164 | ||
163 | static getByTokenAndPopulateUser (bearerToken: string) { | 165 | static getByTokenAndPopulateUser (bearerToken: string): Bluebird<MOAuthTokenUser> { |
164 | const query = { | 166 | const query = { |
165 | where: { | 167 | where: { |
166 | accessToken: bearerToken | 168 | accessToken: bearerToken |
@@ -170,13 +172,13 @@ export class OAuthTokenModel extends Model<OAuthTokenModel> { | |||
170 | return OAuthTokenModel.scope(ScopeNames.WITH_USER) | 172 | return OAuthTokenModel.scope(ScopeNames.WITH_USER) |
171 | .findOne(query) | 173 | .findOne(query) |
172 | .then(token => { | 174 | .then(token => { |
173 | if (token) token[ 'user' ] = token.User | 175 | if (!token) return null |
174 | 176 | ||
175 | return token | 177 | return Object.assign(token, { user: token.User }) |
176 | }) | 178 | }) |
177 | } | 179 | } |
178 | 180 | ||
179 | static getByRefreshTokenAndPopulateUser (refreshToken: string) { | 181 | static getByRefreshTokenAndPopulateUser (refreshToken: string): Bluebird<MOAuthTokenUser> { |
180 | const query = { | 182 | const query = { |
181 | where: { | 183 | where: { |
182 | refreshToken: refreshToken | 184 | refreshToken: refreshToken |
@@ -186,12 +188,9 @@ export class OAuthTokenModel extends Model<OAuthTokenModel> { | |||
186 | return OAuthTokenModel.scope(ScopeNames.WITH_USER) | 188 | return OAuthTokenModel.scope(ScopeNames.WITH_USER) |
187 | .findOne(query) | 189 | .findOne(query) |
188 | .then(token => { | 190 | .then(token => { |
189 | if (token) { | 191 | if (!token) return new OAuthTokenModel() |
190 | token['user'] = token.User | 192 | |
191 | return token | 193 | return Object.assign(token, { user: token.User }) |
192 | } else { | ||
193 | return new OAuthTokenModel() | ||
194 | } | ||
195 | }) | 194 | }) |
196 | } | 195 | } |
197 | 196 | ||
diff --git a/server/models/redundancy/video-redundancy.ts b/server/models/redundancy/video-redundancy.ts index 3df1c4f9c..61d9a5612 100644 --- a/server/models/redundancy/video-redundancy.ts +++ b/server/models/redundancy/video-redundancy.ts | |||
@@ -30,6 +30,7 @@ import * as Bluebird from 'bluebird' | |||
30 | import { col, FindOptions, fn, literal, Op, Transaction } from 'sequelize' | 30 | import { col, FindOptions, fn, literal, Op, Transaction } from 'sequelize' |
31 | import { VideoStreamingPlaylistModel } from '../video/video-streaming-playlist' | 31 | import { VideoStreamingPlaylistModel } from '../video/video-streaming-playlist' |
32 | import { CONFIG } from '../../initializers/config' | 32 | import { CONFIG } from '../../initializers/config' |
33 | import { MVideoRedundancy, MVideoRedundancyAP, MVideoRedundancyVideo } from '@server/typings/models' | ||
33 | 34 | ||
34 | export enum ScopeNames { | 35 | export enum ScopeNames { |
35 | WITH_VIDEO = 'WITH_VIDEO' | 36 | WITH_VIDEO = 'WITH_VIDEO' |
@@ -166,7 +167,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
166 | return undefined | 167 | return undefined |
167 | } | 168 | } |
168 | 169 | ||
169 | static async loadLocalByFileId (videoFileId: number) { | 170 | static async loadLocalByFileId (videoFileId: number): Promise<MVideoRedundancyVideo> { |
170 | const actor = await getServerActor() | 171 | const actor = await getServerActor() |
171 | 172 | ||
172 | const query = { | 173 | const query = { |
@@ -179,7 +180,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
179 | return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query) | 180 | return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query) |
180 | } | 181 | } |
181 | 182 | ||
182 | static async loadLocalByStreamingPlaylistId (videoStreamingPlaylistId: number) { | 183 | static async loadLocalByStreamingPlaylistId (videoStreamingPlaylistId: number): Promise<MVideoRedundancyVideo> { |
183 | const actor = await getServerActor() | 184 | const actor = await getServerActor() |
184 | 185 | ||
185 | const query = { | 186 | const query = { |
@@ -192,7 +193,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
192 | return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query) | 193 | return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query) |
193 | } | 194 | } |
194 | 195 | ||
195 | static loadByUrl (url: string, transaction?: Transaction) { | 196 | static loadByUrl (url: string, transaction?: Transaction): Bluebird<MVideoRedundancy> { |
196 | const query = { | 197 | const query = { |
197 | where: { | 198 | where: { |
198 | url | 199 | url |
@@ -306,7 +307,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
306 | return VideoRedundancyModel.getVideoSample(VideoModel.unscoped().findAll(query)) | 307 | return VideoRedundancyModel.getVideoSample(VideoModel.unscoped().findAll(query)) |
307 | } | 308 | } |
308 | 309 | ||
309 | static async loadOldestLocalThatAlreadyExpired (strategy: VideoRedundancyStrategy, expiresAfterMs: number) { | 310 | static async loadOldestLocalExpired (strategy: VideoRedundancyStrategy, expiresAfterMs: number): Promise<MVideoRedundancyVideo> { |
310 | const expiredDate = new Date() | 311 | const expiredDate = new Date() |
311 | expiredDate.setMilliseconds(expiredDate.getMilliseconds() - expiresAfterMs) | 312 | expiredDate.setMilliseconds(expiredDate.getMilliseconds() - expiresAfterMs) |
312 | 313 | ||
@@ -487,7 +488,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
487 | return !!this.strategy | 488 | return !!this.strategy |
488 | } | 489 | } |
489 | 490 | ||
490 | toActivityPubObject (): CacheFileObject { | 491 | toActivityPubObject (this: MVideoRedundancyAP): CacheFileObject { |
491 | if (this.VideoStreamingPlaylist) { | 492 | if (this.VideoStreamingPlaylist) { |
492 | return { | 493 | return { |
493 | id: this.url, | 494 | id: this.url, |
diff --git a/server/models/server/plugin.ts b/server/models/server/plugin.ts index a15f9a7e2..d094da1f5 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' | |||
11 | import { PeerTubePlugin } from '../../../shared/models/plugins/peertube-plugin.model' | 11 | import { PeerTubePlugin } from '../../../shared/models/plugins/peertube-plugin.model' |
12 | import { FindAndCountOptions, json } from 'sequelize' | 12 | import { FindAndCountOptions, json } from 'sequelize' |
13 | import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model' | 13 | import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model' |
14 | import * as Bluebird from 'bluebird' | ||
15 | import { MPlugin, MPluginFormattable } from '@server/typings/models' | ||
14 | 16 | ||
15 | @DefaultScope(() => ({ | 17 | @DefaultScope(() => ({ |
16 | attributes: { | 18 | attributes: { |
@@ -85,7 +87,7 @@ export class PluginModel extends Model<PluginModel> { | |||
85 | @UpdatedAt | 87 | @UpdatedAt |
86 | updatedAt: Date | 88 | updatedAt: Date |
87 | 89 | ||
88 | static listEnabledPluginsAndThemes () { | 90 | static listEnabledPluginsAndThemes (): Bluebird<MPlugin[]> { |
89 | const query = { | 91 | const query = { |
90 | where: { | 92 | where: { |
91 | enabled: true, | 93 | enabled: true, |
@@ -96,7 +98,7 @@ export class PluginModel extends Model<PluginModel> { | |||
96 | return PluginModel.findAll(query) | 98 | return PluginModel.findAll(query) |
97 | } | 99 | } |
98 | 100 | ||
99 | static loadByNpmName (npmName: string) { | 101 | static loadByNpmName (npmName: string): Bluebird<MPlugin> { |
100 | const name = this.normalizePluginName(npmName) | 102 | const name = this.normalizePluginName(npmName) |
101 | const type = this.getTypeFromNpmName(npmName) | 103 | const type = this.getTypeFromNpmName(npmName) |
102 | 104 | ||
@@ -206,13 +208,13 @@ export class PluginModel extends Model<PluginModel> { | |||
206 | if (options.pluginType) query.where['type'] = options.pluginType | 208 | if (options.pluginType) query.where['type'] = options.pluginType |
207 | 209 | ||
208 | return PluginModel | 210 | return PluginModel |
209 | .findAndCountAll(query) | 211 | .findAndCountAll<MPlugin>(query) |
210 | .then(({ rows, count }) => { | 212 | .then(({ rows, count }) => { |
211 | return { total: count, data: rows } | 213 | return { total: count, data: rows } |
212 | }) | 214 | }) |
213 | } | 215 | } |
214 | 216 | ||
215 | static listInstalled () { | 217 | static listInstalled (): Bluebird<MPlugin[]> { |
216 | const query = { | 218 | const query = { |
217 | where: { | 219 | where: { |
218 | uninstalled: false | 220 | uninstalled: false |
@@ -251,7 +253,7 @@ export class PluginModel extends Model<PluginModel> { | |||
251 | return result | 253 | return result |
252 | } | 254 | } |
253 | 255 | ||
254 | toFormattedJSON (): PeerTubePlugin { | 256 | toFormattedJSON (this: MPluginFormattable): PeerTubePlugin { |
255 | return { | 257 | return { |
256 | name: this.name, | 258 | name: this.name, |
257 | type: this.type, | 259 | type: this.type, |
diff --git a/server/models/server/server-blocklist.ts b/server/models/server/server-blocklist.ts index 5138b0f76..3e9687191 100644 --- a/server/models/server/server-blocklist.ts +++ b/server/models/server/server-blocklist.ts | |||
@@ -3,6 +3,8 @@ import { AccountModel } from '../account/account' | |||
3 | import { ServerModel } from './server' | 3 | import { ServerModel } from './server' |
4 | import { ServerBlock } from '../../../shared/models/blocklist' | 4 | import { ServerBlock } from '../../../shared/models/blocklist' |
5 | import { getSort } from '../utils' | 5 | import { getSort } from '../utils' |
6 | import * as Bluebird from 'bluebird' | ||
7 | import { MServerBlocklist, MServerBlocklistAccountServer, MServerBlocklistFormattable } from '@server/typings/models' | ||
6 | 8 | ||
7 | enum ScopeNames { | 9 | enum ScopeNames { |
8 | WITH_ACCOUNT = 'WITH_ACCOUNT', | 10 | WITH_ACCOUNT = 'WITH_ACCOUNT', |
@@ -73,7 +75,7 @@ export class ServerBlocklistModel extends Model<ServerBlocklistModel> { | |||
73 | }) | 75 | }) |
74 | BlockedServer: ServerModel | 76 | BlockedServer: ServerModel |
75 | 77 | ||
76 | static loadByAccountAndHost (accountId: number, host: string) { | 78 | static loadByAccountAndHost (accountId: number, host: string): Bluebird<MServerBlocklist> { |
77 | const query = { | 79 | const query = { |
78 | where: { | 80 | where: { |
79 | accountId | 81 | accountId |
@@ -104,13 +106,13 @@ export class ServerBlocklistModel extends Model<ServerBlocklistModel> { | |||
104 | 106 | ||
105 | return ServerBlocklistModel | 107 | return ServerBlocklistModel |
106 | .scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_SERVER ]) | 108 | .scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_SERVER ]) |
107 | .findAndCountAll(query) | 109 | .findAndCountAll<MServerBlocklistAccountServer>(query) |
108 | .then(({ rows, count }) => { | 110 | .then(({ rows, count }) => { |
109 | return { total: count, data: rows } | 111 | return { total: count, data: rows } |
110 | }) | 112 | }) |
111 | } | 113 | } |
112 | 114 | ||
113 | toFormattedJSON (): ServerBlock { | 115 | toFormattedJSON (this: MServerBlocklistFormattable): ServerBlock { |
114 | return { | 116 | return { |
115 | byAccount: this.ByAccount.toFormattedJSON(), | 117 | byAccount: this.ByAccount.toFormattedJSON(), |
116 | blockedServer: this.BlockedServer.toFormattedJSON(), | 118 | blockedServer: this.BlockedServer.toFormattedJSON(), |
diff --git a/server/models/server/server.ts b/server/models/server/server.ts index 1d211f1e0..8b07115f1 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 | |||
2 | import { isHostValid } from '../../helpers/custom-validators/servers' | 2 | import { isHostValid } from '../../helpers/custom-validators/servers' |
3 | import { ActorModel } from '../activitypub/actor' | 3 | import { ActorModel } from '../activitypub/actor' |
4 | import { throwIfNotValid } from '../utils' | 4 | import { throwIfNotValid } from '../utils' |
5 | import { AccountBlocklistModel } from '../account/account-blocklist' | ||
6 | import { ServerBlocklistModel } from './server-blocklist' | 5 | import { ServerBlocklistModel } from './server-blocklist' |
6 | import * as Bluebird from 'bluebird' | ||
7 | import { MServer, MServerFormattable } from '@server/typings/models/server' | ||
7 | 8 | ||
8 | @Table({ | 9 | @Table({ |
9 | tableName: 'server', | 10 | tableName: 'server', |
@@ -50,7 +51,17 @@ export class ServerModel extends Model<ServerModel> { | |||
50 | }) | 51 | }) |
51 | BlockedByAccounts: ServerBlocklistModel[] | 52 | BlockedByAccounts: ServerBlocklistModel[] |
52 | 53 | ||
53 | static loadByHost (host: string) { | 54 | static load (id: number): Bluebird<MServer> { |
55 | const query = { | ||
56 | where: { | ||
57 | id | ||
58 | } | ||
59 | } | ||
60 | |||
61 | return ServerModel.findOne(query) | ||
62 | } | ||
63 | |||
64 | static loadByHost (host: string): Bluebird<MServer> { | ||
54 | const query = { | 65 | const query = { |
55 | where: { | 66 | where: { |
56 | host | 67 | host |
@@ -64,7 +75,7 @@ export class ServerModel extends Model<ServerModel> { | |||
64 | return this.BlockedByAccounts && this.BlockedByAccounts.length !== 0 | 75 | return this.BlockedByAccounts && this.BlockedByAccounts.length !== 0 |
65 | } | 76 | } |
66 | 77 | ||
67 | toFormattedJSON () { | 78 | toFormattedJSON (this: MServerFormattable) { |
68 | return { | 79 | return { |
69 | host: this.host | 80 | host: this.host |
70 | } | 81 | } |
diff --git a/server/models/video/schedule-video-update.ts b/server/models/video/schedule-video-update.ts index 603d55692..fc2a424aa 100644 --- a/server/models/video/schedule-video-update.ts +++ b/server/models/video/schedule-video-update.ts | |||
@@ -2,6 +2,7 @@ import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Model, Ta | |||
2 | import { ScopeNames as VideoScopeNames, VideoModel } from './video' | 2 | import { ScopeNames as VideoScopeNames, VideoModel } from './video' |
3 | import { VideoPrivacy } from '../../../shared/models/videos' | 3 | import { VideoPrivacy } from '../../../shared/models/videos' |
4 | import { Op, Transaction } from 'sequelize' | 4 | import { Op, Transaction } from 'sequelize' |
5 | import { MScheduleVideoUpdateFormattable } from '@server/typings/models' | ||
5 | 6 | ||
6 | @Table({ | 7 | @Table({ |
7 | tableName: 'scheduleVideoUpdate', | 8 | tableName: 'scheduleVideoUpdate', |
@@ -96,7 +97,7 @@ export class ScheduleVideoUpdateModel extends Model<ScheduleVideoUpdateModel> { | |||
96 | return ScheduleVideoUpdateModel.destroy(query) | 97 | return ScheduleVideoUpdateModel.destroy(query) |
97 | } | 98 | } |
98 | 99 | ||
99 | toFormattedJSON () { | 100 | toFormattedJSON (this: MScheduleVideoUpdateFormattable) { |
100 | return { | 101 | return { |
101 | updateAt: this.updateAt, | 102 | updateAt: this.updateAt, |
102 | privacy: this.privacy || undefined | 103 | privacy: this.privacy || undefined |
diff --git a/server/models/video/tag.ts b/server/models/video/tag.ts index 0fc3cfd4c..ed8df8b48 100644 --- a/server/models/video/tag.ts +++ b/server/models/video/tag.ts | |||
@@ -1,11 +1,12 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import { QueryTypes, Transaction } from 'sequelize' | 2 | import { fn, QueryTypes, Transaction, col } from 'sequelize' |
3 | import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 3 | import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
4 | import { isVideoTagValid } from '../../helpers/custom-validators/videos' | 4 | import { isVideoTagValid } from '../../helpers/custom-validators/videos' |
5 | import { throwIfNotValid } from '../utils' | 5 | import { throwIfNotValid } from '../utils' |
6 | import { VideoModel } from './video' | 6 | import { VideoModel } from './video' |
7 | import { VideoTagModel } from './video-tag' | 7 | import { VideoTagModel } from './video-tag' |
8 | import { VideoPrivacy, VideoState } from '../../../shared/models/videos' | 8 | import { VideoPrivacy, VideoState } from '../../../shared/models/videos' |
9 | import { MTag } from '@server/typings/models' | ||
9 | 10 | ||
10 | @Table({ | 11 | @Table({ |
11 | tableName: 'tag', | 12 | tableName: 'tag', |
@@ -14,6 +15,10 @@ import { VideoPrivacy, VideoState } from '../../../shared/models/videos' | |||
14 | { | 15 | { |
15 | fields: [ 'name' ], | 16 | fields: [ 'name' ], |
16 | unique: true | 17 | unique: true |
18 | }, | ||
19 | { | ||
20 | name: 'tag_lower_name', | ||
21 | fields: [ fn('lower', col('name')) ] as any // FIXME: typings | ||
17 | } | 22 | } |
18 | ] | 23 | ] |
19 | }) | 24 | }) |
@@ -37,10 +42,10 @@ export class TagModel extends Model<TagModel> { | |||
37 | }) | 42 | }) |
38 | Videos: VideoModel[] | 43 | Videos: VideoModel[] |
39 | 44 | ||
40 | static findOrCreateTags (tags: string[], transaction: Transaction) { | 45 | static findOrCreateTags (tags: string[], transaction: Transaction): Promise<MTag[]> { |
41 | if (tags === null) return [] | 46 | if (tags === null) return Promise.resolve([]) |
42 | 47 | ||
43 | const tasks: Bluebird<TagModel>[] = [] | 48 | const tasks: Bluebird<MTag>[] = [] |
44 | tags.forEach(tag => { | 49 | tags.forEach(tag => { |
45 | const query = { | 50 | const query = { |
46 | where: { | 51 | where: { |
@@ -52,7 +57,7 @@ export class TagModel extends Model<TagModel> { | |||
52 | transaction | 57 | transaction |
53 | } | 58 | } |
54 | 59 | ||
55 | const promise = TagModel.findOrCreate(query) | 60 | const promise = TagModel.findOrCreate<MTag>(query) |
56 | .then(([ tagInstance ]) => tagInstance) | 61 | .then(([ tagInstance ]) => tagInstance) |
57 | tasks.push(promise) | 62 | tasks.push(promise) |
58 | }) | 63 | }) |
diff --git a/server/models/video/video-abuse.ts b/server/models/video/video-abuse.ts index 1ac7919b3..3636db18d 100644 --- a/server/models/video/video-abuse.ts +++ b/server/models/video/video-abuse.ts | |||
@@ -7,10 +7,13 @@ import { | |||
7 | isVideoAbuseStateValid | 7 | isVideoAbuseStateValid |
8 | } from '../../helpers/custom-validators/video-abuses' | 8 | } from '../../helpers/custom-validators/video-abuses' |
9 | import { AccountModel } from '../account/account' | 9 | import { AccountModel } from '../account/account' |
10 | import { getSort, throwIfNotValid } from '../utils' | 10 | import { buildBlockedAccountSQL, getSort, throwIfNotValid } from '../utils' |
11 | import { VideoModel } from './video' | 11 | import { VideoModel } from './video' |
12 | import { VideoAbuseState } from '../../../shared' | 12 | import { VideoAbuseState } from '../../../shared' |
13 | import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants' | 13 | import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants' |
14 | import { MUserAccountId, MVideoAbuse, MVideoAbuseFormattable, MVideoAbuseVideo } from '../../typings/models' | ||
15 | import * as Bluebird from 'bluebird' | ||
16 | import { literal, Op } from 'sequelize' | ||
14 | 17 | ||
15 | @Table({ | 18 | @Table({ |
16 | tableName: 'videoAbuse', | 19 | tableName: 'videoAbuse', |
@@ -73,7 +76,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> { | |||
73 | }) | 76 | }) |
74 | Video: VideoModel | 77 | Video: VideoModel |
75 | 78 | ||
76 | static loadByIdAndVideoId (id: number, videoId: number) { | 79 | static loadByIdAndVideoId (id: number, videoId: number): Bluebird<MVideoAbuse> { |
77 | const query = { | 80 | const query = { |
78 | where: { | 81 | where: { |
79 | id, | 82 | id, |
@@ -83,11 +86,25 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> { | |||
83 | return VideoAbuseModel.findOne(query) | 86 | return VideoAbuseModel.findOne(query) |
84 | } | 87 | } |
85 | 88 | ||
86 | static listForApi (start: number, count: number, sort: string) { | 89 | static listForApi (parameters: { |
90 | start: number, | ||
91 | count: number, | ||
92 | sort: string, | ||
93 | serverAccountId: number | ||
94 | user?: MUserAccountId | ||
95 | }) { | ||
96 | const { start, count, sort, user, serverAccountId } = parameters | ||
97 | const userAccountId = user ? user.Account.id : undefined | ||
98 | |||
87 | const query = { | 99 | const query = { |
88 | offset: start, | 100 | offset: start, |
89 | limit: count, | 101 | limit: count, |
90 | order: getSort(sort), | 102 | order: getSort(sort), |
103 | where: { | ||
104 | reporterAccountId: { | ||
105 | [Op.notIn]: literal('(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')') | ||
106 | } | ||
107 | }, | ||
91 | include: [ | 108 | include: [ |
92 | { | 109 | { |
93 | model: AccountModel, | 110 | model: AccountModel, |
@@ -106,7 +123,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> { | |||
106 | }) | 123 | }) |
107 | } | 124 | } |
108 | 125 | ||
109 | toFormattedJSON (): VideoAbuse { | 126 | toFormattedJSON (this: MVideoAbuseFormattable): VideoAbuse { |
110 | return { | 127 | return { |
111 | id: this.id, | 128 | id: this.id, |
112 | reason: this.reason, | 129 | reason: this.reason, |
@@ -125,7 +142,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> { | |||
125 | } | 142 | } |
126 | } | 143 | } |
127 | 144 | ||
128 | toActivityPubObject (): VideoAbuseObject { | 145 | toActivityPubObject (this: MVideoAbuseVideo): VideoAbuseObject { |
129 | return { | 146 | return { |
130 | type: 'Flag' as 'Flag', | 147 | type: 'Flag' as 'Flag', |
131 | content: this.reason, | 148 | content: this.reason, |
diff --git a/server/models/video/video-blacklist.ts b/server/models/video/video-blacklist.ts index cdb725e7a..533bfe1ad 100644 --- a/server/models/video/video-blacklist.ts +++ b/server/models/video/video-blacklist.ts | |||
@@ -1,12 +1,14 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { getBlacklistSort, getSort, SortType, throwIfNotValid } from '../utils' | 2 | import { getBlacklistSort, SortType, throwIfNotValid } from '../utils' |
3 | import { ScopeNames as VideoModelScopeNames, VideoModel } from './video' | 3 | import { VideoModel } from './video' |
4 | import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from './video-channel' | 4 | import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from './video-channel' |
5 | import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist' | 5 | import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist' |
6 | import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos' | 6 | import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos' |
7 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' | 7 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' |
8 | import { FindOptions, literal } from 'sequelize' | 8 | import { FindOptions, literal } from 'sequelize' |
9 | import { ThumbnailModel } from './thumbnail' | 9 | import { ThumbnailModel } from './thumbnail' |
10 | import * as Bluebird from 'bluebird' | ||
11 | import { MVideoBlacklist, MVideoBlacklistFormattable } from '@server/typings/models' | ||
10 | 12 | ||
11 | @Table({ | 13 | @Table({ |
12 | tableName: 'videoBlacklist', | 14 | tableName: 'videoBlacklist', |
@@ -98,7 +100,7 @@ export class VideoBlacklistModel extends Model<VideoBlacklistModel> { | |||
98 | }) | 100 | }) |
99 | } | 101 | } |
100 | 102 | ||
101 | static loadByVideoId (id: number) { | 103 | static loadByVideoId (id: number): Bluebird<MVideoBlacklist> { |
102 | const query = { | 104 | const query = { |
103 | where: { | 105 | where: { |
104 | videoId: id | 106 | videoId: id |
@@ -108,7 +110,7 @@ export class VideoBlacklistModel extends Model<VideoBlacklistModel> { | |||
108 | return VideoBlacklistModel.findOne(query) | 110 | return VideoBlacklistModel.findOne(query) |
109 | } | 111 | } |
110 | 112 | ||
111 | toFormattedJSON (): VideoBlacklist { | 113 | toFormattedJSON (this: MVideoBlacklistFormattable): VideoBlacklist { |
112 | return { | 114 | return { |
113 | id: this.id, | 115 | id: this.id, |
114 | createdAt: this.createdAt, | 116 | createdAt: this.createdAt, |
diff --git a/server/models/video/video-caption.ts b/server/models/video/video-caption.ts index a01565851..ad5801768 100644 --- a/server/models/video/video-caption.ts +++ b/server/models/video/video-caption.ts | |||
@@ -21,6 +21,8 @@ import { join } from 'path' | |||
21 | import { logger } from '../../helpers/logger' | 21 | import { logger } from '../../helpers/logger' |
22 | import { remove } from 'fs-extra' | 22 | import { remove } from 'fs-extra' |
23 | import { CONFIG } from '../../initializers/config' | 23 | import { CONFIG } from '../../initializers/config' |
24 | import * as Bluebird from 'bluebird' | ||
25 | import { MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/typings/models' | ||
24 | 26 | ||
25 | export enum ScopeNames { | 27 | export enum ScopeNames { |
26 | WITH_VIDEO_UUID_AND_REMOTE = 'WITH_VIDEO_UUID_AND_REMOTE' | 28 | WITH_VIDEO_UUID_AND_REMOTE = 'WITH_VIDEO_UUID_AND_REMOTE' |
@@ -30,7 +32,7 @@ export enum ScopeNames { | |||
30 | [ScopeNames.WITH_VIDEO_UUID_AND_REMOTE]: { | 32 | [ScopeNames.WITH_VIDEO_UUID_AND_REMOTE]: { |
31 | include: [ | 33 | include: [ |
32 | { | 34 | { |
33 | attributes: [ 'uuid', 'remote' ], | 35 | attributes: [ 'id', 'uuid', 'remote' ], |
34 | model: VideoModel.unscoped(), | 36 | model: VideoModel.unscoped(), |
35 | required: true | 37 | required: true |
36 | } | 38 | } |
@@ -93,7 +95,7 @@ export class VideoCaptionModel extends Model<VideoCaptionModel> { | |||
93 | return undefined | 95 | return undefined |
94 | } | 96 | } |
95 | 97 | ||
96 | static loadByVideoIdAndLanguage (videoId: string | number, language: string) { | 98 | static loadByVideoIdAndLanguage (videoId: string | number, language: string): Bluebird<MVideoCaptionVideo> { |
97 | const videoInclude = { | 99 | const videoInclude = { |
98 | model: VideoModel.unscoped(), | 100 | model: VideoModel.unscoped(), |
99 | attributes: [ 'id', 'remote', 'uuid' ], | 101 | attributes: [ 'id', 'remote', 'uuid' ], |
@@ -122,7 +124,7 @@ export class VideoCaptionModel extends Model<VideoCaptionModel> { | |||
122 | .then(([ caption ]) => caption) | 124 | .then(([ caption ]) => caption) |
123 | } | 125 | } |
124 | 126 | ||
125 | static listVideoCaptions (videoId: number) { | 127 | static listVideoCaptions (videoId: number): Bluebird<MVideoCaptionVideo[]> { |
126 | const query = { | 128 | const query = { |
127 | order: [ [ 'language', 'ASC' ] ] as OrderItem[], | 129 | order: [ [ 'language', 'ASC' ] ] as OrderItem[], |
128 | where: { | 130 | where: { |
@@ -152,7 +154,7 @@ export class VideoCaptionModel extends Model<VideoCaptionModel> { | |||
152 | return this.Video.remote === false | 154 | return this.Video.remote === false |
153 | } | 155 | } |
154 | 156 | ||
155 | toFormattedJSON (): VideoCaption { | 157 | toFormattedJSON (this: MVideoCaptionFormattable): VideoCaption { |
156 | return { | 158 | return { |
157 | language: { | 159 | language: { |
158 | id: this.language, | 160 | id: this.language, |
@@ -162,15 +164,15 @@ export class VideoCaptionModel extends Model<VideoCaptionModel> { | |||
162 | } | 164 | } |
163 | } | 165 | } |
164 | 166 | ||
165 | getCaptionStaticPath () { | 167 | getCaptionStaticPath (this: MVideoCaptionFormattable) { |
166 | return join(LAZY_STATIC_PATHS.VIDEO_CAPTIONS, this.getCaptionName()) | 168 | return join(LAZY_STATIC_PATHS.VIDEO_CAPTIONS, this.getCaptionName()) |
167 | } | 169 | } |
168 | 170 | ||
169 | getCaptionName () { | 171 | getCaptionName (this: MVideoCaptionFormattable) { |
170 | return `${this.Video.uuid}-${this.language}.vtt` | 172 | return `${this.Video.uuid}-${this.language}.vtt` |
171 | } | 173 | } |
172 | 174 | ||
173 | removeCaptionFile () { | 175 | removeCaptionFile (this: MVideoCaptionFormattable) { |
174 | return remove(CONFIG.STORAGE.CAPTIONS_DIR + this.getCaptionName()) | 176 | return remove(CONFIG.STORAGE.CAPTIONS_DIR + this.getCaptionName()) |
175 | } | 177 | } |
176 | } | 178 | } |
diff --git a/server/models/video/video-change-ownership.ts b/server/models/video/video-change-ownership.ts index b545a2f8c..f7a351329 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' | |||
3 | import { ScopeNames as VideoScopeNames, VideoModel } from './video' | 3 | import { ScopeNames as VideoScopeNames, VideoModel } from './video' |
4 | import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '../../../shared/models/videos' | 4 | import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '../../../shared/models/videos' |
5 | import { getSort } from '../utils' | 5 | import { getSort } from '../utils' |
6 | import { MVideoChangeOwnershipFormattable, MVideoChangeOwnershipFull } from '@server/typings/models/video/video-change-ownership' | ||
7 | import * as Bluebird from 'bluebird' | ||
6 | 8 | ||
7 | enum ScopeNames { | 9 | enum ScopeNames { |
8 | WITH_ACCOUNTS = 'WITH_ACCOUNTS', | 10 | WITH_ACCOUNTS = 'WITH_ACCOUNTS', |
@@ -108,16 +110,16 @@ export class VideoChangeOwnershipModel extends Model<VideoChangeOwnershipModel> | |||
108 | 110 | ||
109 | return Promise.all([ | 111 | return Promise.all([ |
110 | VideoChangeOwnershipModel.scope(ScopeNames.WITH_ACCOUNTS).count(query), | 112 | VideoChangeOwnershipModel.scope(ScopeNames.WITH_ACCOUNTS).count(query), |
111 | VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ]).findAll(query) | 113 | VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ]).findAll<MVideoChangeOwnershipFull>(query) |
112 | ]).then(([ count, rows ]) => ({ total: count, data: rows })) | 114 | ]).then(([ count, rows ]) => ({ total: count, data: rows })) |
113 | } | 115 | } |
114 | 116 | ||
115 | static load (id: number) { | 117 | static load (id: number): Bluebird<MVideoChangeOwnershipFull> { |
116 | return VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ]) | 118 | return VideoChangeOwnershipModel.scope([ ScopeNames.WITH_ACCOUNTS, ScopeNames.WITH_VIDEO ]) |
117 | .findByPk(id) | 119 | .findByPk(id) |
118 | } | 120 | } |
119 | 121 | ||
120 | toFormattedJSON (): VideoChangeOwnership { | 122 | toFormattedJSON (this: MVideoChangeOwnershipFormattable): VideoChangeOwnership { |
121 | return { | 123 | return { |
122 | id: this.id, | 124 | id: this.id, |
123 | status: this.status, | 125 | status: this.status, |
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index 6241a75a3..05545bd9d 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts | |||
@@ -33,6 +33,15 @@ import { ServerModel } from '../server/server' | |||
33 | import { FindOptions, ModelIndexesOptions, Op } from 'sequelize' | 33 | import { FindOptions, ModelIndexesOptions, Op } from 'sequelize' |
34 | import { AvatarModel } from '../avatar/avatar' | 34 | import { AvatarModel } from '../avatar/avatar' |
35 | import { VideoPlaylistModel } from './video-playlist' | 35 | import { VideoPlaylistModel } from './video-playlist' |
36 | import * as Bluebird from 'bluebird' | ||
37 | import { | ||
38 | MChannelAccountDefault, | ||
39 | MChannelActor, | ||
40 | MChannelActorAccountDefaultVideos, | ||
41 | MChannelAP, | ||
42 | MChannelFormattable, | ||
43 | MChannelSummaryFormattable | ||
44 | } from '../../typings/models/video' | ||
36 | 45 | ||
37 | // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation | 46 | // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation |
38 | const indexes: ModelIndexesOptions[] = [ | 47 | const indexes: ModelIndexesOptions[] = [ |
@@ -47,7 +56,7 @@ const indexes: ModelIndexesOptions[] = [ | |||
47 | ] | 56 | ] |
48 | 57 | ||
49 | export enum ScopeNames { | 58 | export enum ScopeNames { |
50 | AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', | 59 | FOR_API = 'FOR_API', |
51 | WITH_ACCOUNT = 'WITH_ACCOUNT', | 60 | WITH_ACCOUNT = 'WITH_ACCOUNT', |
52 | WITH_ACTOR = 'WITH_ACTOR', | 61 | WITH_ACTOR = 'WITH_ACTOR', |
53 | WITH_VIDEOS = 'WITH_VIDEOS', | 62 | WITH_VIDEOS = 'WITH_VIDEOS', |
@@ -74,10 +83,10 @@ export type SummaryOptions = { | |||
74 | @Scopes(() => ({ | 83 | @Scopes(() => ({ |
75 | [ScopeNames.SUMMARY]: (options: SummaryOptions = {}) => { | 84 | [ScopeNames.SUMMARY]: (options: SummaryOptions = {}) => { |
76 | const base: FindOptions = { | 85 | const base: FindOptions = { |
77 | attributes: [ 'name', 'description', 'id', 'actorId' ], | 86 | attributes: [ 'id', 'name', 'description', 'actorId' ], |
78 | include: [ | 87 | include: [ |
79 | { | 88 | { |
80 | attributes: [ 'preferredUsername', 'url', 'serverId', 'avatarId' ], | 89 | attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ], |
81 | model: ActorModel.unscoped(), | 90 | model: ActorModel.unscoped(), |
82 | required: true, | 91 | required: true, |
83 | include: [ | 92 | include: [ |
@@ -106,7 +115,7 @@ export type SummaryOptions = { | |||
106 | 115 | ||
107 | return base | 116 | return base |
108 | }, | 117 | }, |
109 | [ScopeNames.AVAILABLE_FOR_LIST]: (options: AvailableForListOptions) => { | 118 | [ScopeNames.FOR_API]: (options: AvailableForListOptions) => { |
110 | // Only list local channels OR channels that are on an instance followed by actorId | 119 | // Only list local channels OR channels that are on an instance followed by actorId |
111 | const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId) | 120 | const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId) |
112 | 121 | ||
@@ -268,7 +277,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> { | |||
268 | } | 277 | } |
269 | 278 | ||
270 | const scopes = { | 279 | const scopes = { |
271 | method: [ ScopeNames.AVAILABLE_FOR_LIST, { actorId } as AvailableForListOptions ] | 280 | method: [ ScopeNames.FOR_API, { actorId } as AvailableForListOptions ] |
272 | } | 281 | } |
273 | return VideoChannelModel | 282 | return VideoChannelModel |
274 | .scope(scopes) | 283 | .scope(scopes) |
@@ -278,7 +287,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> { | |||
278 | }) | 287 | }) |
279 | } | 288 | } |
280 | 289 | ||
281 | static listLocalsForSitemap (sort: string) { | 290 | static listLocalsForSitemap (sort: string): Bluebird<MChannelActor[]> { |
282 | const query = { | 291 | const query = { |
283 | attributes: [ ], | 292 | attributes: [ ], |
284 | offset: 0, | 293 | offset: 0, |
@@ -331,7 +340,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> { | |||
331 | } | 340 | } |
332 | 341 | ||
333 | const scopes = { | 342 | const scopes = { |
334 | method: [ ScopeNames.AVAILABLE_FOR_LIST, { actorId: options.actorId } as AvailableForListOptions ] | 343 | method: [ ScopeNames.FOR_API, { actorId: options.actorId } as AvailableForListOptions ] |
335 | } | 344 | } |
336 | return VideoChannelModel | 345 | return VideoChannelModel |
337 | .scope(scopes) | 346 | .scope(scopes) |
@@ -369,13 +378,13 @@ export class VideoChannelModel extends Model<VideoChannelModel> { | |||
369 | }) | 378 | }) |
370 | } | 379 | } |
371 | 380 | ||
372 | static loadByIdAndPopulateAccount (id: number) { | 381 | static loadByIdAndPopulateAccount (id: number): Bluebird<MChannelAccountDefault> { |
373 | return VideoChannelModel.unscoped() | 382 | return VideoChannelModel.unscoped() |
374 | .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) | 383 | .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) |
375 | .findByPk(id) | 384 | .findByPk(id) |
376 | } | 385 | } |
377 | 386 | ||
378 | static loadByIdAndAccount (id: number, accountId: number) { | 387 | static loadByIdAndAccount (id: number, accountId: number): Bluebird<MChannelAccountDefault> { |
379 | const query = { | 388 | const query = { |
380 | where: { | 389 | where: { |
381 | id, | 390 | id, |
@@ -388,13 +397,13 @@ export class VideoChannelModel extends Model<VideoChannelModel> { | |||
388 | .findOne(query) | 397 | .findOne(query) |
389 | } | 398 | } |
390 | 399 | ||
391 | static loadAndPopulateAccount (id: number) { | 400 | static loadAndPopulateAccount (id: number): Bluebird<MChannelAccountDefault> { |
392 | return VideoChannelModel.unscoped() | 401 | return VideoChannelModel.unscoped() |
393 | .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) | 402 | .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) |
394 | .findByPk(id) | 403 | .findByPk(id) |
395 | } | 404 | } |
396 | 405 | ||
397 | static loadByUrlAndPopulateAccount (url: string) { | 406 | static loadByUrlAndPopulateAccount (url: string): Bluebird<MChannelAccountDefault> { |
398 | const query = { | 407 | const query = { |
399 | include: [ | 408 | include: [ |
400 | { | 409 | { |
@@ -420,7 +429,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> { | |||
420 | return VideoChannelModel.loadByNameAndHostAndPopulateAccount(name, host) | 429 | return VideoChannelModel.loadByNameAndHostAndPopulateAccount(name, host) |
421 | } | 430 | } |
422 | 431 | ||
423 | static loadLocalByNameAndPopulateAccount (name: string) { | 432 | static loadLocalByNameAndPopulateAccount (name: string): Bluebird<MChannelAccountDefault> { |
424 | const query = { | 433 | const query = { |
425 | include: [ | 434 | include: [ |
426 | { | 435 | { |
@@ -439,7 +448,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> { | |||
439 | .findOne(query) | 448 | .findOne(query) |
440 | } | 449 | } |
441 | 450 | ||
442 | static loadByNameAndHostAndPopulateAccount (name: string, host: string) { | 451 | static loadByNameAndHostAndPopulateAccount (name: string, host: string): Bluebird<MChannelAccountDefault> { |
443 | const query = { | 452 | const query = { |
444 | include: [ | 453 | include: [ |
445 | { | 454 | { |
@@ -464,7 +473,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> { | |||
464 | .findOne(query) | 473 | .findOne(query) |
465 | } | 474 | } |
466 | 475 | ||
467 | static loadAndPopulateAccountAndVideos (id: number) { | 476 | static loadAndPopulateAccountAndVideos (id: number): Bluebird<MChannelActorAccountDefaultVideos> { |
468 | const options = { | 477 | const options = { |
469 | include: [ | 478 | include: [ |
470 | VideoModel | 479 | VideoModel |
@@ -476,7 +485,20 @@ export class VideoChannelModel extends Model<VideoChannelModel> { | |||
476 | .findByPk(id, options) | 485 | .findByPk(id, options) |
477 | } | 486 | } |
478 | 487 | ||
479 | toFormattedJSON (): VideoChannel { | 488 | toFormattedSummaryJSON (this: MChannelSummaryFormattable): VideoChannelSummary { |
489 | const actor = this.Actor.toFormattedSummaryJSON() | ||
490 | |||
491 | return { | ||
492 | id: this.id, | ||
493 | name: actor.name, | ||
494 | displayName: this.getDisplayName(), | ||
495 | url: actor.url, | ||
496 | host: actor.host, | ||
497 | avatar: actor.avatar | ||
498 | } | ||
499 | } | ||
500 | |||
501 | toFormattedJSON (this: MChannelFormattable): VideoChannel { | ||
480 | const actor = this.Actor.toFormattedJSON() | 502 | const actor = this.Actor.toFormattedJSON() |
481 | const videoChannel = { | 503 | const videoChannel = { |
482 | id: this.id, | 504 | id: this.id, |
@@ -494,21 +516,8 @@ export class VideoChannelModel extends Model<VideoChannelModel> { | |||
494 | return Object.assign(actor, videoChannel) | 516 | return Object.assign(actor, videoChannel) |
495 | } | 517 | } |
496 | 518 | ||
497 | toFormattedSummaryJSON (): VideoChannelSummary { | 519 | toActivityPubObject (this: MChannelAP): ActivityPubActor { |
498 | const actor = this.Actor.toFormattedJSON() | 520 | const obj = this.Actor.toActivityPubObject(this.name) |
499 | |||
500 | return { | ||
501 | id: this.id, | ||
502 | name: actor.name, | ||
503 | displayName: this.getDisplayName(), | ||
504 | url: actor.url, | ||
505 | host: actor.host, | ||
506 | avatar: actor.avatar | ||
507 | } | ||
508 | } | ||
509 | |||
510 | toActivityPubObject (): ActivityPubActor { | ||
511 | const obj = this.Actor.toActivityPubObject(this.name, 'VideoChannel') | ||
512 | 521 | ||
513 | return Object.assign(obj, { | 522 | return Object.assign(obj, { |
514 | summary: this.description, | 523 | summary: this.description, |
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts index 58b75510d..2e4220434 100644 --- a/server/models/video/video-comment.ts +++ b/server/models/video/video-comment.ts | |||
@@ -1,36 +1,32 @@ | |||
1 | import { | 1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' |
2 | AllowNull, | ||
3 | BeforeDestroy, | ||
4 | BelongsTo, | ||
5 | Column, | ||
6 | CreatedAt, | ||
7 | DataType, | ||
8 | ForeignKey, | ||
9 | Is, | ||
10 | Model, | ||
11 | Scopes, | ||
12 | Table, | ||
13 | UpdatedAt | ||
14 | } from 'sequelize-typescript' | ||
15 | import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects' | 2 | import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects' |
16 | import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object' | 3 | import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object' |
17 | import { VideoComment } from '../../../shared/models/videos/video-comment.model' | 4 | import { VideoComment } from '../../../shared/models/videos/video-comment.model' |
18 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 5 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
19 | import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants' | 6 | import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants' |
20 | import { sendDeleteVideoComment } from '../../lib/activitypub/send' | ||
21 | import { AccountModel } from '../account/account' | 7 | import { AccountModel } from '../account/account' |
22 | import { ActorModel } from '../activitypub/actor' | 8 | import { ActorModel } from '../activitypub/actor' |
23 | import { AvatarModel } from '../avatar/avatar' | ||
24 | import { ServerModel } from '../server/server' | ||
25 | import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils' | 9 | import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils' |
26 | import { VideoModel } from './video' | 10 | import { VideoModel } from './video' |
27 | import { VideoChannelModel } from './video-channel' | 11 | import { VideoChannelModel } from './video-channel' |
28 | import { getServerActor } from '../../helpers/utils' | 12 | import { getServerActor } from '../../helpers/utils' |
29 | import { UserModel } from '../account/user' | ||
30 | import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor' | 13 | import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor' |
31 | import { regexpCapture } from '../../helpers/regexp' | 14 | import { regexpCapture } from '../../helpers/regexp' |
32 | import { uniq } from 'lodash' | 15 | import { uniq } from 'lodash' |
33 | import { FindOptions, literal, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize' | 16 | import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize' |
17 | import * as Bluebird from 'bluebird' | ||
18 | import { | ||
19 | MComment, | ||
20 | MCommentAP, | ||
21 | MCommentFormattable, | ||
22 | MCommentId, | ||
23 | MCommentOwner, | ||
24 | MCommentOwnerReplyVideoLight, | ||
25 | MCommentOwnerVideo, | ||
26 | MCommentOwnerVideoFeed, | ||
27 | MCommentOwnerVideoReply | ||
28 | } from '../../typings/models/video' | ||
29 | import { MUserAccountId } from '@server/typings/models' | ||
34 | 30 | ||
35 | enum ScopeNames { | 31 | enum ScopeNames { |
36 | WITH_ACCOUNT = 'WITH_ACCOUNT', | 32 | WITH_ACCOUNT = 'WITH_ACCOUNT', |
@@ -68,22 +64,7 @@ enum ScopeNames { | |||
68 | [ScopeNames.WITH_ACCOUNT]: { | 64 | [ScopeNames.WITH_ACCOUNT]: { |
69 | include: [ | 65 | include: [ |
70 | { | 66 | { |
71 | model: AccountModel, | 67 | model: AccountModel |
72 | include: [ | ||
73 | { | ||
74 | model: ActorModel, | ||
75 | include: [ | ||
76 | { | ||
77 | model: ServerModel, | ||
78 | required: false | ||
79 | }, | ||
80 | { | ||
81 | model: AvatarModel, | ||
82 | required: false | ||
83 | } | ||
84 | ] | ||
85 | } | ||
86 | ] | ||
87 | } | 68 | } |
88 | ] | 69 | ] |
89 | }, | 70 | }, |
@@ -102,22 +83,12 @@ enum ScopeNames { | |||
102 | required: true, | 83 | required: true, |
103 | include: [ | 84 | include: [ |
104 | { | 85 | { |
105 | model: VideoChannelModel.unscoped(), | 86 | model: VideoChannelModel, |
106 | required: true, | 87 | required: true, |
107 | include: [ | 88 | include: [ |
108 | { | 89 | { |
109 | model: ActorModel, | ||
110 | required: true | ||
111 | }, | ||
112 | { | ||
113 | model: AccountModel, | 90 | model: AccountModel, |
114 | required: true, | 91 | required: true |
115 | include: [ | ||
116 | { | ||
117 | model: ActorModel, | ||
118 | required: true | ||
119 | } | ||
120 | ] | ||
121 | } | 92 | } |
122 | ] | 93 | ] |
123 | } | 94 | } |
@@ -212,7 +183,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
212 | }) | 183 | }) |
213 | Account: AccountModel | 184 | Account: AccountModel |
214 | 185 | ||
215 | static loadById (id: number, t?: Transaction) { | 186 | static loadById (id: number, t?: Transaction): Bluebird<MComment> { |
216 | const query: FindOptions = { | 187 | const query: FindOptions = { |
217 | where: { | 188 | where: { |
218 | id | 189 | id |
@@ -224,7 +195,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
224 | return VideoCommentModel.findOne(query) | 195 | return VideoCommentModel.findOne(query) |
225 | } | 196 | } |
226 | 197 | ||
227 | static loadByIdAndPopulateVideoAndAccountAndReply (id: number, t?: Transaction) { | 198 | static loadByIdAndPopulateVideoAndAccountAndReply (id: number, t?: Transaction): Bluebird<MCommentOwnerVideoReply> { |
228 | const query: FindOptions = { | 199 | const query: FindOptions = { |
229 | where: { | 200 | where: { |
230 | id | 201 | id |
@@ -238,7 +209,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
238 | .findOne(query) | 209 | .findOne(query) |
239 | } | 210 | } |
240 | 211 | ||
241 | static loadByUrlAndPopulateAccountAndVideo (url: string, t?: Transaction) { | 212 | static loadByUrlAndPopulateAccountAndVideo (url: string, t?: Transaction): Bluebird<MCommentOwnerVideo> { |
242 | const query: FindOptions = { | 213 | const query: FindOptions = { |
243 | where: { | 214 | where: { |
244 | url | 215 | url |
@@ -250,7 +221,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
250 | return VideoCommentModel.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEO ]).findOne(query) | 221 | return VideoCommentModel.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEO ]).findOne(query) |
251 | } | 222 | } |
252 | 223 | ||
253 | static loadByUrlAndPopulateReplyAndVideoUrlAndAccount (url: string, t?: Transaction) { | 224 | static loadByUrlAndPopulateReplyAndVideoUrlAndAccount (url: string, t?: Transaction): Bluebird<MCommentOwnerReplyVideoLight> { |
254 | const query: FindOptions = { | 225 | const query: FindOptions = { |
255 | where: { | 226 | where: { |
256 | url | 227 | url |
@@ -273,7 +244,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
273 | start: number, | 244 | start: number, |
274 | count: number, | 245 | count: number, |
275 | sort: string, | 246 | sort: string, |
276 | user?: UserModel | 247 | user?: MUserAccountId |
277 | }) { | 248 | }) { |
278 | const { videoId, start, count, sort, user } = parameters | 249 | const { videoId, start, count, sort, user } = parameters |
279 | 250 | ||
@@ -314,7 +285,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
314 | static async listThreadCommentsForApi (parameters: { | 285 | static async listThreadCommentsForApi (parameters: { |
315 | videoId: number, | 286 | videoId: number, |
316 | threadId: number, | 287 | threadId: number, |
317 | user?: UserModel | 288 | user?: MUserAccountId |
318 | }) { | 289 | }) { |
319 | const { videoId, threadId, user } = parameters | 290 | const { videoId, threadId, user } = parameters |
320 | 291 | ||
@@ -353,7 +324,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
353 | }) | 324 | }) |
354 | } | 325 | } |
355 | 326 | ||
356 | static listThreadParentComments (comment: VideoCommentModel, t: Transaction, order: 'ASC' | 'DESC' = 'ASC') { | 327 | static listThreadParentComments (comment: MCommentId, t: Transaction, order: 'ASC' | 'DESC' = 'ASC'): Bluebird<MCommentOwner[]> { |
357 | const query = { | 328 | const query = { |
358 | order: [ [ 'createdAt', order ] ] as Order, | 329 | order: [ [ 'createdAt', order ] ] as Order, |
359 | where: { | 330 | where: { |
@@ -389,10 +360,10 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
389 | transaction: t | 360 | transaction: t |
390 | } | 361 | } |
391 | 362 | ||
392 | return VideoCommentModel.findAndCountAll(query) | 363 | return VideoCommentModel.findAndCountAll<MComment>(query) |
393 | } | 364 | } |
394 | 365 | ||
395 | static listForFeed (start: number, count: number, videoId?: number) { | 366 | static listForFeed (start: number, count: number, videoId?: number): Bluebird<MCommentOwnerVideoFeed[]> { |
396 | const query = { | 367 | const query = { |
397 | order: [ [ 'createdAt', 'DESC' ] ] as Order, | 368 | order: [ [ 'createdAt', 'DESC' ] ] as Order, |
398 | offset: start, | 369 | offset: start, |
@@ -506,7 +477,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
506 | return uniq(result) | 477 | return uniq(result) |
507 | } | 478 | } |
508 | 479 | ||
509 | toFormattedJSON () { | 480 | toFormattedJSON (this: MCommentFormattable) { |
510 | return { | 481 | return { |
511 | id: this.id, | 482 | id: this.id, |
512 | url: this.url, | 483 | url: this.url, |
@@ -521,7 +492,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
521 | } as VideoComment | 492 | } as VideoComment |
522 | } | 493 | } |
523 | 494 | ||
524 | toActivityPubObject (threadParentComments: VideoCommentModel[]): VideoCommentObject { | 495 | toActivityPubObject (this: MCommentAP, threadParentComments: MCommentOwner[]): VideoCommentObject { |
525 | let inReplyTo: string | 496 | let inReplyTo: string |
526 | // New thread, so in AS we reply to the video | 497 | // New thread, so in AS we reply to the video |
527 | if (this.inReplyToCommentId === null) { | 498 | 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' | |||
25 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' | 25 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' |
26 | import { FindOptions, QueryTypes, Transaction } from 'sequelize' | 26 | import { FindOptions, QueryTypes, Transaction } from 'sequelize' |
27 | import { MIMETYPES } from '../../initializers/constants' | 27 | import { MIMETYPES } from '../../initializers/constants' |
28 | import { MVideoFile } from '@server/typings/models' | ||
28 | 29 | ||
29 | @Table({ | 30 | @Table({ |
30 | tableName: 'videoFile', | 31 | tableName: 'videoFile', |
@@ -166,7 +167,7 @@ export class VideoFileModel extends Model<VideoFileModel> { | |||
166 | return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname] | 167 | return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname] |
167 | } | 168 | } |
168 | 169 | ||
169 | hasSameUniqueKeysThan (other: VideoFileModel) { | 170 | hasSameUniqueKeysThan (other: MVideoFile) { |
170 | return this.fps === other.fps && | 171 | return this.fps === other.fps && |
171 | this.resolution === other.resolution && | 172 | this.resolution === other.resolution && |
172 | this.videoId === other.videoId | 173 | this.videoId === other.videoId |
diff --git a/server/models/video/video-format-utils.ts b/server/models/video/video-format-utils.ts index 284539def..2987aa780 100644 --- a/server/models/video/video-format-utils.ts +++ b/server/models/video/video-format-utils.ts | |||
@@ -1,6 +1,5 @@ | |||
1 | import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos' | 1 | import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos' |
2 | import { VideoModel } from './video' | 2 | import { VideoModel } from './video' |
3 | import { VideoFileModel } from './video-file' | ||
4 | import { | 3 | import { |
5 | ActivityPlaylistInfohashesObject, | 4 | ActivityPlaylistInfohashesObject, |
6 | ActivityPlaylistSegmentHashesObject, | 5 | ActivityPlaylistSegmentHashesObject, |
@@ -17,7 +16,9 @@ import { | |||
17 | } from '../../lib/activitypub' | 16 | } from '../../lib/activitypub' |
18 | import { isArray } from '../../helpers/custom-validators/misc' | 17 | import { isArray } from '../../helpers/custom-validators/misc' |
19 | import { VideoStreamingPlaylist } from '../../../shared/models/videos/video-streaming-playlist.model' | 18 | import { VideoStreamingPlaylist } from '../../../shared/models/videos/video-streaming-playlist.model' |
20 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' | 19 | import { MStreamingPlaylistRedundanciesOpt, MVideo, MVideoAP, MVideoFormattable, MVideoFormattableDetails } from '../../typings/models' |
20 | import { MStreamingPlaylistRedundancies } from '../../typings/models/video/video-streaming-playlist' | ||
21 | import { MVideoFileRedundanciesOpt } from '../../typings/models/video/video-file' | ||
21 | 22 | ||
22 | export type VideoFormattingJSONOptions = { | 23 | export type VideoFormattingJSONOptions = { |
23 | completeDescription?: boolean | 24 | completeDescription?: boolean |
@@ -28,7 +29,7 @@ export type VideoFormattingJSONOptions = { | |||
28 | blacklistInfo?: boolean | 29 | blacklistInfo?: boolean |
29 | } | 30 | } |
30 | } | 31 | } |
31 | function videoModelToFormattedJSON (video: VideoModel, options?: VideoFormattingJSONOptions): Video { | 32 | function videoModelToFormattedJSON (video: MVideoFormattable, options?: VideoFormattingJSONOptions): Video { |
32 | const userHistory = isArray(video.UserVideoHistories) ? video.UserVideoHistories[0] : undefined | 33 | const userHistory = isArray(video.UserVideoHistories) ? video.UserVideoHistories[0] : undefined |
33 | 34 | ||
34 | const videoObject: Video = { | 35 | const videoObject: Video = { |
@@ -102,7 +103,7 @@ function videoModelToFormattedJSON (video: VideoModel, options?: VideoFormatting | |||
102 | return videoObject | 103 | return videoObject |
103 | } | 104 | } |
104 | 105 | ||
105 | function videoModelToFormattedDetailsJSON (video: VideoModel): VideoDetails { | 106 | function videoModelToFormattedDetailsJSON (video: MVideoFormattableDetails): VideoDetails { |
106 | const formattedJson = video.toFormattedJSON({ | 107 | const formattedJson = video.toFormattedJSON({ |
107 | additionalAttributes: { | 108 | additionalAttributes: { |
108 | scheduledUpdate: true, | 109 | scheduledUpdate: true, |
@@ -114,7 +115,7 @@ function videoModelToFormattedDetailsJSON (video: VideoModel): VideoDetails { | |||
114 | 115 | ||
115 | const tags = video.Tags ? video.Tags.map(t => t.name) : [] | 116 | const tags = video.Tags ? video.Tags.map(t => t.name) : [] |
116 | 117 | ||
117 | const streamingPlaylists = streamingPlaylistsModelToFormattedJSON(video, video.VideoStreamingPlaylists) | 118 | const streamingPlaylists = streamingPlaylistsModelToFormattedJSON(video.VideoStreamingPlaylists) |
118 | 119 | ||
119 | const detailsJson = { | 120 | const detailsJson = { |
120 | support: video.support, | 121 | support: video.support, |
@@ -142,7 +143,7 @@ function videoModelToFormattedDetailsJSON (video: VideoModel): VideoDetails { | |||
142 | return Object.assign(formattedJson, detailsJson) | 143 | return Object.assign(formattedJson, detailsJson) |
143 | } | 144 | } |
144 | 145 | ||
145 | function streamingPlaylistsModelToFormattedJSON (video: VideoModel, playlists: VideoStreamingPlaylistModel[]): VideoStreamingPlaylist[] { | 146 | function streamingPlaylistsModelToFormattedJSON (playlists: MStreamingPlaylistRedundanciesOpt[]): VideoStreamingPlaylist[] { |
146 | if (isArray(playlists) === false) return [] | 147 | if (isArray(playlists) === false) return [] |
147 | 148 | ||
148 | return playlists | 149 | return playlists |
@@ -161,7 +162,7 @@ function streamingPlaylistsModelToFormattedJSON (video: VideoModel, playlists: V | |||
161 | }) | 162 | }) |
162 | } | 163 | } |
163 | 164 | ||
164 | function videoFilesModelToFormattedJSON (video: VideoModel, videoFiles: VideoFileModel[]): VideoFile[] { | 165 | function videoFilesModelToFormattedJSON (video: MVideo, videoFiles: MVideoFileRedundanciesOpt[]): VideoFile[] { |
165 | const { baseUrlHttp, baseUrlWs } = video.getBaseUrls() | 166 | const { baseUrlHttp, baseUrlWs } = video.getBaseUrls() |
166 | 167 | ||
167 | return videoFiles | 168 | return videoFiles |
@@ -189,7 +190,7 @@ function videoFilesModelToFormattedJSON (video: VideoModel, videoFiles: VideoFil | |||
189 | }) | 190 | }) |
190 | } | 191 | } |
191 | 192 | ||
192 | function videoModelToActivityPubObject (video: VideoModel): VideoTorrentObject { | 193 | function videoModelToActivityPubObject (video: MVideoAP): VideoTorrentObject { |
193 | const { baseUrlHttp, baseUrlWs } = video.getBaseUrls() | 194 | const { baseUrlHttp, baseUrlWs } = video.getBaseUrls() |
194 | if (!video.Tags) video.Tags = [] | 195 | if (!video.Tags) video.Tags = [] |
195 | 196 | ||
diff --git a/server/models/video/video-import.ts b/server/models/video/video-import.ts index 480a671c8..af5314ce9 100644 --- a/server/models/video/video-import.ts +++ b/server/models/video/video-import.ts | |||
@@ -20,6 +20,8 @@ import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../help | |||
20 | import { VideoImport, VideoImportState } from '../../../shared' | 20 | import { VideoImport, VideoImportState } from '../../../shared' |
21 | import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos' | 21 | import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos' |
22 | import { UserModel } from '../account/user' | 22 | import { UserModel } from '../account/user' |
23 | import * as Bluebird from 'bluebird' | ||
24 | import { MVideoImportDefault, MVideoImportFormattable } from '@server/typings/models/video/video-import' | ||
23 | 25 | ||
24 | @DefaultScope(() => ({ | 26 | @DefaultScope(() => ({ |
25 | include: [ | 27 | include: [ |
@@ -28,7 +30,11 @@ import { UserModel } from '../account/user' | |||
28 | required: true | 30 | required: true |
29 | }, | 31 | }, |
30 | { | 32 | { |
31 | model: VideoModel.scope([ VideoModelScopeNames.WITH_ACCOUNT_DETAILS, VideoModelScopeNames.WITH_TAGS]), | 33 | model: VideoModel.scope([ |
34 | VideoModelScopeNames.WITH_ACCOUNT_DETAILS, | ||
35 | VideoModelScopeNames.WITH_TAGS, | ||
36 | VideoModelScopeNames.WITH_THUMBNAILS | ||
37 | ]), | ||
32 | required: false | 38 | required: false |
33 | } | 39 | } |
34 | ] | 40 | ] |
@@ -114,7 +120,7 @@ export class VideoImportModel extends Model<VideoImportModel> { | |||
114 | return undefined | 120 | return undefined |
115 | } | 121 | } |
116 | 122 | ||
117 | static loadAndPopulateVideo (id: number) { | 123 | static loadAndPopulateVideo (id: number): Bluebird<MVideoImportDefault> { |
118 | return VideoImportModel.findByPk(id) | 124 | return VideoImportModel.findByPk(id) |
119 | } | 125 | } |
120 | 126 | ||
@@ -135,7 +141,7 @@ export class VideoImportModel extends Model<VideoImportModel> { | |||
135 | } | 141 | } |
136 | } | 142 | } |
137 | 143 | ||
138 | return VideoImportModel.findAndCountAll(query) | 144 | return VideoImportModel.findAndCountAll<MVideoImportDefault>(query) |
139 | .then(({ rows, count }) => { | 145 | .then(({ rows, count }) => { |
140 | return { | 146 | return { |
141 | data: rows, | 147 | data: rows, |
@@ -148,7 +154,7 @@ export class VideoImportModel extends Model<VideoImportModel> { | |||
148 | return this.targetUrl || this.magnetUri || this.torrentName | 154 | return this.targetUrl || this.magnetUri || this.torrentName |
149 | } | 155 | } |
150 | 156 | ||
151 | toFormattedJSON (): VideoImport { | 157 | toFormattedJSON (this: MVideoImportFormattable): VideoImport { |
152 | const videoFormatOptions = { | 158 | const videoFormatOptions = { |
153 | completeDescription: true, | 159 | completeDescription: true, |
154 | additionalAttributes: { state: true, waitTranscoding: true, scheduledUpdate: true } | 160 | additionalAttributes: { state: true, waitTranscoding: true, scheduledUpdate: true } |
diff --git a/server/models/video/video-playlist-element.ts b/server/models/video/video-playlist-element.ts index dd7653533..a28021313 100644 --- a/server/models/video/video-playlist-element.ts +++ b/server/models/video/video-playlist-element.ts | |||
@@ -21,10 +21,18 @@ import { CONSTRAINTS_FIELDS } from '../../initializers/constants' | |||
21 | import { PlaylistElementObject } from '../../../shared/models/activitypub/objects/playlist-element-object' | 21 | import { PlaylistElementObject } from '../../../shared/models/activitypub/objects/playlist-element-object' |
22 | import * as validator from 'validator' | 22 | import * as validator from 'validator' |
23 | import { AggregateOptions, Op, ScopeOptions, Sequelize, Transaction } from 'sequelize' | 23 | import { AggregateOptions, Op, ScopeOptions, Sequelize, Transaction } from 'sequelize' |
24 | import { UserModel } from '../account/user' | ||
25 | import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../shared/models/videos/playlist/video-playlist-element.model' | 24 | import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../shared/models/videos/playlist/video-playlist-element.model' |
26 | import { AccountModel } from '../account/account' | 25 | import { AccountModel } from '../account/account' |
27 | import { VideoPrivacy } from '../../../shared/models/videos' | 26 | import { VideoPrivacy } from '../../../shared/models/videos' |
27 | import * as Bluebird from 'bluebird' | ||
28 | import { | ||
29 | MVideoPlaylistElement, | ||
30 | MVideoPlaylistElementAP, | ||
31 | MVideoPlaylistElementFormattable, | ||
32 | MVideoPlaylistElementVideoUrlPlaylistPrivacy, | ||
33 | MVideoPlaylistVideoThumbnail | ||
34 | } from '@server/typings/models/video/video-playlist-element' | ||
35 | import { MUserAccountId } from '@server/typings/models' | ||
28 | 36 | ||
29 | @Table({ | 37 | @Table({ |
30 | tableName: 'videoPlaylistElement', | 38 | tableName: 'videoPlaylistElement', |
@@ -116,7 +124,7 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel> | |||
116 | count: number, | 124 | count: number, |
117 | videoPlaylistId: number, | 125 | videoPlaylistId: number, |
118 | serverAccount: AccountModel, | 126 | serverAccount: AccountModel, |
119 | user?: UserModel | 127 | user?: MUserAccountId |
120 | }) { | 128 | }) { |
121 | const accountIds = [ options.serverAccount.id ] | 129 | const accountIds = [ options.serverAccount.id ] |
122 | const videoScope: (ScopeOptions | string)[] = [ | 130 | const videoScope: (ScopeOptions | string)[] = [ |
@@ -162,7 +170,7 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel> | |||
162 | ]).then(([ total, data ]) => ({ total, data })) | 170 | ]).then(([ total, data ]) => ({ total, data })) |
163 | } | 171 | } |
164 | 172 | ||
165 | static loadByPlaylistAndVideo (videoPlaylistId: number, videoId: number) { | 173 | static loadByPlaylistAndVideo (videoPlaylistId: number, videoId: number): Bluebird<MVideoPlaylistElement> { |
166 | const query = { | 174 | const query = { |
167 | where: { | 175 | where: { |
168 | videoPlaylistId, | 176 | videoPlaylistId, |
@@ -173,11 +181,14 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel> | |||
173 | return VideoPlaylistElementModel.findOne(query) | 181 | return VideoPlaylistElementModel.findOne(query) |
174 | } | 182 | } |
175 | 183 | ||
176 | static loadById (playlistElementId: number) { | 184 | static loadById (playlistElementId: number): Bluebird<MVideoPlaylistElement> { |
177 | return VideoPlaylistElementModel.findByPk(playlistElementId) | 185 | return VideoPlaylistElementModel.findByPk(playlistElementId) |
178 | } | 186 | } |
179 | 187 | ||
180 | static loadByPlaylistAndVideoForAP (playlistId: number | string, videoId: number | string) { | 188 | static loadByPlaylistAndVideoForAP ( |
189 | playlistId: number | string, | ||
190 | videoId: number | string | ||
191 | ): Bluebird<MVideoPlaylistElementVideoUrlPlaylistPrivacy> { | ||
181 | const playlistWhere = validator.isUUID('' + playlistId) ? { uuid: playlistId } : { id: playlistId } | 192 | const playlistWhere = validator.isUUID('' + playlistId) ? { uuid: playlistId } : { id: playlistId } |
182 | const videoWhere = validator.isUUID('' + videoId) ? { uuid: videoId } : { id: videoId } | 193 | const videoWhere = validator.isUUID('' + videoId) ? { uuid: videoId } : { id: videoId } |
183 | 194 | ||
@@ -218,7 +229,7 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel> | |||
218 | }) | 229 | }) |
219 | } | 230 | } |
220 | 231 | ||
221 | static loadFirstElementWithVideoThumbnail (videoPlaylistId: number) { | 232 | static loadFirstElementWithVideoThumbnail (videoPlaylistId: number): Bluebird<MVideoPlaylistVideoThumbnail> { |
222 | const query = { | 233 | const query = { |
223 | order: getSort('position'), | 234 | order: getSort('position'), |
224 | where: { | 235 | where: { |
@@ -290,7 +301,7 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel> | |||
290 | return VideoPlaylistElementModel.increment({ position: by }, query) | 301 | return VideoPlaylistElementModel.increment({ position: by }, query) |
291 | } | 302 | } |
292 | 303 | ||
293 | getType (displayNSFW?: boolean, accountId?: number) { | 304 | getType (this: MVideoPlaylistElementFormattable, displayNSFW?: boolean, accountId?: number) { |
294 | const video = this.Video | 305 | const video = this.Video |
295 | 306 | ||
296 | if (!video) return VideoPlaylistElementType.DELETED | 307 | if (!video) return VideoPlaylistElementType.DELETED |
@@ -306,14 +317,17 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel> | |||
306 | return VideoPlaylistElementType.REGULAR | 317 | return VideoPlaylistElementType.REGULAR |
307 | } | 318 | } |
308 | 319 | ||
309 | getVideoElement (displayNSFW?: boolean, accountId?: number) { | 320 | getVideoElement (this: MVideoPlaylistElementFormattable, displayNSFW?: boolean, accountId?: number) { |
310 | if (!this.Video) return null | 321 | if (!this.Video) return null |
311 | if (this.getType(displayNSFW, accountId) !== VideoPlaylistElementType.REGULAR) return null | 322 | if (this.getType(displayNSFW, accountId) !== VideoPlaylistElementType.REGULAR) return null |
312 | 323 | ||
313 | return this.Video.toFormattedJSON() | 324 | return this.Video.toFormattedJSON() |
314 | } | 325 | } |
315 | 326 | ||
316 | toFormattedJSON (options: { displayNSFW?: boolean, accountId?: number } = {}): VideoPlaylistElement { | 327 | toFormattedJSON ( |
328 | this: MVideoPlaylistElementFormattable, | ||
329 | options: { displayNSFW?: boolean, accountId?: number } = {} | ||
330 | ): VideoPlaylistElement { | ||
317 | return { | 331 | return { |
318 | id: this.id, | 332 | id: this.id, |
319 | position: this.position, | 333 | position: this.position, |
@@ -326,7 +340,7 @@ export class VideoPlaylistElementModel extends Model<VideoPlaylistElementModel> | |||
326 | } | 340 | } |
327 | } | 341 | } |
328 | 342 | ||
329 | toActivityPubObject (): PlaylistElementObject { | 343 | toActivityPubObject (this: MVideoPlaylistElementAP): PlaylistElementObject { |
330 | const base: PlaylistElementObject = { | 344 | const base: PlaylistElementObject = { |
331 | id: this.url, | 345 | id: this.url, |
332 | type: 'PlaylistElement', | 346 | type: 'PlaylistElement', |
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts index c8e97c491..278d80ac0 100644 --- a/server/models/video/video-playlist.ts +++ b/server/models/video/video-playlist.ts | |||
@@ -43,6 +43,15 @@ import { VideoPlaylistType } from '../../../shared/models/videos/playlist/video- | |||
43 | import { ThumbnailModel } from './thumbnail' | 43 | import { ThumbnailModel } from './thumbnail' |
44 | import { ActivityIconObject } from '../../../shared/models/activitypub/objects' | 44 | import { ActivityIconObject } from '../../../shared/models/activitypub/objects' |
45 | import { FindOptions, literal, Op, ScopeOptions, Transaction, WhereOptions } from 'sequelize' | 45 | import { FindOptions, literal, Op, ScopeOptions, Transaction, WhereOptions } from 'sequelize' |
46 | import * as Bluebird from 'bluebird' | ||
47 | import { | ||
48 | MVideoPlaylistAccountThumbnail, MVideoPlaylistAP, | ||
49 | MVideoPlaylistFormattable, | ||
50 | MVideoPlaylistFull, | ||
51 | MVideoPlaylistFullSummary, | ||
52 | MVideoPlaylistIdWithElements | ||
53 | } from '../../typings/models/video/video-playlist' | ||
54 | import { MThumbnail } from '../../typings/models/video/thumbnail' | ||
46 | 55 | ||
47 | enum ScopeNames { | 56 | enum ScopeNames { |
48 | AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', | 57 | AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', |
@@ -332,7 +341,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
332 | }) | 341 | }) |
333 | } | 342 | } |
334 | 343 | ||
335 | static listPlaylistIdsOf (accountId: number, videoIds: number[]) { | 344 | static listPlaylistIdsOf (accountId: number, videoIds: number[]): Bluebird<MVideoPlaylistIdWithElements[]> { |
336 | const query = { | 345 | const query = { |
337 | attributes: [ 'id' ], | 346 | attributes: [ 'id' ], |
338 | where: { | 347 | where: { |
@@ -368,7 +377,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
368 | .then(e => !!e) | 377 | .then(e => !!e) |
369 | } | 378 | } |
370 | 379 | ||
371 | static loadWithAccountAndChannelSummary (id: number | string, transaction: Transaction) { | 380 | static loadWithAccountAndChannelSummary (id: number | string, transaction: Transaction): Bluebird<MVideoPlaylistFullSummary> { |
372 | const where = buildWhereIdOrUUID(id) | 381 | const where = buildWhereIdOrUUID(id) |
373 | 382 | ||
374 | const query = { | 383 | const query = { |
@@ -381,7 +390,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
381 | .findOne(query) | 390 | .findOne(query) |
382 | } | 391 | } |
383 | 392 | ||
384 | static loadWithAccountAndChannel (id: number | string, transaction: Transaction) { | 393 | static loadWithAccountAndChannel (id: number | string, transaction: Transaction): Bluebird<MVideoPlaylistFull> { |
385 | const where = buildWhereIdOrUUID(id) | 394 | const where = buildWhereIdOrUUID(id) |
386 | 395 | ||
387 | const query = { | 396 | const query = { |
@@ -394,7 +403,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
394 | .findOne(query) | 403 | .findOne(query) |
395 | } | 404 | } |
396 | 405 | ||
397 | static loadByUrlAndPopulateAccount (url: string) { | 406 | static loadByUrlAndPopulateAccount (url: string): Bluebird<MVideoPlaylistAccountThumbnail> { |
398 | const query = { | 407 | const query = { |
399 | where: { | 408 | where: { |
400 | url | 409 | url |
@@ -423,7 +432,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
423 | return VideoPlaylistModel.update({ privacy: VideoPlaylistPrivacy.PRIVATE, videoChannelId: null }, query) | 432 | return VideoPlaylistModel.update({ privacy: VideoPlaylistPrivacy.PRIVATE, videoChannelId: null }, query) |
424 | } | 433 | } |
425 | 434 | ||
426 | async setAndSaveThumbnail (thumbnail: ThumbnailModel, t: Transaction) { | 435 | async setAndSaveThumbnail (thumbnail: MThumbnail, t: Transaction) { |
427 | thumbnail.videoPlaylistId = this.id | 436 | thumbnail.videoPlaylistId = this.id |
428 | 437 | ||
429 | this.Thumbnail = await thumbnail.save({ transaction: t }) | 438 | this.Thumbnail = await thumbnail.save({ transaction: t }) |
@@ -471,7 +480,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
471 | return isOutdated(this, ACTIVITY_PUB.VIDEO_PLAYLIST_REFRESH_INTERVAL) | 480 | return isOutdated(this, ACTIVITY_PUB.VIDEO_PLAYLIST_REFRESH_INTERVAL) |
472 | } | 481 | } |
473 | 482 | ||
474 | toFormattedJSON (): VideoPlaylist { | 483 | toFormattedJSON (this: MVideoPlaylistFormattable): VideoPlaylist { |
475 | return { | 484 | return { |
476 | id: this.id, | 485 | id: this.id, |
477 | uuid: this.uuid, | 486 | uuid: this.uuid, |
@@ -501,7 +510,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
501 | } | 510 | } |
502 | } | 511 | } |
503 | 512 | ||
504 | toActivityPubObject (page: number, t: Transaction): Promise<PlaylistObject> { | 513 | toActivityPubObject (this: MVideoPlaylistAP, page: number, t: Transaction): Promise<PlaylistObject> { |
505 | const handler = (start: number, count: number) => { | 514 | const handler = (start: number, count: number) => { |
506 | return VideoPlaylistElementModel.listUrlsOfForAP(this.id, start, count, t) | 515 | return VideoPlaylistElementModel.listUrlsOfForAP(this.id, start, count, t) |
507 | } | 516 | } |
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' | |||
8 | import { VideoModel } from './video' | 8 | import { VideoModel } from './video' |
9 | import { VideoChannelModel } from './video-channel' | 9 | import { VideoChannelModel } from './video-channel' |
10 | import { Op, Transaction } from 'sequelize' | 10 | import { Op, Transaction } from 'sequelize' |
11 | import { MVideoShareActor, MVideoShareFull } from '../../typings/models/video' | ||
12 | import { MActorDefault } from '../../typings/models' | ||
11 | 13 | ||
12 | enum ScopeNames { | 14 | enum ScopeNames { |
13 | FULL = 'FULL', | 15 | FULL = 'FULL', |
@@ -88,7 +90,7 @@ export class VideoShareModel extends Model<VideoShareModel> { | |||
88 | }) | 90 | }) |
89 | Video: VideoModel | 91 | Video: VideoModel |
90 | 92 | ||
91 | static load (actorId: number, videoId: number, t?: Transaction) { | 93 | static load (actorId: number, videoId: number, t?: Transaction): Bluebird<MVideoShareActor> { |
92 | return VideoShareModel.scope(ScopeNames.WITH_ACTOR).findOne({ | 94 | return VideoShareModel.scope(ScopeNames.WITH_ACTOR).findOne({ |
93 | where: { | 95 | where: { |
94 | actorId, | 96 | actorId, |
@@ -98,7 +100,7 @@ export class VideoShareModel extends Model<VideoShareModel> { | |||
98 | }) | 100 | }) |
99 | } | 101 | } |
100 | 102 | ||
101 | static loadByUrl (url: string, t: Transaction) { | 103 | static loadByUrl (url: string, t: Transaction): Bluebird<MVideoShareFull> { |
102 | return VideoShareModel.scope(ScopeNames.FULL).findOne({ | 104 | return VideoShareModel.scope(ScopeNames.FULL).findOne({ |
103 | where: { | 105 | where: { |
104 | url | 106 | url |
@@ -107,7 +109,7 @@ export class VideoShareModel extends Model<VideoShareModel> { | |||
107 | }) | 109 | }) |
108 | } | 110 | } |
109 | 111 | ||
110 | static loadActorsByShare (videoId: number, t: Transaction) { | 112 | static loadActorsByShare (videoId: number, t: Transaction): Bluebird<MActorDefault[]> { |
111 | const query = { | 113 | const query = { |
112 | where: { | 114 | where: { |
113 | videoId | 115 | videoId |
@@ -122,10 +124,10 @@ export class VideoShareModel extends Model<VideoShareModel> { | |||
122 | } | 124 | } |
123 | 125 | ||
124 | return VideoShareModel.scope(ScopeNames.FULL).findAll(query) | 126 | return VideoShareModel.scope(ScopeNames.FULL).findAll(query) |
125 | .then(res => res.map(r => r.Actor)) | 127 | .then((res: MVideoShareFull[]) => res.map(r => r.Actor)) |
126 | } | 128 | } |
127 | 129 | ||
128 | static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Transaction): Bluebird<ActorModel[]> { | 130 | static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Transaction): Bluebird<MActorDefault[]> { |
129 | const query = { | 131 | const query = { |
130 | attributes: [], | 132 | attributes: [], |
131 | include: [ | 133 | include: [ |
@@ -163,7 +165,7 @@ export class VideoShareModel extends Model<VideoShareModel> { | |||
163 | .then(res => res.map(r => r.Actor)) | 165 | .then(res => res.map(r => r.Actor)) |
164 | } | 166 | } |
165 | 167 | ||
166 | static loadActorsByVideoChannel (videoChannelId: number, t: Transaction): Bluebird<ActorModel[]> { | 168 | static loadActorsByVideoChannel (videoChannelId: number, t: Transaction): Bluebird<MActorDefault[]> { |
167 | const query = { | 169 | const query = { |
168 | attributes: [], | 170 | attributes: [], |
169 | include: [ | 171 | include: [ |
diff --git a/server/models/video/video-streaming-playlist.ts b/server/models/video/video-streaming-playlist.ts index 31dc82c54..0ea90d28c 100644 --- a/server/models/video/video-streaming-playlist.ts +++ b/server/models/video/video-streaming-playlist.ts | |||
@@ -1,16 +1,16 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, HasMany, Is, Model, Table, UpdatedAt, DataType } from 'sequelize-typescript' | 1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' | 2 | import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' |
3 | import { throwIfNotValid } from '../utils' | 3 | import { throwIfNotValid } from '../utils' |
4 | import { VideoModel } from './video' | 4 | import { VideoModel } from './video' |
5 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' | 5 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' |
6 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' | 6 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' |
7 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 7 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
8 | import { CONSTRAINTS_FIELDS, STATIC_PATHS, P2P_MEDIA_LOADER_PEER_VERSION } from '../../initializers/constants' | 8 | import { CONSTRAINTS_FIELDS, P2P_MEDIA_LOADER_PEER_VERSION, STATIC_PATHS } from '../../initializers/constants' |
9 | import { VideoFileModel } from './video-file' | ||
10 | import { join } from 'path' | 9 | import { join } from 'path' |
11 | import { sha1 } from '../../helpers/core-utils' | 10 | import { sha1 } from '../../helpers/core-utils' |
12 | import { isArrayOf } from '../../helpers/custom-validators/misc' | 11 | import { isArrayOf } from '../../helpers/custom-validators/misc' |
13 | import { QueryTypes, Op } from 'sequelize' | 12 | import { Op, QueryTypes } from 'sequelize' |
13 | import { MStreamingPlaylist, MVideoFile } from '@server/typings/models' | ||
14 | 14 | ||
15 | @Table({ | 15 | @Table({ |
16 | tableName: 'videoStreamingPlaylist', | 16 | tableName: 'videoStreamingPlaylist', |
@@ -91,7 +91,7 @@ export class VideoStreamingPlaylistModel extends Model<VideoStreamingPlaylistMod | |||
91 | .then(results => results.length === 1) | 91 | .then(results => results.length === 1) |
92 | } | 92 | } |
93 | 93 | ||
94 | static buildP2PMediaLoaderInfoHashes (playlistUrl: string, videoFiles: VideoFileModel[]) { | 94 | static buildP2PMediaLoaderInfoHashes (playlistUrl: string, videoFiles: MVideoFile[]) { |
95 | const hashes: string[] = [] | 95 | const hashes: string[] = [] |
96 | 96 | ||
97 | // https://github.com/Novage/p2p-media-loader/blob/master/p2p-media-loader-core/lib/p2p-media-manager.ts#L115 | 97 | // https://github.com/Novage/p2p-media-loader/blob/master/p2p-media-loader-core/lib/p2p-media-manager.ts#L115 |
@@ -165,7 +165,7 @@ export class VideoStreamingPlaylistModel extends Model<VideoStreamingPlaylistMod | |||
165 | return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getStringType() + '/' + this.Video.uuid | 165 | return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getStringType() + '/' + this.Video.uuid |
166 | } | 166 | } |
167 | 167 | ||
168 | hasSameUniqueKeysThan (other: VideoStreamingPlaylistModel) { | 168 | hasSameUniqueKeysThan (other: MStreamingPlaylist) { |
169 | return this.type === other.type && | 169 | return this.type === other.type && |
170 | this.videoId === other.videoId | 170 | this.videoId === other.videoId |
171 | } | 171 | } |
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index b59df397d..6856dcd9f 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -36,7 +36,7 @@ import { | |||
36 | Table, | 36 | Table, |
37 | UpdatedAt | 37 | UpdatedAt |
38 | } from 'sequelize-typescript' | 38 | } from 'sequelize-typescript' |
39 | import { UserRight, VideoPrivacy, VideoResolution, VideoState } from '../../../shared' | 39 | import { UserRight, VideoPrivacy, VideoState } from '../../../shared' |
40 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' | 40 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' |
41 | import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos' | 41 | import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos' |
42 | import { VideoFilter } from '../../../shared/models/videos/video-query.type' | 42 | import { VideoFilter } from '../../../shared/models/videos/video-query.type' |
@@ -111,7 +111,6 @@ import { | |||
111 | videoModelToFormattedJSON | 111 | videoModelToFormattedJSON |
112 | } from './video-format-utils' | 112 | } from './video-format-utils' |
113 | import { UserVideoHistoryModel } from '../account/user-video-history' | 113 | import { UserVideoHistoryModel } from '../account/user-video-history' |
114 | import { UserModel } from '../account/user' | ||
115 | import { VideoImportModel } from './video-import' | 114 | import { VideoImportModel } from './video-import' |
116 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' | 115 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' |
117 | import { VideoPlaylistElementModel } from './video-playlist-element' | 116 | import { VideoPlaylistElementModel } from './video-playlist-element' |
@@ -120,6 +119,29 @@ import { ThumbnailModel } from './thumbnail' | |||
120 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' | 119 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' |
121 | import { createTorrentPromise } from '../../helpers/webtorrent' | 120 | import { createTorrentPromise } from '../../helpers/webtorrent' |
122 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' | 121 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' |
122 | import { | ||
123 | MChannel, | ||
124 | MChannelAccountDefault, | ||
125 | MChannelId, | ||
126 | MUserAccountId, | ||
127 | MUserId, | ||
128 | MVideoAccountLight, | ||
129 | MVideoAccountLightBlacklistAllFiles, | ||
130 | MVideoAP, | ||
131 | MVideoDetails, | ||
132 | MVideoFormattable, | ||
133 | MVideoFormattableDetails, | ||
134 | MVideoForUser, | ||
135 | MVideoFullLight, | ||
136 | MVideoIdThumbnail, | ||
137 | MVideoThumbnail, | ||
138 | MVideoThumbnailBlacklist, | ||
139 | MVideoWithAllFiles, | ||
140 | MVideoWithFile, | ||
141 | MVideoWithRights | ||
142 | } from '../../typings/models' | ||
143 | import { MVideoFile, MVideoFileRedundanciesOpt } from '../../typings/models/video/video-file' | ||
144 | import { MThumbnail } from '../../typings/models/video/thumbnail' | ||
123 | 145 | ||
124 | // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation | 146 | // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation |
125 | const indexes: (ModelIndexesOptions & { where?: WhereOptions })[] = [ | 147 | const indexes: (ModelIndexesOptions & { where?: WhereOptions })[] = [ |
@@ -232,8 +254,8 @@ export type AvailableForListIDsOptions = { | |||
232 | videoPlaylistId?: number | 254 | videoPlaylistId?: number |
233 | 255 | ||
234 | trendingDays?: number | 256 | trendingDays?: number |
235 | user?: UserModel, | 257 | user?: MUserAccountId |
236 | historyOfUser?: UserModel | 258 | historyOfUser?: MUserId |
237 | 259 | ||
238 | baseWhere?: WhereOptions[] | 260 | baseWhere?: WhereOptions[] |
239 | } | 261 | } |
@@ -446,13 +468,15 @@ export type AvailableForListIDsOptions = { | |||
446 | // FIXME: issues with sequelize count when making a join on n:m relation, so we just make a IN() | 468 | // FIXME: issues with sequelize count when making a join on n:m relation, so we just make a IN() |
447 | if (options.tagsAllOf || options.tagsOneOf) { | 469 | if (options.tagsAllOf || options.tagsOneOf) { |
448 | if (options.tagsOneOf) { | 470 | if (options.tagsOneOf) { |
471 | const tagsOneOfLower = options.tagsOneOf.map(t => t.toLowerCase()) | ||
472 | |||
449 | whereAnd.push({ | 473 | whereAnd.push({ |
450 | id: { | 474 | id: { |
451 | [ Op.in ]: Sequelize.literal( | 475 | [ Op.in ]: Sequelize.literal( |
452 | '(' + | 476 | '(' + |
453 | 'SELECT "videoId" FROM "videoTag" ' + | 477 | 'SELECT "videoId" FROM "videoTag" ' + |
454 | 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + | 478 | 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + |
455 | 'WHERE "tag"."name" IN (' + createSafeIn(VideoModel, options.tagsOneOf) + ')' + | 479 | 'WHERE lower("tag"."name") IN (' + createSafeIn(VideoModel, tagsOneOfLower) + ')' + |
456 | ')' | 480 | ')' |
457 | ) | 481 | ) |
458 | } | 482 | } |
@@ -460,14 +484,16 @@ export type AvailableForListIDsOptions = { | |||
460 | } | 484 | } |
461 | 485 | ||
462 | if (options.tagsAllOf) { | 486 | if (options.tagsAllOf) { |
487 | const tagsAllOfLower = options.tagsAllOf.map(t => t.toLowerCase()) | ||
488 | |||
463 | whereAnd.push({ | 489 | whereAnd.push({ |
464 | id: { | 490 | id: { |
465 | [ Op.in ]: Sequelize.literal( | 491 | [ Op.in ]: Sequelize.literal( |
466 | '(' + | 492 | '(' + |
467 | 'SELECT "videoId" FROM "videoTag" ' + | 493 | 'SELECT "videoId" FROM "videoTag" ' + |
468 | 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + | 494 | 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + |
469 | 'WHERE "tag"."name" IN (' + createSafeIn(VideoModel, options.tagsAllOf) + ')' + | 495 | 'WHERE lower("tag"."name") IN (' + createSafeIn(VideoModel, tagsAllOfLower) + ')' + |
470 | 'GROUP BY "videoTag"."videoId" HAVING COUNT(*) = ' + options.tagsAllOf.length + | 496 | 'GROUP BY "videoTag"."videoId" HAVING COUNT(*) = ' + tagsAllOfLower.length + |
471 | ')' | 497 | ')' |
472 | ) | 498 | ) |
473 | } | 499 | } |
@@ -634,7 +660,7 @@ export type AvailableForListIDsOptions = { | |||
634 | [ ScopeNames.WITH_BLACKLISTED ]: { | 660 | [ ScopeNames.WITH_BLACKLISTED ]: { |
635 | include: [ | 661 | include: [ |
636 | { | 662 | { |
637 | attributes: [ 'id', 'reason' ], | 663 | attributes: [ 'id', 'reason', 'unfederated' ], |
638 | model: VideoBlacklistModel, | 664 | model: VideoBlacklistModel, |
639 | required: false | 665 | required: false |
640 | } | 666 | } |
@@ -989,18 +1015,16 @@ export class VideoModel extends Model<VideoModel> { | |||
989 | VideoCaptions: VideoCaptionModel[] | 1015 | VideoCaptions: VideoCaptionModel[] |
990 | 1016 | ||
991 | @BeforeDestroy | 1017 | @BeforeDestroy |
992 | static async sendDelete (instance: VideoModel, options) { | 1018 | static async sendDelete (instance: MVideoAccountLight, options) { |
993 | if (instance.isOwned()) { | 1019 | if (instance.isOwned()) { |
994 | if (!instance.VideoChannel) { | 1020 | if (!instance.VideoChannel) { |
995 | instance.VideoChannel = await instance.$get('VideoChannel', { | 1021 | instance.VideoChannel = await instance.$get('VideoChannel', { |
996 | include: [ | 1022 | include: [ |
997 | { | 1023 | ActorModel, |
998 | model: AccountModel, | 1024 | AccountModel |
999 | include: [ ActorModel ] | ||
1000 | } | ||
1001 | ], | 1025 | ], |
1002 | transaction: options.transaction | 1026 | transaction: options.transaction |
1003 | }) as VideoChannelModel | 1027 | }) as MChannelAccountDefault |
1004 | } | 1028 | } |
1005 | 1029 | ||
1006 | return sendDeleteVideo(instance, options.transaction) | 1030 | return sendDeleteVideo(instance, options.transaction) |
@@ -1039,7 +1063,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1039 | return undefined | 1063 | return undefined |
1040 | } | 1064 | } |
1041 | 1065 | ||
1042 | static listLocal () { | 1066 | static listLocal (): Bluebird<MVideoWithAllFiles[]> { |
1043 | const query = { | 1067 | const query = { |
1044 | where: { | 1068 | where: { |
1045 | remote: false | 1069 | remote: false |
@@ -1159,7 +1183,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1159 | }) | 1183 | }) |
1160 | } | 1184 | } |
1161 | 1185 | ||
1162 | static listUserVideosForApi (accountId: number, start: number, count: number, sort: string, withFiles = false) { | 1186 | static listUserVideosForApi (accountId: number, start: number, count: number, sort: string) { |
1163 | function buildBaseQuery (): FindOptions { | 1187 | function buildBaseQuery (): FindOptions { |
1164 | return { | 1188 | return { |
1165 | offset: start, | 1189 | offset: start, |
@@ -1192,16 +1216,9 @@ export class VideoModel extends Model<VideoModel> { | |||
1192 | ScopeNames.WITH_THUMBNAILS | 1216 | ScopeNames.WITH_THUMBNAILS |
1193 | ] | 1217 | ] |
1194 | 1218 | ||
1195 | if (withFiles === true) { | ||
1196 | findQuery.include.push({ | ||
1197 | model: VideoFileModel.unscoped(), | ||
1198 | required: true | ||
1199 | }) | ||
1200 | } | ||
1201 | |||
1202 | return Promise.all([ | 1219 | return Promise.all([ |
1203 | VideoModel.count(countQuery), | 1220 | VideoModel.count(countQuery), |
1204 | VideoModel.scope(findScopes).findAll(findQuery) | 1221 | VideoModel.scope(findScopes).findAll<MVideoForUser>(findQuery) |
1205 | ]).then(([ count, rows ]) => { | 1222 | ]).then(([ count, rows ]) => { |
1206 | return { | 1223 | return { |
1207 | data: rows, | 1224 | data: rows, |
@@ -1228,8 +1245,8 @@ export class VideoModel extends Model<VideoModel> { | |||
1228 | followerActorId?: number | 1245 | followerActorId?: number |
1229 | videoPlaylistId?: number, | 1246 | videoPlaylistId?: number, |
1230 | trendingDays?: number, | 1247 | trendingDays?: number, |
1231 | user?: UserModel, | 1248 | user?: MUserAccountId, |
1232 | historyOfUser?: UserModel | 1249 | historyOfUser?: MUserId |
1233 | }, countVideos = true) { | 1250 | }, countVideos = true) { |
1234 | if (options.filter && options.filter === 'all-local' && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) { | 1251 | if (options.filter && options.filter === 'all-local' && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) { |
1235 | throw new Error('Try to filter all-local but no user has not the see all videos right') | 1252 | throw new Error('Try to filter all-local but no user has not the see all videos right') |
@@ -1294,7 +1311,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1294 | tagsAllOf?: string[] | 1311 | tagsAllOf?: string[] |
1295 | durationMin?: number // seconds | 1312 | durationMin?: number // seconds |
1296 | durationMax?: number // seconds | 1313 | durationMax?: number // seconds |
1297 | user?: UserModel, | 1314 | user?: MUserAccountId, |
1298 | filter?: VideoFilter | 1315 | filter?: VideoFilter |
1299 | }) { | 1316 | }) { |
1300 | const whereAnd = [] | 1317 | const whereAnd = [] |
@@ -1387,7 +1404,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1387 | return VideoModel.getAvailableForApi(query, queryOptions) | 1404 | return VideoModel.getAvailableForApi(query, queryOptions) |
1388 | } | 1405 | } |
1389 | 1406 | ||
1390 | static load (id: number | string, t?: Transaction) { | 1407 | static load (id: number | string, t?: Transaction): Bluebird<MVideoThumbnail> { |
1391 | const where = buildWhereIdOrUUID(id) | 1408 | const where = buildWhereIdOrUUID(id) |
1392 | const options = { | 1409 | const options = { |
1393 | where, | 1410 | where, |
@@ -1397,7 +1414,20 @@ export class VideoModel extends Model<VideoModel> { | |||
1397 | return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) | 1414 | return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) |
1398 | } | 1415 | } |
1399 | 1416 | ||
1400 | static loadWithRights (id: number | string, t?: Transaction) { | 1417 | static loadWithBlacklist (id: number | string, t?: Transaction): Bluebird<MVideoThumbnailBlacklist> { |
1418 | const where = buildWhereIdOrUUID(id) | ||
1419 | const options = { | ||
1420 | where, | ||
1421 | transaction: t | ||
1422 | } | ||
1423 | |||
1424 | return VideoModel.scope([ | ||
1425 | ScopeNames.WITH_THUMBNAILS, | ||
1426 | ScopeNames.WITH_BLACKLISTED | ||
1427 | ]).findOne(options) | ||
1428 | } | ||
1429 | |||
1430 | static loadWithRights (id: number | string, t?: Transaction): Bluebird<MVideoWithRights> { | ||
1401 | const where = buildWhereIdOrUUID(id) | 1431 | const where = buildWhereIdOrUUID(id) |
1402 | const options = { | 1432 | const options = { |
1403 | where, | 1433 | where, |
@@ -1411,7 +1441,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1411 | ]).findOne(options) | 1441 | ]).findOne(options) |
1412 | } | 1442 | } |
1413 | 1443 | ||
1414 | static loadOnlyId (id: number | string, t?: Transaction) { | 1444 | static loadOnlyId (id: number | string, t?: Transaction): Bluebird<MVideoIdThumbnail> { |
1415 | const where = buildWhereIdOrUUID(id) | 1445 | const where = buildWhereIdOrUUID(id) |
1416 | 1446 | ||
1417 | const options = { | 1447 | const options = { |
@@ -1423,7 +1453,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1423 | return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) | 1453 | return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) |
1424 | } | 1454 | } |
1425 | 1455 | ||
1426 | static loadWithFiles (id: number | string, t?: Transaction, logging?: boolean) { | 1456 | static loadWithFiles (id: number | string, t?: Transaction, logging?: boolean): Bluebird<MVideoWithAllFiles> { |
1427 | const where = buildWhereIdOrUUID(id) | 1457 | const where = buildWhereIdOrUUID(id) |
1428 | 1458 | ||
1429 | const query = { | 1459 | const query = { |
@@ -1439,7 +1469,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1439 | ]).findOne(query) | 1469 | ]).findOne(query) |
1440 | } | 1470 | } |
1441 | 1471 | ||
1442 | static loadByUUID (uuid: string) { | 1472 | static loadByUUID (uuid: string): Bluebird<MVideoThumbnail> { |
1443 | const options = { | 1473 | const options = { |
1444 | where: { | 1474 | where: { |
1445 | uuid | 1475 | uuid |
@@ -1449,7 +1479,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1449 | return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) | 1479 | return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) |
1450 | } | 1480 | } |
1451 | 1481 | ||
1452 | static loadByUrl (url: string, transaction?: Transaction) { | 1482 | static loadByUrl (url: string, transaction?: Transaction): Bluebird<MVideoThumbnail> { |
1453 | const query: FindOptions = { | 1483 | const query: FindOptions = { |
1454 | where: { | 1484 | where: { |
1455 | url | 1485 | url |
@@ -1460,7 +1490,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1460 | return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(query) | 1490 | return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(query) |
1461 | } | 1491 | } |
1462 | 1492 | ||
1463 | static loadByUrlAndPopulateAccount (url: string, transaction?: Transaction) { | 1493 | static loadByUrlAndPopulateAccount (url: string, transaction?: Transaction): Bluebird<MVideoAccountLightBlacklistAllFiles> { |
1464 | const query: FindOptions = { | 1494 | const query: FindOptions = { |
1465 | where: { | 1495 | where: { |
1466 | url | 1496 | url |
@@ -1472,11 +1502,12 @@ export class VideoModel extends Model<VideoModel> { | |||
1472 | ScopeNames.WITH_ACCOUNT_DETAILS, | 1502 | ScopeNames.WITH_ACCOUNT_DETAILS, |
1473 | ScopeNames.WITH_FILES, | 1503 | ScopeNames.WITH_FILES, |
1474 | ScopeNames.WITH_STREAMING_PLAYLISTS, | 1504 | ScopeNames.WITH_STREAMING_PLAYLISTS, |
1475 | ScopeNames.WITH_THUMBNAILS | 1505 | ScopeNames.WITH_THUMBNAILS, |
1506 | ScopeNames.WITH_BLACKLISTED | ||
1476 | ]).findOne(query) | 1507 | ]).findOne(query) |
1477 | } | 1508 | } |
1478 | 1509 | ||
1479 | static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Transaction, userId?: number) { | 1510 | static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Transaction, userId?: number): Bluebird<MVideoFullLight> { |
1480 | const where = buildWhereIdOrUUID(id) | 1511 | const where = buildWhereIdOrUUID(id) |
1481 | 1512 | ||
1482 | const options = { | 1513 | const options = { |
@@ -1508,7 +1539,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1508 | id: number | string, | 1539 | id: number | string, |
1509 | t?: Transaction, | 1540 | t?: Transaction, |
1510 | userId?: number | 1541 | userId?: number |
1511 | }) { | 1542 | }): Bluebird<MVideoDetails> { |
1512 | const { id, t, userId } = parameters | 1543 | const { id, t, userId } = parameters |
1513 | const where = buildWhereIdOrUUID(id) | 1544 | const where = buildWhereIdOrUUID(id) |
1514 | 1545 | ||
@@ -1586,7 +1617,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1586 | .then(results => results.length === 1) | 1617 | .then(results => results.length === 1) |
1587 | } | 1618 | } |
1588 | 1619 | ||
1589 | static bulkUpdateSupportField (videoChannel: VideoChannelModel, t: Transaction) { | 1620 | static bulkUpdateSupportField (videoChannel: MChannel, t: Transaction) { |
1590 | const options = { | 1621 | const options = { |
1591 | where: { | 1622 | where: { |
1592 | channelId: videoChannel.id | 1623 | channelId: videoChannel.id |
@@ -1597,7 +1628,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1597 | return VideoModel.update({ support: videoChannel.support }, options) | 1628 | return VideoModel.update({ support: videoChannel.support }, options) |
1598 | } | 1629 | } |
1599 | 1630 | ||
1600 | static getAllIdsFromChannel (videoChannel: VideoChannelModel) { | 1631 | static getAllIdsFromChannel (videoChannel: MChannelId): Bluebird<number[]> { |
1601 | const query = { | 1632 | const query = { |
1602 | attributes: [ 'id' ], | 1633 | attributes: [ 'id' ], |
1603 | where: { | 1634 | where: { |
@@ -1756,20 +1787,20 @@ export class VideoModel extends Model<VideoModel> { | |||
1756 | this.VideoChannel.Account.isBlocked() | 1787 | this.VideoChannel.Account.isBlocked() |
1757 | } | 1788 | } |
1758 | 1789 | ||
1759 | getOriginalFile () { | 1790 | getOriginalFile <T extends MVideoWithFile> (this: T) { |
1760 | if (Array.isArray(this.VideoFiles) === false) return undefined | 1791 | if (Array.isArray(this.VideoFiles) === false) return undefined |
1761 | 1792 | ||
1762 | // The original file is the file that have the higher resolution | 1793 | // The original file is the file that have the higher resolution |
1763 | return maxBy(this.VideoFiles, file => file.resolution) | 1794 | return maxBy(this.VideoFiles, file => file.resolution) |
1764 | } | 1795 | } |
1765 | 1796 | ||
1766 | getFile (resolution: number) { | 1797 | getFile <T extends MVideoWithFile> (this: T, resolution: number) { |
1767 | if (Array.isArray(this.VideoFiles) === false) return undefined | 1798 | if (Array.isArray(this.VideoFiles) === false) return undefined |
1768 | 1799 | ||
1769 | return this.VideoFiles.find(f => f.resolution === resolution) | 1800 | return this.VideoFiles.find(f => f.resolution === resolution) |
1770 | } | 1801 | } |
1771 | 1802 | ||
1772 | async addAndSaveThumbnail (thumbnail: ThumbnailModel, transaction: Transaction) { | 1803 | async addAndSaveThumbnail (thumbnail: MThumbnail, transaction: Transaction) { |
1773 | thumbnail.videoId = this.id | 1804 | thumbnail.videoId = this.id |
1774 | 1805 | ||
1775 | const savedThumbnail = await thumbnail.save({ transaction }) | 1806 | const savedThumbnail = await thumbnail.save({ transaction }) |
@@ -1782,7 +1813,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1782 | this.Thumbnails.push(savedThumbnail) | 1813 | this.Thumbnails.push(savedThumbnail) |
1783 | } | 1814 | } |
1784 | 1815 | ||
1785 | getVideoFilename (videoFile: VideoFileModel) { | 1816 | getVideoFilename (videoFile: MVideoFile) { |
1786 | return this.uuid + '-' + videoFile.resolution + videoFile.extname | 1817 | return this.uuid + '-' + videoFile.resolution + videoFile.extname |
1787 | } | 1818 | } |
1788 | 1819 | ||
@@ -1806,7 +1837,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1806 | return this.Thumbnails.find(t => t.type === ThumbnailType.PREVIEW) | 1837 | return this.Thumbnails.find(t => t.type === ThumbnailType.PREVIEW) |
1807 | } | 1838 | } |
1808 | 1839 | ||
1809 | getTorrentFileName (videoFile: VideoFileModel) { | 1840 | getTorrentFileName (videoFile: MVideoFile) { |
1810 | const extension = '.torrent' | 1841 | const extension = '.torrent' |
1811 | return this.uuid + '-' + videoFile.resolution + extension | 1842 | return this.uuid + '-' + videoFile.resolution + extension |
1812 | } | 1843 | } |
@@ -1815,15 +1846,15 @@ export class VideoModel extends Model<VideoModel> { | |||
1815 | return this.remote === false | 1846 | return this.remote === false |
1816 | } | 1847 | } |
1817 | 1848 | ||
1818 | getTorrentFilePath (videoFile: VideoFileModel) { | 1849 | getTorrentFilePath (videoFile: MVideoFile) { |
1819 | return join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile)) | 1850 | return join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile)) |
1820 | } | 1851 | } |
1821 | 1852 | ||
1822 | getVideoFilePath (videoFile: VideoFileModel) { | 1853 | getVideoFilePath (videoFile: MVideoFile) { |
1823 | return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile)) | 1854 | return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile)) |
1824 | } | 1855 | } |
1825 | 1856 | ||
1826 | async createTorrentAndSetInfoHash (videoFile: VideoFileModel) { | 1857 | async createTorrentAndSetInfoHash (videoFile: MVideoFile) { |
1827 | const options = { | 1858 | const options = { |
1828 | // Keep the extname, it's used by the client to stream the file inside a web browser | 1859 | // Keep the extname, it's used by the client to stream the file inside a web browser |
1829 | name: `${this.name} ${videoFile.resolution}p${videoFile.extname}`, | 1860 | name: `${this.name} ${videoFile.resolution}p${videoFile.extname}`, |
@@ -1869,11 +1900,11 @@ export class VideoModel extends Model<VideoModel> { | |||
1869 | return join(LAZY_STATIC_PATHS.PREVIEWS, preview.filename) | 1900 | return join(LAZY_STATIC_PATHS.PREVIEWS, preview.filename) |
1870 | } | 1901 | } |
1871 | 1902 | ||
1872 | toFormattedJSON (options?: VideoFormattingJSONOptions): Video { | 1903 | toFormattedJSON (this: MVideoFormattable, options?: VideoFormattingJSONOptions): Video { |
1873 | return videoModelToFormattedJSON(this, options) | 1904 | return videoModelToFormattedJSON(this, options) |
1874 | } | 1905 | } |
1875 | 1906 | ||
1876 | toFormattedDetailsJSON (): VideoDetails { | 1907 | toFormattedDetailsJSON (this: MVideoFormattableDetails): VideoDetails { |
1877 | return videoModelToFormattedDetailsJSON(this) | 1908 | return videoModelToFormattedDetailsJSON(this) |
1878 | } | 1909 | } |
1879 | 1910 | ||
@@ -1881,7 +1912,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1881 | return videoFilesModelToFormattedJSON(this, this.VideoFiles) | 1912 | return videoFilesModelToFormattedJSON(this, this.VideoFiles) |
1882 | } | 1913 | } |
1883 | 1914 | ||
1884 | toActivityPubObject (): VideoTorrentObject { | 1915 | toActivityPubObject (this: MVideoAP): VideoTorrentObject { |
1885 | return videoModelToActivityPubObject(this) | 1916 | return videoModelToActivityPubObject(this) |
1886 | } | 1917 | } |
1887 | 1918 | ||
@@ -1908,7 +1939,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1908 | return this.VideoStreamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS) | 1939 | return this.VideoStreamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS) |
1909 | } | 1940 | } |
1910 | 1941 | ||
1911 | removeFile (videoFile: VideoFileModel, isRedundancy = false) { | 1942 | removeFile (videoFile: MVideoFile, isRedundancy = false) { |
1912 | const baseDir = isRedundancy ? CONFIG.STORAGE.REDUNDANCY_DIR : CONFIG.STORAGE.VIDEOS_DIR | 1943 | const baseDir = isRedundancy ? CONFIG.STORAGE.REDUNDANCY_DIR : CONFIG.STORAGE.VIDEOS_DIR |
1913 | 1944 | ||
1914 | const filePath = join(baseDir, this.getVideoFilename(videoFile)) | 1945 | const filePath = join(baseDir, this.getVideoFilename(videoFile)) |
@@ -1916,7 +1947,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1916 | .catch(err => logger.warn('Cannot delete file %s.', filePath, { err })) | 1947 | .catch(err => logger.warn('Cannot delete file %s.', filePath, { err })) |
1917 | } | 1948 | } |
1918 | 1949 | ||
1919 | removeTorrent (videoFile: VideoFileModel) { | 1950 | removeTorrent (videoFile: MVideoFile) { |
1920 | const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile)) | 1951 | const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile)) |
1921 | return remove(torrentPath) | 1952 | return remove(torrentPath) |
1922 | .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err })) | 1953 | .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err })) |
@@ -1957,7 +1988,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1957 | return { baseUrlHttp, baseUrlWs } | 1988 | return { baseUrlHttp, baseUrlWs } |
1958 | } | 1989 | } |
1959 | 1990 | ||
1960 | generateMagnetUri (videoFile: VideoFileModel, baseUrlHttp: string, baseUrlWs: string) { | 1991 | generateMagnetUri (videoFile: MVideoFileRedundanciesOpt, baseUrlHttp: string, baseUrlWs: string) { |
1961 | const xs = this.getTorrentUrl(videoFile, baseUrlHttp) | 1992 | const xs = this.getTorrentUrl(videoFile, baseUrlHttp) |
1962 | const announce = this.getTrackerUrls(baseUrlHttp, baseUrlWs) | 1993 | const announce = this.getTrackerUrls(baseUrlHttp, baseUrlWs) |
1963 | let urlList = [ this.getVideoFileUrl(videoFile, baseUrlHttp) ] | 1994 | let urlList = [ this.getVideoFileUrl(videoFile, baseUrlHttp) ] |
@@ -1980,27 +2011,27 @@ export class VideoModel extends Model<VideoModel> { | |||
1980 | return [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ] | 2011 | return [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ] |
1981 | } | 2012 | } |
1982 | 2013 | ||
1983 | getTorrentUrl (videoFile: VideoFileModel, baseUrlHttp: string) { | 2014 | getTorrentUrl (videoFile: MVideoFile, baseUrlHttp: string) { |
1984 | return baseUrlHttp + STATIC_PATHS.TORRENTS + this.getTorrentFileName(videoFile) | 2015 | return baseUrlHttp + STATIC_PATHS.TORRENTS + this.getTorrentFileName(videoFile) |
1985 | } | 2016 | } |
1986 | 2017 | ||
1987 | getTorrentDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) { | 2018 | getTorrentDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) { |
1988 | return baseUrlHttp + STATIC_DOWNLOAD_PATHS.TORRENTS + this.getTorrentFileName(videoFile) | 2019 | return baseUrlHttp + STATIC_DOWNLOAD_PATHS.TORRENTS + this.getTorrentFileName(videoFile) |
1989 | } | 2020 | } |
1990 | 2021 | ||
1991 | getVideoFileUrl (videoFile: VideoFileModel, baseUrlHttp: string) { | 2022 | getVideoFileUrl (videoFile: MVideoFile, baseUrlHttp: string) { |
1992 | return baseUrlHttp + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile) | 2023 | return baseUrlHttp + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile) |
1993 | } | 2024 | } |
1994 | 2025 | ||
1995 | getVideoRedundancyUrl (videoFile: VideoFileModel, baseUrlHttp: string) { | 2026 | getVideoRedundancyUrl (videoFile: MVideoFile, baseUrlHttp: string) { |
1996 | return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getVideoFilename(videoFile) | 2027 | return baseUrlHttp + STATIC_PATHS.REDUNDANCY + this.getVideoFilename(videoFile) |
1997 | } | 2028 | } |
1998 | 2029 | ||
1999 | getVideoFileDownloadUrl (videoFile: VideoFileModel, baseUrlHttp: string) { | 2030 | getVideoFileDownloadUrl (videoFile: MVideoFile, baseUrlHttp: string) { |
2000 | return baseUrlHttp + STATIC_DOWNLOAD_PATHS.VIDEOS + this.getVideoFilename(videoFile) | 2031 | return baseUrlHttp + STATIC_DOWNLOAD_PATHS.VIDEOS + this.getVideoFilename(videoFile) |
2001 | } | 2032 | } |
2002 | 2033 | ||
2003 | getBandwidthBits (videoFile: VideoFileModel) { | 2034 | getBandwidthBits (videoFile: MVideoFile) { |
2004 | return Math.ceil((videoFile.size * 8) / this.duration) | 2035 | return Math.ceil((videoFile.size * 8) / this.duration) |
2005 | } | 2036 | } |
2006 | } | 2037 | } |
diff --git a/server/tests/api/activitypub/helpers.ts b/server/tests/api/activitypub/helpers.ts index 365d0e1ae..0d1f154fe 100644 --- a/server/tests/api/activitypub/helpers.ts +++ b/server/tests/api/activitypub/helpers.ts | |||
@@ -53,19 +53,6 @@ describe('Test activity pub helpers', function () { | |||
53 | expect(result).to.be.false | 53 | expect(result).to.be.false |
54 | }) | 54 | }) |
55 | 55 | ||
56 | it('Should fail with an invalid PeerTube URL', async function () { | ||
57 | const keys = require('./json/peertube/keys.json') | ||
58 | const body = require('./json/peertube/announce-without-context.json') | ||
59 | |||
60 | const actorSignature = { url: 'http://localhost:9002/accounts/peertube', privateKey: keys.privateKey } | ||
61 | const signedBody = await buildSignedActivity(actorSignature as any, body) | ||
62 | |||
63 | const fromActor = { publicKey: keys.publicKey, url: 'http://localhost:9003/accounts/peertube' } | ||
64 | const result = await isJsonLDSignatureVerified(fromActor as any, signedBody) | ||
65 | |||
66 | expect(result).to.be.false | ||
67 | }) | ||
68 | |||
69 | it('Should succeed with a valid PeerTube signature', async function () { | 56 | it('Should succeed with a valid PeerTube signature', async function () { |
70 | const keys = require('./json/peertube/keys.json') | 57 | const keys = require('./json/peertube/keys.json') |
71 | const body = require('./json/peertube/announce-without-context.json') | 58 | const body = require('./json/peertube/announce-without-context.json') |
diff --git a/server/tests/api/check-params/config.ts b/server/tests/api/check-params/config.ts index 7773ae1e7..9435bb1e8 100644 --- a/server/tests/api/check-params/config.ts +++ b/server/tests/api/check-params/config.ts | |||
@@ -5,8 +5,16 @@ import 'mocha' | |||
5 | import { CustomConfig } from '../../../../shared/models/server/custom-config.model' | 5 | import { CustomConfig } from '../../../../shared/models/server/custom-config.model' |
6 | 6 | ||
7 | import { | 7 | import { |
8 | createUser, flushTests, killallServers, makeDeleteRequest, makeGetRequest, makePutBodyRequest, flushAndRunServer, ServerInfo, | 8 | cleanupTests, |
9 | setAccessTokensToServers, userLogin, immutableAssign, cleanupTests | 9 | createUser, |
10 | flushAndRunServer, | ||
11 | immutableAssign, | ||
12 | makeDeleteRequest, | ||
13 | makeGetRequest, | ||
14 | makePutBodyRequest, | ||
15 | ServerInfo, | ||
16 | setAccessTokensToServers, | ||
17 | userLogin | ||
10 | } from '../../../../shared/extra-utils' | 18 | } from '../../../../shared/extra-utils' |
11 | 19 | ||
12 | describe('Test config API validators', function () { | 20 | describe('Test config API validators', function () { |
@@ -19,6 +27,18 @@ describe('Test config API validators', function () { | |||
19 | shortDescription: 'my short description', | 27 | shortDescription: 'my short description', |
20 | description: 'my super description', | 28 | description: 'my super description', |
21 | terms: 'my super terms', | 29 | terms: 'my super terms', |
30 | codeOfConduct: 'my super coc', | ||
31 | |||
32 | creationReason: 'my super reason', | ||
33 | moderationInformation: 'my super moderation information', | ||
34 | administrator: 'Kuja', | ||
35 | maintenanceLifetime: 'forever', | ||
36 | businessModel: 'my super business model', | ||
37 | hardwareInformation: '2vCore 3GB RAM', | ||
38 | |||
39 | languages: [ 'en', 'es' ], | ||
40 | categories: [ 1, 2 ], | ||
41 | |||
22 | isNSFW: true, | 42 | isNSFW: true, |
23 | defaultClientRoute: '/videos/recently-added', | 43 | defaultClientRoute: '/videos/recently-added', |
24 | defaultNSFWPolicy: 'blur', | 44 | defaultNSFWPolicy: 'blur', |
@@ -98,6 +118,17 @@ describe('Test config API validators', function () { | |||
98 | enabled: false, | 118 | enabled: false, |
99 | manualApproval: true | 119 | manualApproval: true |
100 | } | 120 | } |
121 | }, | ||
122 | followings: { | ||
123 | instance: { | ||
124 | autoFollowBack: { | ||
125 | enabled: true | ||
126 | }, | ||
127 | autoFollowIndex: { | ||
128 | enabled: true, | ||
129 | indexUrl: 'https://index.example.com' | ||
130 | } | ||
131 | } | ||
101 | } | 132 | } |
102 | } | 133 | } |
103 | 134 | ||
diff --git a/server/tests/api/check-params/user-notifications.ts b/server/tests/api/check-params/user-notifications.ts index 14ee20d45..3b06be7ef 100644 --- a/server/tests/api/check-params/user-notifications.ts +++ b/server/tests/api/check-params/user-notifications.ts | |||
@@ -172,7 +172,8 @@ describe('Test user notifications API validators', function () { | |||
172 | commentMention: UserNotificationSettingValue.WEB, | 172 | commentMention: UserNotificationSettingValue.WEB, |
173 | newFollow: UserNotificationSettingValue.WEB, | 173 | newFollow: UserNotificationSettingValue.WEB, |
174 | newUserRegistration: UserNotificationSettingValue.WEB, | 174 | newUserRegistration: UserNotificationSettingValue.WEB, |
175 | newInstanceFollower: UserNotificationSettingValue.WEB | 175 | newInstanceFollower: UserNotificationSettingValue.WEB, |
176 | autoInstanceFollowing: UserNotificationSettingValue.WEB | ||
176 | } | 177 | } |
177 | 178 | ||
178 | it('Should fail with missing fields', async function () { | 179 | it('Should fail with missing fields', async function () { |
diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts index 939b919ed..55094795c 100644 --- a/server/tests/api/check-params/users.ts +++ b/server/tests/api/check-params/users.ts | |||
@@ -476,6 +476,22 @@ describe('Test users API validators', function () { | |||
476 | await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) | 476 | await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) |
477 | }) | 477 | }) |
478 | 478 | ||
479 | it('Should fail with an invalid noInstanceConfigWarningModal attribute', async function () { | ||
480 | const fields = { | ||
481 | noInstanceConfigWarningModal: -1 | ||
482 | } | ||
483 | |||
484 | await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) | ||
485 | }) | ||
486 | |||
487 | it('Should fail with an invalid noWelcomeModal attribute', async function () { | ||
488 | const fields = { | ||
489 | noWelcomeModal: -1 | ||
490 | } | ||
491 | |||
492 | await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields }) | ||
493 | }) | ||
494 | |||
479 | it('Should succeed to change password with the correct params', async function () { | 495 | it('Should succeed to change password with the correct params', async function () { |
480 | const fields = { | 496 | const fields = { |
481 | currentPassword: 'my super password', | 497 | currentPassword: 'my super password', |
@@ -483,7 +499,9 @@ describe('Test users API validators', function () { | |||
483 | nsfwPolicy: 'blur', | 499 | nsfwPolicy: 'blur', |
484 | autoPlayVideo: false, | 500 | autoPlayVideo: false, |
485 | email: 'super_email@example.com', | 501 | email: 'super_email@example.com', |
486 | theme: 'default' | 502 | theme: 'default', |
503 | noInstanceConfigWarningModal: true, | ||
504 | noWelcomeModal: true | ||
487 | } | 505 | } |
488 | 506 | ||
489 | await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields, statusCodeExpected: 204 }) | 507 | await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields, statusCodeExpected: 204 }) |
diff --git a/server/tests/api/notifications/user-notifications.ts b/server/tests/api/notifications/user-notifications.ts index 6fa630562..15a34f5aa 100644 --- a/server/tests/api/notifications/user-notifications.ts +++ b/server/tests/api/notifications/user-notifications.ts | |||
@@ -14,10 +14,13 @@ import { | |||
14 | getVideoCommentThreads, | 14 | getVideoCommentThreads, |
15 | getVideoThreadComments, | 15 | getVideoThreadComments, |
16 | immutableAssign, | 16 | immutableAssign, |
17 | MockInstancesIndex, | ||
17 | registerUser, | 18 | registerUser, |
18 | removeVideoFromBlacklist, | 19 | removeVideoFromBlacklist, |
19 | reportVideoAbuse, | 20 | reportVideoAbuse, |
21 | unfollow, | ||
20 | updateCustomConfig, | 22 | updateCustomConfig, |
23 | updateCustomSubConfig, | ||
21 | updateMyUser, | 24 | updateMyUser, |
22 | updateVideo, | 25 | updateVideo, |
23 | updateVideoChannel, | 26 | updateVideoChannel, |
@@ -29,6 +32,7 @@ import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/l | |||
29 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | 32 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' |
30 | import { getUserNotificationSocket } from '../../../../shared/extra-utils/socket/socket-io' | 33 | import { getUserNotificationSocket } from '../../../../shared/extra-utils/socket/socket-io' |
31 | import { | 34 | import { |
35 | checkAutoInstanceFollowing, | ||
32 | checkCommentMention, | 36 | checkCommentMention, |
33 | CheckerBaseParams, | 37 | CheckerBaseParams, |
34 | checkMyVideoImportIsFinished, | 38 | checkMyVideoImportIsFinished, |
@@ -108,7 +112,8 @@ describe('Test users notifications', function () { | |||
108 | commentMention: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, | 112 | commentMention: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, |
109 | newFollow: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, | 113 | newFollow: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, |
110 | newUserRegistration: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, | 114 | newUserRegistration: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, |
111 | newInstanceFollower: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL | 115 | newInstanceFollower: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, |
116 | autoInstanceFollowing: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL | ||
112 | } | 117 | } |
113 | 118 | ||
114 | before(async function () { | 119 | before(async function () { |
@@ -873,7 +878,18 @@ describe('Test users notifications', function () { | |||
873 | }) | 878 | }) |
874 | }) | 879 | }) |
875 | 880 | ||
876 | describe('New instance follower', function () { | 881 | describe('New instance follows', function () { |
882 | const instanceIndexServer = new MockInstancesIndex() | ||
883 | const config = { | ||
884 | followings: { | ||
885 | instance: { | ||
886 | autoFollowIndex: { | ||
887 | indexUrl: 'http://localhost:42100', | ||
888 | enabled: true | ||
889 | } | ||
890 | } | ||
891 | } | ||
892 | } | ||
877 | let baseParams: CheckerBaseParams | 893 | let baseParams: CheckerBaseParams |
878 | 894 | ||
879 | before(async () => { | 895 | before(async () => { |
@@ -883,6 +899,9 @@ describe('Test users notifications', function () { | |||
883 | socketNotifications: adminNotifications, | 899 | socketNotifications: adminNotifications, |
884 | token: servers[0].accessToken | 900 | token: servers[0].accessToken |
885 | } | 901 | } |
902 | |||
903 | await instanceIndexServer.initialize() | ||
904 | instanceIndexServer.addInstance(servers[1].host) | ||
886 | }) | 905 | }) |
887 | 906 | ||
888 | it('Should send a notification only to admin when there is a new instance follower', async function () { | 907 | it('Should send a notification only to admin when there is a new instance follower', async function () { |
@@ -897,6 +916,56 @@ describe('Test users notifications', function () { | |||
897 | const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } } | 916 | const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } } |
898 | await checkNewInstanceFollower(immutableAssign(baseParams, userOverride), 'localhost:' + servers[2].port, 'absence') | 917 | await checkNewInstanceFollower(immutableAssign(baseParams, userOverride), 'localhost:' + servers[2].port, 'absence') |
899 | }) | 918 | }) |
919 | |||
920 | it('Should send a notification on auto follow back', async function () { | ||
921 | this.timeout(40000) | ||
922 | |||
923 | await unfollow(servers[2].url, servers[2].accessToken, servers[0]) | ||
924 | await waitJobs(servers) | ||
925 | |||
926 | const config = { | ||
927 | followings: { | ||
928 | instance: { | ||
929 | autoFollowBack: { enabled: true } | ||
930 | } | ||
931 | } | ||
932 | } | ||
933 | await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) | ||
934 | |||
935 | await follow(servers[2].url, [ servers[0].url ], servers[2].accessToken) | ||
936 | |||
937 | await waitJobs(servers) | ||
938 | |||
939 | const followerHost = servers[0].host | ||
940 | const followingHost = servers[2].host | ||
941 | await checkAutoInstanceFollowing(baseParams, followerHost, followingHost, 'presence') | ||
942 | |||
943 | const userOverride = { socketNotifications: userNotifications, token: userAccessToken, check: { web: true, mail: false } } | ||
944 | await checkAutoInstanceFollowing(immutableAssign(baseParams, userOverride), followerHost, followingHost, 'absence') | ||
945 | |||
946 | config.followings.instance.autoFollowBack.enabled = false | ||
947 | await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) | ||
948 | await unfollow(servers[0].url, servers[0].accessToken, servers[2]) | ||
949 | await unfollow(servers[2].url, servers[2].accessToken, servers[0]) | ||
950 | }) | ||
951 | |||
952 | it('Should send a notification on auto instances index follow', async function () { | ||
953 | this.timeout(30000) | ||
954 | await unfollow(servers[0].url, servers[0].accessToken, servers[1]) | ||
955 | |||
956 | await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) | ||
957 | |||
958 | await wait(5000) | ||
959 | await waitJobs(servers) | ||
960 | |||
961 | const followerHost = servers[0].host | ||
962 | const followingHost = servers[1].host | ||
963 | await checkAutoInstanceFollowing(baseParams, followerHost, followingHost, 'presence') | ||
964 | |||
965 | config.followings.instance.autoFollowIndex.enabled = false | ||
966 | await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) | ||
967 | await unfollow(servers[0].url, servers[0].accessToken, servers[1]) | ||
968 | }) | ||
900 | }) | 969 | }) |
901 | 970 | ||
902 | describe('New actor follow', function () { | 971 | describe('New actor follow', function () { |
diff --git a/server/tests/api/search/search-videos.ts b/server/tests/api/search/search-videos.ts index c06200ffe..a3e05156b 100644 --- a/server/tests/api/search/search-videos.ts +++ b/server/tests/api/search/search-videos.ts | |||
@@ -206,7 +206,7 @@ describe('Test videos search', function () { | |||
206 | const query = { | 206 | const query = { |
207 | search: '9999', | 207 | search: '9999', |
208 | categoryOneOf: [ 1 ], | 208 | categoryOneOf: [ 1 ], |
209 | tagsOneOf: [ 'aaaa', 'ffff' ] | 209 | tagsOneOf: [ 'aAaa', 'ffff' ] |
210 | } | 210 | } |
211 | const res1 = await advancedVideosSearch(server.url, query) | 211 | const res1 = await advancedVideosSearch(server.url, query) |
212 | expect(res1.body.total).to.equal(2) | 212 | expect(res1.body.total).to.equal(2) |
@@ -219,15 +219,15 @@ describe('Test videos search', function () { | |||
219 | const query = { | 219 | const query = { |
220 | search: '9999', | 220 | search: '9999', |
221 | categoryOneOf: [ 1 ], | 221 | categoryOneOf: [ 1 ], |
222 | tagsAllOf: [ 'cccc' ] | 222 | tagsAllOf: [ 'CCcc' ] |
223 | } | 223 | } |
224 | const res1 = await advancedVideosSearch(server.url, query) | 224 | const res1 = await advancedVideosSearch(server.url, query) |
225 | expect(res1.body.total).to.equal(2) | 225 | expect(res1.body.total).to.equal(2) |
226 | 226 | ||
227 | const res2 = await advancedVideosSearch(server.url, immutableAssign(query, { tagsAllOf: [ 'blabla' ] })) | 227 | const res2 = await advancedVideosSearch(server.url, immutableAssign(query, { tagsAllOf: [ 'blAbla' ] })) |
228 | expect(res2.body.total).to.equal(0) | 228 | expect(res2.body.total).to.equal(0) |
229 | 229 | ||
230 | const res3 = await advancedVideosSearch(server.url, immutableAssign(query, { tagsAllOf: [ 'bbbb', 'cccc' ] })) | 230 | const res3 = await advancedVideosSearch(server.url, immutableAssign(query, { tagsAllOf: [ 'bbbb', 'CCCC' ] })) |
231 | expect(res3.body.total).to.equal(1) | 231 | expect(res3.body.total).to.equal(1) |
232 | }) | 232 | }) |
233 | 233 | ||
diff --git a/server/tests/api/server/auto-follows.ts b/server/tests/api/server/auto-follows.ts new file mode 100644 index 000000000..df468034c --- /dev/null +++ b/server/tests/api/server/auto-follows.ts | |||
@@ -0,0 +1,211 @@ | |||
1 | /* tslint:disable:no-unused-expression */ | ||
2 | |||
3 | import * as chai from 'chai' | ||
4 | import 'mocha' | ||
5 | import { | ||
6 | acceptFollower, | ||
7 | cleanupTests, | ||
8 | flushAndRunMultipleServers, | ||
9 | MockInstancesIndex, | ||
10 | ServerInfo, | ||
11 | setAccessTokensToServers, | ||
12 | unfollow, | ||
13 | updateCustomSubConfig, | ||
14 | wait | ||
15 | } from '../../../../shared/extra-utils/index' | ||
16 | import { follow, getFollowersListPaginationAndSort, getFollowingListPaginationAndSort } from '../../../../shared/extra-utils/server/follows' | ||
17 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | ||
18 | import { ActorFollow } from '../../../../shared/models/actors' | ||
19 | |||
20 | const expect = chai.expect | ||
21 | |||
22 | async function checkFollow (follower: ServerInfo, following: ServerInfo, exists: boolean) { | ||
23 | { | ||
24 | const res = await getFollowersListPaginationAndSort(following.url, 0, 5, '-createdAt') | ||
25 | const follows = res.body.data as ActorFollow[] | ||
26 | |||
27 | const follow = follows.find(f => { | ||
28 | return f.follower.host === follower.host && f.state === 'accepted' | ||
29 | }) | ||
30 | |||
31 | if (exists === true) { | ||
32 | expect(follow).to.exist | ||
33 | } else { | ||
34 | expect(follow).to.be.undefined | ||
35 | } | ||
36 | } | ||
37 | |||
38 | { | ||
39 | const res = await getFollowingListPaginationAndSort(follower.url, 0, 5, '-createdAt') | ||
40 | const follows = res.body.data as ActorFollow[] | ||
41 | |||
42 | const follow = follows.find(f => { | ||
43 | return f.following.host === following.host && f.state === 'accepted' | ||
44 | }) | ||
45 | |||
46 | if (exists === true) { | ||
47 | expect(follow).to.exist | ||
48 | } else { | ||
49 | expect(follow).to.be.undefined | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | |||
54 | async function server1Follows2 (servers: ServerInfo[]) { | ||
55 | await follow(servers[0].url, [ servers[1].host ], servers[0].accessToken) | ||
56 | |||
57 | await waitJobs(servers) | ||
58 | } | ||
59 | |||
60 | async function resetFollows (servers: ServerInfo[]) { | ||
61 | try { | ||
62 | await unfollow(servers[ 0 ].url, servers[ 0 ].accessToken, servers[ 1 ]) | ||
63 | await unfollow(servers[ 1 ].url, servers[ 1 ].accessToken, servers[ 0 ]) | ||
64 | } catch { /* empty */ } | ||
65 | |||
66 | await waitJobs(servers) | ||
67 | |||
68 | await checkFollow(servers[0], servers[1], false) | ||
69 | await checkFollow(servers[1], servers[0], false) | ||
70 | } | ||
71 | |||
72 | describe('Test auto follows', function () { | ||
73 | let servers: ServerInfo[] = [] | ||
74 | |||
75 | before(async function () { | ||
76 | this.timeout(30000) | ||
77 | |||
78 | servers = await flushAndRunMultipleServers(3) | ||
79 | |||
80 | // Get the access tokens | ||
81 | await setAccessTokensToServers(servers) | ||
82 | }) | ||
83 | |||
84 | describe('Auto follow back', function () { | ||
85 | |||
86 | it('Should not auto follow back if the option is not enabled', async function () { | ||
87 | this.timeout(15000) | ||
88 | |||
89 | await server1Follows2(servers) | ||
90 | |||
91 | await checkFollow(servers[0], servers[1], true) | ||
92 | await checkFollow(servers[1], servers[0], false) | ||
93 | |||
94 | await resetFollows(servers) | ||
95 | }) | ||
96 | |||
97 | it('Should auto follow back on auto accept if the option is enabled', async function () { | ||
98 | this.timeout(15000) | ||
99 | |||
100 | const config = { | ||
101 | followings: { | ||
102 | instance: { | ||
103 | autoFollowBack: { enabled: true } | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | await updateCustomSubConfig(servers[1].url, servers[1].accessToken, config) | ||
108 | |||
109 | await server1Follows2(servers) | ||
110 | |||
111 | await checkFollow(servers[0], servers[1], true) | ||
112 | await checkFollow(servers[1], servers[0], true) | ||
113 | |||
114 | await resetFollows(servers) | ||
115 | }) | ||
116 | |||
117 | it('Should wait the acceptation before auto follow back', async function () { | ||
118 | this.timeout(30000) | ||
119 | |||
120 | const config = { | ||
121 | followings: { | ||
122 | instance: { | ||
123 | autoFollowBack: { enabled: true } | ||
124 | } | ||
125 | }, | ||
126 | followers: { | ||
127 | instance: { | ||
128 | manualApproval: true | ||
129 | } | ||
130 | } | ||
131 | } | ||
132 | await updateCustomSubConfig(servers[1].url, servers[1].accessToken, config) | ||
133 | |||
134 | await server1Follows2(servers) | ||
135 | |||
136 | await checkFollow(servers[0], servers[1], false) | ||
137 | await checkFollow(servers[1], servers[0], false) | ||
138 | |||
139 | await acceptFollower(servers[1].url, servers[1].accessToken, 'peertube@' + servers[0].host) | ||
140 | await waitJobs(servers) | ||
141 | |||
142 | await checkFollow(servers[0], servers[1], true) | ||
143 | await checkFollow(servers[1], servers[0], true) | ||
144 | |||
145 | await resetFollows(servers) | ||
146 | |||
147 | config.followings.instance.autoFollowBack.enabled = false | ||
148 | config.followers.instance.manualApproval = false | ||
149 | await updateCustomSubConfig(servers[1].url, servers[1].accessToken, config) | ||
150 | }) | ||
151 | }) | ||
152 | |||
153 | describe('Auto follow index', function () { | ||
154 | const instanceIndexServer = new MockInstancesIndex() | ||
155 | |||
156 | before(async () => { | ||
157 | await instanceIndexServer.initialize() | ||
158 | }) | ||
159 | |||
160 | it('Should not auto follow index if the option is not enabled', async function () { | ||
161 | this.timeout(30000) | ||
162 | |||
163 | await wait(5000) | ||
164 | await waitJobs(servers) | ||
165 | |||
166 | await checkFollow(servers[ 0 ], servers[ 1 ], false) | ||
167 | await checkFollow(servers[ 1 ], servers[ 0 ], false) | ||
168 | }) | ||
169 | |||
170 | it('Should auto follow the index', async function () { | ||
171 | this.timeout(30000) | ||
172 | |||
173 | instanceIndexServer.addInstance(servers[1].host) | ||
174 | |||
175 | const config = { | ||
176 | followings: { | ||
177 | instance: { | ||
178 | autoFollowIndex: { | ||
179 | indexUrl: 'http://localhost:42100', | ||
180 | enabled: true | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | } | ||
185 | await updateCustomSubConfig(servers[0].url, servers[0].accessToken, config) | ||
186 | |||
187 | await wait(5000) | ||
188 | await waitJobs(servers) | ||
189 | |||
190 | await checkFollow(servers[ 0 ], servers[ 1 ], true) | ||
191 | |||
192 | await resetFollows(servers) | ||
193 | }) | ||
194 | |||
195 | it('Should follow new added instances in the index but not old ones', async function () { | ||
196 | this.timeout(30000) | ||
197 | |||
198 | instanceIndexServer.addInstance(servers[2].host) | ||
199 | |||
200 | await wait(5000) | ||
201 | await waitJobs(servers) | ||
202 | |||
203 | await checkFollow(servers[ 0 ], servers[ 1 ], false) | ||
204 | await checkFollow(servers[ 0 ], servers[ 2 ], true) | ||
205 | }) | ||
206 | }) | ||
207 | |||
208 | after(async function () { | ||
209 | await cleanupTests(servers) | ||
210 | }) | ||
211 | }) | ||
diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts index 78fdc9cc0..97cc99eea 100644 --- a/server/tests/api/server/config.ts +++ b/server/tests/api/server/config.ts | |||
@@ -28,7 +28,19 @@ function checkInitialConfig (server: ServerInfo, data: CustomConfig) { | |||
28 | 'with WebTorrent and Angular.' | 28 | 'with WebTorrent and Angular.' |
29 | ) | 29 | ) |
30 | expect(data.instance.description).to.equal('Welcome to this PeerTube instance!') | 30 | expect(data.instance.description).to.equal('Welcome to this PeerTube instance!') |
31 | |||
31 | expect(data.instance.terms).to.equal('No terms for now.') | 32 | expect(data.instance.terms).to.equal('No terms for now.') |
33 | expect(data.instance.creationReason).to.be.empty | ||
34 | expect(data.instance.codeOfConduct).to.be.empty | ||
35 | expect(data.instance.moderationInformation).to.be.empty | ||
36 | expect(data.instance.administrator).to.be.empty | ||
37 | expect(data.instance.maintenanceLifetime).to.be.empty | ||
38 | expect(data.instance.businessModel).to.be.empty | ||
39 | expect(data.instance.hardwareInformation).to.be.empty | ||
40 | |||
41 | expect(data.instance.languages).to.have.lengthOf(0) | ||
42 | expect(data.instance.categories).to.have.lengthOf(0) | ||
43 | |||
32 | expect(data.instance.defaultClientRoute).to.equal('/videos/trending') | 44 | expect(data.instance.defaultClientRoute).to.equal('/videos/trending') |
33 | expect(data.instance.isNSFW).to.be.false | 45 | expect(data.instance.isNSFW).to.be.false |
34 | expect(data.instance.defaultNSFWPolicy).to.equal('display') | 46 | expect(data.instance.defaultNSFWPolicy).to.equal('display') |
@@ -68,13 +80,29 @@ function checkInitialConfig (server: ServerInfo, data: CustomConfig) { | |||
68 | 80 | ||
69 | expect(data.followers.instance.enabled).to.be.true | 81 | expect(data.followers.instance.enabled).to.be.true |
70 | expect(data.followers.instance.manualApproval).to.be.false | 82 | expect(data.followers.instance.manualApproval).to.be.false |
83 | |||
84 | expect(data.followings.instance.autoFollowBack.enabled).to.be.false | ||
85 | expect(data.followings.instance.autoFollowIndex.enabled).to.be.false | ||
86 | expect(data.followings.instance.autoFollowIndex.indexUrl).to.equal('https://instances.joinpeertube.org') | ||
71 | } | 87 | } |
72 | 88 | ||
73 | function checkUpdatedConfig (data: CustomConfig) { | 89 | function checkUpdatedConfig (data: CustomConfig) { |
74 | expect(data.instance.name).to.equal('PeerTube updated') | 90 | expect(data.instance.name).to.equal('PeerTube updated') |
75 | expect(data.instance.shortDescription).to.equal('my short description') | 91 | expect(data.instance.shortDescription).to.equal('my short description') |
76 | expect(data.instance.description).to.equal('my super description') | 92 | expect(data.instance.description).to.equal('my super description') |
93 | |||
77 | expect(data.instance.terms).to.equal('my super terms') | 94 | expect(data.instance.terms).to.equal('my super terms') |
95 | expect(data.instance.creationReason).to.equal('my super creation reason') | ||
96 | expect(data.instance.codeOfConduct).to.equal('my super coc') | ||
97 | expect(data.instance.moderationInformation).to.equal('my super moderation information') | ||
98 | expect(data.instance.administrator).to.equal('Kuja') | ||
99 | expect(data.instance.maintenanceLifetime).to.equal('forever') | ||
100 | expect(data.instance.businessModel).to.equal('my super business model') | ||
101 | expect(data.instance.hardwareInformation).to.equal('2vCore 3GB RAM') | ||
102 | |||
103 | expect(data.instance.languages).to.deep.equal([ 'en', 'es' ]) | ||
104 | expect(data.instance.categories).to.deep.equal([ 1, 2 ]) | ||
105 | |||
78 | expect(data.instance.defaultClientRoute).to.equal('/videos/recently-added') | 106 | expect(data.instance.defaultClientRoute).to.equal('/videos/recently-added') |
79 | expect(data.instance.isNSFW).to.be.true | 107 | expect(data.instance.isNSFW).to.be.true |
80 | expect(data.instance.defaultNSFWPolicy).to.equal('blur') | 108 | expect(data.instance.defaultNSFWPolicy).to.equal('blur') |
@@ -119,6 +147,10 @@ function checkUpdatedConfig (data: CustomConfig) { | |||
119 | 147 | ||
120 | expect(data.followers.instance.enabled).to.be.false | 148 | expect(data.followers.instance.enabled).to.be.false |
121 | expect(data.followers.instance.manualApproval).to.be.true | 149 | expect(data.followers.instance.manualApproval).to.be.true |
150 | |||
151 | expect(data.followings.instance.autoFollowBack.enabled).to.be.true | ||
152 | expect(data.followings.instance.autoFollowIndex.enabled).to.be.true | ||
153 | expect(data.followings.instance.autoFollowIndex.indexUrl).to.equal('https://updated.example.com') | ||
122 | } | 154 | } |
123 | 155 | ||
124 | describe('Test config', function () { | 156 | describe('Test config', function () { |
@@ -182,6 +214,18 @@ describe('Test config', function () { | |||
182 | shortDescription: 'my short description', | 214 | shortDescription: 'my short description', |
183 | description: 'my super description', | 215 | description: 'my super description', |
184 | terms: 'my super terms', | 216 | terms: 'my super terms', |
217 | codeOfConduct: 'my super coc', | ||
218 | |||
219 | creationReason: 'my super creation reason', | ||
220 | moderationInformation: 'my super moderation information', | ||
221 | administrator: 'Kuja', | ||
222 | maintenanceLifetime: 'forever', | ||
223 | businessModel: 'my super business model', | ||
224 | hardwareInformation: '2vCore 3GB RAM', | ||
225 | |||
226 | languages: [ 'en', 'es' ], | ||
227 | categories: [ 1, 2 ], | ||
228 | |||
185 | defaultClientRoute: '/videos/recently-added', | 229 | defaultClientRoute: '/videos/recently-added', |
186 | isNSFW: true, | 230 | isNSFW: true, |
187 | defaultNSFWPolicy: 'blur' as 'blur', | 231 | defaultNSFWPolicy: 'blur' as 'blur', |
@@ -261,6 +305,17 @@ describe('Test config', function () { | |||
261 | enabled: false, | 305 | enabled: false, |
262 | manualApproval: true | 306 | manualApproval: true |
263 | } | 307 | } |
308 | }, | ||
309 | followings: { | ||
310 | instance: { | ||
311 | autoFollowBack: { | ||
312 | enabled: true | ||
313 | }, | ||
314 | autoFollowIndex: { | ||
315 | enabled: true, | ||
316 | indexUrl: 'https://updated.example.com' | ||
317 | } | ||
318 | } | ||
264 | } | 319 | } |
265 | } | 320 | } |
266 | await updateCustomConfig(server.url, server.accessToken, newCustomConfig) | 321 | await updateCustomConfig(server.url, server.accessToken, newCustomConfig) |
@@ -310,6 +365,17 @@ describe('Test config', function () { | |||
310 | expect(data.instance.shortDescription).to.equal('my short description') | 365 | expect(data.instance.shortDescription).to.equal('my short description') |
311 | expect(data.instance.description).to.equal('my super description') | 366 | expect(data.instance.description).to.equal('my super description') |
312 | expect(data.instance.terms).to.equal('my super terms') | 367 | expect(data.instance.terms).to.equal('my super terms') |
368 | expect(data.instance.codeOfConduct).to.equal('my super coc') | ||
369 | |||
370 | expect(data.instance.creationReason).to.equal('my super creation reason') | ||
371 | expect(data.instance.moderationInformation).to.equal('my super moderation information') | ||
372 | expect(data.instance.administrator).to.equal('Kuja') | ||
373 | expect(data.instance.maintenanceLifetime).to.equal('forever') | ||
374 | expect(data.instance.businessModel).to.equal('my super business model') | ||
375 | expect(data.instance.hardwareInformation).to.equal('2vCore 3GB RAM') | ||
376 | |||
377 | expect(data.instance.languages).to.deep.equal([ 'en', 'es' ]) | ||
378 | expect(data.instance.categories).to.deep.equal([ 1, 2 ]) | ||
313 | }) | 379 | }) |
314 | 380 | ||
315 | it('Should remove the custom configuration', async function () { | 381 | it('Should remove the custom configuration', async function () { |
diff --git a/server/tests/api/server/index.ts b/server/tests/api/server/index.ts index 3daeeb49a..08205b2c8 100644 --- a/server/tests/api/server/index.ts +++ b/server/tests/api/server/index.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | import './auto-follows' | ||
1 | import './config' | 2 | import './config' |
2 | import './contact-form' | 3 | import './contact-form' |
3 | import './email' | 4 | import './email' |
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts index 3a3fabb4c..95b1bb626 100644 --- a/server/tests/api/users/users.ts +++ b/server/tests/api/users/users.ts | |||
@@ -442,7 +442,7 @@ describe('Test users', function () { | |||
442 | url: server.url, | 442 | url: server.url, |
443 | accessToken: accessTokenUser, | 443 | accessToken: accessTokenUser, |
444 | currentPassword: 'super password', | 444 | currentPassword: 'super password', |
445 | newPassword: 'new password' | 445 | password: 'new password' |
446 | }) | 446 | }) |
447 | user.password = 'new password' | 447 | user.password = 'new password' |
448 | 448 | ||
@@ -543,7 +543,7 @@ describe('Test users', function () { | |||
543 | }) | 543 | }) |
544 | 544 | ||
545 | const res = await getMyUserInformation(server.url, accessTokenUser) | 545 | const res = await getMyUserInformation(server.url, accessTokenUser) |
546 | const user = res.body | 546 | const user: User = res.body |
547 | 547 | ||
548 | expect(user.username).to.equal('user_1') | 548 | expect(user.username).to.equal('user_1') |
549 | expect(user.email).to.equal('updated@example.com') | 549 | expect(user.email).to.equal('updated@example.com') |
@@ -552,6 +552,8 @@ describe('Test users', function () { | |||
552 | expect(user.id).to.be.a('number') | 552 | expect(user.id).to.be.a('number') |
553 | expect(user.account.displayName).to.equal('new display name') | 553 | expect(user.account.displayName).to.equal('new display name') |
554 | expect(user.account.description).to.equal('my super description updated') | 554 | expect(user.account.description).to.equal('my super description updated') |
555 | expect(user.noWelcomeModal).to.be.false | ||
556 | expect(user.noInstanceConfigWarningModal).to.be.false | ||
555 | }) | 557 | }) |
556 | 558 | ||
557 | it('Should be able to update my theme', async function () { | 559 | it('Should be able to update my theme', async function () { |
@@ -568,6 +570,21 @@ describe('Test users', function () { | |||
568 | expect(body.theme).to.equal(theme) | 570 | expect(body.theme).to.equal(theme) |
569 | } | 571 | } |
570 | }) | 572 | }) |
573 | |||
574 | it('Should be able to update my modal preferences', async function () { | ||
575 | await updateMyUser({ | ||
576 | url: server.url, | ||
577 | accessToken: accessTokenUser, | ||
578 | noInstanceConfigWarningModal: true, | ||
579 | noWelcomeModal: true | ||
580 | }) | ||
581 | |||
582 | const res = await getMyUserInformation(server.url, accessTokenUser) | ||
583 | const user: User = res.body | ||
584 | |||
585 | expect(user.noWelcomeModal).to.be.true | ||
586 | expect(user.noInstanceConfigWarningModal).to.be.true | ||
587 | }) | ||
571 | }) | 588 | }) |
572 | 589 | ||
573 | describe('Updating another user', function () { | 590 | describe('Updating another user', function () { |
diff --git a/server/tests/api/videos/video-abuse.ts b/server/tests/api/videos/video-abuse.ts index a2f3ee161..0cd6f22c7 100644 --- a/server/tests/api/videos/video-abuse.ts +++ b/server/tests/api/videos/video-abuse.ts | |||
@@ -17,6 +17,12 @@ import { | |||
17 | } from '../../../../shared/extra-utils/index' | 17 | } from '../../../../shared/extra-utils/index' |
18 | import { doubleFollow } from '../../../../shared/extra-utils/server/follows' | 18 | import { doubleFollow } from '../../../../shared/extra-utils/server/follows' |
19 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | 19 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' |
20 | import { | ||
21 | addAccountToServerBlocklist, | ||
22 | addServerToServerBlocklist, | ||
23 | removeAccountFromServerBlocklist, | ||
24 | removeServerFromServerBlocklist | ||
25 | } from '../../../../shared/extra-utils/users/blocklist' | ||
20 | 26 | ||
21 | const expect = chai.expect | 27 | const expect = chai.expect |
22 | 28 | ||
@@ -163,13 +169,76 @@ describe('Test video abuses', function () { | |||
163 | expect(res.body.data[0].moderationComment).to.equal('It is valid') | 169 | expect(res.body.data[0].moderationComment).to.equal('It is valid') |
164 | }) | 170 | }) |
165 | 171 | ||
172 | it('Should hide video abuses from blocked accounts', async function () { | ||
173 | this.timeout(10000) | ||
174 | |||
175 | { | ||
176 | await reportVideoAbuse(servers[1].url, servers[1].accessToken, servers[0].video.uuid, 'will mute this') | ||
177 | await waitJobs(servers) | ||
178 | |||
179 | const res = await getVideoAbusesList(servers[0].url, servers[0].accessToken) | ||
180 | expect(res.body.total).to.equal(3) | ||
181 | } | ||
182 | |||
183 | const accountToBlock = 'root@localhost:' + servers[1].port | ||
184 | |||
185 | { | ||
186 | await addAccountToServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, accountToBlock) | ||
187 | |||
188 | const res = await getVideoAbusesList(servers[ 0 ].url, servers[ 0 ].accessToken) | ||
189 | expect(res.body.total).to.equal(2) | ||
190 | |||
191 | const abuse = res.body.data.find(a => a.reason === 'will mute this') | ||
192 | expect(abuse).to.be.undefined | ||
193 | } | ||
194 | |||
195 | { | ||
196 | await removeAccountFromServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, accountToBlock) | ||
197 | |||
198 | const res = await getVideoAbusesList(servers[ 0 ].url, servers[ 0 ].accessToken) | ||
199 | expect(res.body.total).to.equal(3) | ||
200 | } | ||
201 | }) | ||
202 | |||
203 | it('Should hide video abuses from blocked servers', async function () { | ||
204 | const serverToBlock = servers[1].host | ||
205 | |||
206 | { | ||
207 | await addServerToServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, servers[1].host) | ||
208 | |||
209 | const res = await getVideoAbusesList(servers[ 0 ].url, servers[ 0 ].accessToken) | ||
210 | expect(res.body.total).to.equal(2) | ||
211 | |||
212 | const abuse = res.body.data.find(a => a.reason === 'will mute this') | ||
213 | expect(abuse).to.be.undefined | ||
214 | } | ||
215 | |||
216 | { | ||
217 | await removeServerFromServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, serverToBlock) | ||
218 | |||
219 | const res = await getVideoAbusesList(servers[ 0 ].url, servers[ 0 ].accessToken) | ||
220 | expect(res.body.total).to.equal(3) | ||
221 | } | ||
222 | }) | ||
223 | |||
166 | it('Should delete the video abuse', async function () { | 224 | it('Should delete the video abuse', async function () { |
225 | this.timeout(10000) | ||
226 | |||
167 | await deleteVideoAbuse(servers[1].url, servers[1].accessToken, abuseServer2.video.uuid, abuseServer2.id) | 227 | await deleteVideoAbuse(servers[1].url, servers[1].accessToken, abuseServer2.video.uuid, abuseServer2.id) |
168 | 228 | ||
169 | const res = await getVideoAbusesList(servers[1].url, servers[1].accessToken) | 229 | await waitJobs(servers) |
170 | expect(res.body.total).to.equal(0) | 230 | |
171 | expect(res.body.data).to.be.an('array') | 231 | { |
172 | expect(res.body.data.length).to.equal(0) | 232 | const res = await getVideoAbusesList(servers[1].url, servers[1].accessToken) |
233 | expect(res.body.total).to.equal(1) | ||
234 | expect(res.body.data.length).to.equal(1) | ||
235 | expect(res.body.data[0].id).to.not.equal(abuseServer2.id) | ||
236 | } | ||
237 | |||
238 | { | ||
239 | const res = await getVideoAbusesList(servers[0].url, servers[0].accessToken) | ||
240 | expect(res.body.total).to.equal(3) | ||
241 | } | ||
173 | }) | 242 | }) |
174 | 243 | ||
175 | after(async function () { | 244 | after(async function () { |
diff --git a/server/tests/api/videos/video-change-ownership.ts b/server/tests/api/videos/video-change-ownership.ts index 3a3add71b..64ee2355a 100644 --- a/server/tests/api/videos/video-change-ownership.ts +++ b/server/tests/api/videos/video-change-ownership.ts | |||
@@ -191,7 +191,7 @@ describe('Test video change ownership - nominal', function () { | |||
191 | await waitJobs(servers) | 191 | await waitJobs(servers) |
192 | }) | 192 | }) |
193 | 193 | ||
194 | it('Should have video channel updated', async function () { | 194 | it('Should have the channel of the video updated', async function () { |
195 | for (const server of servers) { | 195 | for (const server of servers) { |
196 | const res = await getVideo(server.url, servers[0].video.uuid) | 196 | const res = await getVideo(server.url, servers[0].video.uuid) |
197 | 197 | ||
diff --git a/server/tools/cli.ts b/server/tools/cli.ts index 8599a270f..58e2445ac 100644 --- a/server/tools/cli.ts +++ b/server/tools/cli.ts | |||
@@ -5,6 +5,7 @@ import { root } from '../../shared/extra-utils/miscs/miscs' | |||
5 | import { getVideoChannel } from '../../shared/extra-utils/videos/video-channels' | 5 | import { getVideoChannel } from '../../shared/extra-utils/videos/video-channels' |
6 | import { Command } from 'commander' | 6 | import { Command } from 'commander' |
7 | import { VideoChannel, VideoPrivacy } from '../../shared/models/videos' | 7 | import { VideoChannel, VideoPrivacy } from '../../shared/models/videos' |
8 | import { createLogger, format, transports } from 'winston' | ||
8 | 9 | ||
9 | let configName = 'PeerTube/CLI' | 10 | let configName = 'PeerTube/CLI' |
10 | if (isTestInstance()) configName += `-${getAppNumber()}` | 11 | if (isTestInstance()) configName += `-${getAppNumber()}` |
@@ -119,6 +120,7 @@ function buildCommonVideoOptions (command: Command) { | |||
119 | .option('-m, --comments-enabled', 'Enable comments') | 120 | .option('-m, --comments-enabled', 'Enable comments') |
120 | .option('-s, --support <support>', 'Video support text') | 121 | .option('-s, --support <support>', 'Video support text') |
121 | .option('-w, --wait-transcoding', 'Wait transcoding before publishing the video') | 122 | .option('-w, --wait-transcoding', 'Wait transcoding before publishing the video') |
123 | .option('-v, --verbose <verbose>', 'Verbosity, from 0/\'error\' to 4/\'debug\'', 'info') | ||
122 | } | 124 | } |
123 | 125 | ||
124 | async function buildVideoAttributesFromCommander (url: string, command: Command, defaultAttributes: any = {}) { | 126 | async function buildVideoAttributesFromCommander (url: string, command: Command, defaultAttributes: any = {}) { |
@@ -175,11 +177,42 @@ function getServerCredentials (program: any) { | |||
175 | }) | 177 | }) |
176 | } | 178 | } |
177 | 179 | ||
180 | function getLogger (logLevel = 'info') { | ||
181 | const logLevels = { | ||
182 | 0: 0, | ||
183 | error: 0, | ||
184 | 1: 1, | ||
185 | warn: 1, | ||
186 | 2: 2, | ||
187 | info: 2, | ||
188 | 3: 3, | ||
189 | verbose: 3, | ||
190 | 4: 4, | ||
191 | debug: 4 | ||
192 | } | ||
193 | |||
194 | const logger = createLogger({ | ||
195 | levels: logLevels, | ||
196 | format: format.combine( | ||
197 | format.splat(), | ||
198 | format.simple() | ||
199 | ), | ||
200 | transports: [ | ||
201 | new (transports.Console)({ | ||
202 | level: logLevel | ||
203 | }) | ||
204 | ] | ||
205 | }) | ||
206 | |||
207 | return logger | ||
208 | } | ||
209 | |||
178 | // --------------------------------------------------------------------------- | 210 | // --------------------------------------------------------------------------- |
179 | 211 | ||
180 | export { | 212 | export { |
181 | version, | 213 | version, |
182 | config, | 214 | config, |
215 | getLogger, | ||
183 | getSettings, | 216 | getSettings, |
184 | getNetrc, | 217 | getNetrc, |
185 | getRemoteObjectOrDie, | 218 | getRemoteObjectOrDie, |
diff --git a/server/tools/peertube-import-videos.ts b/server/tools/peertube-import-videos.ts index 0ebfa7442..fcb90cca3 100644 --- a/server/tools/peertube-import-videos.ts +++ b/server/tools/peertube-import-videos.ts | |||
@@ -8,10 +8,11 @@ import { CONSTRAINTS_FIELDS } from '../initializers/constants' | |||
8 | import { getClient, getVideoCategories, login, searchVideoWithSort, uploadVideo } from '../../shared/extra-utils/index' | 8 | import { getClient, getVideoCategories, login, searchVideoWithSort, uploadVideo } from '../../shared/extra-utils/index' |
9 | import { truncate } from 'lodash' | 9 | import { truncate } from 'lodash' |
10 | import * as prompt from 'prompt' | 10 | import * as prompt from 'prompt' |
11 | import { accessSync, constants } from 'fs' | ||
11 | import { remove } from 'fs-extra' | 12 | import { remove } from 'fs-extra' |
12 | import { sha256 } from '../helpers/core-utils' | 13 | import { sha256 } from '../helpers/core-utils' |
13 | import { buildOriginallyPublishedAt, safeGetYoutubeDL } from '../helpers/youtube-dl' | 14 | import { buildOriginallyPublishedAt, safeGetYoutubeDL } from '../helpers/youtube-dl' |
14 | import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getServerCredentials } from './cli' | 15 | import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getServerCredentials, getLogger } from './cli' |
15 | 16 | ||
16 | type UserInfo = { | 17 | type UserInfo = { |
17 | username: string | 18 | username: string |
@@ -19,7 +20,6 @@ type UserInfo = { | |||
19 | } | 20 | } |
20 | 21 | ||
21 | const processOptions = { | 22 | const processOptions = { |
22 | cwd: __dirname, | ||
23 | maxBuffer: Infinity | 23 | maxBuffer: Infinity |
24 | } | 24 | } |
25 | 25 | ||
@@ -35,15 +35,23 @@ command | |||
35 | .option('--target-url <targetUrl>', 'Video target URL') | 35 | .option('--target-url <targetUrl>', 'Video target URL') |
36 | .option('--since <since>', 'Publication date (inclusive) since which the videos can be imported (YYYY-MM-DD)', parseDate) | 36 | .option('--since <since>', 'Publication date (inclusive) since which the videos can be imported (YYYY-MM-DD)', parseDate) |
37 | .option('--until <until>', 'Publication date (inclusive) until which the videos can be imported (YYYY-MM-DD)', parseDate) | 37 | .option('--until <until>', 'Publication date (inclusive) until which the videos can be imported (YYYY-MM-DD)', parseDate) |
38 | .option('-v, --verbose', 'Verbose mode') | 38 | .option('--first <first>', 'Process first n elements of returned playlist') |
39 | .option('--last <last>', 'Process last n elements of returned playlist') | ||
40 | .option('-T, --tmpdir <tmpdir>', 'Working directory', __dirname) | ||
39 | .parse(process.argv) | 41 | .parse(process.argv) |
40 | 42 | ||
43 | let log = getLogger(program[ 'verbose' ]) | ||
44 | |||
41 | getServerCredentials(command) | 45 | getServerCredentials(command) |
42 | .then(({ url, username, password }) => { | 46 | .then(({ url, username, password }) => { |
43 | if (!program[ 'targetUrl' ]) { | 47 | if (!program[ 'targetUrl' ]) { |
44 | console.error('--targetUrl field is required.') | 48 | exitError('--target-url field is required.') |
49 | } | ||
45 | 50 | ||
46 | process.exit(-1) | 51 | try { |
52 | accessSync(program[ 'tmpdir' ], constants.R_OK | constants.W_OK) | ||
53 | } catch (e) { | ||
54 | exitError('--tmpdir %s: directory does not exist or is not accessible', program[ 'tmpdir' ]) | ||
47 | } | 55 | } |
48 | 56 | ||
49 | removeEndSlashes(url) | 57 | removeEndSlashes(url) |
@@ -53,8 +61,7 @@ getServerCredentials(command) | |||
53 | 61 | ||
54 | run(url, user) | 62 | run(url, user) |
55 | .catch(err => { | 63 | .catch(err => { |
56 | console.error(err) | 64 | exitError(err) |
57 | process.exit(-1) | ||
58 | }) | 65 | }) |
59 | }) | 66 | }) |
60 | 67 | ||
@@ -68,30 +75,32 @@ async function run (url: string, user: UserInfo) { | |||
68 | const options = [ '-j', '--flat-playlist', '--playlist-reverse' ] | 75 | const options = [ '-j', '--flat-playlist', '--playlist-reverse' ] |
69 | youtubeDL.getInfo(program[ 'targetUrl' ], options, processOptions, async (err, info) => { | 76 | youtubeDL.getInfo(program[ 'targetUrl' ], options, processOptions, async (err, info) => { |
70 | if (err) { | 77 | if (err) { |
71 | console.log(err.message) | 78 | exitError(err.message) |
72 | process.exit(1) | ||
73 | } | 79 | } |
74 | 80 | ||
75 | let infoArray: any[] | 81 | let infoArray: any[] |
76 | 82 | ||
77 | // Normalize utf8 fields | 83 | // Normalize utf8 fields |
78 | if (Array.isArray(info) === true) { | 84 | infoArray = [].concat(info); |
79 | infoArray = info.map(i => normalizeObject(i)) | 85 | if (program[ 'first' ]) { |
80 | } else { | 86 | infoArray = infoArray.slice(0, program[ 'first' ]) |
81 | infoArray = [ normalizeObject(info) ] | 87 | } else if (program[ 'last' ]) { |
88 | infoArray = infoArray.slice(- program[ 'last' ]) | ||
82 | } | 89 | } |
83 | console.log('Will download and upload %d videos.\n', infoArray.length) | 90 | infoArray = infoArray.map(i => normalizeObject(i)) |
91 | |||
92 | log.info('Will download and upload %d videos.\n', infoArray.length) | ||
84 | 93 | ||
85 | for (const info of infoArray) { | 94 | for (const info of infoArray) { |
86 | await processVideo({ | 95 | await processVideo({ |
87 | cwd: processOptions.cwd, | 96 | cwd: program[ 'tmpdir' ], |
88 | url, | 97 | url, |
89 | user, | 98 | user, |
90 | youtubeInfo: info | 99 | youtubeInfo: info |
91 | }) | 100 | }) |
92 | } | 101 | } |
93 | 102 | ||
94 | console.log('Video/s for user %s imported: %s', program[ 'username' ], program[ 'targetUrl' ]) | 103 | log.info('Video/s for user %s imported: %s', user.username, program[ 'targetUrl' ]) |
95 | process.exit(0) | 104 | process.exit(0) |
96 | }) | 105 | }) |
97 | } | 106 | } |
@@ -105,21 +114,21 @@ function processVideo (parameters: { | |||
105 | const { youtubeInfo, cwd, url, user } = parameters | 114 | const { youtubeInfo, cwd, url, user } = parameters |
106 | 115 | ||
107 | return new Promise(async res => { | 116 | return new Promise(async res => { |
108 | if (program[ 'verbose' ]) console.log('Fetching object.', youtubeInfo) | 117 | log.debug('Fetching object.', youtubeInfo) |
109 | 118 | ||
110 | const videoInfo = await fetchObject(youtubeInfo) | 119 | const videoInfo = await fetchObject(youtubeInfo) |
111 | if (program[ 'verbose' ]) console.log('Fetched object.', videoInfo) | 120 | log.debug('Fetched object.', videoInfo) |
112 | 121 | ||
113 | if (program[ 'since' ]) { | 122 | if (program[ 'since' ]) { |
114 | if (buildOriginallyPublishedAt(videoInfo).getTime() < program[ 'since' ].getTime()) { | 123 | if (buildOriginallyPublishedAt(videoInfo).getTime() < program[ 'since' ].getTime()) { |
115 | console.log('Video "%s" has been published before "%s", don\'t upload it.\n', | 124 | log.info('Video "%s" has been published before "%s", don\'t upload it.\n', |
116 | videoInfo.title, formatDate(program[ 'since' ])); | 125 | videoInfo.title, formatDate(program[ 'since' ])); |
117 | return res(); | 126 | return res(); |
118 | } | 127 | } |
119 | } | 128 | } |
120 | if (program[ 'until' ]) { | 129 | if (program[ 'until' ]) { |
121 | if (buildOriginallyPublishedAt(videoInfo).getTime() > program[ 'until' ].getTime()) { | 130 | if (buildOriginallyPublishedAt(videoInfo).getTime() > program[ 'until' ].getTime()) { |
122 | console.log('Video "%s" has been published after "%s", don\'t upload it.\n', | 131 | log.info('Video "%s" has been published after "%s", don\'t upload it.\n', |
123 | videoInfo.title, formatDate(program[ 'until' ])); | 132 | videoInfo.title, formatDate(program[ 'until' ])); |
124 | return res(); | 133 | return res(); |
125 | } | 134 | } |
@@ -127,27 +136,27 @@ function processVideo (parameters: { | |||
127 | 136 | ||
128 | const result = await searchVideoWithSort(url, videoInfo.title, '-match') | 137 | const result = await searchVideoWithSort(url, videoInfo.title, '-match') |
129 | 138 | ||
130 | console.log('############################################################\n') | 139 | log.info('############################################################\n') |
131 | 140 | ||
132 | if (result.body.data.find(v => v.name === videoInfo.title)) { | 141 | if (result.body.data.find(v => v.name === videoInfo.title)) { |
133 | console.log('Video "%s" already exists, don\'t reupload it.\n', videoInfo.title) | 142 | log.info('Video "%s" already exists, don\'t reupload it.\n', videoInfo.title) |
134 | return res() | 143 | return res() |
135 | } | 144 | } |
136 | 145 | ||
137 | const path = join(cwd, sha256(videoInfo.url) + '.mp4') | 146 | const path = join(cwd, sha256(videoInfo.url) + '.mp4') |
138 | 147 | ||
139 | console.log('Downloading video "%s"...', videoInfo.title) | 148 | log.info('Downloading video "%s"...', videoInfo.title) |
140 | 149 | ||
141 | const options = [ '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best', '-o', path ] | 150 | const options = [ '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best', '-o', path ] |
142 | try { | 151 | try { |
143 | const youtubeDL = await safeGetYoutubeDL() | 152 | const youtubeDL = await safeGetYoutubeDL() |
144 | youtubeDL.exec(videoInfo.url, options, processOptions, async (err, output) => { | 153 | youtubeDL.exec(videoInfo.url, options, processOptions, async (err, output) => { |
145 | if (err) { | 154 | if (err) { |
146 | console.error(err) | 155 | log.error(err) |
147 | return res() | 156 | return res() |
148 | } | 157 | } |
149 | 158 | ||
150 | console.log(output.join('\n')) | 159 | log.info(output.join('\n')) |
151 | await uploadVideoOnPeerTube({ | 160 | await uploadVideoOnPeerTube({ |
152 | cwd, | 161 | cwd, |
153 | url, | 162 | url, |
@@ -158,7 +167,7 @@ function processVideo (parameters: { | |||
158 | return res() | 167 | return res() |
159 | }) | 168 | }) |
160 | } catch (err) { | 169 | } catch (err) { |
161 | console.log(err.message) | 170 | log.error(err.message) |
162 | return res() | 171 | return res() |
163 | } | 172 | } |
164 | }) | 173 | }) |
@@ -217,7 +226,7 @@ async function uploadVideoOnPeerTube (parameters: { | |||
217 | fixture: videoPath | 226 | fixture: videoPath |
218 | }) | 227 | }) |
219 | 228 | ||
220 | console.log('\nUploading on PeerTube video "%s".', videoAttributes.name) | 229 | log.info('\nUploading on PeerTube video "%s".', videoAttributes.name) |
221 | 230 | ||
222 | let accessToken = await getAccessTokenOrDie(url, user) | 231 | let accessToken = await getAccessTokenOrDie(url, user) |
223 | 232 | ||
@@ -225,21 +234,20 @@ async function uploadVideoOnPeerTube (parameters: { | |||
225 | await uploadVideo(url, accessToken, videoAttributes) | 234 | await uploadVideo(url, accessToken, videoAttributes) |
226 | } catch (err) { | 235 | } catch (err) { |
227 | if (err.message.indexOf('401') !== -1) { | 236 | if (err.message.indexOf('401') !== -1) { |
228 | console.log('Got 401 Unauthorized, token may have expired, renewing token and retry.') | 237 | log.info('Got 401 Unauthorized, token may have expired, renewing token and retry.') |
229 | 238 | ||
230 | accessToken = await getAccessTokenOrDie(url, user) | 239 | accessToken = await getAccessTokenOrDie(url, user) |
231 | 240 | ||
232 | await uploadVideo(url, accessToken, videoAttributes) | 241 | await uploadVideo(url, accessToken, videoAttributes) |
233 | } else { | 242 | } else { |
234 | console.log(err.message) | 243 | exitError(err.message) |
235 | process.exit(1) | ||
236 | } | 244 | } |
237 | } | 245 | } |
238 | 246 | ||
239 | await remove(videoPath) | 247 | await remove(videoPath) |
240 | if (thumbnailfile) await remove(thumbnailfile) | 248 | if (thumbnailfile) await remove(thumbnailfile) |
241 | 249 | ||
242 | console.log('Uploaded video "%s"!\n', videoAttributes.name) | 250 | log.warn('Uploaded video "%s"!\n', videoAttributes.name) |
243 | } | 251 | } |
244 | 252 | ||
245 | /* ---------------------------------------------------------- */ | 253 | /* ---------------------------------------------------------- */ |
@@ -355,20 +363,17 @@ async function getAccessTokenOrDie (url: string, user: UserInfo) { | |||
355 | const res = await login(url, client, user) | 363 | const res = await login(url, client, user) |
356 | return res.body.access_token | 364 | return res.body.access_token |
357 | } catch (err) { | 365 | } catch (err) { |
358 | console.error('Cannot authenticate. Please check your username/password.') | 366 | exitError('Cannot authenticate. Please check your username/password.') |
359 | process.exit(-1) | ||
360 | } | 367 | } |
361 | } | 368 | } |
362 | 369 | ||
363 | function parseDate (dateAsStr: string): Date { | 370 | function parseDate (dateAsStr: string): Date { |
364 | if (!/\d{4}-\d{2}-\d{2}/.test(dateAsStr)) { | 371 | if (!/\d{4}-\d{2}-\d{2}/.test(dateAsStr)) { |
365 | console.error(`Invalid date passed: ${dateAsStr}. Expected format: YYYY-MM-DD. See help for usage.`); | 372 | exitError(`Invalid date passed: ${dateAsStr}. Expected format: YYYY-MM-DD. See help for usage.`); |
366 | process.exit(-1); | ||
367 | } | 373 | } |
368 | const date = new Date(dateAsStr); | 374 | const date = new Date(dateAsStr); |
369 | if (isNaN(date.getTime())) { | 375 | if (isNaN(date.getTime())) { |
370 | console.error(`Invalid date passed: ${dateAsStr}. See help for usage.`); | 376 | exitError(`Invalid date passed: ${dateAsStr}. See help for usage.`); |
371 | process.exit(-1); | ||
372 | } | 377 | } |
373 | return date; | 378 | return date; |
374 | } | 379 | } |
@@ -376,3 +381,9 @@ function parseDate (dateAsStr: string): Date { | |||
376 | function formatDate (date: Date): string { | 381 | function formatDate (date: Date): string { |
377 | return date.toISOString().split('T')[0]; | 382 | return date.toISOString().split('T')[0]; |
378 | } | 383 | } |
384 | |||
385 | function exitError (message:string, ...meta: any[]) { | ||
386 | // use console.error instead of log.error here | ||
387 | console.error(message, ...meta) | ||
388 | process.exit(-1) | ||
389 | } | ||
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 @@ | |||
1 | import { Activity } from '../../shared/models/activitypub' | 1 | import { Activity } from '../../shared/models/activitypub' |
2 | import { ActorModel } from '../models/activitypub/actor' | 2 | import { MActorDefault, MActorSignature } from './models' |
3 | import { SignatureActorModel } from './models' | ||
4 | 3 | ||
5 | export type APProcessorOptions<T extends Activity> = { | 4 | export type APProcessorOptions<T extends Activity> = { |
6 | activity: T | 5 | activity: T |
7 | byActor: SignatureActorModel | 6 | byActor: MActorSignature |
8 | inboxActor?: ActorModel | 7 | inboxActor?: MActorDefault |
9 | fromFetch?: boolean | 8 | fromFetch?: boolean |
10 | } | 9 | } |
diff --git a/server/typings/express.ts b/server/typings/express.ts index f7da55ab0..3cc7c7632 100644 --- a/server/typings/express.ts +++ b/server/typings/express.ts | |||
@@ -1,89 +1,103 @@ | |||
1 | import { VideoChannelModel } from '../models/video/video-channel' | ||
2 | import { VideoPlaylistModel } from '../models/video/video-playlist' | ||
3 | import { VideoPlaylistElementModel } from '../models/video/video-playlist-element' | ||
4 | import { UserModel } from '../models/account/user' | ||
5 | import { VideoModel } from '../models/video/video' | ||
6 | import { AccountModel } from '../models/account/account' | ||
7 | import { VideoChangeOwnershipModel } from '../models/video/video-change-ownership' | ||
8 | import { ActorModel } from '../models/activitypub/actor' | ||
9 | import { VideoCommentModel } from '../models/video/video-comment' | ||
10 | import { VideoShareModel } from '../models/video/video-share' | ||
11 | import { AccountVideoRateModel } from '../models/account/account-video-rate' | ||
12 | import { ActorFollowModel } from '../models/activitypub/actor-follow' | ||
13 | import { ServerModel } from '../models/server/server' | ||
14 | import { VideoFileModel } from '../models/video/video-file' | ||
15 | import { VideoRedundancyModel } from '../models/redundancy/video-redundancy' | ||
16 | import { ServerBlocklistModel } from '../models/server/server-blocklist' | ||
17 | import { AccountBlocklistModel } from '../models/account/account-blocklist' | ||
18 | import { VideoImportModel } from '../models/video/video-import' | ||
19 | import { VideoAbuseModel } from '../models/video/video-abuse' | ||
20 | import { VideoBlacklistModel } from '../models/video/video-blacklist' | ||
21 | import { VideoCaptionModel } from '../models/video/video-caption' | ||
22 | import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' | ||
23 | import { RegisteredPlugin } from '../lib/plugins/plugin-manager' | 1 | import { RegisteredPlugin } from '../lib/plugins/plugin-manager' |
24 | import { PluginModel } from '../models/server/plugin' | 2 | import { |
25 | import { SignatureActorModel } from './models' | 3 | MAccountDefault, |
4 | MActorAccountChannelId, | ||
5 | MActorFollowActorsDefault, | ||
6 | MActorFollowActorsDefaultSubscription, | ||
7 | MActorFull, | ||
8 | MChannelAccountDefault, | ||
9 | MComment, | ||
10 | MCommentOwnerVideoReply, | ||
11 | MUserDefault, | ||
12 | MVideoAbuse, | ||
13 | MVideoBlacklist, | ||
14 | MVideoCaptionVideo, | ||
15 | MVideoFullLight, | ||
16 | MVideoIdThumbnail, | ||
17 | MVideoRedundancyVideo, | ||
18 | MVideoShareActor, | ||
19 | MVideoThumbnail, | ||
20 | MVideoWithRights | ||
21 | } from './models' | ||
22 | import { MVideoPlaylistFull, MVideoPlaylistFullSummary } from './models/video/video-playlist' | ||
23 | import { MVideoImportDefault } from '@server/typings/models/video/video-import' | ||
24 | import { MAccountBlocklist, MStreamingPlaylist, MVideoFile } from '@server/typings/models' | ||
25 | import { MVideoPlaylistElement, MVideoPlaylistElementVideoUrlPlaylistPrivacy } from '@server/typings/models/video/video-playlist-element' | ||
26 | import { MAccountVideoRateAccountVideo } from '@server/typings/models/video/video-rate' | ||
27 | import { MVideoChangeOwnershipFull } from './models/video/video-change-ownership' | ||
28 | import { MPlugin, MServer } from '@server/typings/models/server' | ||
29 | import { MServerBlocklist } from './models/server/server-blocklist' | ||
30 | import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token' | ||
26 | 31 | ||
27 | declare module 'express' { | 32 | declare module 'express' { |
28 | 33 | ||
29 | interface Response { | 34 | interface Response { |
35 | |||
30 | locals: { | 36 | locals: { |
31 | video?: VideoModel | 37 | videoAll?: MVideoFullLight |
32 | videoShare?: VideoShareModel | 38 | onlyVideo?: MVideoThumbnail |
33 | videoFile?: VideoFileModel | 39 | onlyVideoWithRights?: MVideoWithRights |
40 | videoId?: MVideoIdThumbnail | ||
41 | |||
42 | videoShare?: MVideoShareActor | ||
43 | |||
44 | videoFile?: MVideoFile | ||
45 | |||
46 | videoImport?: MVideoImportDefault | ||
47 | |||
48 | videoBlacklist?: MVideoBlacklist | ||
49 | |||
50 | videoCaption?: MVideoCaptionVideo | ||
51 | |||
52 | videoAbuse?: MVideoAbuse | ||
34 | 53 | ||
35 | videoImport?: VideoImportModel | 54 | videoStreamingPlaylist?: MStreamingPlaylist |
36 | 55 | ||
37 | videoBlacklist?: VideoBlacklistModel | 56 | videoChannel?: MChannelAccountDefault |
38 | 57 | ||
39 | videoCaption?: VideoCaptionModel | 58 | videoPlaylistFull?: MVideoPlaylistFull |
59 | videoPlaylistSummary?: MVideoPlaylistFullSummary | ||
40 | 60 | ||
41 | videoAbuse?: VideoAbuseModel | 61 | videoPlaylistElement?: MVideoPlaylistElement |
62 | videoPlaylistElementAP?: MVideoPlaylistElementVideoUrlPlaylistPrivacy | ||
42 | 63 | ||
43 | videoStreamingPlaylist?: VideoStreamingPlaylistModel | 64 | accountVideoRate?: MAccountVideoRateAccountVideo |
44 | 65 | ||
45 | videoChannel?: VideoChannelModel | 66 | videoCommentFull?: MCommentOwnerVideoReply |
67 | videoCommentThread?: MComment | ||
46 | 68 | ||
47 | videoPlaylist?: VideoPlaylistModel | 69 | follow?: MActorFollowActorsDefault |
48 | videoPlaylistElement?: VideoPlaylistElementModel | 70 | subscription?: MActorFollowActorsDefaultSubscription |
49 | 71 | ||
50 | accountVideoRate?: AccountVideoRateModel | 72 | nextOwner?: MAccountDefault |
73 | videoChangeOwnership?: MVideoChangeOwnershipFull | ||
51 | 74 | ||
52 | videoComment?: VideoCommentModel | 75 | account?: MAccountDefault |
53 | videoCommentThread?: VideoCommentModel | ||
54 | 76 | ||
55 | follow?: ActorFollowModel | 77 | actorFull?: MActorFull |
56 | subscription?: ActorFollowModel | ||
57 | 78 | ||
58 | nextOwner?: AccountModel | 79 | user?: MUserDefault |
59 | videoChangeOwnership?: VideoChangeOwnershipModel | ||
60 | account?: AccountModel | ||
61 | actor?: ActorModel | ||
62 | user?: UserModel | ||
63 | 80 | ||
64 | server?: ServerModel | 81 | server?: MServer |
65 | 82 | ||
66 | videoRedundancy?: VideoRedundancyModel | 83 | videoRedundancy?: MVideoRedundancyVideo |
67 | 84 | ||
68 | accountBlock?: AccountBlocklistModel | 85 | accountBlock?: MAccountBlocklist |
69 | serverBlock?: ServerBlocklistModel | 86 | serverBlock?: MServerBlocklist |
70 | 87 | ||
71 | oauth?: { | 88 | oauth?: { |
72 | token: { | 89 | token: MOAuthTokenUser |
73 | User: UserModel | ||
74 | user: UserModel | ||
75 | } | ||
76 | } | 90 | } |
77 | 91 | ||
78 | signature?: { | 92 | signature?: { |
79 | actor: SignatureActorModel | 93 | actor: MActorAccountChannelId |
80 | } | 94 | } |
81 | 95 | ||
82 | authenticated?: boolean | 96 | authenticated?: boolean |
83 | 97 | ||
84 | registeredPlugin?: RegisteredPlugin | 98 | registeredPlugin?: RegisteredPlugin |
85 | 99 | ||
86 | plugin?: PluginModel | 100 | plugin?: MPlugin |
87 | } | 101 | } |
88 | } | 102 | } |
89 | } | 103 | } |
diff --git a/server/typings/models/account/account-blocklist.ts b/server/typings/models/account/account-blocklist.ts new file mode 100644 index 000000000..c9cb55332 --- /dev/null +++ b/server/typings/models/account/account-blocklist.ts | |||
@@ -0,0 +1,25 @@ | |||
1 | import { AccountBlocklistModel } from '../../../models/account/account-blocklist' | ||
2 | import { PickWith } from '../../utils' | ||
3 | import { MAccountDefault, MAccountFormattable } from './account' | ||
4 | |||
5 | type Use<K extends keyof AccountBlocklistModel, M> = PickWith<AccountBlocklistModel, K, M> | ||
6 | |||
7 | // ############################################################################ | ||
8 | |||
9 | export type MAccountBlocklist = Omit<AccountBlocklistModel, 'ByAccount' | 'BlockedAccount'> | ||
10 | |||
11 | // ############################################################################ | ||
12 | |||
13 | export type MAccountBlocklistId = Pick<AccountBlocklistModel, 'id'> | ||
14 | |||
15 | export type MAccountBlocklistAccounts = MAccountBlocklist & | ||
16 | Use<'ByAccount', MAccountDefault> & | ||
17 | Use<'BlockedAccount', MAccountDefault> | ||
18 | |||
19 | // ############################################################################ | ||
20 | |||
21 | // Format for API or AP object | ||
22 | |||
23 | export type MAccountBlocklistFormattable = Pick<MAccountBlocklist, 'createdAt'> & | ||
24 | Use<'ByAccount', MAccountFormattable> & | ||
25 | Use<'BlockedAccount', MAccountFormattable> | ||
diff --git a/server/typings/models/account/account.ts b/server/typings/models/account/account.ts new file mode 100644 index 000000000..ec78fece8 --- /dev/null +++ b/server/typings/models/account/account.ts | |||
@@ -0,0 +1,95 @@ | |||
1 | import { AccountModel } from '../../../models/account/account' | ||
2 | import { | ||
3 | MActor, | ||
4 | MActorAP, | ||
5 | MActorAPI, | ||
6 | MActorAudience, | ||
7 | MActorDefault, | ||
8 | MActorDefaultLight, | ||
9 | MActorFormattable, | ||
10 | MActorId, | ||
11 | MActorServer, | ||
12 | MActorSummary, | ||
13 | MActorSummaryFormattable, | ||
14 | MActorUrl | ||
15 | } from './actor' | ||
16 | import { FunctionProperties, PickWith } from '../../utils' | ||
17 | import { MAccountBlocklistId } from './account-blocklist' | ||
18 | import { MChannelDefault } from '@server/typings/models' | ||
19 | |||
20 | type Use<K extends keyof AccountModel, M> = PickWith<AccountModel, K, M> | ||
21 | |||
22 | // ############################################################################ | ||
23 | |||
24 | export type MAccount = Omit<AccountModel, 'Actor' | 'User' | 'Application' | 'VideoChannels' | 'VideoPlaylists' | | ||
25 | 'VideoComments' | 'BlockedAccounts'> | ||
26 | |||
27 | // ############################################################################ | ||
28 | |||
29 | // Only some attributes | ||
30 | export type MAccountId = Pick<MAccount, 'id'> | ||
31 | export type MAccountUserId = Pick<MAccount, 'userId'> | ||
32 | |||
33 | // Only some Actor attributes | ||
34 | export type MAccountUrl = Use<'Actor', MActorUrl> | ||
35 | export type MAccountAudience = Use<'Actor', MActorAudience> | ||
36 | |||
37 | export type MAccountIdActor = MAccountId & | ||
38 | Use<'Actor', MActor> | ||
39 | |||
40 | export type MAccountIdActorId = MAccountId & | ||
41 | Use<'Actor', MActorId> | ||
42 | |||
43 | // ############################################################################ | ||
44 | |||
45 | // Default scope | ||
46 | export type MAccountDefault = MAccount & | ||
47 | Use<'Actor', MActorDefault> | ||
48 | |||
49 | // Default with default association scopes | ||
50 | export type MAccountDefaultChannelDefault = MAccount & | ||
51 | Use<'Actor', MActorDefault> & | ||
52 | Use<'VideoChannels', MChannelDefault[]> | ||
53 | |||
54 | // We don't need some actors attributes | ||
55 | export type MAccountLight = MAccount & | ||
56 | Use<'Actor', MActorDefaultLight> | ||
57 | |||
58 | // ############################################################################ | ||
59 | |||
60 | // Full actor | ||
61 | export type MAccountActor = MAccount & | ||
62 | Use<'Actor', MActor> | ||
63 | |||
64 | // Full actor with server | ||
65 | export type MAccountServer = MAccount & | ||
66 | Use<'Actor', MActorServer> | ||
67 | |||
68 | // ############################################################################ | ||
69 | |||
70 | // For API | ||
71 | |||
72 | export type MAccountSummary = FunctionProperties<MAccount> & | ||
73 | Pick<MAccount, 'id' | 'name'> & | ||
74 | Use<'Actor', MActorSummary> | ||
75 | |||
76 | export type MAccountSummaryBlocks = MAccountSummary & | ||
77 | Use<'BlockedAccounts', MAccountBlocklistId[]> | ||
78 | |||
79 | export type MAccountAPI = MAccount & | ||
80 | Use<'Actor', MActorAPI> | ||
81 | |||
82 | // ############################################################################ | ||
83 | |||
84 | // Format for API or AP object | ||
85 | |||
86 | export type MAccountSummaryFormattable = FunctionProperties<MAccount> & | ||
87 | Pick<MAccount, 'id' | 'name'> & | ||
88 | Use<'Actor', MActorSummaryFormattable> | ||
89 | |||
90 | export type MAccountFormattable = FunctionProperties<MAccount> & | ||
91 | Pick<MAccount, 'id' | 'name' | 'description' | 'createdAt' | 'updatedAt' | 'userId'> & | ||
92 | Use<'Actor', MActorFormattable> | ||
93 | |||
94 | export type MAccountAP = Pick<MAccount, 'name' | 'description'> & | ||
95 | Use<'Actor', MActorAP> | ||
diff --git a/server/typings/models/account/actor-follow.ts b/server/typings/models/account/actor-follow.ts new file mode 100644 index 000000000..1c66eb0a0 --- /dev/null +++ b/server/typings/models/account/actor-follow.ts | |||
@@ -0,0 +1,63 @@ | |||
1 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | ||
2 | import { | ||
3 | MActor, | ||
4 | MActorAccount, | ||
5 | MActorDefaultAccountChannel, | ||
6 | MActorChannelAccountActor, | ||
7 | MActorDefault, | ||
8 | MActorFormattable, | ||
9 | MActorHost, | ||
10 | MActorUsername | ||
11 | } from './actor' | ||
12 | import { PickWith } from '../../utils' | ||
13 | import { ActorModel } from '@server/models/activitypub/actor' | ||
14 | import { MChannelDefault } from '@server/typings/models' | ||
15 | |||
16 | type Use<K extends keyof ActorFollowModel, M> = PickWith<ActorFollowModel, K, M> | ||
17 | |||
18 | // ############################################################################ | ||
19 | |||
20 | export type MActorFollow = Omit<ActorFollowModel, 'ActorFollower' | 'ActorFollowing'> | ||
21 | |||
22 | // ############################################################################ | ||
23 | |||
24 | export type MActorFollowFollowingHost = MActorFollow & | ||
25 | Use<'ActorFollowing', MActorUsername & MActorHost> | ||
26 | |||
27 | // ############################################################################ | ||
28 | |||
29 | // With actors or actors default | ||
30 | |||
31 | export type MActorFollowActors = MActorFollow & | ||
32 | Use<'ActorFollower', MActor> & | ||
33 | Use<'ActorFollowing', MActor> | ||
34 | |||
35 | export type MActorFollowActorsDefault = MActorFollow & | ||
36 | Use<'ActorFollower', MActorDefault> & | ||
37 | Use<'ActorFollowing', MActorDefault> | ||
38 | |||
39 | export type MActorFollowFull = MActorFollow & | ||
40 | Use<'ActorFollower', MActorDefaultAccountChannel> & | ||
41 | Use<'ActorFollowing', MActorDefaultAccountChannel> | ||
42 | |||
43 | // ############################################################################ | ||
44 | |||
45 | // For subscriptions | ||
46 | |||
47 | type SubscriptionFollowing = MActorDefault & | ||
48 | PickWith<ActorModel, 'VideoChannel', MChannelDefault> | ||
49 | |||
50 | export type MActorFollowActorsDefaultSubscription = MActorFollow & | ||
51 | Use<'ActorFollower', MActorDefault> & | ||
52 | Use<'ActorFollowing', SubscriptionFollowing> | ||
53 | |||
54 | export type MActorFollowSubscriptions = MActorFollow & | ||
55 | Use<'ActorFollowing', MActorChannelAccountActor> | ||
56 | |||
57 | // ############################################################################ | ||
58 | |||
59 | // Format for API or AP object | ||
60 | |||
61 | export type MActorFollowFormattable = Pick<MActorFollow, 'id' | 'score' | 'state' | 'createdAt' | 'updatedAt'> & | ||
62 | Use<'ActorFollower', MActorFormattable> & | ||
63 | Use<'ActorFollowing', MActorFormattable> | ||
diff --git a/server/typings/models/account/actor.ts b/server/typings/models/account/actor.ts new file mode 100644 index 000000000..bcacb8351 --- /dev/null +++ b/server/typings/models/account/actor.ts | |||
@@ -0,0 +1,121 @@ | |||
1 | import { ActorModel } from '../../../models/activitypub/actor' | ||
2 | import { FunctionProperties, PickWith, PickWithOpt } from '../../utils' | ||
3 | import { MAccount, MAccountDefault, MAccountId, MAccountIdActor } from './account' | ||
4 | import { MServer, MServerHost, MServerHostBlocks, MServerRedundancyAllowed } from '../server' | ||
5 | import { MAvatar, MAvatarFormattable } from './avatar' | ||
6 | import { MChannel, MChannelAccountActor, MChannelAccountDefault, MChannelId, MChannelIdActor } from '../video' | ||
7 | |||
8 | type Use<K extends keyof ActorModel, M> = PickWith<ActorModel, K, M> | ||
9 | |||
10 | // ############################################################################ | ||
11 | |||
12 | export type MActor = Omit<ActorModel, 'Account' | 'VideoChannel' | 'ActorFollowing' | 'Avatar' | 'ActorFollowers' | 'Server'> | ||
13 | |||
14 | // ############################################################################ | ||
15 | |||
16 | export type MActorUrl = Pick<MActor, 'url'> | ||
17 | export type MActorId = Pick<MActor, 'id'> | ||
18 | export type MActorUsername = Pick<MActor, 'preferredUsername'> | ||
19 | |||
20 | export type MActorFollowersUrl = Pick<MActor, 'followersUrl'> | ||
21 | export type MActorAudience = MActorUrl & MActorFollowersUrl | ||
22 | export type MActorFollowerException = Pick<ActorModel, 'sharedInboxUrl' | 'inboxUrl'> | ||
23 | export type MActorSignature = MActorAccountChannelId | ||
24 | |||
25 | export type MActorLight = Omit<MActor, 'privateKey' | 'privateKey'> | ||
26 | |||
27 | // ############################################################################ | ||
28 | |||
29 | // Some association attributes | ||
30 | |||
31 | export type MActorHost = Use<'Server', MServerHost> | ||
32 | export type MActorRedundancyAllowedOpt = PickWithOpt<ActorModel, 'Server', MServerRedundancyAllowed> | ||
33 | |||
34 | export type MActorDefaultLight = MActorLight & | ||
35 | Use<'Server', MServerHost> & | ||
36 | Use<'Avatar', MAvatar> | ||
37 | |||
38 | export type MActorAccountId = MActor & | ||
39 | Use<'Account', MAccountId> | ||
40 | export type MActorAccountIdActor = MActor & | ||
41 | Use<'Account', MAccountIdActor> | ||
42 | |||
43 | export type MActorChannelId = MActor & | ||
44 | Use<'VideoChannel', MChannelId> | ||
45 | export type MActorChannelIdActor = MActor & | ||
46 | Use<'VideoChannel', MChannelIdActor> | ||
47 | |||
48 | export type MActorAccountChannelId = MActorAccountId & MActorChannelId | ||
49 | export type MActorAccountChannelIdActor = MActorAccountIdActor & MActorChannelIdActor | ||
50 | |||
51 | // ############################################################################ | ||
52 | |||
53 | // Include raw account/channel/server | ||
54 | |||
55 | export type MActorAccount = MActor & | ||
56 | Use<'Account', MAccount> | ||
57 | |||
58 | export type MActorChannel = MActor & | ||
59 | Use<'VideoChannel', MChannel> | ||
60 | |||
61 | export type MActorDefaultAccountChannel = MActorDefault & MActorAccount & MActorChannel | ||
62 | |||
63 | export type MActorServer = MActor & | ||
64 | Use<'Server', MServer> | ||
65 | |||
66 | // ############################################################################ | ||
67 | |||
68 | // Complex actor associations | ||
69 | |||
70 | export type MActorDefault = MActor & | ||
71 | Use<'Server', MServer> & | ||
72 | Use<'Avatar', MAvatar> | ||
73 | |||
74 | // Actor with channel that is associated to an account and its actor | ||
75 | // Actor -> VideoChannel -> Account -> Actor | ||
76 | export type MActorChannelAccountActor = MActor & | ||
77 | Use<'VideoChannel', MChannelAccountActor> | ||
78 | |||
79 | export type MActorFull = MActor & | ||
80 | Use<'Server', MServer> & | ||
81 | Use<'Avatar', MAvatar> & | ||
82 | Use<'Account', MAccount> & | ||
83 | Use<'VideoChannel', MChannelAccountActor> | ||
84 | |||
85 | // Same than ActorFull, but the account and the channel have their actor | ||
86 | export type MActorFullActor = MActor & | ||
87 | Use<'Server', MServer> & | ||
88 | Use<'Avatar', MAvatar> & | ||
89 | Use<'Account', MAccountDefault> & | ||
90 | Use<'VideoChannel', MChannelAccountDefault> | ||
91 | |||
92 | // ############################################################################ | ||
93 | |||
94 | // API | ||
95 | |||
96 | export type MActorSummary = FunctionProperties<MActor> & | ||
97 | Pick<MActor, 'id' | 'preferredUsername' | 'url' | 'serverId' | 'avatarId'> & | ||
98 | Use<'Server', MServerHost> & | ||
99 | Use<'Avatar', MAvatar> | ||
100 | |||
101 | export type MActorSummaryBlocks = MActorSummary & | ||
102 | Use<'Server', MServerHostBlocks> | ||
103 | |||
104 | export type MActorAPI = Omit<MActorDefault, 'publicKey' | 'privateKey' | 'inboxUrl' | 'outboxUrl' | 'sharedInboxUrl' | | ||
105 | 'followersUrl' | 'followingUrl' | 'url' | 'createdAt' | 'updatedAt'> | ||
106 | |||
107 | // ############################################################################ | ||
108 | |||
109 | // Format for API or AP object | ||
110 | |||
111 | export type MActorSummaryFormattable = FunctionProperties<MActor> & | ||
112 | Pick<MActor, 'url' | 'preferredUsername'> & | ||
113 | Use<'Server', MServerHost> & | ||
114 | Use<'Avatar', MAvatarFormattable> | ||
115 | |||
116 | export type MActorFormattable = MActorSummaryFormattable & | ||
117 | Pick<MActor, 'id' | 'followingCount' | 'followersCount' | 'createdAt' | 'updatedAt'> & | ||
118 | Use<'Server', MServerHost & Partial<Pick<MServer, 'redundancyAllowed'>>> | ||
119 | |||
120 | export type MActorAP = MActor & | ||
121 | Use<'Avatar', MAvatar> | ||
diff --git a/server/typings/models/account/avatar.ts b/server/typings/models/account/avatar.ts new file mode 100644 index 000000000..8af6cc787 --- /dev/null +++ b/server/typings/models/account/avatar.ts | |||
@@ -0,0 +1,11 @@ | |||
1 | import { AvatarModel } from '../../../models/avatar/avatar' | ||
2 | import { FunctionProperties } from '@server/typings/utils' | ||
3 | |||
4 | export type MAvatar = AvatarModel | ||
5 | |||
6 | // ############################################################################ | ||
7 | |||
8 | // Format for API or AP object | ||
9 | |||
10 | export type MAvatarFormattable = FunctionProperties<MAvatar> & | ||
11 | Pick<MAvatar, 'filename' | 'createdAt' | 'updatedAt'> | ||
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 @@ | |||
1 | export * from './account' | ||
2 | export * from './account-blocklist' | ||
3 | export * from './actor' | ||
4 | export * from './actor-follow' | ||
5 | 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 @@ | |||
1 | import { ActorFollowModel } from '../../models/activitypub/actor-follow' | ||
2 | import { ActorModelOnly } from './actor' | ||
3 | |||
4 | export type ActorFollowModelOnly = Omit<ActorFollowModel, 'ActorFollower' | 'ActorFollowing'> | ||
5 | export type ActorFollowModelLight = ActorFollowModelOnly & { | ||
6 | ActorFollower: ActorModelOnly | ||
7 | ActorFollowing: ActorModelOnly | ||
8 | } | ||
diff --git a/server/typings/models/actor.ts b/server/typings/models/actor.ts deleted file mode 100644 index 2656c7b66..000000000 --- a/server/typings/models/actor.ts +++ /dev/null | |||
@@ -1,22 +0,0 @@ | |||
1 | import { ActorModel } from '../../models/activitypub/actor' | ||
2 | import { VideoChannelModel } from '../../models/video/video-channel' | ||
3 | import { AccountModel } from '../../models/account/account' | ||
4 | import { FunctionProperties } from '../utils' | ||
5 | |||
6 | export type VideoChannelModelId = FunctionProperties<VideoChannelModel> | ||
7 | export type AccountModelId = FunctionProperties<AccountModel> | Pick<AccountModel, 'id'> | ||
8 | |||
9 | export type VideoChannelModelIdActor = VideoChannelModelId & Pick<VideoChannelModel, 'Actor'> | ||
10 | export type AccountModelIdActor = AccountModelId & Pick<AccountModel, 'Actor'> | ||
11 | |||
12 | export type ActorModelUrl = Pick<ActorModel, 'url'> | ||
13 | export type ActorModelOnly = Omit<ActorModel, 'Account' | 'VideoChannel' | 'ActorFollowing' | 'Avatar' | 'ActorFollowers' | 'Server'> | ||
14 | export type ActorModelId = Pick<ActorModelOnly, 'id'> | ||
15 | |||
16 | export type SignatureActorModel = ActorModelOnly & { | ||
17 | VideoChannel: VideoChannelModelIdActor | ||
18 | |||
19 | Account: AccountModelIdActor | ||
20 | } | ||
21 | |||
22 | export type ActorFollowerException = Pick<ActorModel, 'sharedInboxUrl' | 'inboxUrl'> | ||
diff --git a/server/typings/models/index.d.ts b/server/typings/models/index.d.ts index c90656965..78b4948ce 100644 --- a/server/typings/models/index.d.ts +++ b/server/typings/models/index.d.ts | |||
@@ -1 +1,5 @@ | |||
1 | export * from './actor' | 1 | export * from './account' |
2 | export * from './oauth' | ||
3 | export * from './server' | ||
4 | export * from './user' | ||
5 | export * from './video' | ||
diff --git a/server/typings/models/oauth/index.d.ts b/server/typings/models/oauth/index.d.ts new file mode 100644 index 000000000..36b7ea8ca --- /dev/null +++ b/server/typings/models/oauth/index.d.ts | |||
@@ -0,0 +1,2 @@ | |||
1 | export * from './oauth-client' | ||
2 | export * from './oauth-token' | ||
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 @@ | |||
1 | import { OAuthClientModel } from '@server/models/oauth/oauth-client' | ||
2 | |||
3 | export type MOAuthClient = Omit<OAuthClientModel, 'OAuthTokens'> | ||
diff --git a/server/typings/models/oauth/oauth-token.ts b/server/typings/models/oauth/oauth-token.ts new file mode 100644 index 000000000..af3412925 --- /dev/null +++ b/server/typings/models/oauth/oauth-token.ts | |||
@@ -0,0 +1,13 @@ | |||
1 | import { OAuthTokenModel } from '@server/models/oauth/oauth-token' | ||
2 | import { PickWith } from '@server/typings/utils' | ||
3 | import { MUserAccountUrl } from '@server/typings/models' | ||
4 | |||
5 | type Use<K extends keyof OAuthTokenModel, M> = PickWith<OAuthTokenModel, K, M> | ||
6 | |||
7 | // ############################################################################ | ||
8 | |||
9 | export type MOAuthToken = Omit<OAuthTokenModel, 'User' | 'OAuthClients'> | ||
10 | |||
11 | export type MOAuthTokenUser = MOAuthToken & | ||
12 | Use<'User', MUserAccountUrl> & | ||
13 | { 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 @@ | |||
1 | export * from './plugin' | ||
2 | export * from './server' | ||
3 | 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..94674c318 --- /dev/null +++ b/server/typings/models/server/plugin.ts | |||
@@ -0,0 +1,10 @@ | |||
1 | import { PluginModel } from '@server/models/server/plugin' | ||
2 | |||
3 | export type MPlugin = PluginModel | ||
4 | |||
5 | // ############################################################################ | ||
6 | |||
7 | // Format for API or AP object | ||
8 | |||
9 | export type MPluginFormattable = Pick<MPlugin, 'name' | 'type' | 'version' | 'latestVersion' | 'enabled' | 'uninstalled' | ||
10 | | 'peertubeEngine' | 'description' | 'homepage' | 'settings' | 'createdAt' | 'updatedAt'> | ||
diff --git a/server/typings/models/server/server-blocklist.ts b/server/typings/models/server/server-blocklist.ts new file mode 100644 index 000000000..c81f604f5 --- /dev/null +++ b/server/typings/models/server/server-blocklist.ts | |||
@@ -0,0 +1,23 @@ | |||
1 | import { ServerBlocklistModel } from '@server/models/server/server-blocklist' | ||
2 | import { PickWith } from '@server/typings/utils' | ||
3 | import { MAccountDefault, MAccountFormattable, MServer, MServerFormattable } from '@server/typings/models' | ||
4 | |||
5 | type Use<K extends keyof ServerBlocklistModel, M> = PickWith<ServerBlocklistModel, K, M> | ||
6 | |||
7 | // ############################################################################ | ||
8 | |||
9 | export type MServerBlocklist = Omit<ServerBlocklistModel, 'ByAccount' | 'BlockedServer'> | ||
10 | |||
11 | // ############################################################################ | ||
12 | |||
13 | export type MServerBlocklistAccountServer = MServerBlocklist & | ||
14 | Use<'ByAccount', MAccountDefault> & | ||
15 | Use<'BlockedServer', MServer> | ||
16 | |||
17 | // ############################################################################ | ||
18 | |||
19 | // Format for API or AP object | ||
20 | |||
21 | export type MServerBlocklistFormattable = Pick<MServerBlocklist, 'createdAt'> & | ||
22 | Use<'ByAccount', MAccountFormattable> & | ||
23 | Use<'BlockedServer', MServerFormattable> | ||
diff --git a/server/typings/models/server/server.ts b/server/typings/models/server/server.ts new file mode 100644 index 000000000..190cc0c28 --- /dev/null +++ b/server/typings/models/server/server.ts | |||
@@ -0,0 +1,24 @@ | |||
1 | import { ServerModel } from '../../../models/server/server' | ||
2 | import { FunctionProperties, PickWith } from '../../utils' | ||
3 | import { MAccountBlocklistId } from '../account' | ||
4 | |||
5 | type Use<K extends keyof ServerModel, M> = PickWith<ServerModel, K, M> | ||
6 | |||
7 | // ############################################################################ | ||
8 | |||
9 | export type MServer = Omit<ServerModel, 'Actors' | 'BlockedByAccounts'> | ||
10 | |||
11 | // ############################################################################ | ||
12 | |||
13 | export type MServerHost = Pick<MServer, 'host'> | ||
14 | export type MServerRedundancyAllowed = Pick<MServer, 'redundancyAllowed'> | ||
15 | |||
16 | export type MServerHostBlocks = MServerHost & | ||
17 | Use<'BlockedByAccounts', MAccountBlocklistId[]> | ||
18 | |||
19 | // ############################################################################ | ||
20 | |||
21 | // Format for API or AP object | ||
22 | |||
23 | export type MServerFormattable = FunctionProperties<MServer> & | ||
24 | Pick<MServer, 'host'> | ||
diff --git a/server/typings/models/user/index.d.ts b/server/typings/models/user/index.d.ts new file mode 100644 index 000000000..6657b2128 --- /dev/null +++ b/server/typings/models/user/index.d.ts | |||
@@ -0,0 +1,4 @@ | |||
1 | export * from './user' | ||
2 | export * from './user-notification' | ||
3 | export * from './user-notification-setting' | ||
4 | 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..c674add1b --- /dev/null +++ b/server/typings/models/user/user-notification-setting.ts | |||
@@ -0,0 +1,9 @@ | |||
1 | import { UserNotificationSettingModel } from '@server/models/account/user-notification-setting' | ||
2 | |||
3 | export type MNotificationSetting = Omit<UserNotificationSettingModel, 'User'> | ||
4 | |||
5 | // ############################################################################ | ||
6 | |||
7 | // Format for API or AP object | ||
8 | |||
9 | export type MNotificationSettingFormattable = MNotificationSetting | ||
diff --git a/server/typings/models/user/user-notification.ts b/server/typings/models/user/user-notification.ts new file mode 100644 index 000000000..1cdc691b0 --- /dev/null +++ b/server/typings/models/user/user-notification.ts | |||
@@ -0,0 +1,78 @@ | |||
1 | import { UserNotificationModel } from '../../../models/account/user-notification' | ||
2 | import { PickWith, PickWithOpt } from '../../utils' | ||
3 | import { VideoModel } from '../../../models/video/video' | ||
4 | import { ActorModel } from '../../../models/activitypub/actor' | ||
5 | import { ServerModel } from '../../../models/server/server' | ||
6 | import { AvatarModel } from '../../../models/avatar/avatar' | ||
7 | import { VideoChannelModel } from '../../../models/video/video-channel' | ||
8 | import { AccountModel } from '../../../models/account/account' | ||
9 | import { VideoCommentModel } from '../../../models/video/video-comment' | ||
10 | import { VideoAbuseModel } from '../../../models/video/video-abuse' | ||
11 | import { VideoBlacklistModel } from '../../../models/video/video-blacklist' | ||
12 | import { VideoImportModel } from '../../../models/video/video-import' | ||
13 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | ||
14 | |||
15 | type Use<K extends keyof UserNotificationModel, M> = PickWith<UserNotificationModel, K, M> | ||
16 | |||
17 | // ############################################################################ | ||
18 | |||
19 | export namespace UserNotificationIncludes { | ||
20 | export type VideoInclude = Pick<VideoModel, 'id' | 'uuid' | 'name'> | ||
21 | export type VideoIncludeChannel = VideoInclude & | ||
22 | PickWith<VideoModel, 'VideoChannel', VideoChannelIncludeActor> | ||
23 | |||
24 | export type ActorInclude = Pick<ActorModel, 'preferredUsername' | 'getHost'> & | ||
25 | PickWith<ActorModel, 'Avatar', Pick<AvatarModel, 'filename' | 'getStaticPath'>> & | ||
26 | PickWith<ActorModel, 'Server', Pick<ServerModel, 'host'>> | ||
27 | |||
28 | export type VideoChannelInclude = Pick<VideoChannelModel, 'id' | 'name' | 'getDisplayName'> | ||
29 | export type VideoChannelIncludeActor = VideoChannelInclude & | ||
30 | PickWith<VideoChannelModel, 'Actor', ActorInclude> | ||
31 | |||
32 | export type AccountInclude = Pick<AccountModel, 'id' | 'name' | 'getDisplayName'> | ||
33 | export type AccountIncludeActor = AccountInclude & | ||
34 | PickWith<AccountModel, 'Actor', ActorInclude> | ||
35 | |||
36 | export type VideoCommentInclude = Pick<VideoCommentModel, 'id' | 'originCommentId' | 'getThreadId'> & | ||
37 | PickWith<VideoCommentModel, 'Account', AccountIncludeActor> & | ||
38 | PickWith<VideoCommentModel, 'Video', VideoInclude> | ||
39 | |||
40 | export type VideoAbuseInclude = Pick<VideoAbuseModel, 'id'> & | ||
41 | PickWith<VideoAbuseModel, 'Video', VideoInclude> | ||
42 | |||
43 | export type VideoBlacklistInclude = Pick<VideoBlacklistModel, 'id'> & | ||
44 | PickWith<VideoAbuseModel, 'Video', VideoInclude> | ||
45 | |||
46 | export type VideoImportInclude = Pick<VideoImportModel, 'id' | 'magnetUri' | 'targetUrl' | 'torrentName'> & | ||
47 | PickWith<VideoImportModel, 'Video', VideoInclude> | ||
48 | |||
49 | export type ActorFollower = Pick<ActorModel, 'preferredUsername' | 'getHost'> & | ||
50 | PickWith<ActorModel, 'Account', AccountInclude> & | ||
51 | PickWith<ActorModel, 'Server', Pick<ServerModel, 'host'>> & | ||
52 | PickWithOpt<ActorModel, 'Avatar', Pick<AvatarModel, 'filename' | 'getStaticPath'>> | ||
53 | |||
54 | export type ActorFollowing = Pick<ActorModel, 'preferredUsername' | 'type' | 'getHost'> & | ||
55 | PickWith<ActorModel, 'VideoChannel', VideoChannelInclude> & | ||
56 | PickWith<ActorModel, 'Account', AccountInclude> & | ||
57 | PickWith<ActorModel, 'Server', Pick<ServerModel, 'host'>> | ||
58 | |||
59 | export type ActorFollowInclude = Pick<ActorFollowModel, 'id' | 'state'> & | ||
60 | PickWith<ActorFollowModel, 'ActorFollower', ActorFollower> & | ||
61 | PickWith<ActorFollowModel, 'ActorFollowing', ActorFollowing> | ||
62 | } | ||
63 | |||
64 | // ############################################################################ | ||
65 | |||
66 | export type MUserNotification = Omit<UserNotificationModel, 'User' | 'Video' | 'Comment' | 'VideoAbuse' | 'VideoBlacklist' | | ||
67 | 'VideoImport' | 'Account' | 'ActorFollow'> | ||
68 | |||
69 | // ############################################################################ | ||
70 | |||
71 | export type UserNotificationModelForApi = MUserNotification & | ||
72 | Use<'Video', UserNotificationIncludes.VideoIncludeChannel> & | ||
73 | Use<'Comment', UserNotificationIncludes.VideoCommentInclude> & | ||
74 | Use<'VideoAbuse', UserNotificationIncludes.VideoAbuseInclude> & | ||
75 | Use<'VideoBlacklist', UserNotificationIncludes.VideoBlacklistInclude> & | ||
76 | Use<'VideoImport', UserNotificationIncludes.VideoImportInclude> & | ||
77 | Use<'ActorFollow', UserNotificationIncludes.ActorFollowInclude> & | ||
78 | Use<'Account', UserNotificationIncludes.AccountIncludeActor> | ||
diff --git a/server/typings/models/user/user-video-history.ts b/server/typings/models/user/user-video-history.ts new file mode 100644 index 000000000..62673ab1b --- /dev/null +++ b/server/typings/models/user/user-video-history.ts | |||
@@ -0,0 +1,5 @@ | |||
1 | import { UserVideoHistoryModel } from '../../../models/account/user-video-history' | ||
2 | |||
3 | export type MUserVideoHistory = Omit<UserVideoHistoryModel, 'Video' | 'User'> | ||
4 | |||
5 | export type MUserVideoHistoryTime = Pick<MUserVideoHistory, 'currentTime'> | ||
diff --git a/server/typings/models/user/user.ts b/server/typings/models/user/user.ts new file mode 100644 index 000000000..52d6d4a05 --- /dev/null +++ b/server/typings/models/user/user.ts | |||
@@ -0,0 +1,70 @@ | |||
1 | import { UserModel } from '../../../models/account/user' | ||
2 | import { PickWith, PickWithOpt } from '../../utils' | ||
3 | import { | ||
4 | MAccount, | ||
5 | MAccountDefault, | ||
6 | MAccountDefaultChannelDefault, | ||
7 | MAccountFormattable, | ||
8 | MAccountId, | ||
9 | MAccountIdActorId, | ||
10 | MAccountUrl | ||
11 | } from '../account' | ||
12 | import { MNotificationSetting, MNotificationSettingFormattable } from './user-notification-setting' | ||
13 | import { AccountModel } from '@server/models/account/account' | ||
14 | import { MChannelFormattable } from '@server/typings/models' | ||
15 | |||
16 | type Use<K extends keyof UserModel, M> = PickWith<UserModel, K, M> | ||
17 | |||
18 | // ############################################################################ | ||
19 | |||
20 | export type MUser = Omit<UserModel, 'Account' | 'NotificationSetting' | 'VideoImports' | 'OAuthTokens'> | ||
21 | |||
22 | // ############################################################################ | ||
23 | |||
24 | export type MUserQuotaUsed = MUser & { videoQuotaUsed?: number, videoQuotaUsedDaily?: number } | ||
25 | export type MUserId = Pick<UserModel, 'id'> | ||
26 | |||
27 | // ############################################################################ | ||
28 | |||
29 | // With account | ||
30 | |||
31 | export type MUserAccountId = MUser & | ||
32 | Use<'Account', MAccountId> | ||
33 | |||
34 | export type MUserAccountUrl = MUser & | ||
35 | Use<'Account', MAccountUrl & MAccountIdActorId> | ||
36 | |||
37 | export type MUserAccount = MUser & | ||
38 | Use<'Account', MAccount> | ||
39 | |||
40 | export type MUserAccountDefault = MUser & | ||
41 | Use<'Account', MAccountDefault> | ||
42 | |||
43 | // With channel | ||
44 | |||
45 | export type MUserNotifSettingChannelDefault = MUser & | ||
46 | Use<'NotificationSetting', MNotificationSetting> & | ||
47 | Use<'Account', MAccountDefaultChannelDefault> | ||
48 | |||
49 | // With notification settings | ||
50 | |||
51 | export type MUserWithNotificationSetting = MUser & | ||
52 | Use<'NotificationSetting', MNotificationSetting> | ||
53 | |||
54 | export type MUserNotifSettingAccount = MUser & | ||
55 | Use<'NotificationSetting', MNotificationSetting> & | ||
56 | Use<'Account', MAccount> | ||
57 | |||
58 | // Default scope | ||
59 | |||
60 | export type MUserDefault = MUser & | ||
61 | Use<'NotificationSetting', MNotificationSetting> & | ||
62 | Use<'Account', MAccountDefault> | ||
63 | |||
64 | // ############################################################################ | ||
65 | |||
66 | // Format for API or AP object | ||
67 | |||
68 | export type MUserFormattable = MUserQuotaUsed & | ||
69 | Use<'Account', MAccountFormattable & PickWithOpt<AccountModel, 'VideoChannels', MChannelFormattable[]>> & | ||
70 | PickWithOpt<UserModel, 'NotificationSetting', MNotificationSettingFormattable> | ||
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 @@ | |||
1 | import { VideoShareModel } from '../../models/video/video-share' | ||
2 | |||
3 | export type VideoShareModelOnly = Omit<VideoShareModel, 'Actor' | 'Video'> | ||
diff --git a/server/typings/models/video/index.d.ts b/server/typings/models/video/index.d.ts new file mode 100644 index 000000000..bd69c8a4b --- /dev/null +++ b/server/typings/models/video/index.d.ts | |||
@@ -0,0 +1,18 @@ | |||
1 | export * from './schedule-video-update' | ||
2 | export * from './tag' | ||
3 | export * from './thumbnail' | ||
4 | export * from './video' | ||
5 | export * from './video-abuse' | ||
6 | export * from './video-blacklist' | ||
7 | export * from './video-caption' | ||
8 | export * from './video-change-ownership' | ||
9 | export * from './video-channels' | ||
10 | export * from './video-comment' | ||
11 | export * from './video-file' | ||
12 | export * from './video-import' | ||
13 | export * from './video-playlist' | ||
14 | export * from './video-playlist-element' | ||
15 | export * from './video-rate' | ||
16 | export * from './video-redundancy' | ||
17 | export * from './video-share' | ||
18 | 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..ada9af06e --- /dev/null +++ b/server/typings/models/video/schedule-video-update.ts | |||
@@ -0,0 +1,9 @@ | |||
1 | import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' | ||
2 | |||
3 | export type MScheduleVideoUpdate = Omit<ScheduleVideoUpdateModel, 'Video'> | ||
4 | |||
5 | // ############################################################################ | ||
6 | |||
7 | // Format for API or AP object | ||
8 | |||
9 | export type MScheduleVideoUpdateFormattable = Pick<MScheduleVideoUpdate, 'updateAt' | 'privacy'> | ||
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 @@ | |||
1 | import { TagModel } from '../../../models/video/tag' | ||
2 | |||
3 | export type MTag = Omit<TagModel, 'Videos'> | ||
diff --git a/server/typings/models/video/thumbnail.ts b/server/typings/models/video/thumbnail.ts new file mode 100644 index 000000000..c03ba55ac --- /dev/null +++ b/server/typings/models/video/thumbnail.ts | |||
@@ -0,0 +1,3 @@ | |||
1 | import { ThumbnailModel } from '../../../models/video/thumbnail' | ||
2 | |||
3 | export type MThumbnail = Omit<ThumbnailModel, 'Video' | 'VideoPlaylist'> | ||
diff --git a/server/typings/models/video/video-abuse.ts b/server/typings/models/video/video-abuse.ts new file mode 100644 index 000000000..e38c3f586 --- /dev/null +++ b/server/typings/models/video/video-abuse.ts | |||
@@ -0,0 +1,31 @@ | |||
1 | import { VideoAbuseModel } from '../../../models/video/video-abuse' | ||
2 | import { PickWith } from '../../utils' | ||
3 | import { MVideo } from './video' | ||
4 | import { MAccountDefault, MAccountFormattable } from '../account' | ||
5 | |||
6 | type Use<K extends keyof VideoAbuseModel, M> = PickWith<VideoAbuseModel, K, M> | ||
7 | |||
8 | // ############################################################################ | ||
9 | |||
10 | export type MVideoAbuse = Omit<VideoAbuseModel, 'Account' | 'Video' | 'toActivityPubObject'> | ||
11 | |||
12 | // ############################################################################ | ||
13 | |||
14 | export type MVideoAbuseId = Pick<VideoAbuseModel, 'id'> | ||
15 | |||
16 | export type MVideoAbuseVideo = MVideoAbuse & | ||
17 | Pick<VideoAbuseModel, 'toActivityPubObject'> & | ||
18 | Use<'Video', MVideo> | ||
19 | |||
20 | export type MVideoAbuseAccountVideo = MVideoAbuse & | ||
21 | Pick<VideoAbuseModel, 'toActivityPubObject'> & | ||
22 | Use<'Video', MVideo> & | ||
23 | Use<'Account', MAccountDefault> | ||
24 | |||
25 | // ############################################################################ | ||
26 | |||
27 | // Format for API or AP object | ||
28 | |||
29 | export type MVideoAbuseFormattable = MVideoAbuse & | ||
30 | Use<'Account', MAccountFormattable> & | ||
31 | Use<'Video', Pick<MVideo, 'id' | 'uuid' | 'name'>> | ||
diff --git a/server/typings/models/video/video-blacklist.ts b/server/typings/models/video/video-blacklist.ts new file mode 100644 index 000000000..e12880454 --- /dev/null +++ b/server/typings/models/video/video-blacklist.ts | |||
@@ -0,0 +1,27 @@ | |||
1 | import { VideoBlacklistModel } from '../../../models/video/video-blacklist' | ||
2 | import { PickWith } from '@server/typings/utils' | ||
3 | import { MVideo, MVideoFormattable } from '@server/typings/models' | ||
4 | |||
5 | type Use<K extends keyof VideoBlacklistModel, M> = PickWith<VideoBlacklistModel, K, M> | ||
6 | |||
7 | // ############################################################################ | ||
8 | |||
9 | export type MVideoBlacklist = Omit<VideoBlacklistModel, 'Video'> | ||
10 | |||
11 | export type MVideoBlacklistLight = Pick<MVideoBlacklist, 'id' | 'reason' | 'unfederated'> | ||
12 | export type MVideoBlacklistUnfederated = Pick<MVideoBlacklist, 'unfederated'> | ||
13 | |||
14 | // ############################################################################ | ||
15 | |||
16 | export type MVideoBlacklistLightVideo = MVideoBlacklistLight & | ||
17 | Use<'Video', MVideo> | ||
18 | |||
19 | export type MVideoBlacklistVideo = MVideoBlacklist & | ||
20 | Use<'Video', MVideo> | ||
21 | |||
22 | // ############################################################################ | ||
23 | |||
24 | // Format for API or AP object | ||
25 | |||
26 | export type MVideoBlacklistFormattable = MVideoBlacklist & | ||
27 | Use<'Video', MVideoFormattable> | ||
diff --git a/server/typings/models/video/video-caption.ts b/server/typings/models/video/video-caption.ts new file mode 100644 index 000000000..7cb2a2ad3 --- /dev/null +++ b/server/typings/models/video/video-caption.ts | |||
@@ -0,0 +1,24 @@ | |||
1 | import { VideoCaptionModel } from '../../../models/video/video-caption' | ||
2 | import { FunctionProperties, PickWith } from '@server/typings/utils' | ||
3 | import { MVideo, MVideoUUID } from '@server/typings/models' | ||
4 | |||
5 | type Use<K extends keyof VideoCaptionModel, M> = PickWith<VideoCaptionModel, K, M> | ||
6 | |||
7 | // ############################################################################ | ||
8 | |||
9 | export type MVideoCaption = Omit<VideoCaptionModel, 'Video'> | ||
10 | |||
11 | // ############################################################################ | ||
12 | |||
13 | export type MVideoCaptionLanguage = Pick<MVideoCaption, 'language'> | ||
14 | |||
15 | export type MVideoCaptionVideo = MVideoCaption & | ||
16 | Use<'Video', Pick<MVideo, 'id' | 'remote' | 'uuid'>> | ||
17 | |||
18 | // ############################################################################ | ||
19 | |||
20 | // Format for API or AP object | ||
21 | |||
22 | export type MVideoCaptionFormattable = FunctionProperties<MVideoCaption> & | ||
23 | Pick<MVideoCaption, 'language'> & | ||
24 | Use<'Video', MVideoUUID> | ||
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..72634cdb2 --- /dev/null +++ b/server/typings/models/video/video-change-ownership.ts | |||
@@ -0,0 +1,23 @@ | |||
1 | import { VideoChangeOwnershipModel } from '@server/models/video/video-change-ownership' | ||
2 | import { PickWith } from '@server/typings/utils' | ||
3 | import { MAccountDefault, MAccountFormattable, MVideo, MVideoWithFileThumbnail } from '@server/typings/models' | ||
4 | |||
5 | type Use<K extends keyof VideoChangeOwnershipModel, M> = PickWith<VideoChangeOwnershipModel, K, M> | ||
6 | |||
7 | // ############################################################################ | ||
8 | |||
9 | export type MVideoChangeOwnership = Omit<VideoChangeOwnershipModel, 'Initiator' | 'NextOwner' | 'Video'> | ||
10 | |||
11 | export type MVideoChangeOwnershipFull = MVideoChangeOwnership & | ||
12 | Use<'Initiator', MAccountDefault> & | ||
13 | Use<'NextOwner', MAccountDefault> & | ||
14 | Use<'Video', MVideoWithFileThumbnail> | ||
15 | |||
16 | // ############################################################################ | ||
17 | |||
18 | // Format for API or AP object | ||
19 | |||
20 | export type MVideoChangeOwnershipFormattable = Pick<MVideoChangeOwnership, 'id' | 'status' | 'createdAt'> & | ||
21 | Use<'Initiator', MAccountFormattable> & | ||
22 | Use<'NextOwner', MAccountFormattable> & | ||
23 | Use<'Video', Pick<MVideo, 'id' | 'uuid' | 'url' | 'name'>> | ||
diff --git a/server/typings/models/video/video-channels.ts b/server/typings/models/video/video-channels.ts new file mode 100644 index 000000000..292d0ac95 --- /dev/null +++ b/server/typings/models/video/video-channels.ts | |||
@@ -0,0 +1,126 @@ | |||
1 | import { FunctionProperties, PickWith, PickWithOpt } from '../../utils' | ||
2 | import { VideoChannelModel } from '../../../models/video/video-channel' | ||
3 | import { | ||
4 | MAccountActor, | ||
5 | MAccountAPI, | ||
6 | MAccountDefault, | ||
7 | MAccountFormattable, | ||
8 | MAccountLight, | ||
9 | MAccountSummaryBlocks, | ||
10 | MAccountSummaryFormattable, | ||
11 | MAccountUrl, | ||
12 | MAccountUserId, | ||
13 | MActor, | ||
14 | MActorAccountChannelId, | ||
15 | MActorAP, | ||
16 | MActorAPI, | ||
17 | MActorDefault, | ||
18 | MActorDefaultLight, | ||
19 | MActorFormattable, | ||
20 | MActorLight, | ||
21 | MActorSummary, | ||
22 | MActorSummaryFormattable, MActorUrl | ||
23 | } from '../account' | ||
24 | import { MVideo } from './video' | ||
25 | |||
26 | type Use<K extends keyof VideoChannelModel, M> = PickWith<VideoChannelModel, K, M> | ||
27 | |||
28 | // ############################################################################ | ||
29 | |||
30 | export type MChannel = Omit<VideoChannelModel, 'Actor' | 'Account' | 'Videos' | 'VideoPlaylists'> | ||
31 | |||
32 | // ############################################################################ | ||
33 | |||
34 | export type MChannelId = Pick<MChannel, 'id'> | ||
35 | |||
36 | // ############################################################################ | ||
37 | |||
38 | export type MChannelIdActor = MChannelId & | ||
39 | Use<'Actor', MActorAccountChannelId> | ||
40 | |||
41 | export type MChannelUserId = Pick<MChannel, 'accountId'> & | ||
42 | Use<'Account', MAccountUserId> | ||
43 | |||
44 | export type MChannelActor = MChannel & | ||
45 | Use<'Actor', MActor> | ||
46 | |||
47 | export type MChannelUrl = Use<'Actor', MActorUrl> | ||
48 | |||
49 | // Default scope | ||
50 | export type MChannelDefault = MChannel & | ||
51 | Use<'Actor', MActorDefault> | ||
52 | |||
53 | // ############################################################################ | ||
54 | |||
55 | // Not all association attributes | ||
56 | |||
57 | export type MChannelLight = MChannel & | ||
58 | Use<'Actor', MActorDefaultLight> | ||
59 | |||
60 | export type MChannelActorLight = MChannel & | ||
61 | Use<'Actor', MActorLight> | ||
62 | |||
63 | export type MChannelAccountLight = MChannel & | ||
64 | Use<'Actor', MActorDefaultLight> & | ||
65 | Use<'Account', MAccountLight> | ||
66 | |||
67 | // ############################################################################ | ||
68 | |||
69 | // Account associations | ||
70 | |||
71 | export type MChannelAccountActor = MChannel & | ||
72 | Use<'Account', MAccountActor> | ||
73 | |||
74 | export type MChannelAccountDefault = MChannel & | ||
75 | Use<'Actor', MActorDefault> & | ||
76 | Use<'Account', MAccountDefault> | ||
77 | |||
78 | export type MChannelActorAccountActor = MChannel & | ||
79 | Use<'Account', MAccountActor> & | ||
80 | Use<'Actor', MActor> | ||
81 | |||
82 | // ############################################################################ | ||
83 | |||
84 | // Videos associations | ||
85 | export type MChannelVideos = MChannel & | ||
86 | Use<'Videos', MVideo[]> | ||
87 | |||
88 | export type MChannelActorAccountDefaultVideos = MChannel & | ||
89 | Use<'Actor', MActorDefault> & | ||
90 | Use<'Account', MAccountDefault> & | ||
91 | Use<'Videos', MVideo[]> | ||
92 | |||
93 | // ############################################################################ | ||
94 | |||
95 | // For API | ||
96 | |||
97 | export type MChannelSummary = FunctionProperties<MChannel> & | ||
98 | Pick<MChannel, 'id' | 'name' | 'description' | 'actorId'> & | ||
99 | Use<'Actor', MActorSummary> | ||
100 | |||
101 | export type MChannelSummaryAccount = MChannelSummary & | ||
102 | Use<'Account', MAccountSummaryBlocks> | ||
103 | |||
104 | export type MChannelAPI = MChannel & | ||
105 | Use<'Actor', MActorAPI> & | ||
106 | Use<'Account', MAccountAPI> | ||
107 | |||
108 | // ############################################################################ | ||
109 | |||
110 | // Format for API or AP object | ||
111 | |||
112 | export type MChannelSummaryFormattable = FunctionProperties<MChannel> & | ||
113 | Pick<MChannel, 'id' | 'name'> & | ||
114 | Use<'Actor', MActorSummaryFormattable> | ||
115 | |||
116 | export type MChannelAccountSummaryFormattable = MChannelSummaryFormattable & | ||
117 | Use<'Account', MAccountSummaryFormattable> | ||
118 | |||
119 | export type MChannelFormattable = FunctionProperties<MChannel> & | ||
120 | Pick<MChannel, 'id' | 'name' | 'description' | 'createdAt' | 'updatedAt' | 'support'> & | ||
121 | Use<'Actor', MActorFormattable> & | ||
122 | PickWithOpt<VideoChannelModel, 'Account', MAccountFormattable> | ||
123 | |||
124 | export type MChannelAP = Pick<MChannel, 'name' | 'description' | 'support'> & | ||
125 | Use<'Actor', MActorAP> & | ||
126 | Use<'Account', MAccountUrl> | ||
diff --git a/server/typings/models/video/video-comment.ts b/server/typings/models/video/video-comment.ts new file mode 100644 index 000000000..4fd1c29e8 --- /dev/null +++ b/server/typings/models/video/video-comment.ts | |||
@@ -0,0 +1,57 @@ | |||
1 | import { VideoCommentModel } from '../../../models/video/video-comment' | ||
2 | import { PickWith, PickWithOpt } from '../../utils' | ||
3 | import { MAccountDefault, MAccountFormattable, MAccountUrl, MActorUrl } from '../account' | ||
4 | import { MVideoAccountLight, MVideoFeed, MVideoIdUrl, MVideoUrl } from './video' | ||
5 | |||
6 | type Use<K extends keyof VideoCommentModel, M> = PickWith<VideoCommentModel, K, M> | ||
7 | |||
8 | // ############################################################################ | ||
9 | |||
10 | export type MComment = Omit<VideoCommentModel, 'OriginVideoComment' | 'InReplyToVideoComment' | 'Video' | 'Account'> | ||
11 | export type MCommentTotalReplies = MComment & { totalReplies?: number } | ||
12 | export type MCommentId = Pick<MComment, 'id'> | ||
13 | export type MCommentUrl = Pick<MComment, 'url'> | ||
14 | |||
15 | // ############################################################################ | ||
16 | |||
17 | export type MCommentOwner = MComment & | ||
18 | Use<'Account', MAccountDefault> | ||
19 | |||
20 | export type MCommentVideo = MComment & | ||
21 | Use<'Video', MVideoAccountLight> | ||
22 | |||
23 | export type MCommentReply = MComment & | ||
24 | Use<'InReplyToVideoComment', MComment> | ||
25 | |||
26 | export type MCommentOwnerVideo = MComment & | ||
27 | Use<'Account', MAccountDefault> & | ||
28 | Use<'Video', MVideoAccountLight> | ||
29 | |||
30 | export type MCommentOwnerVideoReply = MComment & | ||
31 | Use<'Account', MAccountDefault> & | ||
32 | Use<'Video', MVideoAccountLight> & | ||
33 | Use<'InReplyToVideoComment', MComment> | ||
34 | |||
35 | export type MCommentOwnerReplyVideoLight = MComment & | ||
36 | Use<'Account', MAccountDefault> & | ||
37 | Use<'InReplyToVideoComment', MComment> & | ||
38 | Use<'Video', MVideoIdUrl> | ||
39 | |||
40 | export type MCommentOwnerVideoFeed = MCommentOwner & | ||
41 | Use<'Video', MVideoFeed> | ||
42 | |||
43 | // ############################################################################ | ||
44 | |||
45 | export type MCommentAPI = MComment & { totalReplies: number } | ||
46 | |||
47 | // ############################################################################ | ||
48 | |||
49 | // Format for API or AP object | ||
50 | |||
51 | export type MCommentFormattable = MCommentTotalReplies & | ||
52 | Use<'Account', MAccountFormattable> | ||
53 | |||
54 | export type MCommentAP = MComment & | ||
55 | Use<'Account', MAccountUrl> & | ||
56 | PickWithOpt<VideoCommentModel, 'Video', MVideoUrl> & | ||
57 | PickWithOpt<VideoCommentModel, 'InReplyToVideoComment', MCommentUrl> | ||
diff --git a/server/typings/models/video/video-file.ts b/server/typings/models/video/video-file.ts new file mode 100644 index 000000000..484351a8d --- /dev/null +++ b/server/typings/models/video/video-file.ts | |||
@@ -0,0 +1,19 @@ | |||
1 | import { VideoFileModel } from '../../../models/video/video-file' | ||
2 | import { PickWith, PickWithOpt } from '../../utils' | ||
3 | import { MVideo, MVideoUUID } from './video' | ||
4 | import { MVideoRedundancyFileUrl } from './video-redundancy' | ||
5 | |||
6 | type Use<K extends keyof VideoFileModel, M> = PickWith<VideoFileModel, K, M> | ||
7 | |||
8 | // ############################################################################ | ||
9 | |||
10 | export type MVideoFile = Omit<VideoFileModel, 'Video' | 'RedundancyVideos'> | ||
11 | |||
12 | export type MVideoFileVideo = MVideoFile & | ||
13 | Use<'Video', MVideo> | ||
14 | |||
15 | export type MVideoFileVideoUUID = MVideoFile & | ||
16 | Use<'Video', MVideoUUID> | ||
17 | |||
18 | export type MVideoFileRedundanciesOpt = MVideoFile & | ||
19 | PickWithOpt<VideoFileModel, 'RedundancyVideos', MVideoRedundancyFileUrl[]> | ||
diff --git a/server/typings/models/video/video-import.ts b/server/typings/models/video/video-import.ts new file mode 100644 index 000000000..c6a1c5b66 --- /dev/null +++ b/server/typings/models/video/video-import.ts | |||
@@ -0,0 +1,31 @@ | |||
1 | import { VideoImportModel } from '@server/models/video/video-import' | ||
2 | import { PickWith, PickWithOpt } from '@server/typings/utils' | ||
3 | import { MUser, MVideo, MVideoAccountLight, MVideoFormattable, MVideoTag, MVideoThumbnail, MVideoWithFile } from '@server/typings/models' | ||
4 | |||
5 | type Use<K extends keyof VideoImportModel, M> = PickWith<VideoImportModel, K, M> | ||
6 | |||
7 | // ############################################################################ | ||
8 | |||
9 | export type MVideoImport = Omit<VideoImportModel, 'User' | 'Video'> | ||
10 | |||
11 | export type MVideoImportVideo = MVideoImport & | ||
12 | Use<'Video', MVideo> | ||
13 | |||
14 | // ############################################################################ | ||
15 | |||
16 | type VideoAssociation = MVideoTag & MVideoAccountLight & MVideoThumbnail | ||
17 | |||
18 | export type MVideoImportDefault = MVideoImport & | ||
19 | Use<'User', MUser> & | ||
20 | Use<'Video', VideoAssociation> | ||
21 | |||
22 | export type MVideoImportDefaultFiles = MVideoImport & | ||
23 | Use<'User', MUser> & | ||
24 | Use<'Video', VideoAssociation & MVideoWithFile> | ||
25 | |||
26 | // ############################################################################ | ||
27 | |||
28 | // Format for API or AP object | ||
29 | |||
30 | export type MVideoImportFormattable = MVideoImport & | ||
31 | PickWithOpt<VideoImportModel, 'Video', MVideoFormattable & MVideoTag> | ||
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..7b1b993ce --- /dev/null +++ b/server/typings/models/video/video-playlist-element.ts | |||
@@ -0,0 +1,34 @@ | |||
1 | import { VideoPlaylistElementModel } from '@server/models/video/video-playlist-element' | ||
2 | import { PickWith } from '@server/typings/utils' | ||
3 | import { MVideoFormattable, MVideoPlaylistPrivacy, MVideoThumbnail, MVideoUrl } from '@server/typings/models' | ||
4 | |||
5 | type Use<K extends keyof VideoPlaylistElementModel, M> = PickWith<VideoPlaylistElementModel, K, M> | ||
6 | |||
7 | // ############################################################################ | ||
8 | |||
9 | export type MVideoPlaylistElement = Omit<VideoPlaylistElementModel, 'VideoPlaylist' | 'Video'> | ||
10 | |||
11 | // ############################################################################ | ||
12 | |||
13 | export type MVideoPlaylistElementId = Pick<MVideoPlaylistElement, 'id'> | ||
14 | |||
15 | export type MVideoPlaylistElementLight = Pick<MVideoPlaylistElement, 'id' | 'videoId' | 'startTimestamp' | 'stopTimestamp'> | ||
16 | |||
17 | // ############################################################################ | ||
18 | |||
19 | export type MVideoPlaylistVideoThumbnail = MVideoPlaylistElement & | ||
20 | Use<'Video', MVideoThumbnail> | ||
21 | |||
22 | export type MVideoPlaylistElementVideoUrlPlaylistPrivacy = MVideoPlaylistElement & | ||
23 | Use<'Video', MVideoUrl> & | ||
24 | Use<'VideoPlaylist', MVideoPlaylistPrivacy> | ||
25 | |||
26 | // ############################################################################ | ||
27 | |||
28 | // Format for API or AP object | ||
29 | |||
30 | export type MVideoPlaylistElementFormattable = MVideoPlaylistElement & | ||
31 | Use<'Video', MVideoFormattable> | ||
32 | |||
33 | export type MVideoPlaylistElementAP = MVideoPlaylistElement & | ||
34 | Use<'Video', MVideoUrl> | ||
diff --git a/server/typings/models/video/video-playlist.ts b/server/typings/models/video/video-playlist.ts new file mode 100644 index 000000000..a40c7aca9 --- /dev/null +++ b/server/typings/models/video/video-playlist.ts | |||
@@ -0,0 +1,92 @@ | |||
1 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' | ||
2 | import { PickWith } from '../../utils' | ||
3 | import { MAccount, MAccountDefault, MAccountSummary, MAccountSummaryFormattable } from '../account' | ||
4 | import { MThumbnail } from './thumbnail' | ||
5 | import { MChannelDefault, MChannelSummary, MChannelSummaryFormattable, MChannelUrl } from './video-channels' | ||
6 | import { MVideoPlaylistElementLight } from '@server/typings/models/video/video-playlist-element' | ||
7 | |||
8 | type Use<K extends keyof VideoPlaylistModel, M> = PickWith<VideoPlaylistModel, K, M> | ||
9 | |||
10 | // ############################################################################ | ||
11 | |||
12 | export type MVideoPlaylist = Omit<VideoPlaylistModel, 'OwnerAccount' | 'VideoChannel' | 'VideoPlaylistElements' | 'Thumbnail'> | ||
13 | |||
14 | // ############################################################################ | ||
15 | |||
16 | export type MVideoPlaylistId = Pick<MVideoPlaylist, 'id'> | ||
17 | export type MVideoPlaylistPrivacy = Pick<MVideoPlaylist, 'privacy'> | ||
18 | export type MVideoPlaylistUUID = Pick<MVideoPlaylist, 'uuid'> | ||
19 | export type MVideoPlaylistVideosLength = MVideoPlaylist & { videosLength?: number } | ||
20 | |||
21 | // ############################################################################ | ||
22 | |||
23 | // With elements | ||
24 | |||
25 | export type MVideoPlaylistWithElements = MVideoPlaylist & | ||
26 | Use<'VideoPlaylistElements', MVideoPlaylistElementLight[]> | ||
27 | |||
28 | export type MVideoPlaylistIdWithElements = MVideoPlaylistId & | ||
29 | Use<'VideoPlaylistElements', MVideoPlaylistElementLight[]> | ||
30 | |||
31 | // ############################################################################ | ||
32 | |||
33 | // With account | ||
34 | |||
35 | export type MVideoPlaylistOwner = MVideoPlaylist & | ||
36 | Use<'OwnerAccount', MAccount> | ||
37 | |||
38 | export type MVideoPlaylistOwnerDefault = MVideoPlaylist & | ||
39 | Use<'OwnerAccount', MAccountDefault> | ||
40 | |||
41 | // ############################################################################ | ||
42 | |||
43 | // With thumbnail | ||
44 | |||
45 | export type MVideoPlaylistThumbnail = MVideoPlaylist & | ||
46 | Use<'Thumbnail', MThumbnail> | ||
47 | |||
48 | export type MVideoPlaylistAccountThumbnail = MVideoPlaylist & | ||
49 | Use<'OwnerAccount', MAccountDefault> & | ||
50 | Use<'Thumbnail', MThumbnail> | ||
51 | |||
52 | // ############################################################################ | ||
53 | |||
54 | // With channel | ||
55 | |||
56 | export type MVideoPlaylistAccountChannelDefault = MVideoPlaylist & | ||
57 | Use<'OwnerAccount', MAccountDefault> & | ||
58 | Use<'VideoChannel', MChannelDefault> | ||
59 | |||
60 | // ############################################################################ | ||
61 | |||
62 | // With all associations | ||
63 | |||
64 | export type MVideoPlaylistFull = MVideoPlaylist & | ||
65 | Use<'OwnerAccount', MAccountDefault> & | ||
66 | Use<'VideoChannel', MChannelDefault> & | ||
67 | Use<'Thumbnail', MThumbnail> | ||
68 | |||
69 | // ############################################################################ | ||
70 | |||
71 | // For API | ||
72 | |||
73 | export type MVideoPlaylistAccountChannelSummary = MVideoPlaylist & | ||
74 | Use<'OwnerAccount', MAccountSummary> & | ||
75 | Use<'VideoChannel', MChannelSummary> | ||
76 | |||
77 | export type MVideoPlaylistFullSummary = MVideoPlaylist & | ||
78 | Use<'Thumbnail', MThumbnail> & | ||
79 | Use<'OwnerAccount', MAccountSummary> & | ||
80 | Use<'VideoChannel', MChannelSummary> | ||
81 | |||
82 | // ############################################################################ | ||
83 | |||
84 | // Format for API or AP object | ||
85 | |||
86 | export type MVideoPlaylistFormattable = MVideoPlaylistVideosLength & | ||
87 | Use<'OwnerAccount', MAccountSummaryFormattable> & | ||
88 | Use<'VideoChannel', MChannelSummaryFormattable> | ||
89 | |||
90 | export type MVideoPlaylistAP = MVideoPlaylist & | ||
91 | Use<'Thumbnail', MThumbnail> & | ||
92 | Use<'VideoChannel', MChannelUrl> | ||
diff --git a/server/typings/models/video/video-rate.ts b/server/typings/models/video/video-rate.ts new file mode 100644 index 000000000..2ff8a625b --- /dev/null +++ b/server/typings/models/video/video-rate.ts | |||
@@ -0,0 +1,23 @@ | |||
1 | import { AccountVideoRateModel } from '@server/models/account/account-video-rate' | ||
2 | import { PickWith } from '@server/typings/utils' | ||
3 | import { MAccountAudience, MAccountUrl, MVideo, MVideoFormattable } from '..' | ||
4 | |||
5 | type Use<K extends keyof AccountVideoRateModel, M> = PickWith<AccountVideoRateModel, K, M> | ||
6 | |||
7 | // ############################################################################ | ||
8 | |||
9 | export type MAccountVideoRate = Omit<AccountVideoRateModel, 'Video' | 'Account'> | ||
10 | |||
11 | export type MAccountVideoRateAccountUrl = MAccountVideoRate & | ||
12 | Use<'Account', MAccountUrl> | ||
13 | |||
14 | export type MAccountVideoRateAccountVideo = MAccountVideoRate & | ||
15 | Use<'Account', MAccountAudience> & | ||
16 | Use<'Video', MVideo> | ||
17 | |||
18 | // ############################################################################ | ||
19 | |||
20 | // Format for API or AP object | ||
21 | |||
22 | export type MAccountVideoRateFormattable = Pick<MAccountVideoRate, 'type'> & | ||
23 | Use<'Video', MVideoFormattable> | ||
diff --git a/server/typings/models/video/video-redundancy.ts b/server/typings/models/video/video-redundancy.ts new file mode 100644 index 000000000..f3846afd7 --- /dev/null +++ b/server/typings/models/video/video-redundancy.ts | |||
@@ -0,0 +1,38 @@ | |||
1 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' | ||
2 | import { PickWith, PickWithOpt } from '@server/typings/utils' | ||
3 | import { MStreamingPlaylistVideo, MVideoFile, MVideoFileVideo, MVideoUrl } from '@server/typings/models' | ||
4 | import { VideoStreamingPlaylist } from '../../../../shared/models/videos/video-streaming-playlist.model' | ||
5 | import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' | ||
6 | import { VideoFile } from '../../../../shared/models/videos' | ||
7 | import { VideoFileModel } from '@server/models/video/video-file' | ||
8 | |||
9 | type Use<K extends keyof VideoRedundancyModel, M> = PickWith<VideoRedundancyModel, K, M> | ||
10 | |||
11 | // ############################################################################ | ||
12 | |||
13 | export type MVideoRedundancy = Omit<VideoRedundancyModel, 'VideoFile' | 'VideoStreamingPlaylist' | 'Actor'> | ||
14 | |||
15 | export type MVideoRedundancyFileUrl = Pick<MVideoRedundancy, 'fileUrl'> | ||
16 | |||
17 | // ############################################################################ | ||
18 | |||
19 | export type MVideoRedundancyFile = MVideoRedundancy & | ||
20 | Use<'VideoFile', MVideoFile> | ||
21 | |||
22 | export type MVideoRedundancyFileVideo = MVideoRedundancy & | ||
23 | Use<'VideoFile', MVideoFileVideo> | ||
24 | |||
25 | export type MVideoRedundancyStreamingPlaylistVideo = MVideoRedundancy & | ||
26 | Use<'VideoStreamingPlaylist', MStreamingPlaylistVideo> | ||
27 | |||
28 | export type MVideoRedundancyVideo = MVideoRedundancy & | ||
29 | Use<'VideoFile', MVideoFileVideo> & | ||
30 | Use<'VideoStreamingPlaylist', MStreamingPlaylistVideo> | ||
31 | |||
32 | // ############################################################################ | ||
33 | |||
34 | // Format for API or AP object | ||
35 | |||
36 | export type MVideoRedundancyAP = MVideoRedundancy & | ||
37 | PickWithOpt<VideoRedundancyModel, 'VideoFile', MVideoFile & PickWith<VideoFileModel, 'Video', MVideoUrl>> & | ||
38 | PickWithOpt<VideoRedundancyModel, 'VideoStreamingPlaylist', PickWith<VideoStreamingPlaylistModel, 'Video', MVideoUrl>> | ||
diff --git a/server/typings/models/video/video-share.ts b/server/typings/models/video/video-share.ts new file mode 100644 index 000000000..a7a90beeb --- /dev/null +++ b/server/typings/models/video/video-share.ts | |||
@@ -0,0 +1,17 @@ | |||
1 | import { VideoShareModel } from '../../../models/video/video-share' | ||
2 | import { PickWith } from '../../utils' | ||
3 | import { MActorDefault } from '../account' | ||
4 | import { MVideo } from './video' | ||
5 | |||
6 | type Use<K extends keyof VideoShareModel, M> = PickWith<VideoShareModel, K, M> | ||
7 | |||
8 | // ############################################################################ | ||
9 | |||
10 | export type MVideoShare = Omit<VideoShareModel, 'Actor' | 'Video'> | ||
11 | |||
12 | export type MVideoShareActor = MVideoShare & | ||
13 | Use<'Actor', MActorDefault> | ||
14 | |||
15 | export type MVideoShareFull = MVideoShare & | ||
16 | Use<'Actor', MActorDefault> & | ||
17 | Use<'Video', MVideo> | ||
diff --git a/server/typings/models/video/video-streaming-playlist.ts b/server/typings/models/video/video-streaming-playlist.ts new file mode 100644 index 000000000..79696bcff --- /dev/null +++ b/server/typings/models/video/video-streaming-playlist.ts | |||
@@ -0,0 +1,19 @@ | |||
1 | import { VideoStreamingPlaylistModel } from '../../../models/video/video-streaming-playlist' | ||
2 | import { PickWith, PickWithOpt } from '../../utils' | ||
3 | import { MVideoRedundancyFileUrl } from './video-redundancy' | ||
4 | import { MVideo, MVideoUrl } from '@server/typings/models' | ||
5 | |||
6 | type Use<K extends keyof VideoStreamingPlaylistModel, M> = PickWith<VideoStreamingPlaylistModel, K, M> | ||
7 | |||
8 | // ############################################################################ | ||
9 | |||
10 | export type MStreamingPlaylist = Omit<VideoStreamingPlaylistModel, 'Video' | 'RedundancyVideos'> | ||
11 | |||
12 | export type MStreamingPlaylistVideo = MStreamingPlaylist & | ||
13 | Use<'Video', MVideo> | ||
14 | |||
15 | export type MStreamingPlaylistRedundancies = MStreamingPlaylist & | ||
16 | Use<'RedundancyVideos', MVideoRedundancyFileUrl[]> | ||
17 | |||
18 | export type MStreamingPlaylistRedundanciesOpt = MStreamingPlaylist & | ||
19 | PickWithOpt<VideoStreamingPlaylistModel, 'RedundancyVideos', MVideoRedundancyFileUrl[]> | ||
diff --git a/server/typings/models/video/video.ts b/server/typings/models/video/video.ts new file mode 100644 index 000000000..9a53bd337 --- /dev/null +++ b/server/typings/models/video/video.ts | |||
@@ -0,0 +1,173 @@ | |||
1 | import { VideoModel } from '../../../models/video/video' | ||
2 | import { PickWith, PickWithOpt } from '../../utils' | ||
3 | import { | ||
4 | MChannelAccountDefault, | ||
5 | MChannelAccountLight, | ||
6 | MChannelAccountSummaryFormattable, | ||
7 | MChannelActor, | ||
8 | MChannelFormattable, | ||
9 | MChannelUserId | ||
10 | } from './video-channels' | ||
11 | import { MTag } from './tag' | ||
12 | import { MVideoCaptionLanguage } from './video-caption' | ||
13 | import { MStreamingPlaylist, MStreamingPlaylistRedundancies, MStreamingPlaylistRedundanciesOpt } from './video-streaming-playlist' | ||
14 | import { MVideoFile, MVideoFileRedundanciesOpt } from './video-file' | ||
15 | import { MThumbnail } from './thumbnail' | ||
16 | import { MVideoBlacklist, MVideoBlacklistLight, MVideoBlacklistUnfederated } from './video-blacklist' | ||
17 | import { MScheduleVideoUpdate } from './schedule-video-update' | ||
18 | import { MUserVideoHistoryTime } from '../user/user-video-history' | ||
19 | |||
20 | type Use<K extends keyof VideoModel, M> = PickWith<VideoModel, K, M> | ||
21 | |||
22 | // ############################################################################ | ||
23 | |||
24 | export type MVideo = Omit<VideoModel, 'VideoChannel' | 'Tags' | 'Thumbnails' | 'VideoPlaylistElements' | 'VideoAbuses' | | ||
25 | 'VideoFiles' | 'VideoStreamingPlaylists' | 'VideoShares' | 'AccountVideoRates' | 'VideoComments' | 'VideoViews' | 'UserVideoHistories' | | ||
26 | 'ScheduleVideoUpdate' | 'VideoBlacklist' | 'VideoImport' | 'VideoCaptions'> | ||
27 | |||
28 | // ############################################################################ | ||
29 | |||
30 | export type MVideoId = Pick<MVideo, 'id'> | ||
31 | export type MVideoUrl = Pick<MVideo, 'url'> | ||
32 | export type MVideoUUID = Pick<MVideo, 'uuid'> | ||
33 | |||
34 | export type MVideoIdUrl = MVideoId & MVideoUrl | ||
35 | export type MVideoFeed = Pick<MVideo, 'name' | 'uuid'> | ||
36 | |||
37 | // ############################################################################ | ||
38 | |||
39 | // Video raw associations: schedules, video files, tags, thumbnails, captions, streaming playlists | ||
40 | |||
41 | // "With" to not confuse with the VideoFile model | ||
42 | export type MVideoWithFile = MVideo & | ||
43 | Use<'VideoFiles', MVideoFile[]> | ||
44 | |||
45 | export type MVideoThumbnail = MVideo & | ||
46 | Use<'Thumbnails', MThumbnail[]> | ||
47 | |||
48 | export type MVideoIdThumbnail = MVideoId & | ||
49 | Use<'Thumbnails', MThumbnail[]> | ||
50 | |||
51 | export type MVideoWithFileThumbnail = MVideo & | ||
52 | Use<'VideoFiles', MVideoFile[]> & | ||
53 | Use<'Thumbnails', MThumbnail[]> | ||
54 | |||
55 | export type MVideoThumbnailBlacklist = MVideo & | ||
56 | Use<'Thumbnails', MThumbnail[]> & | ||
57 | Use<'VideoBlacklist', MVideoBlacklistLight> | ||
58 | |||
59 | export type MVideoTag = MVideo & | ||
60 | Use<'Tags', MTag[]> | ||
61 | |||
62 | export type MVideoWithSchedule = MVideo & | ||
63 | PickWithOpt<VideoModel, 'ScheduleVideoUpdate', MScheduleVideoUpdate> | ||
64 | |||
65 | export type MVideoWithCaptions = MVideo & | ||
66 | Use<'VideoCaptions', MVideoCaptionLanguage[]> | ||
67 | |||
68 | export type MVideoWithStreamingPlaylist = MVideo & | ||
69 | Use<'VideoStreamingPlaylists', MStreamingPlaylist[]> | ||
70 | |||
71 | // ############################################################################ | ||
72 | |||
73 | // Associations with not all their attributes | ||
74 | |||
75 | export type MVideoUserHistory = MVideo & | ||
76 | Use<'UserVideoHistories', MUserVideoHistoryTime[]> | ||
77 | |||
78 | export type MVideoWithBlacklistLight = MVideo & | ||
79 | Use<'VideoBlacklist', MVideoBlacklistLight> | ||
80 | |||
81 | export type MVideoAccountLight = MVideo & | ||
82 | Use<'VideoChannel', MChannelAccountLight> | ||
83 | |||
84 | export type MVideoWithRights = MVideo & | ||
85 | Use<'VideoBlacklist', MVideoBlacklistLight> & | ||
86 | Use<'Thumbnails', MThumbnail[]> & | ||
87 | Use<'VideoChannel', MChannelUserId> | ||
88 | |||
89 | // ############################################################################ | ||
90 | |||
91 | // All files with some additional associations | ||
92 | |||
93 | export type MVideoWithAllFiles = MVideo & | ||
94 | Use<'VideoFiles', MVideoFile[]> & | ||
95 | Use<'Thumbnails', MThumbnail[]> & | ||
96 | Use<'VideoStreamingPlaylists', MStreamingPlaylist[]> | ||
97 | |||
98 | export type MVideoAccountLightBlacklistAllFiles = MVideo & | ||
99 | Use<'VideoFiles', MVideoFile[]> & | ||
100 | Use<'Thumbnails', MThumbnail[]> & | ||
101 | Use<'VideoStreamingPlaylists', MStreamingPlaylist[]> & | ||
102 | Use<'VideoChannel', MChannelAccountLight> & | ||
103 | Use<'VideoBlacklist', MVideoBlacklistLight> | ||
104 | |||
105 | // ############################################################################ | ||
106 | |||
107 | // With account | ||
108 | |||
109 | export type MVideoAccountDefault = MVideo & | ||
110 | Use<'VideoChannel', MChannelAccountDefault> | ||
111 | |||
112 | export type MVideoThumbnailAccountDefault = MVideo & | ||
113 | Use<'Thumbnails', MThumbnail[]> & | ||
114 | Use<'VideoChannel', MChannelAccountDefault> | ||
115 | |||
116 | export type MVideoWithChannelActor = MVideo & | ||
117 | Use<'VideoChannel', MChannelActor> | ||
118 | |||
119 | export type MVideoFullLight = MVideo & | ||
120 | Use<'Thumbnails', MThumbnail[]> & | ||
121 | Use<'VideoBlacklist', MVideoBlacklistLight> & | ||
122 | Use<'Tags', MTag[]> & | ||
123 | Use<'VideoChannel', MChannelAccountLight> & | ||
124 | Use<'UserVideoHistories', MUserVideoHistoryTime[]> & | ||
125 | Use<'VideoFiles', MVideoFile[]> & | ||
126 | Use<'ScheduleVideoUpdate', MScheduleVideoUpdate> & | ||
127 | Use<'VideoStreamingPlaylists', MStreamingPlaylist[]> | ||
128 | |||
129 | // ############################################################################ | ||
130 | |||
131 | // API | ||
132 | |||
133 | export type MVideoAP = MVideo & | ||
134 | Use<'Tags', MTag[]> & | ||
135 | Use<'VideoChannel', MChannelAccountLight> & | ||
136 | Use<'VideoStreamingPlaylists', MStreamingPlaylist[]> & | ||
137 | Use<'VideoCaptions', MVideoCaptionLanguage[]> & | ||
138 | Use<'VideoBlacklist', MVideoBlacklistUnfederated> & | ||
139 | Use<'VideoFiles', MVideoFileRedundanciesOpt[]> | ||
140 | |||
141 | export type MVideoAPWithoutCaption = Omit<MVideoAP, 'VideoCaptions'> | ||
142 | |||
143 | export type MVideoDetails = MVideo & | ||
144 | Use<'VideoBlacklist', MVideoBlacklistLight> & | ||
145 | Use<'Tags', MTag[]> & | ||
146 | Use<'VideoChannel', MChannelAccountLight> & | ||
147 | Use<'ScheduleVideoUpdate', MScheduleVideoUpdate> & | ||
148 | Use<'Thumbnails', MThumbnail[]> & | ||
149 | Use<'UserVideoHistories', MUserVideoHistoryTime[]> & | ||
150 | Use<'VideoStreamingPlaylists', MStreamingPlaylistRedundancies[]> & | ||
151 | Use<'VideoFiles', MVideoFileRedundanciesOpt[]> | ||
152 | |||
153 | export type MVideoForUser = MVideo & | ||
154 | Use<'VideoChannel', MChannelAccountDefault> & | ||
155 | Use<'ScheduleVideoUpdate', MScheduleVideoUpdate> & | ||
156 | Use<'VideoBlacklist', MVideoBlacklistLight> & | ||
157 | Use<'Thumbnails', MThumbnail[]> | ||
158 | |||
159 | // ############################################################################ | ||
160 | |||
161 | // Format for API or AP object | ||
162 | |||
163 | export type MVideoFormattable = MVideo & | ||
164 | PickWithOpt<VideoModel, 'UserVideoHistories', MUserVideoHistoryTime[]> & | ||
165 | Use<'VideoChannel', MChannelAccountSummaryFormattable> & | ||
166 | PickWithOpt<VideoModel, 'ScheduleVideoUpdate', Pick<MScheduleVideoUpdate, 'updateAt' | 'privacy'>> & | ||
167 | PickWithOpt<VideoModel, 'VideoBlacklist', Pick<MVideoBlacklist, 'reason'>> | ||
168 | |||
169 | export type MVideoFormattableDetails = MVideoFormattable & | ||
170 | Use<'VideoChannel', MChannelFormattable> & | ||
171 | Use<'Tags', MTag[]> & | ||
172 | Use<'VideoStreamingPlaylists', MStreamingPlaylistRedundanciesOpt[]> & | ||
173 | Use<'VideoFiles', MVideoFileRedundanciesOpt[]> | ||
diff --git a/server/typings/utils.ts b/server/typings/utils.ts index a86b05be2..24d43b258 100644 --- a/server/typings/utils.ts +++ b/server/typings/utils.ts | |||
@@ -1,3 +1,22 @@ | |||
1 | export type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T] | 1 | export type FunctionPropertyNames<T> = { |
2 | [K in keyof T]: T[K] extends Function ? K : never | ||
3 | }[keyof T] | ||
2 | 4 | ||
3 | export type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>> | 5 | export type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>> |
6 | |||
7 | export type PickWith<T, KT extends keyof T, V> = { | ||
8 | [P in KT]: T[P] extends V ? V : never | ||
9 | } | ||
10 | |||
11 | export type PickWithOpt<T, KT extends keyof T, V> = { | ||
12 | [P in KT]?: T[P] extends V ? V : never | ||
13 | } | ||
14 | |||
15 | // https://github.com/krzkaczor/ts-essentials Rocks! | ||
16 | export type DeepPartial<T> = { | ||
17 | [P in keyof T]?: T[P] extends Array<infer U> | ||
18 | ? Array<DeepPartial<U>> | ||
19 | : T[P] extends ReadonlyArray<infer U> | ||
20 | ? ReadonlyArray<DeepPartial<U>> | ||
21 | : DeepPartial<T[P]> | ||
22 | } | ||