]>
Commit | Line | Data |
---|---|---|
5e47f6ab C |
1 | |
2 | import { basename } from 'path' | |
3 | import { logger } from '@server/helpers/logger' | |
ab14f0e0 | 4 | import { onVideoStudioEnded, safeCleanupStudioTMPFiles } from '@server/lib/video-studio' |
5e47f6ab C |
5 | import { MVideo } from '@server/types/models' |
6 | import { MRunnerJob } from '@server/types/models/runners' | |
7 | import { buildUUID } from '@shared/extra-utils' | |
8 | import { | |
9 | isVideoStudioTaskIntro, | |
10 | isVideoStudioTaskOutro, | |
11 | isVideoStudioTaskWatermark, | |
12 | RunnerJobState, | |
13 | RunnerJobUpdatePayload, | |
ab14f0e0 C |
14 | RunnerJobStudioTranscodingPayload, |
15 | RunnerJobVideoStudioTranscodingPrivatePayload, | |
16 | VideoStudioTranscodingSuccess, | |
5e47f6ab C |
17 | VideoState, |
18 | VideoStudioTaskPayload | |
19 | } from '@shared/models' | |
20 | import { generateRunnerEditionTranscodingVideoInputFileUrl, generateRunnerTranscodingVideoInputFileUrl } from '../runner-urls' | |
21 | import { AbstractJobHandler } from './abstract-job-handler' | |
22 | import { loadTranscodingRunnerVideo } from './shared' | |
23 | ||
24 | type CreateOptions = { | |
25 | video: MVideo | |
26 | tasks: VideoStudioTaskPayload[] | |
27 | priority: number | |
28 | } | |
29 | ||
30 | // eslint-disable-next-line max-len | |
ab14f0e0 | 31 | export class VideoStudioTranscodingJobHandler extends AbstractJobHandler<CreateOptions, RunnerJobUpdatePayload, VideoStudioTranscodingSuccess> { |
5e47f6ab C |
32 | |
33 | async create (options: CreateOptions) { | |
34 | const { video, priority, tasks } = options | |
35 | ||
36 | const jobUUID = buildUUID() | |
ab14f0e0 | 37 | const payload: RunnerJobStudioTranscodingPayload = { |
5e47f6ab C |
38 | input: { |
39 | videoFileUrl: generateRunnerTranscodingVideoInputFileUrl(jobUUID, video.uuid) | |
40 | }, | |
41 | tasks: tasks.map(t => { | |
42 | if (isVideoStudioTaskIntro(t) || isVideoStudioTaskOutro(t)) { | |
43 | return { | |
44 | ...t, | |
45 | ||
46 | options: { | |
47 | ...t.options, | |
48 | ||
49 | file: generateRunnerEditionTranscodingVideoInputFileUrl(jobUUID, video.uuid, basename(t.options.file)) | |
50 | } | |
51 | } | |
52 | } | |
53 | ||
54 | if (isVideoStudioTaskWatermark(t)) { | |
55 | return { | |
56 | ...t, | |
57 | ||
58 | options: { | |
59 | ...t.options, | |
60 | ||
61 | file: generateRunnerEditionTranscodingVideoInputFileUrl(jobUUID, video.uuid, basename(t.options.file)) | |
62 | } | |
63 | } | |
64 | } | |
65 | ||
66 | return t | |
67 | }) | |
68 | } | |
69 | ||
ab14f0e0 | 70 | const privatePayload: RunnerJobVideoStudioTranscodingPrivatePayload = { |
5e47f6ab C |
71 | videoUUID: video.uuid, |
72 | originalTasks: tasks | |
73 | } | |
74 | ||
75 | const job = await this.createRunnerJob({ | |
ab14f0e0 | 76 | type: 'video-studio-transcoding', |
5e47f6ab C |
77 | jobUUID, |
78 | payload, | |
79 | privatePayload, | |
80 | priority | |
81 | }) | |
82 | ||
83 | return job | |
84 | } | |
85 | ||
86 | // --------------------------------------------------------------------------- | |
87 | ||
88 | protected isAbortSupported () { | |
89 | return true | |
90 | } | |
91 | ||
92 | protected specificUpdate (_options: { | |
93 | runnerJob: MRunnerJob | |
94 | }) { | |
95 | // empty | |
96 | } | |
97 | ||
98 | protected specificAbort (_options: { | |
99 | runnerJob: MRunnerJob | |
100 | }) { | |
101 | // empty | |
102 | } | |
103 | ||
104 | protected async specificComplete (options: { | |
105 | runnerJob: MRunnerJob | |
ab14f0e0 | 106 | resultPayload: VideoStudioTranscodingSuccess |
5e47f6ab C |
107 | }) { |
108 | const { runnerJob, resultPayload } = options | |
ab14f0e0 | 109 | const privatePayload = runnerJob.privatePayload as RunnerJobVideoStudioTranscodingPrivatePayload |
5e47f6ab C |
110 | |
111 | const video = await loadTranscodingRunnerVideo(runnerJob, this.lTags) | |
112 | if (!video) { | |
113 | await safeCleanupStudioTMPFiles(privatePayload.originalTasks) | |
114 | ||
115 | } | |
116 | ||
117 | const videoFilePath = resultPayload.videoFile as string | |
118 | ||
ab14f0e0 | 119 | await onVideoStudioEnded({ video, editionResultPath: videoFilePath, tasks: privatePayload.originalTasks }) |
5e47f6ab C |
120 | |
121 | logger.info( | |
122 | 'Runner video edition transcoding job %s for %s ended.', | |
123 | runnerJob.uuid, video.uuid, this.lTags(video.uuid, runnerJob.uuid) | |
124 | ) | |
125 | } | |
126 | ||
127 | protected specificError (options: { | |
128 | runnerJob: MRunnerJob | |
129 | nextState: RunnerJobState | |
130 | }) { | |
131 | if (options.nextState === RunnerJobState.ERRORED) { | |
132 | return this.specificErrorOrCancel(options) | |
133 | } | |
134 | ||
135 | return Promise.resolve() | |
136 | } | |
137 | ||
138 | protected specificCancel (options: { | |
139 | runnerJob: MRunnerJob | |
140 | }) { | |
141 | return this.specificErrorOrCancel(options) | |
142 | } | |
143 | ||
144 | private async specificErrorOrCancel (options: { | |
145 | runnerJob: MRunnerJob | |
146 | }) { | |
147 | const { runnerJob } = options | |
148 | ||
ab14f0e0 | 149 | const payload = runnerJob.privatePayload as RunnerJobVideoStudioTranscodingPrivatePayload |
5e47f6ab C |
150 | await safeCleanupStudioTMPFiles(payload.originalTasks) |
151 | ||
152 | const video = await loadTranscodingRunnerVideo(options.runnerJob, this.lTags) | |
153 | if (!video) return | |
154 | ||
155 | return video.setNewState(VideoState.PUBLISHED, false, undefined) | |
156 | } | |
157 | } |