-import { Job } from 'bull'
+import { Job } from 'bullmq'
import { copyFile, ensureDir, move, remove, stat } from 'fs-extra'
import { basename, extname as extnameUtil, join } from 'path'
import { toEven } from '@server/helpers/core-utils'
import {
buildFileMetadata,
canDoQuickTranscode,
+ computeResolutionsToTranscode,
getVideoStreamDuration,
getVideoStreamFPS,
transcodeVOD,
*/
// Optimize the original video file and replace it. The resolution is not changed.
-function optimizeOriginalVideofile (video: MVideoFullLight, inputVideoFile: MVideoFile, job?: Job) {
+function optimizeOriginalVideofile (options: {
+ video: MVideoFullLight
+ inputVideoFile: MVideoFile
+ job: Job
+}) {
+ const { video, inputVideoFile, job } = options
+
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
const newExtname = '.mp4'
? 'quick-transcode'
: 'video'
- const resolution = toEven(inputVideoFile.resolution)
+ const resolution = buildOriginalFileResolution(inputVideoFile.resolution)
const transcodeOptions: TranscodeVODOptions = {
type: transcodeType,
await transcodeVOD(transcodeOptions)
// Important to do this before getVideoFilename() to take in account the new filename
+ inputVideoFile.resolution = resolution
inputVideoFile.extname = newExtname
inputVideoFile.filename = generateWebTorrentVideoFilename(resolution, newExtname)
inputVideoFile.storage = VideoStorage.FILE_SYSTEM
})
}
-// Transcode the original video file to a lower resolution
-// We are sure it's x264 in mp4 because optimizeOriginalVideofile was already executed
-function transcodeNewWebTorrentResolution (video: MVideoFullLight, resolution: VideoResolution, isPortrait: boolean, job: Job) {
+// Transcode the original video file to a lower resolution compatible with WebTorrent
+function transcodeNewWebTorrentResolution (options: {
+ video: MVideoFullLight
+ resolution: VideoResolution
+ job: Job
+}) {
+ const { video, resolution, job } = options
+
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
- const extname = '.mp4'
+ const newExtname = '.mp4'
return VideoPathManager.Instance.makeAvailableVideoFile(video.getMaxQualityFile().withVideoOrPlaylist(video), async videoInputPath => {
const newVideoFile = new VideoFileModel({
resolution,
- extname,
- filename: generateWebTorrentVideoFilename(resolution, extname),
+ extname: newExtname,
+ filename: generateWebTorrentVideoFilename(resolution, newExtname),
size: 0,
videoId: video.id
})
profile: CONFIG.TRANSCODING.PROFILE,
resolution,
- isPortraitMode: isPortrait,
job
}
}
// Merge an image with an audio file to create a video
-function mergeAudioVideofile (video: MVideoFullLight, resolution: VideoResolution, job: Job) {
+function mergeAudioVideofile (options: {
+ video: MVideoFullLight
+ resolution: VideoResolution
+ job: Job
+}) {
+ const { video, resolution, job } = options
+
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
const newExtname = '.mp4'
video: MVideo
concatenatedTsFilePath: string
resolution: VideoResolution
- isPortraitMode: boolean
isAAC: boolean
}) {
return generateHlsPlaylistCommon({
video: options.video,
resolution: options.resolution,
- isPortraitMode: options.isPortraitMode,
inputPath: options.concatenatedTsFilePath,
type: 'hls-from-ts' as 'hls-from-ts',
isAAC: options.isAAC
videoInputPath: string
resolution: VideoResolution
copyCodecs: boolean
- isPortraitMode: boolean
job?: Job
}) {
return generateHlsPlaylistCommon({
video: options.video,
resolution: options.resolution,
copyCodecs: options.copyCodecs,
- isPortraitMode: options.isPortraitMode,
inputPath: options.videoInputPath,
type: 'hls' as 'hls',
job: options.job
resolution: VideoResolution
copyCodecs?: boolean
isAAC?: boolean
- isPortraitMode: boolean
job?: Job
}) {
- const { type, video, inputPath, resolution, copyCodecs, isPortraitMode, isAAC, job } = options
+ const { type, video, inputPath, resolution, copyCodecs, isAAC, job } = options
const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
const videoTranscodedBasePath = join(transcodeDirectory, type)
resolution,
copyCodecs,
- isPortraitMode,
isAAC,
// Move video file
await move(join(videoTranscodedBasePath, videoFilename), videoFilePath, { overwrite: true })
+ // Update video duration if it was not set (in case of a live for example)
+ if (!video.duration) {
+ video.duration = await getVideoStreamDuration(videoFilePath)
+ await video.save()
+ }
+
const stats = await stat(videoFilePath)
newVideoFile.size = stats.size
return { resolutionPlaylistPath, videoFile: savedVideoFile }
}
+
+function buildOriginalFileResolution (inputResolution: number) {
+ if (CONFIG.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION === true) return toEven(inputResolution)
+
+ const resolutions = computeResolutionsToTranscode({ input: inputResolution, type: 'vod', includeInput: false, strictLower: false })
+ if (resolutions.length === 0) return toEven(inputResolution)
+
+ return Math.max(...resolutions)
+}