mirror of https://github.com/Chocobozzz/PeerTube
				
				
				
			
		
			
				
	
	
		
			147 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
			
		
		
	
	
			147 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
| import { move, remove } from 'fs-extra/esm'
 | |
| import { join } from 'path'
 | |
| import { logger, loggerTagsFactory } from '@server/helpers/logger.js'
 | |
| import { createTorrentAndSetInfoHashFromPath } from '@server/helpers/webtorrent.js'
 | |
| import { CONFIG } from '@server/initializers/config.js'
 | |
| import { MUser, MVideo, MVideoFile, MVideoFullLight, MVideoWithAllFiles } from '@server/types/models/index.js'
 | |
| import { getVideoStreamDuration } from '@peertube/peertube-ffmpeg'
 | |
| import { VideoStudioEditionPayload, VideoStudioTask, VideoStudioTaskPayload } from '@peertube/peertube-models'
 | |
| import { JobQueue } from './job-queue/index.js'
 | |
| import { VideoStudioTranscodingJobHandler } from './runners/index.js'
 | |
| import { getTranscodingJobPriority } from './transcoding/transcoding-priority.js'
 | |
| import { buildNewFile, removeHLSPlaylist, removeWebVideoFile } from './video-file.js'
 | |
| import { VideoPathManager } from './video-path-manager.js'
 | |
| import { buildStoryboardJobIfNeeded } from './video-jobs.js'
 | |
| import { buildAspectRatio } from '@peertube/peertube-core-utils'
 | |
| 
 | |
| const lTags = loggerTagsFactory('video-studio')
 | |
| 
 | |
| export function buildTaskFileFieldname (indice: number, fieldName = 'file') {
 | |
|   return `tasks[${indice}][options][${fieldName}]`
 | |
| }
 | |
| 
 | |
| export function getTaskFileFromReq (files: Express.Multer.File[], indice: number, fieldName = 'file') {
 | |
|   return files.find(f => f.fieldname === buildTaskFileFieldname(indice, fieldName))
 | |
| }
 | |
| 
 | |
| 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++) {
 | |
|     const task = tasks[i]
 | |
| 
 | |
|     if (task.name !== 'add-intro' && task.name !== 'add-outro') continue
 | |
| 
 | |
|     const filePath = fileFinder(i)
 | |
|     additionalDuration += await getVideoStreamDuration(filePath)
 | |
|   }
 | |
| 
 | |
|   return (video.getMaxQualityFile().size / video.duration) * additionalDuration
 | |
| }
 | |
| 
 | |
| // ---------------------------------------------------------------------------
 | |
| 
 | |
| 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)
 | |
|   video.aspectRatio = buildAspectRatio({ width: newFile.width, height: newFile.height })
 | |
|   await video.save()
 | |
| 
 | |
|   return JobQueue.Instance.createSequentialJobFlow(
 | |
|     buildStoryboardJobIfNeeded({ video, federate: false }),
 | |
| 
 | |
|     {
 | |
|       type: 'federate-video' as 'federate-video',
 | |
|       payload: {
 | |
|         videoUUID: video.uuid,
 | |
|         isNewVideoForFederation: false
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     {
 | |
|       type: 'transcoding-job-builder' as 'transcoding-job-builder',
 | |
|       payload: {
 | |
|         videoUUID: video.uuid,
 | |
|         optimizeJob: {
 | |
|           isNewVideo: false
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   )
 | |
| }
 | |
| 
 | |
| // ---------------------------------------------------------------------------
 | |
| // Private
 | |
| // ---------------------------------------------------------------------------
 | |
| 
 | |
| async function removeAllFiles (video: MVideoWithAllFiles, webVideoFileException: MVideoFile) {
 | |
|   await removeHLSPlaylist(video)
 | |
| 
 | |
|   for (const file of video.VideoFiles) {
 | |
|     if (file.id === webVideoFileException.id) continue
 | |
| 
 | |
|     await removeWebVideoFile(video, file.id)
 | |
|   }
 | |
| }
 |