diff options
author | Chocobozzz <me@florianbigard.com> | 2023-06-01 14:51:16 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2023-06-29 10:16:55 +0200 |
commit | d8f39b126d9fe4bec1c12fb213548cc6edc87867 (patch) | |
tree | 7f0f1cb23165cf4dd789b2d78b1fef7ee116f647 /server/lib/activitypub/videos | |
parent | 1fb7d094229acdc190c3f7551b43ac5445814dee (diff) | |
download | PeerTube-d8f39b126d9fe4bec1c12fb213548cc6edc87867.tar.gz PeerTube-d8f39b126d9fe4bec1c12fb213548cc6edc87867.tar.zst PeerTube-d8f39b126d9fe4bec1c12fb213548cc6edc87867.zip |
Add storyboard support
Diffstat (limited to 'server/lib/activitypub/videos')
5 files changed, 46 insertions, 11 deletions
diff --git a/server/lib/activitypub/videos/federate.ts b/server/lib/activitypub/videos/federate.ts index bd0c54b0c..d7e251153 100644 --- a/server/lib/activitypub/videos/federate.ts +++ b/server/lib/activitypub/videos/federate.ts | |||
@@ -1,10 +1,9 @@ | |||
1 | import { Transaction } from 'sequelize/types' | 1 | import { Transaction } from 'sequelize/types' |
2 | import { isArray } from '@server/helpers/custom-validators/misc' | 2 | import { MVideoAP, MVideoAPLight } from '@server/types/models' |
3 | import { MVideoAP, MVideoAPWithoutCaption } from '@server/types/models' | ||
4 | import { sendCreateVideo, sendUpdateVideo } from '../send' | 3 | import { sendCreateVideo, sendUpdateVideo } from '../send' |
5 | import { shareVideoByServerAndChannel } from '../share' | 4 | import { shareVideoByServerAndChannel } from '../share' |
6 | 5 | ||
7 | async function federateVideoIfNeeded (videoArg: MVideoAPWithoutCaption, isNewVideo: boolean, transaction?: Transaction) { | 6 | async function federateVideoIfNeeded (videoArg: MVideoAPLight, isNewVideo: boolean, transaction?: Transaction) { |
8 | const video = videoArg as MVideoAP | 7 | const video = videoArg as MVideoAP |
9 | 8 | ||
10 | if ( | 9 | if ( |
@@ -13,13 +12,7 @@ async function federateVideoIfNeeded (videoArg: MVideoAPWithoutCaption, isNewVid | |||
13 | // Check the video is public/unlisted and published | 12 | // Check the video is public/unlisted and published |
14 | video.hasPrivacyForFederation() && video.hasStateForFederation() | 13 | video.hasPrivacyForFederation() && video.hasStateForFederation() |
15 | ) { | 14 | ) { |
16 | // Fetch more attributes that we will need to serialize in AP object | 15 | const video = await videoArg.lightAPToFullAP(transaction) |
17 | if (isArray(video.VideoCaptions) === false) { | ||
18 | video.VideoCaptions = await video.$get('VideoCaptions', { | ||
19 | attributes: [ 'filename', 'language' ], | ||
20 | transaction | ||
21 | }) | ||
22 | } | ||
23 | 16 | ||
24 | if (isNewVideo) { | 17 | if (isNewVideo) { |
25 | // Now we'll add the video's meta data to our followers | 18 | // Now we'll add the video's meta data to our followers |
diff --git a/server/lib/activitypub/videos/shared/abstract-builder.ts b/server/lib/activitypub/videos/shared/abstract-builder.ts index c0b92c93d..7c5c73139 100644 --- a/server/lib/activitypub/videos/shared/abstract-builder.ts +++ b/server/lib/activitypub/videos/shared/abstract-builder.ts | |||
@@ -3,6 +3,7 @@ import { deleteAllModels, filterNonExistingModels } from '@server/helpers/databa | |||
3 | import { logger, LoggerTagsFn } from '@server/helpers/logger' | 3 | import { logger, LoggerTagsFn } from '@server/helpers/logger' |
4 | import { updatePlaceholderThumbnail, updateVideoMiniatureFromUrl } from '@server/lib/thumbnail' | 4 | import { updatePlaceholderThumbnail, updateVideoMiniatureFromUrl } from '@server/lib/thumbnail' |
5 | import { setVideoTags } from '@server/lib/video' | 5 | import { setVideoTags } from '@server/lib/video' |
6 | import { StoryboardModel } from '@server/models/video/storyboard' | ||
6 | import { VideoCaptionModel } from '@server/models/video/video-caption' | 7 | import { VideoCaptionModel } from '@server/models/video/video-caption' |
7 | import { VideoFileModel } from '@server/models/video/video-file' | 8 | import { VideoFileModel } from '@server/models/video/video-file' |
8 | import { VideoLiveModel } from '@server/models/video/video-live' | 9 | import { VideoLiveModel } from '@server/models/video/video-live' |
@@ -24,6 +25,7 @@ import { | |||
24 | getFileAttributesFromUrl, | 25 | getFileAttributesFromUrl, |
25 | getLiveAttributesFromObject, | 26 | getLiveAttributesFromObject, |
26 | getPreviewFromIcons, | 27 | getPreviewFromIcons, |
28 | getStoryboardAttributeFromObject, | ||
27 | getStreamingPlaylistAttributesFromObject, | 29 | getStreamingPlaylistAttributesFromObject, |
28 | getTagsFromObject, | 30 | getTagsFromObject, |
29 | getThumbnailFromIcons | 31 | getThumbnailFromIcons |
@@ -107,6 +109,16 @@ export abstract class APVideoAbstractBuilder { | |||
107 | } | 109 | } |
108 | } | 110 | } |
109 | 111 | ||
112 | protected async insertOrReplaceStoryboard (video: MVideoFullLight, t: Transaction) { | ||
113 | const existingStoryboard = await StoryboardModel.loadByVideo(video.id, t) | ||
114 | if (existingStoryboard) await existingStoryboard.destroy({ transaction: t }) | ||
115 | |||
116 | const storyboardAttributes = getStoryboardAttributeFromObject(video, this.videoObject) | ||
117 | if (!storyboardAttributes) return | ||
118 | |||
119 | return StoryboardModel.create(storyboardAttributes, { transaction: t }) | ||
120 | } | ||
121 | |||
110 | protected async insertOrReplaceLive (video: MVideoFullLight, transaction: Transaction) { | 122 | protected async insertOrReplaceLive (video: MVideoFullLight, transaction: Transaction) { |
111 | const attributes = getLiveAttributesFromObject(video, this.videoObject) | 123 | const attributes = getLiveAttributesFromObject(video, this.videoObject) |
112 | const [ videoLive ] = await VideoLiveModel.upsert(attributes, { transaction, returning: true }) | 124 | const [ videoLive ] = await VideoLiveModel.upsert(attributes, { transaction, returning: true }) |
diff --git a/server/lib/activitypub/videos/shared/creator.ts b/server/lib/activitypub/videos/shared/creator.ts index 77321d8a5..e6d7bc23c 100644 --- a/server/lib/activitypub/videos/shared/creator.ts +++ b/server/lib/activitypub/videos/shared/creator.ts | |||
@@ -48,6 +48,7 @@ export class APVideoCreator extends APVideoAbstractBuilder { | |||
48 | await this.setTrackers(videoCreated, t) | 48 | await this.setTrackers(videoCreated, t) |
49 | await this.insertOrReplaceCaptions(videoCreated, t) | 49 | await this.insertOrReplaceCaptions(videoCreated, t) |
50 | await this.insertOrReplaceLive(videoCreated, t) | 50 | await this.insertOrReplaceLive(videoCreated, t) |
51 | await this.insertOrReplaceStoryboard(videoCreated, t) | ||
51 | 52 | ||
52 | // We added a video in this channel, set it as updated | 53 | // We added a video in this channel, set it as updated |
53 | await channel.setAsUpdated(t) | 54 | await channel.setAsUpdated(t) |
diff --git a/server/lib/activitypub/videos/shared/object-to-model-attributes.ts b/server/lib/activitypub/videos/shared/object-to-model-attributes.ts index 8fd0a816c..a9e0bed97 100644 --- a/server/lib/activitypub/videos/shared/object-to-model-attributes.ts +++ b/server/lib/activitypub/videos/shared/object-to-model-attributes.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { maxBy, minBy } from 'lodash' | 1 | import { maxBy, minBy } from 'lodash' |
2 | import { decode as magnetUriDecode } from 'magnet-uri' | 2 | import { decode as magnetUriDecode } from 'magnet-uri' |
3 | import { basename } from 'path' | 3 | import { basename, extname } from 'path' |
4 | import { isAPVideoFileUrlMetadataObject } from '@server/helpers/custom-validators/activitypub/videos' | 4 | import { isAPVideoFileUrlMetadataObject } from '@server/helpers/custom-validators/activitypub/videos' |
5 | import { isVideoFileInfoHashValid } from '@server/helpers/custom-validators/videos' | 5 | import { isVideoFileInfoHashValid } from '@server/helpers/custom-validators/videos' |
6 | import { logger } from '@server/helpers/logger' | 6 | import { logger } from '@server/helpers/logger' |
@@ -25,6 +25,9 @@ import { | |||
25 | VideoStreamingPlaylistType | 25 | VideoStreamingPlaylistType |
26 | } from '@shared/models' | 26 | } from '@shared/models' |
27 | import { getDurationFromActivityStream } from '../../activity' | 27 | import { getDurationFromActivityStream } from '../../activity' |
28 | import { isArray } from '@server/helpers/custom-validators/misc' | ||
29 | import { generateImageFilename } from '@server/helpers/image-utils' | ||
30 | import { arrayify } from '@shared/core-utils' | ||
28 | 31 | ||
29 | function getThumbnailFromIcons (videoObject: VideoObject) { | 32 | function getThumbnailFromIcons (videoObject: VideoObject) { |
30 | let validIcons = videoObject.icon.filter(i => i.width > THUMBNAILS_SIZE.minWidth) | 33 | let validIcons = videoObject.icon.filter(i => i.width > THUMBNAILS_SIZE.minWidth) |
@@ -166,6 +169,26 @@ function getCaptionAttributesFromObject (video: MVideoId, videoObject: VideoObje | |||
166 | })) | 169 | })) |
167 | } | 170 | } |
168 | 171 | ||
172 | function getStoryboardAttributeFromObject (video: MVideoId, videoObject: VideoObject) { | ||
173 | if (!isArray(videoObject.preview)) return undefined | ||
174 | |||
175 | const storyboard = videoObject.preview.find(p => p.rel.includes('storyboard')) | ||
176 | if (!storyboard) return undefined | ||
177 | |||
178 | const url = arrayify(storyboard.url).find(u => u.mediaType === 'image/jpeg') | ||
179 | |||
180 | return { | ||
181 | filename: generateImageFilename(extname(url.href)), | ||
182 | totalHeight: url.height, | ||
183 | totalWidth: url.width, | ||
184 | spriteHeight: url.tileHeight, | ||
185 | spriteWidth: url.tileWidth, | ||
186 | spriteDuration: getDurationFromActivityStream(url.tileDuration), | ||
187 | fileUrl: url.href, | ||
188 | videoId: video.id | ||
189 | } | ||
190 | } | ||
191 | |||
169 | function getVideoAttributesFromObject (videoChannel: MChannelId, videoObject: VideoObject, to: string[] = []) { | 192 | function getVideoAttributesFromObject (videoChannel: MChannelId, videoObject: VideoObject, to: string[] = []) { |
170 | const privacy = to.includes(ACTIVITY_PUB.PUBLIC) | 193 | const privacy = to.includes(ACTIVITY_PUB.PUBLIC) |
171 | ? VideoPrivacy.PUBLIC | 194 | ? VideoPrivacy.PUBLIC |
@@ -228,6 +251,7 @@ export { | |||
228 | 251 | ||
229 | getLiveAttributesFromObject, | 252 | getLiveAttributesFromObject, |
230 | getCaptionAttributesFromObject, | 253 | getCaptionAttributesFromObject, |
254 | getStoryboardAttributeFromObject, | ||
231 | 255 | ||
232 | getVideoAttributesFromObject | 256 | getVideoAttributesFromObject |
233 | } | 257 | } |
diff --git a/server/lib/activitypub/videos/updater.ts b/server/lib/activitypub/videos/updater.ts index 6ddd2301b..3a0886523 100644 --- a/server/lib/activitypub/videos/updater.ts +++ b/server/lib/activitypub/videos/updater.ts | |||
@@ -57,6 +57,7 @@ export class APVideoUpdater extends APVideoAbstractBuilder { | |||
57 | await Promise.all([ | 57 | await Promise.all([ |
58 | runInReadCommittedTransaction(t => this.setTags(videoUpdated, t)), | 58 | runInReadCommittedTransaction(t => this.setTags(videoUpdated, t)), |
59 | runInReadCommittedTransaction(t => this.setTrackers(videoUpdated, t)), | 59 | runInReadCommittedTransaction(t => this.setTrackers(videoUpdated, t)), |
60 | runInReadCommittedTransaction(t => this.setStoryboard(videoUpdated, t)), | ||
60 | this.setOrDeleteLive(videoUpdated), | 61 | this.setOrDeleteLive(videoUpdated), |
61 | this.setPreview(videoUpdated) | 62 | this.setPreview(videoUpdated) |
62 | ]) | 63 | ]) |
@@ -138,6 +139,10 @@ export class APVideoUpdater extends APVideoAbstractBuilder { | |||
138 | await this.insertOrReplaceCaptions(videoUpdated, t) | 139 | await this.insertOrReplaceCaptions(videoUpdated, t) |
139 | } | 140 | } |
140 | 141 | ||
142 | private async setStoryboard (videoUpdated: MVideoFullLight, t: Transaction) { | ||
143 | await this.insertOrReplaceStoryboard(videoUpdated, t) | ||
144 | } | ||
145 | |||
141 | private async setOrDeleteLive (videoUpdated: MVideoFullLight, transaction?: Transaction) { | 146 | private async setOrDeleteLive (videoUpdated: MVideoFullLight, transaction?: Transaction) { |
142 | if (!this.video.isLive) return | 147 | if (!this.video.isLive) return |
143 | 148 | ||