]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/lib/activitypub/videos/shared/video-create.ts
Refactor AP video update
[github/Chocobozzz/PeerTube.git] / server / lib / activitypub / videos / shared / video-create.ts
CommitLineData
69290ab3
C
1import { logger } from '@server/helpers/logger'
2import { sequelizeTypescript } from '@server/initializers/database'
3import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '@server/lib/thumbnail'
4import { setVideoTags } from '@server/lib/video'
5import { autoBlacklistVideoIfNeeded } from '@server/lib/video-blacklist'
6import { VideoModel } from '@server/models/video/video'
7import { VideoCaptionModel } from '@server/models/video/video-caption'
8import { VideoFileModel } from '@server/models/video/video-file'
9import { VideoLiveModel } from '@server/models/video/video-live'
10import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
11import {
12 MChannelAccountLight,
13 MStreamingPlaylistFilesVideo,
14 MThumbnail,
15 MVideoCaption,
16 MVideoFullLight,
17 MVideoThumbnail
18} from '@server/types/models'
19import { ThumbnailType, VideoObject } from '@shared/models'
20import {
21 getPreviewFromIcons,
22 getTagsFromObject,
23 getThumbnailFromIcons,
24 streamingPlaylistActivityUrlToDBAttributes,
25 videoActivityObjectToDBAttributes,
26 videoFileActivityUrlToDBAttributes
27} from './object-to-model-attributes'
28import { getTrackerUrls, setVideoTrackers } from './trackers'
29
30async function createVideo (videoObject: VideoObject, channel: MChannelAccountLight, waitThumbnail = false) {
31 logger.debug('Adding remote video %s.', videoObject.id)
32
33 const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, videoObject.to)
34 const video = VideoModel.build(videoData) as MVideoThumbnail
35
36 const promiseThumbnail = createVideoMiniatureFromUrl({
37 downloadUrl: getThumbnailFromIcons(videoObject).url,
38 video,
39 type: ThumbnailType.MINIATURE
40 }).catch(err => {
41 logger.error('Cannot create miniature from url.', { err })
42 return undefined
43 })
44
45 let thumbnailModel: MThumbnail
46 if (waitThumbnail === true) {
47 thumbnailModel = await promiseThumbnail
48 }
49
50 const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => {
51 try {
52 const sequelizeOptions = { transaction: t }
53
54 const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight
55 videoCreated.VideoChannel = channel
56
57 if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t)
58
59 const previewIcon = getPreviewFromIcons(videoObject)
60 if (previewIcon) {
61 const previewModel = createPlaceholderThumbnail({
62 fileUrl: previewIcon.url,
63 video: videoCreated,
64 type: ThumbnailType.PREVIEW,
65 size: previewIcon
66 })
67
68 await videoCreated.addAndSaveThumbnail(previewModel, t)
69 }
70
71 // Process files
72 const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoCreated, videoObject.url)
73
74 const videoFilePromises = videoFileAttributes.map(f => VideoFileModel.create(f, { transaction: t }))
75 const videoFiles = await Promise.all(videoFilePromises)
76
77 const streamingPlaylistsAttributes = streamingPlaylistActivityUrlToDBAttributes(videoCreated, videoObject, videoFiles)
78 videoCreated.VideoStreamingPlaylists = []
79
80 for (const playlistAttributes of streamingPlaylistsAttributes) {
81 const playlist = await VideoStreamingPlaylistModel.create(playlistAttributes, { transaction: t }) as MStreamingPlaylistFilesVideo
82 playlist.Video = videoCreated
83
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)
87
88 videoCreated.VideoStreamingPlaylists.push(playlist)
89 }
90
91 // Process tags
92 const tags = getTagsFromObject(videoObject)
93 await setVideoTags({ video: videoCreated, tags, transaction: t })
94
95 // Process captions
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,
101 fileUrl: c.url
102 }) as MVideoCaption
103
104 return VideoCaptionModel.insertOrReplaceLanguage(caption, t)
105 })
106 await Promise.all(videoCaptionsPromises)
107
108 // Process trackers
109 {
110 const trackers = getTrackerUrls(videoObject, videoCreated)
111 await setVideoTrackers({ video: videoCreated, trackers, transaction: t })
112 }
113
114 videoCreated.VideoFiles = videoFiles
115
116 if (videoCreated.isLive) {
117 const videoLive = new VideoLiveModel({
118 streamKey: null,
119 saveReplay: videoObject.liveSaveReplay,
120 permanentLive: videoObject.permanentLive,
121 videoId: videoCreated.id
122 })
123
124 videoCreated.VideoLive = await videoLive.save({ transaction: t })
125 }
126
127 // We added a video in this channel, set it as updated
128 await channel.setAsUpdated(t)
129
130 const autoBlacklisted = await autoBlacklistVideoIfNeeded({
131 video: videoCreated,
132 user: undefined,
133 isRemote: true,
134 isNew: true,
135 transaction: t
136 })
137
138 logger.info('Remote video with uuid %s inserted.', videoObject.uuid)
139
140 return { autoBlacklisted, videoCreated }
141 } catch (err) {
142 // FIXME: Use rollback hook when https://github.com/sequelize/sequelize/pull/13038 is released
143 // Remove thumbnail
144 if (thumbnailModel) await thumbnailModel.removeThumbnail()
145
146 throw err
147 }
148 })
149
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
155
156 thumbnailModel = videoCreated.id
157
158 return thumbnailModel.save()
159 })
160 }
161
162 return { autoBlacklisted, videoCreated }
163}
164
165export {
166 createVideo
167}