]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/lib/activitypub/videos/shared/abstract-builder.ts
Refactor AP video create/update
[github/Chocobozzz/PeerTube.git] / server / lib / activitypub / videos / shared / abstract-builder.ts
CommitLineData
08a47c75
C
1import { Transaction } from 'sequelize/types'
2import { deleteNonExistingModels } from '@server/helpers/database-utils'
3import { logger } from '@server/helpers/logger'
4import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '@server/lib/thumbnail'
5import { setVideoTags } from '@server/lib/video'
6import { VideoCaptionModel } from '@server/models/video/video-caption'
7import { VideoFileModel } from '@server/models/video/video-file'
8import { VideoLiveModel } from '@server/models/video/video-live'
9import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
10import { MStreamingPlaylistFilesVideo, MThumbnail, MVideoCaption, MVideoFile, MVideoFullLight, MVideoThumbnail } from '@server/types/models'
11import { ActivityTagObject, ThumbnailType, VideoObject, VideoStreamingPlaylistType } from '@shared/models'
12import {
13 getCaptionAttributesFromObject,
14 getFileAttributesFromUrl,
15 getLiveAttributesFromObject,
16 getPreviewFromIcons,
17 getStreamingPlaylistAttributesFromObject,
18 getTagsFromObject,
19 getThumbnailFromIcons
20} from './object-to-model-attributes'
21import { getTrackerUrls, setVideoTrackers } from './trackers'
22
23export abstract class APVideoAbstractBuilder {
24 protected abstract videoObject: VideoObject
25
26 protected tryToGenerateThumbnail (video: MVideoThumbnail): Promise<MThumbnail> {
27 return createVideoMiniatureFromUrl({
28 downloadUrl: getThumbnailFromIcons(this.videoObject).url,
29 video,
30 type: ThumbnailType.MINIATURE
31 }).catch(err => {
32 logger.warn('Cannot generate thumbnail of %s.', this.videoObject.id, { err })
33
34 return undefined
35 })
36 }
37
38 protected async setPreview (video: MVideoFullLight, t: Transaction) {
39 // Don't fetch the preview that could be big, create a placeholder instead
40 const previewIcon = getPreviewFromIcons(this.videoObject)
41 if (!previewIcon) return
42
43 const previewModel = createPlaceholderThumbnail({
44 fileUrl: previewIcon.url,
45 video,
46 type: ThumbnailType.PREVIEW,
47 size: previewIcon
48 })
49
50 await video.addAndSaveThumbnail(previewModel, t)
51 }
52
53 protected async setTags (video: MVideoFullLight, t: Transaction) {
54 const tags = getTagsFromObject(this.videoObject)
55 await setVideoTags({ video, tags, transaction: t })
56 }
57
58 protected async setTrackers (video: MVideoFullLight, t: Transaction) {
59 const trackers = getTrackerUrls(this.videoObject, video)
60 await setVideoTrackers({ video, trackers, transaction: t })
61 }
62
63 protected async insertOrReplaceCaptions (video: MVideoFullLight, t: Transaction) {
64 const videoCaptionsPromises = getCaptionAttributesFromObject(video, this.videoObject)
65 .map(a => new VideoCaptionModel(a) as MVideoCaption)
66 .map(c => VideoCaptionModel.insertOrReplaceLanguage(c, t))
67
68 await Promise.all(videoCaptionsPromises)
69 }
70
71 protected async insertOrReplaceLive (video: MVideoFullLight, transaction: Transaction) {
72 const attributes = getLiveAttributesFromObject(video, this.videoObject)
73 const [ videoLive ] = await VideoLiveModel.upsert(attributes, { transaction, returning: true })
74
75 video.VideoLive = videoLive
76 }
77
78 protected async setWebTorrentFiles (video: MVideoFullLight, t: Transaction) {
79 const videoFileAttributes = getFileAttributesFromUrl(video, this.videoObject.url)
80 const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a))
81
82 // Remove video files that do not exist anymore
83 const destroyTasks = deleteNonExistingModels(video.VideoFiles || [], newVideoFiles, t)
84 await Promise.all(destroyTasks)
85
86 // Update or add other one
87 const upsertTasks = newVideoFiles.map(f => VideoFileModel.customUpsert(f, 'video', t))
88 video.VideoFiles = await Promise.all(upsertTasks)
89 }
90
91 protected async setStreamingPlaylists (video: MVideoFullLight, t: Transaction) {
92 const streamingPlaylistAttributes = getStreamingPlaylistAttributesFromObject(video, this.videoObject, video.VideoFiles || [])
93 const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a))
94
95 // Remove video playlists that do not exist anymore
96 const destroyTasks = deleteNonExistingModels(video.VideoStreamingPlaylists || [], newStreamingPlaylists, t)
97 await Promise.all(destroyTasks)
98
99 video.VideoStreamingPlaylists = []
100
101 for (const playlistAttributes of streamingPlaylistAttributes) {
102
103 const streamingPlaylistModel = await this.insertOrReplaceStreamingPlaylist(playlistAttributes, t)
104 streamingPlaylistModel.Video = video
105
106 await this.setStreamingPlaylistFiles(video, streamingPlaylistModel, playlistAttributes.tagAPObject, t)
107
108 video.VideoStreamingPlaylists.push(streamingPlaylistModel)
109 }
110 }
111
112 private async insertOrReplaceStreamingPlaylist (attributes: VideoStreamingPlaylistModel['_creationAttributes'], t: Transaction) {
113 const [ streamingPlaylist ] = await VideoStreamingPlaylistModel.upsert(attributes, { returning: true, transaction: t })
114
115 return streamingPlaylist as MStreamingPlaylistFilesVideo
116 }
117
118 private getStreamingPlaylistFiles (video: MVideoFullLight, type: VideoStreamingPlaylistType) {
119 const playlist = video.VideoStreamingPlaylists.find(s => s.type === type)
120 if (!playlist) return []
121
122 return playlist.VideoFiles
123 }
124
125 private async setStreamingPlaylistFiles (
126 video: MVideoFullLight,
127 playlistModel: MStreamingPlaylistFilesVideo,
128 tagObjects: ActivityTagObject[],
129 t: Transaction
130 ) {
131 const oldStreamingPlaylistFiles = this.getStreamingPlaylistFiles(video, playlistModel.type)
132
133 const newVideoFiles: MVideoFile[] = getFileAttributesFromUrl(playlistModel, tagObjects).map(a => new VideoFileModel(a))
134
135 const destroyTasks = deleteNonExistingModels(oldStreamingPlaylistFiles, newVideoFiles, t)
136 await Promise.all(destroyTasks)
137
138 // Update or add other one
139 const upsertTasks = newVideoFiles.map(f => VideoFileModel.customUpsert(f, 'streaming-playlist', t))
140 playlistModel.VideoFiles = await Promise.all(upsertTasks)
141 }
142}