+ const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl)
+ if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl)
+
+ const channelActor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo)
+ const video = await retryTransactionWrapper(createVideo, fetchedVideo, channelActor, syncParam.thumbnail)
+
+ await syncVideoExternalAttributes(video, fetchedVideo, syncParam)
+
+ return { video }
+}
+
+async function updateVideoFromAP (options: {
+ video: VideoModel,
+ videoObject: VideoTorrentObject,
+ account: AccountModel,
+ channel: VideoChannelModel,
+ updateViews: boolean,
+ overrideTo?: string[]
+}) {
+ logger.debug('Updating remote video "%s".', options.videoObject.uuid)
+ let videoFieldsSave: any
+
+ try {
+ const updatedVideo: VideoModel = await sequelizeTypescript.transaction(async t => {
+ const sequelizeOptions = {
+ transaction: t
+ }
+
+ videoFieldsSave = options.video.toJSON()
+
+ // Check actor has the right to update the video
+ const videoChannel = options.video.VideoChannel
+ if (videoChannel.Account.id !== options.account.id) {
+ throw new Error('Account ' + options.account.Actor.url + ' does not own video channel ' + videoChannel.Actor.url)
+ }
+
+ const to = options.overrideTo ? options.overrideTo : options.videoObject.to
+ const videoData = await videoActivityObjectToDBAttributes(options.channel, options.videoObject, to)
+ options.video.set('name', videoData.name)
+ options.video.set('uuid', videoData.uuid)
+ options.video.set('url', videoData.url)
+ options.video.set('category', videoData.category)
+ options.video.set('licence', videoData.licence)
+ options.video.set('language', videoData.language)
+ options.video.set('description', videoData.description)
+ options.video.set('support', videoData.support)
+ options.video.set('nsfw', videoData.nsfw)
+ options.video.set('commentsEnabled', videoData.commentsEnabled)
+ options.video.set('waitTranscoding', videoData.waitTranscoding)
+ options.video.set('state', videoData.state)
+ options.video.set('duration', videoData.duration)
+ options.video.set('createdAt', videoData.createdAt)
+ options.video.set('publishedAt', videoData.publishedAt)
+ options.video.set('privacy', videoData.privacy)
+ options.video.set('channelId', videoData.channelId)
+
+ if (options.updateViews === true) options.video.set('views', videoData.views)
+ await options.video.save(sequelizeOptions)
+
+ // Don't block on request
+ generateThumbnailFromUrl(options.video, options.videoObject.icon)
+ .catch(err => logger.warn('Cannot generate thumbnail of %s.', options.videoObject.id, { err }))
+
+ // Remove old video files
+ const videoFileDestroyTasks: Bluebird<void>[] = []
+ for (const videoFile of options.video.VideoFiles) {
+ videoFileDestroyTasks.push(videoFile.destroy(sequelizeOptions))
+ }
+ await Promise.all(videoFileDestroyTasks)
+
+ const videoFileAttributes = videoFileActivityUrlToDBAttributes(options.video, options.videoObject)
+ const tasks = videoFileAttributes.map(f => VideoFileModel.create(f, sequelizeOptions))
+ await Promise.all(tasks)
+
+ // Update Tags
+ const tags = options.videoObject.tag.map(tag => tag.name)
+ const tagInstances = await TagModel.findOrCreateTags(tags, t)
+ await options.video.$set('Tags', tagInstances, sequelizeOptions)
+
+ // Update captions
+ await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(options.video.id, t)
+
+ const videoCaptionsPromises = options.videoObject.subtitleLanguage.map(c => {
+ return VideoCaptionModel.insertOrReplaceLanguage(options.video.id, c.identifier, t)
+ })
+ await Promise.all(videoCaptionsPromises)