+ 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, created: true }
+}
+
+async function updateVideoFromAP (options: {
+ video: VideoModel,
+ videoObject: VideoTorrentObject,
+ account: AccountModel,
+ channel: VideoChannelModel,
+ overrideTo?: string[]
+}) {
+ logger.debug('Updating remote video "%s".', options.videoObject.uuid)
+
+ let videoFieldsSave: any
+ const wasPrivateVideo = options.video.privacy === VideoPrivacy.PRIVATE
+ const wasUnlistedVideo = options.video.privacy === VideoPrivacy.UNLISTED
+
+ try {
+ 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('downloadEnabled', videoData.downloadEnabled)
+ 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('originallyPublishedAt', videoData.originallyPublishedAt)
+ options.video.set('privacy', videoData.privacy)
+ options.video.set('channelId', videoData.channelId)
+ options.video.set('views', videoData.views)
+
+ await options.video.save(sequelizeOptions)
+
+ {
+ const videoFileAttributes = videoFileActivityUrlToDBAttributes(options.video, options.videoObject)
+ const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a))
+
+ // Remove video files that do not exist anymore
+ const destroyTasks = options.video.VideoFiles
+ .filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f)))
+ .map(f => f.destroy(sequelizeOptions))
+ await Promise.all(destroyTasks)
+
+ // Update or add other one
+ const upsertTasks = videoFileAttributes.map(a => {
+ return VideoFileModel.upsert<VideoFileModel>(a, { returning: true, transaction: t })
+ .then(([ file ]) => file)
+ })
+
+ options.video.VideoFiles = await Promise.all(upsertTasks)
+ }
+
+ {
+ const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(options.video, options.videoObject)
+ const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a))
+
+ // Remove video files that do not exist anymore
+ const destroyTasks = options.video.VideoStreamingPlaylists
+ .filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f)))
+ .map(f => f.destroy(sequelizeOptions))
+ await Promise.all(destroyTasks)
+
+ // Update or add other one
+ const upsertTasks = streamingPlaylistAttributes.map(a => {
+ return VideoStreamingPlaylistModel.upsert<VideoStreamingPlaylistModel>(a, { returning: true, transaction: t })
+ .then(([ streamingPlaylist ]) => streamingPlaylist)
+ })
+
+ options.video.VideoStreamingPlaylists = await Promise.all(upsertTasks)
+ }
+
+ {
+ // 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)
+ })
+ options.video.VideoCaptions = await Promise.all(videoCaptionsPromises)
+ }