diff options
Diffstat (limited to 'server/lib')
-rw-r--r-- | server/lib/job-queue/handlers/video-studio-edition.ts | 90 | ||||
-rw-r--r-- | server/lib/video-studio.ts | 23 |
2 files changed, 70 insertions, 43 deletions
diff --git a/server/lib/job-queue/handlers/video-studio-edition.ts b/server/lib/job-queue/handlers/video-studio-edition.ts index fbb55a388..5e8dd4f51 100644 --- a/server/lib/job-queue/handlers/video-studio-edition.ts +++ b/server/lib/job-queue/handlers/video-studio-edition.ts | |||
@@ -12,7 +12,7 @@ import { VideoTranscodingProfilesManager } from '@server/lib/transcoding/default | |||
12 | import { isAbleToUploadVideo } from '@server/lib/user' | 12 | import { isAbleToUploadVideo } from '@server/lib/user' |
13 | import { buildFileMetadata, removeHLSPlaylist, removeWebTorrentFile } from '@server/lib/video-file' | 13 | import { buildFileMetadata, removeHLSPlaylist, removeWebTorrentFile } from '@server/lib/video-file' |
14 | import { VideoPathManager } from '@server/lib/video-path-manager' | 14 | import { VideoPathManager } from '@server/lib/video-path-manager' |
15 | import { approximateIntroOutroAdditionalSize } from '@server/lib/video-studio' | 15 | import { approximateIntroOutroAdditionalSize, safeCleanupStudioTMPFiles } from '@server/lib/video-studio' |
16 | import { UserModel } from '@server/models/user/user' | 16 | import { UserModel } from '@server/models/user/user' |
17 | import { VideoModel } from '@server/models/video/video' | 17 | import { VideoModel } from '@server/models/video/video' |
18 | import { VideoFileModel } from '@server/models/video/video-file' | 18 | import { VideoFileModel } from '@server/models/video/video-file' |
@@ -39,63 +39,73 @@ async function processVideoStudioEdition (job: Job) { | |||
39 | 39 | ||
40 | logger.info('Process video studio edition of %s in job %s.', payload.videoUUID, job.id, lTags) | 40 | logger.info('Process video studio edition of %s in job %s.', payload.videoUUID, job.id, lTags) |
41 | 41 | ||
42 | const video = await VideoModel.loadFull(payload.videoUUID) | 42 | try { |
43 | const video = await VideoModel.loadFull(payload.videoUUID) | ||
43 | 44 | ||
44 | // No video, maybe deleted? | 45 | // No video, maybe deleted? |
45 | if (!video) { | 46 | if (!video) { |
46 | logger.info('Can\'t process job %d, video does not exist.', job.id, lTags) | 47 | logger.info('Can\'t process job %d, video does not exist.', job.id, lTags) |
47 | return undefined | ||
48 | } | ||
49 | 48 | ||
50 | await checkUserQuotaOrThrow(video, payload) | 49 | await safeCleanupStudioTMPFiles(payload) |
50 | return undefined | ||
51 | } | ||
51 | 52 | ||
52 | const inputFile = video.getMaxQualityFile() | 53 | await checkUserQuotaOrThrow(video, payload) |
53 | 54 | ||
54 | const editionResultPath = await VideoPathManager.Instance.makeAvailableVideoFile(inputFile, async originalFilePath => { | 55 | const inputFile = video.getMaxQualityFile() |
55 | let tmpInputFilePath: string | ||
56 | let outputPath: string | ||
57 | 56 | ||
58 | for (const task of payload.tasks) { | 57 | const editionResultPath = await VideoPathManager.Instance.makeAvailableVideoFile(inputFile, async originalFilePath => { |
59 | const outputFilename = buildUUID() + inputFile.extname | 58 | let tmpInputFilePath: string |
60 | outputPath = join(CONFIG.STORAGE.TMP_DIR, outputFilename) | 59 | let outputPath: string |
61 | 60 | ||
62 | await processTask({ | 61 | for (const task of payload.tasks) { |
63 | inputPath: tmpInputFilePath ?? originalFilePath, | 62 | const outputFilename = buildUUID() + inputFile.extname |
64 | video, | 63 | outputPath = join(CONFIG.STORAGE.TMP_DIR, outputFilename) |
65 | outputPath, | ||
66 | task, | ||
67 | lTags | ||
68 | }) | ||
69 | 64 | ||
70 | if (tmpInputFilePath) await remove(tmpInputFilePath) | 65 | await processTask({ |
66 | inputPath: tmpInputFilePath ?? originalFilePath, | ||
67 | video, | ||
68 | outputPath, | ||
69 | task, | ||
70 | lTags | ||
71 | }) | ||
71 | 72 | ||
72 | // For the next iteration | 73 | if (tmpInputFilePath) await remove(tmpInputFilePath) |
73 | tmpInputFilePath = outputPath | ||
74 | } | ||
75 | 74 | ||
76 | return outputPath | 75 | // For the next iteration |
77 | }) | 76 | tmpInputFilePath = outputPath |
77 | } | ||
78 | 78 | ||
79 | logger.info('Video edition ended for video %s.', video.uuid, lTags) | 79 | return outputPath |
80 | }) | ||
80 | 81 | ||
81 | const newFile = await buildNewFile(video, editionResultPath) | 82 | logger.info('Video edition ended for video %s.', video.uuid, lTags) |
82 | 83 | ||
83 | const outputPath = VideoPathManager.Instance.getFSVideoFileOutputPath(video, newFile) | 84 | const newFile = await buildNewFile(video, editionResultPath) |
84 | await move(editionResultPath, outputPath) | ||
85 | 85 | ||
86 | await createTorrentAndSetInfoHashFromPath(video, newFile, outputPath) | 86 | const outputPath = VideoPathManager.Instance.getFSVideoFileOutputPath(video, newFile) |
87 | await removeAllFiles(video, newFile) | 87 | await move(editionResultPath, outputPath) |
88 | 88 | ||
89 | await newFile.save() | 89 | await safeCleanupStudioTMPFiles(payload) |
90 | 90 | ||
91 | video.duration = await getVideoStreamDuration(outputPath) | 91 | await createTorrentAndSetInfoHashFromPath(video, newFile, outputPath) |
92 | await video.save() | 92 | await removeAllFiles(video, newFile) |
93 | 93 | ||
94 | await federateVideoIfNeeded(video, false, undefined) | 94 | await newFile.save() |
95 | 95 | ||
96 | const user = await UserModel.loadByVideoId(video.id) | 96 | video.duration = await getVideoStreamDuration(outputPath) |
97 | await video.save() | ||
97 | 98 | ||
98 | await createOptimizeOrMergeAudioJobs({ video, videoFile: newFile, isNewVideo: false, user, videoFileAlreadyLocked: false }) | 99 | await federateVideoIfNeeded(video, false, undefined) |
100 | |||
101 | const user = await UserModel.loadByVideoId(video.id) | ||
102 | |||
103 | await createOptimizeOrMergeAudioJobs({ video, videoFile: newFile, isNewVideo: false, user, videoFileAlreadyLocked: false }) | ||
104 | } catch (err) { | ||
105 | await safeCleanupStudioTMPFiles(payload) | ||
106 | |||
107 | throw err | ||
108 | } | ||
99 | } | 109 | } |
100 | 110 | ||
101 | // --------------------------------------------------------------------------- | 111 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/video-studio.ts b/server/lib/video-studio.ts index b392bdb00..beda326a0 100644 --- a/server/lib/video-studio.ts +++ b/server/lib/video-studio.ts | |||
@@ -1,15 +1,31 @@ | |||
1 | import { logger } from '@server/helpers/logger' | ||
1 | import { MVideoFullLight } from '@server/types/models' | 2 | import { MVideoFullLight } from '@server/types/models' |
2 | import { getVideoStreamDuration } from '@shared/ffmpeg' | 3 | import { getVideoStreamDuration } from '@shared/ffmpeg' |
3 | import { VideoStudioTask } from '@shared/models' | 4 | import { VideoStudioEditionPayload, VideoStudioTask } from '@shared/models' |
5 | import { remove } from 'fs-extra' | ||
4 | 6 | ||
5 | function buildTaskFileFieldname (indice: number, fieldName = 'file') { | 7 | function buildTaskFileFieldname (indice: number, fieldName = 'file') { |
6 | return `tasks[${indice}][options][${fieldName}]` | 8 | return `tasks[${indice}][options][${fieldName}]` |
7 | } | 9 | } |
8 | 10 | ||
9 | function getTaskFile (files: Express.Multer.File[], indice: number, fieldName = 'file') { | 11 | function getTaskFileFromReq (files: Express.Multer.File[], indice: number, fieldName = 'file') { |
10 | return files.find(f => f.fieldname === buildTaskFileFieldname(indice, fieldName)) | 12 | return files.find(f => f.fieldname === buildTaskFileFieldname(indice, fieldName)) |
11 | } | 13 | } |
12 | 14 | ||
15 | async function safeCleanupStudioTMPFiles (payload: VideoStudioEditionPayload) { | ||
16 | for (const task of payload.tasks) { | ||
17 | try { | ||
18 | if (task.name === 'add-intro' || task.name === 'add-outro') { | ||
19 | await remove(task.options.file) | ||
20 | } else if (task.name === 'add-watermark') { | ||
21 | await remove(task.options.file) | ||
22 | } | ||
23 | } catch (err) { | ||
24 | logger.error('Cannot remove studio file', { err }) | ||
25 | } | ||
26 | } | ||
27 | } | ||
28 | |||
13 | async function approximateIntroOutroAdditionalSize (video: MVideoFullLight, tasks: VideoStudioTask[], fileFinder: (i: number) => string) { | 29 | async function approximateIntroOutroAdditionalSize (video: MVideoFullLight, tasks: VideoStudioTask[], fileFinder: (i: number) => string) { |
14 | let additionalDuration = 0 | 30 | let additionalDuration = 0 |
15 | 31 | ||
@@ -28,5 +44,6 @@ async function approximateIntroOutroAdditionalSize (video: MVideoFullLight, task | |||
28 | export { | 44 | export { |
29 | approximateIntroOutroAdditionalSize, | 45 | approximateIntroOutroAdditionalSize, |
30 | buildTaskFileFieldname, | 46 | buildTaskFileFieldname, |
31 | getTaskFile | 47 | getTaskFileFromReq, |
48 | safeCleanupStudioTMPFiles | ||
32 | } | 49 | } |