X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Flib%2Fvideo-studio.ts;h=0d3db8f60df884a0a25df35fcd7599b61d544aaf;hb=e901579b00fbcd8fc0f7b45fd841636329148a34;hp=cdacd35f2c15bead905ad3fb0dcec76f213382da;hpb=92e66e04f7f51d37b465cff442ce47f6d6d7cadd;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/lib/video-studio.ts b/server/lib/video-studio.ts index cdacd35f2..0d3db8f60 100644 --- a/server/lib/video-studio.ts +++ b/server/lib/video-studio.ts @@ -1,16 +1,57 @@ -import { MVideoFullLight } from '@server/types/models' -import { getVideoStreamDuration } from '@shared/extra-utils' -import { VideoStudioTask } from '@shared/models' +import { move, remove } from 'fs-extra' +import { join } from 'path' +import { logger, loggerTagsFactory } from '@server/helpers/logger' +import { createTorrentAndSetInfoHashFromPath } from '@server/helpers/webtorrent' +import { CONFIG } from '@server/initializers/config' +import { UserModel } from '@server/models/user/user' +import { MUser, MVideo, MVideoFile, MVideoFullLight, MVideoWithAllFiles } from '@server/types/models' +import { getVideoStreamDuration } from '@shared/ffmpeg' +import { VideoStudioEditionPayload, VideoStudioTask, VideoStudioTaskPayload } from '@shared/models' +import { federateVideoIfNeeded } from './activitypub/videos' +import { JobQueue } from './job-queue' +import { VideoStudioTranscodingJobHandler } from './runners' +import { createOptimizeOrMergeAudioJobs } from './transcoding/create-transcoding-job' +import { getTranscodingJobPriority } from './transcoding/transcoding-priority' +import { buildNewFile, removeHLSPlaylist, removeWebTorrentFile } from './video-file' +import { VideoPathManager } from './video-path-manager' -function buildTaskFileFieldname (indice: number, fieldName = 'file') { +const lTags = loggerTagsFactory('video-studio') + +export function buildTaskFileFieldname (indice: number, fieldName = 'file') { return `tasks[${indice}][options][${fieldName}]` } -function getTaskFile (files: Express.Multer.File[], indice: number, fieldName = 'file') { +export function getTaskFileFromReq (files: Express.Multer.File[], indice: number, fieldName = 'file') { return files.find(f => f.fieldname === buildTaskFileFieldname(indice, fieldName)) } -async function approximateIntroOutroAdditionalSize (video: MVideoFullLight, tasks: VideoStudioTask[], fileFinder: (i: number) => string) { +export function getStudioTaskFilePath (filename: string) { + return join(CONFIG.STORAGE.TMP_PERSISTENT_DIR, filename) +} + +export async function safeCleanupStudioTMPFiles (tasks: VideoStudioTaskPayload[]) { + logger.info('Removing studio task files', { tasks, ...lTags() }) + + for (const task of tasks) { + try { + if (task.name === 'add-intro' || task.name === 'add-outro') { + await remove(task.options.file) + } else if (task.name === 'add-watermark') { + await remove(task.options.file) + } + } catch (err) { + logger.error('Cannot remove studio file', { err }) + } + } +} + +// --------------------------------------------------------------------------- + +export async function approximateIntroOutroAdditionalSize ( + video: MVideoFullLight, + tasks: VideoStudioTask[], + fileFinder: (i: number) => string +) { let additionalDuration = 0 for (let i = 0; i < tasks.length; i++) { @@ -25,8 +66,65 @@ async function approximateIntroOutroAdditionalSize (video: MVideoFullLight, task return (video.getMaxQualityFile().size / video.duration) * additionalDuration } -export { - approximateIntroOutroAdditionalSize, - buildTaskFileFieldname, - getTaskFile +// --------------------------------------------------------------------------- + +export async function createVideoStudioJob (options: { + video: MVideo + user: MUser + payload: VideoStudioEditionPayload +}) { + const { video, user, payload } = options + + const priority = await getTranscodingJobPriority({ user, type: 'studio', fallback: 0 }) + + if (CONFIG.VIDEO_STUDIO.REMOTE_RUNNERS.ENABLED) { + await new VideoStudioTranscodingJobHandler().create({ video, tasks: payload.tasks, priority }) + return + } + + await JobQueue.Instance.createJob({ type: 'video-studio-edition', payload, priority }) +} + +export async function onVideoStudioEnded (options: { + editionResultPath: string + tasks: VideoStudioTaskPayload[] + video: MVideoFullLight +}) { + const { video, tasks, editionResultPath } = options + + const newFile = await buildNewFile({ path: editionResultPath, mode: 'web-video' }) + newFile.videoId = video.id + + const outputPath = VideoPathManager.Instance.getFSVideoFileOutputPath(video, newFile) + await move(editionResultPath, outputPath) + + await safeCleanupStudioTMPFiles(tasks) + + await createTorrentAndSetInfoHashFromPath(video, newFile, outputPath) + await removeAllFiles(video, newFile) + + await newFile.save() + + video.duration = await getVideoStreamDuration(outputPath) + await video.save() + + await federateVideoIfNeeded(video, false, undefined) + + const user = await UserModel.loadByVideoId(video.id) + + await createOptimizeOrMergeAudioJobs({ video, videoFile: newFile, isNewVideo: false, user, videoFileAlreadyLocked: false }) +} + +// --------------------------------------------------------------------------- +// Private +// --------------------------------------------------------------------------- + +async function removeAllFiles (video: MVideoWithAllFiles, webTorrentFileException: MVideoFile) { + await removeHLSPlaylist(video) + + for (const file of video.VideoFiles) { + if (file.id === webTorrentFileException.id) continue + + await removeWebTorrentFile(video, file.id) + } }