diff options
Diffstat (limited to 'server/lib/activitypub/videos.ts')
-rw-r--r-- | server/lib/activitypub/videos.ts | 143 |
1 files changed, 85 insertions, 58 deletions
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts index 3a8451a32..5c10f9764 100644 --- a/server/lib/activitypub/videos.ts +++ b/server/lib/activitypub/videos.ts | |||
@@ -24,7 +24,6 @@ import { | |||
24 | REMOTE_SCHEME, | 24 | REMOTE_SCHEME, |
25 | STATIC_PATHS | 25 | STATIC_PATHS |
26 | } from '../../initializers/constants' | 26 | } from '../../initializers/constants' |
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,33 @@ 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 | MAccountActor, | ||
57 | MChannelAccountLight, | ||
58 | MChannelDefault, | ||
59 | MChannelId, | ||
60 | MVideo, | ||
61 | MVideoAccountAllFiles, | ||
62 | MVideoAccountLight, | ||
63 | MVideoAP, | ||
64 | MVideoAPWithoutCaption, | ||
65 | MVideoFile, | ||
66 | MVideoFullLight, | ||
67 | MVideoId, | ||
68 | MVideoTag, | ||
69 | MVideoThumbnail, | ||
70 | MVideoWithAllFiles | ||
71 | } from '../../typings/models' | ||
72 | import { MThumbnail } from '../../typings/models/video/thumbnail' | ||
73 | |||
74 | async function federateVideoIfNeeded (videoArg: MVideoAPWithoutCaption, isNewVideo: boolean, transaction?: sequelize.Transaction) { | ||
75 | const video = videoArg as MVideoAP | ||
59 | 76 | ||
60 | async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) { | ||
61 | if ( | 77 | if ( |
62 | // Check this is not a blacklisted video, or unfederated blacklisted video | 78 | // Check this is not a blacklisted video, or unfederated blacklisted video |
63 | (video.isBlacklisted() === false || (isNewVideo === false && video.VideoBlacklist.unfederated === false)) && | 79 | (video.isBlacklisted() === false || (isNewVideo === false && video.VideoBlacklist.unfederated === false)) && |
@@ -102,7 +118,7 @@ async function fetchRemoteVideo (videoUrl: string): Promise<{ response: request. | |||
102 | return { response, videoObject: body } | 118 | return { response, videoObject: body } |
103 | } | 119 | } |
104 | 120 | ||
105 | async function fetchRemoteVideoDescription (video: VideoModel) { | 121 | async function fetchRemoteVideoDescription (video: MVideoAccountLight) { |
106 | const host = video.VideoChannel.Account.Actor.Server.host | 122 | const host = video.VideoChannel.Account.Actor.Server.host |
107 | const path = video.getDescriptionAPIPath() | 123 | const path = video.getDescriptionAPIPath() |
108 | const options = { | 124 | const options = { |
@@ -114,14 +130,14 @@ async function fetchRemoteVideoDescription (video: VideoModel) { | |||
114 | return body.description ? body.description : '' | 130 | return body.description ? body.description : '' |
115 | } | 131 | } |
116 | 132 | ||
117 | function fetchRemoteVideoStaticFile (video: VideoModel, path: string, destPath: string) { | 133 | function fetchRemoteVideoStaticFile (video: MVideoAccountLight, path: string, destPath: string) { |
118 | const url = buildRemoteBaseUrl(video, path) | 134 | const url = buildRemoteBaseUrl(video, path) |
119 | 135 | ||
120 | // We need to provide a callback, if no we could have an uncaught exception | 136 | // We need to provide a callback, if no we could have an uncaught exception |
121 | return doRequestAndSaveToFile({ uri: url }, destPath) | 137 | return doRequestAndSaveToFile({ uri: url }, destPath) |
122 | } | 138 | } |
123 | 139 | ||
124 | function buildRemoteBaseUrl (video: VideoModel, path: string) { | 140 | function buildRemoteBaseUrl (video: MVideoAccountLight, path: string) { |
125 | const host = video.VideoChannel.Account.Actor.Server.host | 141 | const host = video.VideoChannel.Account.Actor.Server.host |
126 | 142 | ||
127 | return REMOTE_SCHEME.HTTP + '://' + host + path | 143 | return REMOTE_SCHEME.HTTP + '://' + host + path |
@@ -146,7 +162,7 @@ type SyncParam = { | |||
146 | thumbnail: boolean | 162 | thumbnail: boolean |
147 | refreshVideo?: boolean | 163 | refreshVideo?: boolean |
148 | } | 164 | } |
149 | async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) { | 165 | async function syncVideoExternalAttributes (video: MVideo, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) { |
150 | logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid) | 166 | logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid) |
151 | 167 | ||
152 | const jobPayloads: ActivitypubHttpFetcherPayload[] = [] | 168 | const jobPayloads: ActivitypubHttpFetcherPayload[] = [] |
@@ -194,12 +210,24 @@ async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: Vid | |||
194 | await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })) | 210 | await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })) |
195 | } | 211 | } |
196 | 212 | ||
213 | function getOrCreateVideoAndAccountAndChannel (options: { | ||
214 | videoObject: { id: string } | string, | ||
215 | syncParam?: SyncParam, | ||
216 | fetchType?: 'all', | ||
217 | allowRefresh?: boolean | ||
218 | }): Promise<{ video: MVideoAccountAllFiles, created: boolean, autoBlacklisted?: boolean }> | ||
219 | function getOrCreateVideoAndAccountAndChannel (options: { | ||
220 | videoObject: { id: string } | string, | ||
221 | syncParam?: SyncParam, | ||
222 | fetchType?: VideoFetchByUrlType, | ||
223 | allowRefresh?: boolean | ||
224 | }): Promise<{ video: MVideoAccountAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }> | ||
197 | async function getOrCreateVideoAndAccountAndChannel (options: { | 225 | async function getOrCreateVideoAndAccountAndChannel (options: { |
198 | videoObject: { id: string } | string, | 226 | videoObject: { id: string } | string, |
199 | syncParam?: SyncParam, | 227 | syncParam?: SyncParam, |
200 | fetchType?: VideoFetchByUrlType, | 228 | fetchType?: VideoFetchByUrlType, |
201 | allowRefresh?: boolean // true by default | 229 | allowRefresh?: boolean // true by default |
202 | }) { | 230 | }): Promise<{ video: MVideoAccountAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }> { |
203 | // Default params | 231 | // Default params |
204 | const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } | 232 | const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } |
205 | const fetchType = options.fetchType || 'all' | 233 | const fetchType = options.fetchType || 'all' |
@@ -227,8 +255,9 @@ async function getOrCreateVideoAndAccountAndChannel (options: { | |||
227 | const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl) | 255 | const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl) |
228 | if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl) | 256 | if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl) |
229 | 257 | ||
230 | const channelActor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo) | 258 | const actor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo) |
231 | const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, channelActor, syncParam.thumbnail) | 259 | const videoChannel = actor.VideoChannel |
260 | const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, videoChannel, syncParam.thumbnail) | ||
232 | 261 | ||
233 | await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam) | 262 | await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam) |
234 | 263 | ||
@@ -236,22 +265,22 @@ async function getOrCreateVideoAndAccountAndChannel (options: { | |||
236 | } | 265 | } |
237 | 266 | ||
238 | async function updateVideoFromAP (options: { | 267 | async function updateVideoFromAP (options: { |
239 | video: VideoModel, | 268 | video: MVideoAccountAllFiles, |
240 | videoObject: VideoTorrentObject, | 269 | videoObject: VideoTorrentObject, |
241 | account: AccountModelIdActor, | 270 | account: MAccountActor, |
242 | channel: VideoChannelModelIdActor, | 271 | channel: MChannelDefault, |
243 | overrideTo?: string[] | 272 | overrideTo?: string[] |
244 | }) { | 273 | }) { |
245 | const { video, videoObject, account, channel, overrideTo } = options | 274 | const { video, videoObject, account, channel, overrideTo } = options |
246 | 275 | ||
247 | logger.debug('Updating remote video "%s".', options.videoObject.uuid) | 276 | logger.debug('Updating remote video "%s".', options.videoObject.uuid, { account, channel }) |
248 | 277 | ||
249 | let videoFieldsSave: any | 278 | let videoFieldsSave: any |
250 | const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE | 279 | const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE |
251 | const wasUnlistedVideo = video.privacy === VideoPrivacy.UNLISTED | 280 | const wasUnlistedVideo = video.privacy === VideoPrivacy.UNLISTED |
252 | 281 | ||
253 | try { | 282 | try { |
254 | let thumbnailModel: ThumbnailModel | 283 | let thumbnailModel: MThumbnail |
255 | 284 | ||
256 | try { | 285 | try { |
257 | thumbnailModel = await createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) | 286 | thumbnailModel = await createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) |
@@ -259,7 +288,7 @@ async function updateVideoFromAP (options: { | |||
259 | logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err }) | 288 | logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err }) |
260 | } | 289 | } |
261 | 290 | ||
262 | await sequelizeTypescript.transaction(async t => { | 291 | const videoUpdated = await sequelizeTypescript.transaction(async t => { |
263 | const sequelizeOptions = { transaction: t } | 292 | const sequelizeOptions = { transaction: t } |
264 | 293 | ||
265 | videoFieldsSave = video.toJSON() | 294 | videoFieldsSave = video.toJSON() |
@@ -293,21 +322,21 @@ async function updateVideoFromAP (options: { | |||
293 | video.channelId = videoData.channelId | 322 | video.channelId = videoData.channelId |
294 | video.views = videoData.views | 323 | video.views = videoData.views |
295 | 324 | ||
296 | await video.save(sequelizeOptions) | 325 | const videoUpdated = await video.save(sequelizeOptions) as MVideoFullLight |
297 | 326 | ||
298 | if (thumbnailModel) await video.addAndSaveThumbnail(thumbnailModel, t) | 327 | if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel, t) |
299 | 328 | ||
300 | // FIXME: use icon URL instead | 329 | // FIXME: use icon URL instead |
301 | const previewUrl = buildRemoteBaseUrl(video, join(STATIC_PATHS.PREVIEWS, video.getPreview().filename)) | 330 | const previewUrl = buildRemoteBaseUrl(videoUpdated, join(STATIC_PATHS.PREVIEWS, videoUpdated.getPreview().filename)) |
302 | const previewModel = createPlaceholderThumbnail(previewUrl, video, ThumbnailType.PREVIEW, PREVIEWS_SIZE) | 331 | const previewModel = createPlaceholderThumbnail(previewUrl, video, ThumbnailType.PREVIEW, PREVIEWS_SIZE) |
303 | await video.addAndSaveThumbnail(previewModel, t) | 332 | await videoUpdated.addAndSaveThumbnail(previewModel, t) |
304 | 333 | ||
305 | { | 334 | { |
306 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(video, videoObject) | 335 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoUpdated, videoObject) |
307 | const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a)) | 336 | const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a)) |
308 | 337 | ||
309 | // Remove video files that do not exist anymore | 338 | // Remove video files that do not exist anymore |
310 | const destroyTasks = video.VideoFiles | 339 | const destroyTasks = videoUpdated.VideoFiles |
311 | .filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f))) | 340 | .filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f))) |
312 | .map(f => f.destroy(sequelizeOptions)) | 341 | .map(f => f.destroy(sequelizeOptions)) |
313 | await Promise.all(destroyTasks) | 342 | await Promise.all(destroyTasks) |
@@ -318,15 +347,15 @@ async function updateVideoFromAP (options: { | |||
318 | .then(([ file ]) => file) | 347 | .then(([ file ]) => file) |
319 | }) | 348 | }) |
320 | 349 | ||
321 | video.VideoFiles = await Promise.all(upsertTasks) | 350 | videoUpdated.VideoFiles = await Promise.all(upsertTasks) |
322 | } | 351 | } |
323 | 352 | ||
324 | { | 353 | { |
325 | const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(video, videoObject, video.VideoFiles) | 354 | const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(videoUpdated, videoObject, videoUpdated.VideoFiles) |
326 | const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) | 355 | const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) |
327 | 356 | ||
328 | // Remove video files that do not exist anymore | 357 | // Remove video files that do not exist anymore |
329 | const destroyTasks = video.VideoStreamingPlaylists | 358 | const destroyTasks = videoUpdated.VideoStreamingPlaylists |
330 | .filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f))) | 359 | .filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f))) |
331 | .map(f => f.destroy(sequelizeOptions)) | 360 | .map(f => f.destroy(sequelizeOptions)) |
332 | await Promise.all(destroyTasks) | 361 | await Promise.all(destroyTasks) |
@@ -337,38 +366,42 @@ async function updateVideoFromAP (options: { | |||
337 | .then(([ streamingPlaylist ]) => streamingPlaylist) | 366 | .then(([ streamingPlaylist ]) => streamingPlaylist) |
338 | }) | 367 | }) |
339 | 368 | ||
340 | video.VideoStreamingPlaylists = await Promise.all(upsertTasks) | 369 | videoUpdated.VideoStreamingPlaylists = await Promise.all(upsertTasks) |
341 | } | 370 | } |
342 | 371 | ||
343 | { | 372 | { |
344 | // Update Tags | 373 | // Update Tags |
345 | const tags = videoObject.tag.map(tag => tag.name) | 374 | const tags = videoObject.tag.map(tag => tag.name) |
346 | const tagInstances = await TagModel.findOrCreateTags(tags, t) | 375 | const tagInstances = await TagModel.findOrCreateTags(tags, t) |
347 | await video.$set('Tags', tagInstances, sequelizeOptions) | 376 | await videoUpdated.$set('Tags', tagInstances, sequelizeOptions) |
348 | } | 377 | } |
349 | 378 | ||
350 | { | 379 | { |
351 | // Update captions | 380 | // Update captions |
352 | await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(video.id, t) | 381 | await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(videoUpdated.id, t) |
353 | 382 | ||
354 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { | 383 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { |
355 | return VideoCaptionModel.insertOrReplaceLanguage(video.id, c.identifier, t) | 384 | return VideoCaptionModel.insertOrReplaceLanguage(videoUpdated.id, c.identifier, t) |
356 | }) | 385 | }) |
357 | video.VideoCaptions = await Promise.all(videoCaptionsPromises) | 386 | await Promise.all(videoCaptionsPromises) |
358 | } | 387 | } |
388 | |||
389 | return videoUpdated | ||
359 | }) | 390 | }) |
360 | 391 | ||
361 | await autoBlacklistVideoIfNeeded({ | 392 | await autoBlacklistVideoIfNeeded({ |
362 | video, | 393 | video: videoUpdated, |
363 | user: undefined, | 394 | user: undefined, |
364 | isRemote: true, | 395 | isRemote: true, |
365 | isNew: false, | 396 | isNew: false, |
366 | transaction: undefined | 397 | transaction: undefined |
367 | }) | 398 | }) |
368 | 399 | ||
369 | if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(video) // Notify our users? | 400 | if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated) // Notify our users? |
370 | 401 | ||
371 | logger.info('Remote video with uuid %s updated', videoObject.uuid) | 402 | logger.info('Remote video with uuid %s updated', videoObject.uuid) |
403 | |||
404 | return videoUpdated | ||
372 | } catch (err) { | 405 | } catch (err) { |
373 | if (video !== undefined && videoFieldsSave !== undefined) { | 406 | if (video !== undefined && videoFieldsSave !== undefined) { |
374 | resetSequelizeInstance(video, videoFieldsSave) | 407 | resetSequelizeInstance(video, videoFieldsSave) |
@@ -381,15 +414,15 @@ async function updateVideoFromAP (options: { | |||
381 | } | 414 | } |
382 | 415 | ||
383 | async function refreshVideoIfNeeded (options: { | 416 | async function refreshVideoIfNeeded (options: { |
384 | video: VideoModel, | 417 | video: MVideoThumbnail, |
385 | fetchedType: VideoFetchByUrlType, | 418 | fetchedType: VideoFetchByUrlType, |
386 | syncParam: SyncParam | 419 | syncParam: SyncParam |
387 | }): Promise<VideoModel> { | 420 | }): Promise<MVideoThumbnail> { |
388 | if (!options.video.isOutdated()) return options.video | 421 | if (!options.video.isOutdated()) return options.video |
389 | 422 | ||
390 | // We need more attributes if the argument video was fetched with not enough joints | 423 | // We need more attributes if the argument video was fetched with not enough joints |
391 | const video = options.fetchedType === 'all' | 424 | const video = options.fetchedType === 'all' |
392 | ? options.video | 425 | ? options.video as MVideoAccountAllFiles |
393 | : await VideoModel.loadByUrlAndPopulateAccount(options.video.url) | 426 | : await VideoModel.loadByUrlAndPopulateAccount(options.video.url) |
394 | 427 | ||
395 | try { | 428 | try { |
@@ -410,12 +443,11 @@ async function refreshVideoIfNeeded (options: { | |||
410 | } | 443 | } |
411 | 444 | ||
412 | const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) | 445 | const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) |
413 | const account = await AccountModel.load(channelActor.VideoChannel.accountId) | ||
414 | 446 | ||
415 | const updateOptions = { | 447 | const updateOptions = { |
416 | video, | 448 | video, |
417 | videoObject, | 449 | videoObject, |
418 | account, | 450 | account: channelActor.VideoChannel.Account, |
419 | channel: channelActor.VideoChannel | 451 | channel: channelActor.VideoChannel |
420 | } | 452 | } |
421 | await retryTransactionWrapper(updateVideoFromAP, updateOptions) | 453 | await retryTransactionWrapper(updateVideoFromAP, updateOptions) |
@@ -467,15 +499,15 @@ function isAPPlaylistSegmentHashesUrlObject (tag: any): tag is ActivityPlaylistS | |||
467 | return tag.name === 'sha256' && tag.type === 'Link' && urlMediaType === 'application/json' | 499 | return tag.name === 'sha256' && tag.type === 'Link' && urlMediaType === 'application/json' |
468 | } | 500 | } |
469 | 501 | ||
470 | async function createVideo (videoObject: VideoTorrentObject, channelActor: ActorModel, waitThumbnail = false) { | 502 | async function createVideo (videoObject: VideoTorrentObject, channel: MChannelAccountLight, waitThumbnail = false) { |
471 | logger.debug('Adding remote video %s.', videoObject.id) | 503 | logger.debug('Adding remote video %s.', videoObject.id) |
472 | 504 | ||
473 | const videoData = await videoActivityObjectToDBAttributes(channelActor.VideoChannel, videoObject, videoObject.to) | 505 | const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, videoObject.to) |
474 | const video = VideoModel.build(videoData) | 506 | const video = VideoModel.build(videoData) as MVideoThumbnail |
475 | 507 | ||
476 | const promiseThumbnail = createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) | 508 | const promiseThumbnail = createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) |
477 | 509 | ||
478 | let thumbnailModel: ThumbnailModel | 510 | let thumbnailModel: MThumbnail |
479 | if (waitThumbnail === true) { | 511 | if (waitThumbnail === true) { |
480 | thumbnailModel = await promiseThumbnail | 512 | thumbnailModel = await promiseThumbnail |
481 | } | 513 | } |
@@ -483,8 +515,8 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor | |||
483 | const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => { | 515 | const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => { |
484 | const sequelizeOptions = { transaction: t } | 516 | const sequelizeOptions = { transaction: t } |
485 | 517 | ||
486 | const videoCreated = await video.save(sequelizeOptions) | 518 | const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight |
487 | videoCreated.VideoChannel = channelActor.VideoChannel | 519 | videoCreated.VideoChannel = channel |
488 | 520 | ||
489 | if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) | 521 | if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) |
490 | 522 | ||
@@ -517,15 +549,14 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor | |||
517 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { | 549 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { |
518 | return VideoCaptionModel.insertOrReplaceLanguage(videoCreated.id, c.identifier, t) | 550 | return VideoCaptionModel.insertOrReplaceLanguage(videoCreated.id, c.identifier, t) |
519 | }) | 551 | }) |
520 | const captions = await Promise.all(videoCaptionsPromises) | 552 | await Promise.all(videoCaptionsPromises) |
521 | 553 | ||
522 | video.VideoFiles = videoFiles | 554 | videoCreated.VideoFiles = videoFiles |
523 | video.VideoStreamingPlaylists = streamingPlaylists | 555 | videoCreated.VideoStreamingPlaylists = streamingPlaylists |
524 | video.Tags = tagInstances | 556 | videoCreated.Tags = tagInstances |
525 | video.VideoCaptions = captions | ||
526 | 557 | ||
527 | const autoBlacklisted = await autoBlacklistVideoIfNeeded({ | 558 | const autoBlacklisted = await autoBlacklistVideoIfNeeded({ |
528 | video, | 559 | video: videoCreated, |
529 | user: undefined, | 560 | user: undefined, |
530 | isRemote: true, | 561 | isRemote: true, |
531 | isNew: true, | 562 | isNew: true, |
@@ -548,11 +579,7 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor | |||
548 | return { autoBlacklisted, videoCreated } | 579 | return { autoBlacklisted, videoCreated } |
549 | } | 580 | } |
550 | 581 | ||
551 | async function videoActivityObjectToDBAttributes ( | 582 | 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 | 583 | const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED |
557 | const duration = videoObject.duration.replace(/[^\d]+/, '') | 584 | const duration = videoObject.duration.replace(/[^\d]+/, '') |
558 | 585 | ||
@@ -603,7 +630,7 @@ async function videoActivityObjectToDBAttributes ( | |||
603 | } | 630 | } |
604 | } | 631 | } |
605 | 632 | ||
606 | function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: VideoTorrentObject) { | 633 | function videoFileActivityUrlToDBAttributes (video: MVideo, videoObject: VideoTorrentObject) { |
607 | const fileUrls = videoObject.url.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[] | 634 | const fileUrls = videoObject.url.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[] |
608 | 635 | ||
609 | if (fileUrls.length === 0) { | 636 | if (fileUrls.length === 0) { |
@@ -641,7 +668,7 @@ function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: Vid | |||
641 | return attributes | 668 | return attributes |
642 | } | 669 | } |
643 | 670 | ||
644 | function streamingPlaylistActivityUrlToDBAttributes (video: VideoModel, videoObject: VideoTorrentObject, videoFiles: VideoFileModel[]) { | 671 | function streamingPlaylistActivityUrlToDBAttributes (video: MVideoId, videoObject: VideoTorrentObject, videoFiles: MVideoFile[]) { |
645 | const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[] | 672 | const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[] |
646 | if (playlistUrls.length === 0) return [] | 673 | if (playlistUrls.length === 0) return [] |
647 | 674 | ||