diff options
Diffstat (limited to 'server/lib/job-queue/handlers/video-file.ts')
-rw-r--r-- | server/lib/job-queue/handlers/video-file.ts | 106 |
1 files changed, 82 insertions, 24 deletions
diff --git a/server/lib/job-queue/handlers/video-file.ts b/server/lib/job-queue/handlers/video-file.ts index 1463c93fc..04983155c 100644 --- a/server/lib/job-queue/handlers/video-file.ts +++ b/server/lib/job-queue/handlers/video-file.ts | |||
@@ -5,16 +5,18 @@ import { VideoModel } from '../../../models/video/video' | |||
5 | import { JobQueue } from '../job-queue' | 5 | import { JobQueue } from '../job-queue' |
6 | import { federateVideoIfNeeded } from '../../activitypub' | 6 | import { federateVideoIfNeeded } from '../../activitypub' |
7 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 7 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
8 | import { sequelizeTypescript } from '../../../initializers' | 8 | import { sequelizeTypescript, CONFIG } from '../../../initializers' |
9 | import * as Bluebird from 'bluebird' | 9 | import * as Bluebird from 'bluebird' |
10 | import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils' | 10 | import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils' |
11 | import { importVideoFile, transcodeOriginalVideofile, optimizeOriginalVideofile } from '../../video-transcoding' | 11 | import { generateHlsPlaylist, importVideoFile, optimizeVideofile, transcodeOriginalVideofile } from '../../video-transcoding' |
12 | import { Notifier } from '../../notifier' | ||
12 | 13 | ||
13 | export type VideoFilePayload = { | 14 | export type VideoFilePayload = { |
14 | videoUUID: string | 15 | videoUUID: string |
15 | isNewVideo?: boolean | ||
16 | resolution?: VideoResolution | 16 | resolution?: VideoResolution |
17 | isNewVideo?: boolean | ||
17 | isPortraitMode?: boolean | 18 | isPortraitMode?: boolean |
19 | generateHlsPlaylist?: boolean | ||
18 | } | 20 | } |
19 | 21 | ||
20 | export type VideoFileImportPayload = { | 22 | export type VideoFileImportPayload = { |
@@ -50,34 +52,51 @@ async function processVideoFile (job: Bull.Job) { | |||
50 | return undefined | 52 | return undefined |
51 | } | 53 | } |
52 | 54 | ||
53 | // Transcoding in other resolution | 55 | if (payload.generateHlsPlaylist) { |
54 | if (payload.resolution) { | 56 | await generateHlsPlaylist(video, payload.resolution, payload.isPortraitMode || false) |
57 | |||
58 | await retryTransactionWrapper(onHlsPlaylistGenerationSuccess, video) | ||
59 | } else if (payload.resolution) { // Transcoding in other resolution | ||
55 | await transcodeOriginalVideofile(video, payload.resolution, payload.isPortraitMode || false) | 60 | await transcodeOriginalVideofile(video, payload.resolution, payload.isPortraitMode || false) |
56 | 61 | ||
57 | await retryTransactionWrapper(onVideoFileTranscoderOrImportSuccess, video) | 62 | await retryTransactionWrapper(onVideoFileTranscoderOrImportSuccess, video, payload) |
58 | } else { | 63 | } else { |
59 | await optimizeOriginalVideofile(video) | 64 | await optimizeVideofile(video) |
60 | 65 | ||
61 | await retryTransactionWrapper(onVideoFileOptimizerSuccess, video, payload.isNewVideo) | 66 | await retryTransactionWrapper(onVideoFileOptimizerSuccess, video, payload) |
62 | } | 67 | } |
63 | 68 | ||
64 | return video | 69 | return video |
65 | } | 70 | } |
66 | 71 | ||
67 | async function onVideoFileTranscoderOrImportSuccess (video: VideoModel) { | 72 | async function onHlsPlaylistGenerationSuccess (video: VideoModel) { |
73 | if (video === undefined) return undefined | ||
74 | |||
75 | await sequelizeTypescript.transaction(async t => { | ||
76 | // Maybe the video changed in database, refresh it | ||
77 | let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t) | ||
78 | // Video does not exist anymore | ||
79 | if (!videoDatabase) return undefined | ||
80 | |||
81 | // If the video was not published, we consider it is a new one for other instances | ||
82 | await federateVideoIfNeeded(videoDatabase, false, t) | ||
83 | }) | ||
84 | } | ||
85 | |||
86 | async function onVideoFileTranscoderOrImportSuccess (video: VideoModel, payload?: VideoFilePayload) { | ||
68 | if (video === undefined) return undefined | 87 | if (video === undefined) return undefined |
69 | 88 | ||
70 | return sequelizeTypescript.transaction(async t => { | 89 | const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => { |
71 | // Maybe the video changed in database, refresh it | 90 | // Maybe the video changed in database, refresh it |
72 | let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t) | 91 | let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t) |
73 | // Video does not exist anymore | 92 | // Video does not exist anymore |
74 | if (!videoDatabase) return undefined | 93 | if (!videoDatabase) return undefined |
75 | 94 | ||
76 | let isNewVideo = false | 95 | let videoPublished = false |
77 | 96 | ||
78 | // We transcoded the video file in another format, now we can publish it | 97 | // We transcoded the video file in another format, now we can publish it |
79 | if (videoDatabase.state !== VideoState.PUBLISHED) { | 98 | if (videoDatabase.state !== VideoState.PUBLISHED) { |
80 | isNewVideo = true | 99 | videoPublished = true |
81 | 100 | ||
82 | videoDatabase.state = VideoState.PUBLISHED | 101 | videoDatabase.state = VideoState.PUBLISHED |
83 | videoDatabase.publishedAt = new Date() | 102 | videoDatabase.publishedAt = new Date() |
@@ -85,21 +104,29 @@ async function onVideoFileTranscoderOrImportSuccess (video: VideoModel) { | |||
85 | } | 104 | } |
86 | 105 | ||
87 | // If the video was not published, we consider it is a new one for other instances | 106 | // If the video was not published, we consider it is a new one for other instances |
88 | await federateVideoIfNeeded(videoDatabase, isNewVideo, t) | 107 | await federateVideoIfNeeded(videoDatabase, videoPublished, t) |
89 | 108 | ||
90 | return undefined | 109 | return { videoDatabase, videoPublished } |
91 | }) | 110 | }) |
111 | |||
112 | // don't notify prior to scheduled video update | ||
113 | if (videoPublished && !videoDatabase.ScheduleVideoUpdate) { | ||
114 | Notifier.Instance.notifyOnNewVideo(videoDatabase) | ||
115 | Notifier.Instance.notifyOnPendingVideoPublished(videoDatabase) | ||
116 | } | ||
117 | |||
118 | await createHlsJobIfEnabled(payload) | ||
92 | } | 119 | } |
93 | 120 | ||
94 | async function onVideoFileOptimizerSuccess (video: VideoModel, isNewVideo: boolean) { | 121 | async function onVideoFileOptimizerSuccess (videoArg: VideoModel, payload: VideoFilePayload) { |
95 | if (video === undefined) return undefined | 122 | if (videoArg === undefined) return undefined |
96 | 123 | ||
97 | // Outside the transaction (IO on disk) | 124 | // Outside the transaction (IO on disk) |
98 | const { videoFileResolution } = await video.getOriginalFileResolution() | 125 | const { videoFileResolution } = await videoArg.getOriginalFileResolution() |
99 | 126 | ||
100 | return sequelizeTypescript.transaction(async t => { | 127 | const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => { |
101 | // Maybe the video changed in database, refresh it | 128 | // Maybe the video changed in database, refresh it |
102 | const videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t) | 129 | let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid, t) |
103 | // Video does not exist anymore | 130 | // Video does not exist anymore |
104 | if (!videoDatabase) return undefined | 131 | if (!videoDatabase) return undefined |
105 | 132 | ||
@@ -110,8 +137,10 @@ async function onVideoFileOptimizerSuccess (video: VideoModel, isNewVideo: boole | |||
110 | { resolutions: resolutionsEnabled } | 137 | { resolutions: resolutionsEnabled } |
111 | ) | 138 | ) |
112 | 139 | ||
140 | let videoPublished = false | ||
141 | |||
113 | if (resolutionsEnabled.length !== 0) { | 142 | if (resolutionsEnabled.length !== 0) { |
114 | const tasks: Bluebird<any>[] = [] | 143 | const tasks: Bluebird<Bull.Job<any>>[] = [] |
115 | 144 | ||
116 | for (const resolution of resolutionsEnabled) { | 145 | for (const resolution of resolutionsEnabled) { |
117 | const dataInput = { | 146 | const dataInput = { |
@@ -127,15 +156,27 @@ async function onVideoFileOptimizerSuccess (video: VideoModel, isNewVideo: boole | |||
127 | 156 | ||
128 | logger.info('Transcoding jobs created for uuid %s.', videoDatabase.uuid, { resolutionsEnabled }) | 157 | logger.info('Transcoding jobs created for uuid %s.', videoDatabase.uuid, { resolutionsEnabled }) |
129 | } else { | 158 | } else { |
159 | videoPublished = true | ||
160 | |||
130 | // No transcoding to do, it's now published | 161 | // No transcoding to do, it's now published |
131 | video.state = VideoState.PUBLISHED | 162 | videoDatabase.state = VideoState.PUBLISHED |
132 | video = await video.save({ transaction: t }) | 163 | videoDatabase = await videoDatabase.save({ transaction: t }) |
133 | 164 | ||
134 | logger.info('No transcoding jobs created for video %s (no resolutions).', video.uuid) | 165 | logger.info('No transcoding jobs created for video %s (no resolutions).', videoDatabase.uuid, { privacy: videoDatabase.privacy }) |
135 | } | 166 | } |
136 | 167 | ||
137 | return federateVideoIfNeeded(video, isNewVideo, t) | 168 | await federateVideoIfNeeded(videoDatabase, payload.isNewVideo, t) |
169 | |||
170 | return { videoDatabase, videoPublished } | ||
138 | }) | 171 | }) |
172 | |||
173 | // don't notify prior to scheduled video update | ||
174 | if (!videoDatabase.ScheduleVideoUpdate) { | ||
175 | if (payload.isNewVideo) Notifier.Instance.notifyOnNewVideo(videoDatabase) | ||
176 | if (videoPublished) Notifier.Instance.notifyOnPendingVideoPublished(videoDatabase) | ||
177 | } | ||
178 | |||
179 | await createHlsJobIfEnabled(Object.assign({}, payload, { resolution: videoDatabase.getOriginalFile().resolution })) | ||
139 | } | 180 | } |
140 | 181 | ||
141 | // --------------------------------------------------------------------------- | 182 | // --------------------------------------------------------------------------- |
@@ -144,3 +185,20 @@ export { | |||
144 | processVideoFile, | 185 | processVideoFile, |
145 | processVideoFileImport | 186 | processVideoFileImport |
146 | } | 187 | } |
188 | |||
189 | // --------------------------------------------------------------------------- | ||
190 | |||
191 | function createHlsJobIfEnabled (payload?: VideoFilePayload) { | ||
192 | // Generate HLS playlist? | ||
193 | if (payload && CONFIG.TRANSCODING.HLS.ENABLED) { | ||
194 | const hlsTranscodingPayload = { | ||
195 | videoUUID: payload.videoUUID, | ||
196 | resolution: payload.resolution, | ||
197 | isPortraitMode: payload.isPortraitMode, | ||
198 | |||
199 | generateHlsPlaylist: true | ||
200 | } | ||
201 | |||
202 | return JobQueue.Instance.createJob({ type: 'video-file', payload: hlsTranscodingPayload }) | ||
203 | } | ||
204 | } | ||