| 
							- import { buildAspectRatio } from '@peertube/peertube-core-utils'
 - import { TranscodeVODOptionsType, getVideoStreamDuration } from '@peertube/peertube-ffmpeg'
 - import { computeOutputFPS } from '@server/helpers/ffmpeg/index.js'
 - import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent.js'
 - import { VideoModel } from '@server/models/video/video.js'
 - import { MVideoFile, MVideoFullLight } from '@server/types/models/index.js'
 - import { Job } from 'bullmq'
 - import { move, remove } from 'fs-extra/esm'
 - import { copyFile } from 'fs/promises'
 - import { basename, join } from 'path'
 - import { CONFIG } from '../../initializers/config.js'
 - import { VideoFileModel } from '../../models/video/video-file.js'
 - import { JobQueue } from '../job-queue/index.js'
 - import { generateWebVideoFilename } from '../paths.js'
 - import { buildNewFile, saveNewOriginalFileIfNeeded } from '../video-file.js'
 - import { buildStoryboardJobIfNeeded } from '../video-jobs.js'
 - import { VideoPathManager } from '../video-path-manager.js'
 - import { buildFFmpegVOD } from './shared/index.js'
 - import { buildOriginalFileResolution } from './transcoding-resolutions.js'
 - 
 - // Optimize the original video file and replace it. The resolution is not changed.
 - export async function optimizeOriginalVideofile (options: {
 -   video: MVideoFullLight
 -   inputVideoFile: MVideoFile
 -   quickTranscode: boolean
 -   job: Job
 - }) {
 -   const { video, inputVideoFile, quickTranscode, job } = options
 - 
 -   const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
 -   const newExtname = '.mp4'
 - 
 -   // Will be released by our transcodeVOD function once ffmpeg is ran
 -   const inputFileMutexReleaser = await VideoPathManager.Instance.lockFiles(video.uuid)
 - 
 -   try {
 -     await video.reload()
 -     await inputVideoFile.reload()
 - 
 -     const fileWithVideoOrPlaylist = inputVideoFile.withVideoOrPlaylist(video)
 - 
 -     const result = await VideoPathManager.Instance.makeAvailableVideoFile(fileWithVideoOrPlaylist, async videoInputPath => {
 -       const videoOutputPath = join(transcodeDirectory, video.id + '-transcoded' + newExtname)
 - 
 -       const transcodeType: TranscodeVODOptionsType = quickTranscode
 -         ? 'quick-transcode'
 -         : 'video'
 - 
 -       const resolution = buildOriginalFileResolution(inputVideoFile.resolution)
 -       const fps = computeOutputFPS({ inputFPS: inputVideoFile.fps, resolution })
 - 
 -       // Could be very long!
 -       await buildFFmpegVOD(job).transcode({
 -         type: transcodeType,
 - 
 -         inputPath: videoInputPath,
 -         outputPath: videoOutputPath,
 - 
 -         inputFileMutexReleaser,
 - 
 -         resolution,
 -         fps
 -       })
 - 
 -       const { videoFile } = await onWebVideoFileTranscoding({ video, videoOutputPath, deleteWebInputVideoFile: inputVideoFile })
 - 
 -       return { transcodeType, videoFile }
 -     })
 - 
 -     return result
 -   } finally {
 -     inputFileMutexReleaser()
 -   }
 - }
 - 
 - // Transcode the original/old/source video file to a lower resolution compatible with web browsers
 - export async function transcodeNewWebVideoResolution (options: {
 -   video: MVideoFullLight
 -   resolution: number
 -   fps: number
 -   job: Job
 - }) {
 -   const { video: videoArg, resolution, fps, job } = options
 - 
 -   const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
 -   const newExtname = '.mp4'
 - 
 -   const inputFileMutexReleaser = await VideoPathManager.Instance.lockFiles(videoArg.uuid)
 - 
 -   try {
 -     const video = await VideoModel.loadFull(videoArg.uuid)
 -     const file = video.getMaxQualityFile().withVideoOrPlaylist(video)
 - 
 -     const result = await VideoPathManager.Instance.makeAvailableVideoFile(file, async videoInputPath => {
 -       const filename = generateWebVideoFilename(resolution, newExtname)
 -       const videoOutputPath = join(transcodeDirectory, filename)
 - 
 -       const transcodeOptions = {
 -         type: 'video' as 'video',
 - 
 -         inputPath: videoInputPath,
 -         outputPath: videoOutputPath,
 - 
 -         inputFileMutexReleaser,
 - 
 -         resolution,
 -         fps
 -       }
 - 
 -       await buildFFmpegVOD(job).transcode(transcodeOptions)
 - 
 -       return onWebVideoFileTranscoding({ video, videoOutputPath })
 -     })
 - 
 -     return result
 -   } finally {
 -     inputFileMutexReleaser()
 -   }
 - }
 - 
 - // Merge an image with an audio file to create a video
 - export async function mergeAudioVideofile (options: {
 -   video: MVideoFullLight
 -   resolution: number
 -   fps: number
 -   job: Job
 - }) {
 -   const { video: videoArg, resolution, fps, job } = options
 - 
 -   const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
 -   const newExtname = '.mp4'
 - 
 -   const inputFileMutexReleaser = await VideoPathManager.Instance.lockFiles(videoArg.uuid)
 - 
 -   try {
 -     const video = await VideoModel.loadFull(videoArg.uuid)
 -     const inputVideoFile = video.getMinQualityFile()
 - 
 -     const fileWithVideoOrPlaylist = inputVideoFile.withVideoOrPlaylist(video)
 - 
 -     const result = await VideoPathManager.Instance.makeAvailableVideoFile(fileWithVideoOrPlaylist, async audioInputPath => {
 -       const videoOutputPath = join(transcodeDirectory, video.id + '-transcoded' + newExtname)
 - 
 -       // If the user updates the video preview during transcoding
 -       const previewPath = video.getPreview().getPath()
 -       const tmpPreviewPath = join(CONFIG.STORAGE.TMP_DIR, basename(previewPath))
 -       await copyFile(previewPath, tmpPreviewPath)
 - 
 -       const transcodeOptions = {
 -         type: 'merge-audio' as 'merge-audio',
 - 
 -         inputPath: tmpPreviewPath,
 -         outputPath: videoOutputPath,
 - 
 -         inputFileMutexReleaser,
 - 
 -         audioPath: audioInputPath,
 -         resolution,
 -         fps
 -       }
 - 
 -       try {
 -         await buildFFmpegVOD(job).transcode(transcodeOptions)
 - 
 -         await remove(tmpPreviewPath)
 -       } catch (err) {
 -         await remove(tmpPreviewPath)
 -         throw err
 -       }
 - 
 -       await onWebVideoFileTranscoding({
 -         video,
 -         videoOutputPath,
 -         deleteWebInputVideoFile: inputVideoFile,
 -         wasAudioFile: true
 -       })
 -     })
 - 
 -     return result
 -   } finally {
 -     inputFileMutexReleaser()
 -   }
 - }
 - 
 - export async function onWebVideoFileTranscoding (options: {
 -   video: MVideoFullLight
 -   videoOutputPath: string
 -   wasAudioFile?: boolean // default false
 -   deleteWebInputVideoFile?: MVideoFile
 - }) {
 -   const { video, videoOutputPath, wasAudioFile, deleteWebInputVideoFile } = options
 - 
 -   const mutexReleaser = await VideoPathManager.Instance.lockFiles(video.uuid)
 - 
 -   const videoFile = await buildNewFile({ mode: 'web-video', path: videoOutputPath })
 -   videoFile.videoId = video.id
 - 
 -   try {
 -     await video.reload()
 - 
 -     // ffmpeg generated a new video file, so update the video duration
 -     // See https://trac.ffmpeg.org/ticket/5456
 -     if (wasAudioFile) {
 -       video.duration = await getVideoStreamDuration(videoOutputPath)
 -       video.aspectRatio = buildAspectRatio({ width: videoFile.width, height: videoFile.height })
 -       await video.save()
 -     }
 - 
 -     const outputPath = VideoPathManager.Instance.getFSVideoFileOutputPath(video, videoFile)
 - 
 -     await move(videoOutputPath, outputPath, { overwrite: true })
 - 
 -     await createTorrentAndSetInfoHash(video, videoFile)
 - 
 -     if (deleteWebInputVideoFile) {
 -       await saveNewOriginalFileIfNeeded(video, deleteWebInputVideoFile)
 - 
 -       await video.removeWebVideoFile(deleteWebInputVideoFile)
 -       await deleteWebInputVideoFile.destroy()
 -     }
 - 
 -     const existingFile = await VideoFileModel.loadWebVideoFile({ videoId: video.id, fps: videoFile.fps, resolution: videoFile.resolution })
 -     if (existingFile) await video.removeWebVideoFile(existingFile)
 - 
 -     await VideoFileModel.customUpsert(videoFile, 'video', undefined)
 -     video.VideoFiles = await video.$get('VideoFiles')
 - 
 -     if (wasAudioFile) {
 -       await JobQueue.Instance.createJob(buildStoryboardJobIfNeeded({ video, federate: false }))
 -     }
 - 
 -     return { video, videoFile }
 -   } finally {
 -     mutexReleaser()
 -   }
 - }
 
 
  |