aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/job-queue/handlers/video-file.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib/job-queue/handlers/video-file.ts')
-rw-r--r--server/lib/job-queue/handlers/video-file.ts106
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'
5import { JobQueue } from '../job-queue' 5import { JobQueue } from '../job-queue'
6import { federateVideoIfNeeded } from '../../activitypub' 6import { federateVideoIfNeeded } from '../../activitypub'
7import { retryTransactionWrapper } from '../../../helpers/database-utils' 7import { retryTransactionWrapper } from '../../../helpers/database-utils'
8import { sequelizeTypescript } from '../../../initializers' 8import { sequelizeTypescript, CONFIG } from '../../../initializers'
9import * as Bluebird from 'bluebird' 9import * as Bluebird from 'bluebird'
10import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils' 10import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils'
11import { importVideoFile, transcodeOriginalVideofile, optimizeOriginalVideofile } from '../../video-transcoding' 11import { generateHlsPlaylist, importVideoFile, optimizeVideofile, transcodeOriginalVideofile } from '../../video-transcoding'
12import { Notifier } from '../../notifier'
12 13
13export type VideoFilePayload = { 14export 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
20export type VideoFileImportPayload = { 22export 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
67async function onVideoFileTranscoderOrImportSuccess (video: VideoModel) { 72async 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
86async 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
94async function onVideoFileOptimizerSuccess (video: VideoModel, isNewVideo: boolean) { 121async 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
191function 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}