diff options
Diffstat (limited to 'server/lib/video-studio.ts')
-rw-r--r-- | server/lib/video-studio.ts | 109 |
1 files changed, 95 insertions, 14 deletions
diff --git a/server/lib/video-studio.ts b/server/lib/video-studio.ts index beda326a0..2c993faeb 100644 --- a/server/lib/video-studio.ts +++ b/server/lib/video-studio.ts | |||
@@ -1,19 +1,38 @@ | |||
1 | import { logger } from '@server/helpers/logger' | 1 | import { move, remove } from 'fs-extra' |
2 | import { MVideoFullLight } from '@server/types/models' | 2 | import { join } from 'path' |
3 | import { logger, loggerTagsFactory } from '@server/helpers/logger' | ||
4 | import { createTorrentAndSetInfoHashFromPath } from '@server/helpers/webtorrent' | ||
5 | import { CONFIG } from '@server/initializers/config' | ||
6 | import { UserModel } from '@server/models/user/user' | ||
7 | import { MUser, MVideo, MVideoFile, MVideoFullLight, MVideoWithAllFiles } from '@server/types/models' | ||
3 | import { getVideoStreamDuration } from '@shared/ffmpeg' | 8 | import { getVideoStreamDuration } from '@shared/ffmpeg' |
4 | import { VideoStudioEditionPayload, VideoStudioTask } from '@shared/models' | 9 | import { VideoStudioEditionPayload, VideoStudioTask, VideoStudioTaskPayload } from '@shared/models' |
5 | import { remove } from 'fs-extra' | 10 | import { federateVideoIfNeeded } from './activitypub/videos' |
11 | import { JobQueue } from './job-queue' | ||
12 | import { VideoEditionTranscodingJobHandler } from './runners' | ||
13 | import { createOptimizeOrMergeAudioJobs } from './transcoding/create-transcoding-job' | ||
14 | import { getTranscodingJobPriority } from './transcoding/transcoding-priority' | ||
15 | import { buildNewFile, removeHLSPlaylist, removeWebTorrentFile } from './video-file' | ||
16 | import { VideoPathManager } from './video-path-manager' | ||
6 | 17 | ||
7 | function buildTaskFileFieldname (indice: number, fieldName = 'file') { | 18 | const lTags = loggerTagsFactory('video-edition') |
19 | |||
20 | export function buildTaskFileFieldname (indice: number, fieldName = 'file') { | ||
8 | return `tasks[${indice}][options][${fieldName}]` | 21 | return `tasks[${indice}][options][${fieldName}]` |
9 | } | 22 | } |
10 | 23 | ||
11 | function getTaskFileFromReq (files: Express.Multer.File[], indice: number, fieldName = 'file') { | 24 | export function getTaskFileFromReq (files: Express.Multer.File[], indice: number, fieldName = 'file') { |
12 | return files.find(f => f.fieldname === buildTaskFileFieldname(indice, fieldName)) | 25 | return files.find(f => f.fieldname === buildTaskFileFieldname(indice, fieldName)) |
13 | } | 26 | } |
14 | 27 | ||
15 | async function safeCleanupStudioTMPFiles (payload: VideoStudioEditionPayload) { | 28 | export function getStudioTaskFilePath (filename: string) { |
16 | for (const task of payload.tasks) { | 29 | return join(CONFIG.STORAGE.TMP_PERSISTENT_DIR, filename) |
30 | } | ||
31 | |||
32 | export async function safeCleanupStudioTMPFiles (tasks: VideoStudioTaskPayload[]) { | ||
33 | logger.info('Removing studio task files', { tasks, ...lTags() }) | ||
34 | |||
35 | for (const task of tasks) { | ||
17 | try { | 36 | try { |
18 | if (task.name === 'add-intro' || task.name === 'add-outro') { | 37 | if (task.name === 'add-intro' || task.name === 'add-outro') { |
19 | await remove(task.options.file) | 38 | await remove(task.options.file) |
@@ -26,7 +45,13 @@ async function safeCleanupStudioTMPFiles (payload: VideoStudioEditionPayload) { | |||
26 | } | 45 | } |
27 | } | 46 | } |
28 | 47 | ||
29 | async function approximateIntroOutroAdditionalSize (video: MVideoFullLight, tasks: VideoStudioTask[], fileFinder: (i: number) => string) { | 48 | // --------------------------------------------------------------------------- |
49 | |||
50 | export async function approximateIntroOutroAdditionalSize ( | ||
51 | video: MVideoFullLight, | ||
52 | tasks: VideoStudioTask[], | ||
53 | fileFinder: (i: number) => string | ||
54 | ) { | ||
30 | let additionalDuration = 0 | 55 | let additionalDuration = 0 |
31 | 56 | ||
32 | for (let i = 0; i < tasks.length; i++) { | 57 | for (let i = 0; i < tasks.length; i++) { |
@@ -41,9 +66,65 @@ async function approximateIntroOutroAdditionalSize (video: MVideoFullLight, task | |||
41 | return (video.getMaxQualityFile().size / video.duration) * additionalDuration | 66 | return (video.getMaxQualityFile().size / video.duration) * additionalDuration |
42 | } | 67 | } |
43 | 68 | ||
44 | export { | 69 | // --------------------------------------------------------------------------- |
45 | approximateIntroOutroAdditionalSize, | 70 | |
46 | buildTaskFileFieldname, | 71 | export async function createVideoStudioJob (options: { |
47 | getTaskFileFromReq, | 72 | video: MVideo |
48 | safeCleanupStudioTMPFiles | 73 | user: MUser |
74 | payload: VideoStudioEditionPayload | ||
75 | }) { | ||
76 | const { video, user, payload } = options | ||
77 | |||
78 | const priority = await getTranscodingJobPriority({ user, type: 'studio', fallback: 0 }) | ||
79 | |||
80 | if (CONFIG.VIDEO_STUDIO.REMOTE_RUNNERS.ENABLED) { | ||
81 | await new VideoEditionTranscodingJobHandler().create({ video, tasks: payload.tasks, priority }) | ||
82 | return | ||
83 | } | ||
84 | |||
85 | await JobQueue.Instance.createJob({ type: 'video-studio-edition', payload, priority }) | ||
86 | } | ||
87 | |||
88 | export async function onVideoEditionEnded (options: { | ||
89 | editionResultPath: string | ||
90 | tasks: VideoStudioTaskPayload[] | ||
91 | video: MVideoFullLight | ||
92 | }) { | ||
93 | const { video, tasks, editionResultPath } = options | ||
94 | |||
95 | const newFile = await buildNewFile({ path: editionResultPath, mode: 'web-video' }) | ||
96 | newFile.videoId = video.id | ||
97 | |||
98 | const outputPath = VideoPathManager.Instance.getFSVideoFileOutputPath(video, newFile) | ||
99 | await move(editionResultPath, outputPath) | ||
100 | |||
101 | await safeCleanupStudioTMPFiles(tasks) | ||
102 | |||
103 | await createTorrentAndSetInfoHashFromPath(video, newFile, outputPath) | ||
104 | await removeAllFiles(video, newFile) | ||
105 | |||
106 | await newFile.save() | ||
107 | |||
108 | video.duration = await getVideoStreamDuration(outputPath) | ||
109 | await video.save() | ||
110 | |||
111 | await federateVideoIfNeeded(video, false, undefined) | ||
112 | |||
113 | const user = await UserModel.loadByVideoId(video.id) | ||
114 | |||
115 | await createOptimizeOrMergeAudioJobs({ video, videoFile: newFile, isNewVideo: false, user, videoFileAlreadyLocked: false }) | ||
116 | } | ||
117 | |||
118 | // --------------------------------------------------------------------------- | ||
119 | // Private | ||
120 | // --------------------------------------------------------------------------- | ||
121 | |||
122 | async function removeAllFiles (video: MVideoWithAllFiles, webTorrentFileException: MVideoFile) { | ||
123 | await removeHLSPlaylist(video) | ||
124 | |||
125 | for (const file of video.VideoFiles) { | ||
126 | if (file.id === webTorrentFileException.id) continue | ||
127 | |||
128 | await removeWebTorrentFile(video, file.id) | ||
129 | } | ||
49 | } | 130 | } |