aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/transcoding/web-transcoding.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib/transcoding/web-transcoding.ts')
-rw-r--r--server/lib/transcoding/web-transcoding.ts263
1 files changed, 0 insertions, 263 deletions
diff --git a/server/lib/transcoding/web-transcoding.ts b/server/lib/transcoding/web-transcoding.ts
deleted file mode 100644
index f92d457a0..000000000
--- a/server/lib/transcoding/web-transcoding.ts
+++ /dev/null
@@ -1,263 +0,0 @@
1import { Job } from 'bullmq'
2import { copyFile, move, remove, stat } from 'fs-extra'
3import { basename, join } from 'path'
4import { computeOutputFPS } from '@server/helpers/ffmpeg'
5import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
6import { VideoModel } from '@server/models/video/video'
7import { MVideoFile, MVideoFullLight } from '@server/types/models'
8import { ffprobePromise, getVideoStreamDuration, getVideoStreamFPS, TranscodeVODOptionsType } from '@shared/ffmpeg'
9import { VideoResolution, VideoStorage } from '@shared/models'
10import { CONFIG } from '../../initializers/config'
11import { VideoFileModel } from '../../models/video/video-file'
12import { JobQueue } from '../job-queue'
13import { generateWebVideoFilename } from '../paths'
14import { buildFileMetadata } from '../video-file'
15import { VideoPathManager } from '../video-path-manager'
16import { buildFFmpegVOD } from './shared'
17import { buildOriginalFileResolution } from './transcoding-resolutions'
18
19// Optimize the original video file and replace it. The resolution is not changed.
20export async function optimizeOriginalVideofile (options: {
21 video: MVideoFullLight
22 inputVideoFile: MVideoFile
23 quickTranscode: boolean
24 job: Job
25}) {
26 const { video, inputVideoFile, quickTranscode, job } = options
27
28 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
29 const newExtname = '.mp4'
30
31 // Will be released by our transcodeVOD function once ffmpeg is ran
32 const inputFileMutexReleaser = await VideoPathManager.Instance.lockFiles(video.uuid)
33
34 try {
35 await video.reload()
36 await inputVideoFile.reload()
37
38 const fileWithVideoOrPlaylist = inputVideoFile.withVideoOrPlaylist(video)
39
40 const result = await VideoPathManager.Instance.makeAvailableVideoFile(fileWithVideoOrPlaylist, async videoInputPath => {
41 const videoOutputPath = join(transcodeDirectory, video.id + '-transcoded' + newExtname)
42
43 const transcodeType: TranscodeVODOptionsType = quickTranscode
44 ? 'quick-transcode'
45 : 'video'
46
47 const resolution = buildOriginalFileResolution(inputVideoFile.resolution)
48 const fps = computeOutputFPS({ inputFPS: inputVideoFile.fps, resolution })
49
50 // Could be very long!
51 await buildFFmpegVOD(job).transcode({
52 type: transcodeType,
53
54 inputPath: videoInputPath,
55 outputPath: videoOutputPath,
56
57 inputFileMutexReleaser,
58
59 resolution,
60 fps
61 })
62
63 // Important to do this before getVideoFilename() to take in account the new filename
64 inputVideoFile.resolution = resolution
65 inputVideoFile.extname = newExtname
66 inputVideoFile.filename = generateWebVideoFilename(resolution, newExtname)
67 inputVideoFile.storage = VideoStorage.FILE_SYSTEM
68
69 const { videoFile } = await onWebVideoFileTranscoding({
70 video,
71 videoFile: inputVideoFile,
72 videoOutputPath
73 })
74
75 await remove(videoInputPath)
76
77 return { transcodeType, videoFile }
78 })
79
80 return result
81 } finally {
82 inputFileMutexReleaser()
83 }
84}
85
86// Transcode the original video file to a lower resolution compatible with web browsers
87export async function transcodeNewWebVideoResolution (options: {
88 video: MVideoFullLight
89 resolution: VideoResolution
90 fps: number
91 job: Job
92}) {
93 const { video: videoArg, resolution, fps, job } = options
94
95 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
96 const newExtname = '.mp4'
97
98 const inputFileMutexReleaser = await VideoPathManager.Instance.lockFiles(videoArg.uuid)
99
100 try {
101 const video = await VideoModel.loadFull(videoArg.uuid)
102 const file = video.getMaxQualityFile().withVideoOrPlaylist(video)
103
104 const result = await VideoPathManager.Instance.makeAvailableVideoFile(file, async videoInputPath => {
105 const newVideoFile = new VideoFileModel({
106 resolution,
107 extname: newExtname,
108 filename: generateWebVideoFilename(resolution, newExtname),
109 size: 0,
110 videoId: video.id
111 })
112
113 const videoOutputPath = join(transcodeDirectory, newVideoFile.filename)
114
115 const transcodeOptions = {
116 type: 'video' as 'video',
117
118 inputPath: videoInputPath,
119 outputPath: videoOutputPath,
120
121 inputFileMutexReleaser,
122
123 resolution,
124 fps
125 }
126
127 await buildFFmpegVOD(job).transcode(transcodeOptions)
128
129 return onWebVideoFileTranscoding({ video, videoFile: newVideoFile, videoOutputPath })
130 })
131
132 return result
133 } finally {
134 inputFileMutexReleaser()
135 }
136}
137
138// Merge an image with an audio file to create a video
139export async function mergeAudioVideofile (options: {
140 video: MVideoFullLight
141 resolution: VideoResolution
142 fps: number
143 job: Job
144}) {
145 const { video: videoArg, resolution, fps, job } = options
146
147 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
148 const newExtname = '.mp4'
149
150 const inputFileMutexReleaser = await VideoPathManager.Instance.lockFiles(videoArg.uuid)
151
152 try {
153 const video = await VideoModel.loadFull(videoArg.uuid)
154 const inputVideoFile = video.getMinQualityFile()
155
156 const fileWithVideoOrPlaylist = inputVideoFile.withVideoOrPlaylist(video)
157
158 const result = await VideoPathManager.Instance.makeAvailableVideoFile(fileWithVideoOrPlaylist, async audioInputPath => {
159 const videoOutputPath = join(transcodeDirectory, video.id + '-transcoded' + newExtname)
160
161 // If the user updates the video preview during transcoding
162 const previewPath = video.getPreview().getPath()
163 const tmpPreviewPath = join(CONFIG.STORAGE.TMP_DIR, basename(previewPath))
164 await copyFile(previewPath, tmpPreviewPath)
165
166 const transcodeOptions = {
167 type: 'merge-audio' as 'merge-audio',
168
169 inputPath: tmpPreviewPath,
170 outputPath: videoOutputPath,
171
172 inputFileMutexReleaser,
173
174 audioPath: audioInputPath,
175 resolution,
176 fps
177 }
178
179 try {
180 await buildFFmpegVOD(job).transcode(transcodeOptions)
181
182 await remove(audioInputPath)
183 await remove(tmpPreviewPath)
184 } catch (err) {
185 await remove(tmpPreviewPath)
186 throw err
187 }
188
189 // Important to do this before getVideoFilename() to take in account the new file extension
190 inputVideoFile.extname = newExtname
191 inputVideoFile.resolution = resolution
192 inputVideoFile.filename = generateWebVideoFilename(inputVideoFile.resolution, newExtname)
193
194 // ffmpeg generated a new video file, so update the video duration
195 // See https://trac.ffmpeg.org/ticket/5456
196 video.duration = await getVideoStreamDuration(videoOutputPath)
197 await video.save()
198
199 return onWebVideoFileTranscoding({
200 video,
201 videoFile: inputVideoFile,
202 videoOutputPath,
203 wasAudioFile: true
204 })
205 })
206
207 return result
208 } finally {
209 inputFileMutexReleaser()
210 }
211}
212
213export async function onWebVideoFileTranscoding (options: {
214 video: MVideoFullLight
215 videoFile: MVideoFile
216 videoOutputPath: string
217 wasAudioFile?: boolean // default false
218}) {
219 const { video, videoFile, videoOutputPath, wasAudioFile } = options
220
221 const mutexReleaser = await VideoPathManager.Instance.lockFiles(video.uuid)
222
223 try {
224 await video.reload()
225
226 const outputPath = VideoPathManager.Instance.getFSVideoFileOutputPath(video, videoFile)
227
228 const stats = await stat(videoOutputPath)
229
230 const probe = await ffprobePromise(videoOutputPath)
231 const fps = await getVideoStreamFPS(videoOutputPath, probe)
232 const metadata = await buildFileMetadata(videoOutputPath, probe)
233
234 await move(videoOutputPath, outputPath, { overwrite: true })
235
236 videoFile.size = stats.size
237 videoFile.fps = fps
238 videoFile.metadata = metadata
239
240 await createTorrentAndSetInfoHash(video, videoFile)
241
242 const oldFile = await VideoFileModel.loadWebVideoFile({ videoId: video.id, fps: videoFile.fps, resolution: videoFile.resolution })
243 if (oldFile) await video.removeWebVideoFile(oldFile)
244
245 await VideoFileModel.customUpsert(videoFile, 'video', undefined)
246 video.VideoFiles = await video.$get('VideoFiles')
247
248 if (wasAudioFile) {
249 await JobQueue.Instance.createJob({
250 type: 'generate-video-storyboard' as 'generate-video-storyboard',
251 payload: {
252 videoUUID: video.uuid,
253 // No need to federate, we process these jobs sequentially
254 federate: false
255 }
256 })
257 }
258
259 return { video, videoFile }
260 } finally {
261 mutexReleaser()
262 }
263}