1 import { logger } from '@server/helpers/logger'
2 import { sequelizeTypescript } from '@server/initializers/database'
3 import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '@server/lib/thumbnail'
4 import { setVideoTags } from '@server/lib/video'
5 import { autoBlacklistVideoIfNeeded } from '@server/lib/video-blacklist'
6 import { VideoModel } from '@server/models/video/video'
7 import { VideoCaptionModel } from '@server/models/video/video-caption'
8 import { VideoFileModel } from '@server/models/video/video-file'
9 import { VideoLiveModel } from '@server/models/video/video-live'
10 import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
13 MStreamingPlaylistFilesVideo,
18 } from '@server/types/models'
19 import { ThumbnailType, VideoObject } from '@shared/models'
23 getThumbnailFromIcons,
24 streamingPlaylistActivityUrlToDBAttributes,
25 videoActivityObjectToDBAttributes,
26 videoFileActivityUrlToDBAttributes
27 } from './object-to-model-attributes'
28 import { getTrackerUrls, setVideoTrackers } from './trackers'
30 async function createVideo (videoObject: VideoObject, channel: MChannelAccountLight, waitThumbnail = false) {
31 logger.debug('Adding remote video %s.', videoObject.id)
33 const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, videoObject.to)
34 const video = VideoModel.build(videoData) as MVideoThumbnail
36 const promiseThumbnail = createVideoMiniatureFromUrl({
37 downloadUrl: getThumbnailFromIcons(videoObject).url,
39 type: ThumbnailType.MINIATURE
41 logger.error('Cannot create miniature from url.', { err })
45 let thumbnailModel: MThumbnail
46 if (waitThumbnail === true) {
47 thumbnailModel = await promiseThumbnail
50 const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => {
52 const sequelizeOptions = { transaction: t }
54 const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight
55 videoCreated.VideoChannel = channel
57 if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t)
59 const previewIcon = getPreviewFromIcons(videoObject)
61 const previewModel = createPlaceholderThumbnail({
62 fileUrl: previewIcon.url,
64 type: ThumbnailType.PREVIEW,
68 await videoCreated.addAndSaveThumbnail(previewModel, t)
72 const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoCreated, videoObject.url)
74 const videoFilePromises = videoFileAttributes.map(f => VideoFileModel.create(f, { transaction: t }))
75 const videoFiles = await Promise.all(videoFilePromises)
77 const streamingPlaylistsAttributes = streamingPlaylistActivityUrlToDBAttributes(videoCreated, videoObject, videoFiles)
78 videoCreated.VideoStreamingPlaylists = []
80 for (const playlistAttributes of streamingPlaylistsAttributes) {
81 const playlist = await VideoStreamingPlaylistModel.create(playlistAttributes, { transaction: t }) as MStreamingPlaylistFilesVideo
82 playlist.Video = videoCreated
84 const playlistFiles = videoFileActivityUrlToDBAttributes(playlist, playlistAttributes.tagAPObject)
85 const videoFilePromises = playlistFiles.map(f => VideoFileModel.create(f, { transaction: t }))
86 playlist.VideoFiles = await Promise.all(videoFilePromises)
88 videoCreated.VideoStreamingPlaylists.push(playlist)
92 const tags = getTagsFromObject(videoObject)
93 await setVideoTags({ video: videoCreated, tags, transaction: t })
96 const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => {
97 const caption = new VideoCaptionModel({
98 videoId: videoCreated.id,
99 filename: VideoCaptionModel.generateCaptionName(c.identifier),
100 language: c.identifier,
104 return VideoCaptionModel.insertOrReplaceLanguage(caption, t)
106 await Promise.all(videoCaptionsPromises)
110 const trackers = getTrackerUrls(videoObject, videoCreated)
111 await setVideoTrackers({ video: videoCreated, trackers, transaction: t })
114 videoCreated.VideoFiles = videoFiles
116 if (videoCreated.isLive) {
117 const videoLive = new VideoLiveModel({
119 saveReplay: videoObject.liveSaveReplay,
120 permanentLive: videoObject.permanentLive,
121 videoId: videoCreated.id
124 videoCreated.VideoLive = await videoLive.save({ transaction: t })
127 // We added a video in this channel, set it as updated
128 await channel.setAsUpdated(t)
130 const autoBlacklisted = await autoBlacklistVideoIfNeeded({
138 logger.info('Remote video with uuid %s inserted.', videoObject.uuid)
140 return { autoBlacklisted, videoCreated }
142 // FIXME: Use rollback hook when https://github.com/sequelize/sequelize/pull/13038 is released
144 if (thumbnailModel) await thumbnailModel.removeThumbnail()
150 if (waitThumbnail === false) {
151 // Error is already caught above
152 // eslint-disable-next-line @typescript-eslint/no-floating-promises
153 promiseThumbnail.then(thumbnailModel => {
154 if (!thumbnailModel) return
156 thumbnailModel = videoCreated.id
158 return thumbnailModel.save()
162 return { autoBlacklisted, videoCreated }