@remotion/cli 3.3.41 → 3.3.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/audio-codec.d.ts +3 -0
- package/dist/config/audio-codec.js +20 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +4 -0
- package/dist/editor/components/Modals.js +1 -1
- package/dist/editor/components/RenderButton.js +8 -29
- package/dist/editor/components/RenderModal/CrfSetting.d.ts +2 -2
- package/dist/editor/components/RenderModal/CrfSetting.js +24 -11
- package/dist/editor/components/RenderModal/RenderModal.d.ts +5 -19
- package/dist/editor/components/RenderModal/RenderModal.js +181 -225
- package/dist/editor/components/RenderModal/ScaleSetting.js +18 -2
- package/dist/editor/components/RenderModal/layout.d.ts +0 -1
- package/dist/editor/components/RenderModal/layout.js +2 -8
- package/dist/editor/components/RenderQueue/RenderQueueRemoveItem.js +1 -2
- package/dist/editor/components/RenderQueue/actions.d.ts +2 -15
- package/dist/editor/components/RenderQueue/actions.js +1 -14
- package/dist/editor/components/RenderToolbarIcon.js +8 -30
- package/dist/editor/components/SegmentedControl.js +1 -1
- package/dist/editor/components/SidebarContent.js +1 -2
- package/dist/get-audio-codec.d.ts +2 -0
- package/dist/get-audio-codec.js +10 -0
- package/dist/get-filename.d.ts +1 -3
- package/dist/get-filename.js +5 -23
- package/dist/get-final-output-codec.js +23 -31
- package/dist/get-render-media-options.js +3 -0
- package/dist/parse-command-line.d.ts +2 -1
- package/dist/preview-server/render-queue/get-default-video-contexts.d.ts +1 -1
- package/dist/preview-server/render-queue/job.d.ts +1 -27
- package/dist/preview-server/render-queue/make-retry-payload.js +10 -52
- package/dist/preview-server/render-queue/process-video.js +9 -16
- package/dist/preview-server/routes/add-render.js +0 -13
- package/dist/render-flows/render.d.ts +4 -14
- package/dist/render-flows/render.js +13 -40
- package/dist/render.js +3 -2
- package/package.json +7 -7
- package/dist/editor/components/RenderModal/EveryNthFrameSetting.d.ts +0 -5
- package/dist/editor/components/RenderModal/EveryNthFrameSetting.js +0 -9
- package/dist/editor/components/RenderModal/NumberOfLoopsSetting.d.ts +0 -5
- package/dist/editor/components/RenderModal/NumberOfLoopsSetting.js +0 -26
- package/dist/editor/components/RenderModal/NumberSetting.d.ts +0 -9
- package/dist/editor/components/RenderModal/NumberSetting.js +0 -26
- package/dist/editor/components/RenderModal/RenderModalAdvanced.d.ts +0 -11
- package/dist/editor/components/RenderModal/RenderModalAdvanced.js +0 -15
- package/dist/editor/components/RenderModal/RenderModalAudio.d.ts +0 -13
- package/dist/editor/components/RenderModal/RenderModalAudio.js +0 -22
- package/dist/editor/components/RenderModal/RenderModalBasic.d.ts +0 -22
- package/dist/editor/components/RenderModal/RenderModalBasic.js +0 -70
- package/dist/editor/components/RenderModal/RenderModalGeneral.d.ts +0 -0
- package/dist/editor/components/RenderModal/RenderModalGeneral.js +0 -1
- package/dist/editor/components/RenderModal/RenderModalGif.d.ts +0 -9
- package/dist/editor/components/RenderModal/RenderModalGif.js +0 -16
- package/dist/editor/components/RenderModal/RenderModalHr.d.ts +0 -2
- package/dist/editor/components/RenderModal/RenderModalHr.js +0 -18
- package/dist/editor/components/RenderModal/RenderModalPicture.d.ts +0 -28
- package/dist/editor/components/RenderModal/RenderModalPicture.js +0 -51
- package/dist/editor/components/RenderModal/human-readable-codec.d.ts +0 -2
- package/dist/editor/components/RenderModal/human-readable-codec.js +0 -36
- package/dist/editor/icons/audio.d.ts +0 -2
- package/dist/editor/icons/audio.js +0 -6
- package/dist/editor/icons/file.d.ts +0 -2
- package/dist/editor/icons/file.js +0 -6
- package/dist/editor/icons/frame.d.ts +0 -2
- package/dist/editor/icons/frame.js +0 -6
- package/dist/editor/icons/gear.d.ts +0 -2
- package/dist/editor/icons/gear.js +0 -6
- package/dist/editor/icons/gif.d.ts +0 -2
- package/dist/editor/icons/gif.js +0 -6
|
@@ -6,25 +6,19 @@ const client_1 = require("@remotion/renderer/client");
|
|
|
6
6
|
const react_1 = require("react");
|
|
7
7
|
const remotion_1 = require("remotion");
|
|
8
8
|
const Button_1 = require("../../../preview-server/error-overlay/remotion-overlay/Button");
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const frame_1 = require("../../icons/frame");
|
|
12
|
-
const gear_1 = require("../../icons/gear");
|
|
13
|
-
const gif_1 = require("../../icons/gif");
|
|
9
|
+
const colors_1 = require("../../helpers/colors");
|
|
10
|
+
const use_file_existence_1 = require("../../helpers/use-file-existence");
|
|
14
11
|
const modals_1 = require("../../state/modals");
|
|
12
|
+
const CollapsableOptions_1 = require("../CollapsableOptions");
|
|
15
13
|
const layout_1 = require("../layout");
|
|
16
14
|
const ModalContainer_1 = require("../ModalContainer");
|
|
17
15
|
const ModalHeader_1 = require("../ModalHeader");
|
|
16
|
+
const InputDragger_1 = require("../NewComposition/InputDragger");
|
|
17
|
+
const RemInput_1 = require("../NewComposition/RemInput");
|
|
18
|
+
const ValidationMessage_1 = require("../NewComposition/ValidationMessage");
|
|
18
19
|
const actions_1 = require("../RenderQueue/actions");
|
|
19
20
|
const SegmentedControl_1 = require("../SegmentedControl");
|
|
20
21
|
const SidebarContent_1 = require("../SidebarContent");
|
|
21
|
-
const Tabs_1 = require("../Tabs");
|
|
22
|
-
const CrfSetting_1 = require("./CrfSetting");
|
|
23
|
-
const RenderModalAdvanced_1 = require("./RenderModalAdvanced");
|
|
24
|
-
const RenderModalAudio_1 = require("./RenderModalAudio");
|
|
25
|
-
const RenderModalBasic_1 = require("./RenderModalBasic");
|
|
26
|
-
const RenderModalGif_1 = require("./RenderModalGif");
|
|
27
|
-
const RenderModalPicture_1 = require("./RenderModalPicture");
|
|
28
22
|
const initialState = { type: 'idle' };
|
|
29
23
|
const reducer = (state, action) => {
|
|
30
24
|
if (action.type === 'start') {
|
|
@@ -52,130 +46,69 @@ const container = {
|
|
|
52
46
|
width: '100%',
|
|
53
47
|
borderBottom: '1px solid black',
|
|
54
48
|
};
|
|
55
|
-
const
|
|
56
|
-
minHeight: '35vh',
|
|
57
|
-
maxHeight: '50vh',
|
|
58
|
-
overflow: 'auto',
|
|
59
|
-
minWidth: 650,
|
|
60
|
-
};
|
|
61
|
-
const horizontalLayout = {
|
|
49
|
+
const optionRow = {
|
|
62
50
|
display: 'flex',
|
|
63
51
|
flexDirection: 'row',
|
|
52
|
+
alignItems: 'flex-start',
|
|
53
|
+
minHeight: 40,
|
|
54
|
+
paddingLeft: 16,
|
|
55
|
+
paddingRight: 16,
|
|
64
56
|
};
|
|
65
|
-
const
|
|
66
|
-
|
|
57
|
+
const label = {
|
|
58
|
+
width: 150,
|
|
59
|
+
fontSize: 14,
|
|
60
|
+
lineHeight: '40px',
|
|
61
|
+
color: colors_1.LIGHT_TEXT,
|
|
67
62
|
};
|
|
68
|
-
const
|
|
69
|
-
width: 250,
|
|
63
|
+
const rightRow = {
|
|
70
64
|
display: 'flex',
|
|
71
65
|
flexDirection: 'row',
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
fontWeight: 'bold',
|
|
76
|
-
paddingLeft: 15,
|
|
77
|
-
paddingTop: 12,
|
|
78
|
-
paddingBottom: 12,
|
|
79
|
-
};
|
|
80
|
-
const iconContainer = {
|
|
81
|
-
width: 20,
|
|
82
|
-
height: 20,
|
|
83
|
-
marginRight: 15,
|
|
84
|
-
display: 'inline-flex',
|
|
85
|
-
justifyContent: 'center',
|
|
86
|
-
alignItems: 'center',
|
|
87
|
-
};
|
|
88
|
-
const icon = {
|
|
89
|
-
color: 'currentcolor',
|
|
90
|
-
height: 20,
|
|
66
|
+
justifyContent: 'flex-end',
|
|
67
|
+
alignSelf: 'center',
|
|
68
|
+
flex: 1,
|
|
91
69
|
};
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
70
|
+
const buttonRow = {
|
|
71
|
+
display: 'flex',
|
|
72
|
+
flexDirection: 'row',
|
|
73
|
+
justifyContent: 'flex-end',
|
|
74
|
+
borderTop: '1px solid black',
|
|
75
|
+
paddingTop: 8,
|
|
76
|
+
paddingBottom: 8,
|
|
77
|
+
paddingLeft: 16,
|
|
78
|
+
paddingRight: 16,
|
|
95
79
|
};
|
|
96
|
-
const
|
|
97
|
-
|
|
80
|
+
const input = {
|
|
81
|
+
minWidth: 250,
|
|
82
|
+
textAlign: 'right',
|
|
98
83
|
};
|
|
99
|
-
const
|
|
84
|
+
const MIN_QUALITY = 1;
|
|
85
|
+
const MAX_QUALITY = 100;
|
|
86
|
+
const MIN_SCALE = 0.1;
|
|
87
|
+
const MAX_SCALE = 10;
|
|
88
|
+
const RenderModal = ({ compositionId, initialFrame, initialImageFormat, initialQuality, initialScale, initialVerbose, initialOutName, initialRenderType, initialCodec, }) => {
|
|
100
89
|
const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext);
|
|
101
90
|
const onQuit = (0, react_1.useCallback)(() => {
|
|
102
91
|
setSelectedModal(null);
|
|
103
92
|
}, [setSelectedModal]);
|
|
104
93
|
const isMounted = (0, react_1.useRef)(true);
|
|
105
94
|
const [state, dispatch] = (0, react_1.useReducer)(reducer, initialState);
|
|
106
|
-
const [
|
|
107
|
-
const [
|
|
108
|
-
const [
|
|
109
|
-
const [
|
|
110
|
-
const [
|
|
111
|
-
const [
|
|
112
|
-
const [mutedState, setMuted] = (0, react_1.useState)(() => initialMuted);
|
|
113
|
-
const [enforceAudioTrackState, setEnforceAudioTrackState] = (0, react_1.useState)(() => initialEnforceAudioTrack);
|
|
114
|
-
const [renderMode, setRenderModeState] = (0, react_1.useState)(initialRenderType);
|
|
115
|
-
const [quality, setQuality] = (0, react_1.useState)(() => initialQuality);
|
|
95
|
+
const [frame, setFrame] = (0, react_1.useState)(() => initialFrame);
|
|
96
|
+
const [imageFormat, setImageFormat] = (0, react_1.useState)(() => initialImageFormat);
|
|
97
|
+
const [videoCodec, setVideoCodec] = (0, react_1.useState)(initialCodec);
|
|
98
|
+
const [videoImageFormat, setVideoImageFormat] = (0, react_1.useState)('jpeg');
|
|
99
|
+
const [renderMode, setRenderMode] = (0, react_1.useState)(initialRenderType);
|
|
100
|
+
const [quality, setQuality] = (0, react_1.useState)(() => initialQuality !== null && initialQuality !== void 0 ? initialQuality : 80);
|
|
116
101
|
const [scale, setScale] = (0, react_1.useState)(() => initialScale);
|
|
117
102
|
const [verbose, setVerboseLogging] = (0, react_1.useState)(() => initialVerbose);
|
|
118
103
|
const [outName, setOutName] = (0, react_1.useState)(() => initialOutName);
|
|
119
|
-
const [endFrameOrNull, setEndFrame] = (0, react_1.useState)(() => null);
|
|
120
|
-
const [startFrameOrNull, setStartFrame] = (0, react_1.useState)(() => null);
|
|
121
|
-
const [proResProfileSetting, setProResProfile] = (0, react_1.useState)(() => initialProResProfile);
|
|
122
|
-
const [pixelFormat, setPixelFormat] = (0, react_1.useState)(() => initialPixelFormat);
|
|
123
|
-
const [qualityControlType, setQualityControl] = (0, react_1.useState)(() => initialVideoBitrate === null ? 'crf' : 'bitrate');
|
|
124
|
-
const [shouldHaveCustomTargetAudioBitrate, setShouldHaveCustomTargetAudioBitrate,] = (0, react_1.useState)(() => initialAudioBitrate !== null);
|
|
125
|
-
const [customTargetAudioBitrate, setCustomTargetAudioBitrateValue] = (0, react_1.useState)(() => initialAudioBitrate !== null && initialAudioBitrate !== void 0 ? initialAudioBitrate : '256K');
|
|
126
|
-
const [customTargetVideoBitrate, setCustomTargetVideoBitrateValue] = (0, react_1.useState)(() => initialVideoBitrate !== null && initialVideoBitrate !== void 0 ? initialVideoBitrate : '1M');
|
|
127
|
-
const [limitNumberOfGifLoops, setLimitNumberOfGifLoops] = (0, react_1.useState)(() => initialNumberOfGifLoops !== null);
|
|
128
|
-
const [numberOfGifLoopsSetting, setNumberOfGifLoopsSetting] = (0, react_1.useState)(() => initialNumberOfGifLoops !== null && initialNumberOfGifLoops !== void 0 ? initialNumberOfGifLoops : 1);
|
|
129
|
-
// TODO: Allow to modify
|
|
130
|
-
const [delayRenderTimeout] = (0, react_1.useState)(() => initialDelayRenderTimeout);
|
|
131
|
-
const codec = (0, react_1.useMemo)(() => {
|
|
132
|
-
if (renderMode === 'audio') {
|
|
133
|
-
return audioCodec;
|
|
134
|
-
}
|
|
135
|
-
return videoCodec;
|
|
136
|
-
}, [audioCodec, renderMode, videoCodec]);
|
|
137
|
-
const numberOfGifLoops = (0, react_1.useMemo)(() => {
|
|
138
|
-
if (codec === 'gif' && limitNumberOfGifLoops) {
|
|
139
|
-
return numberOfGifLoopsSetting;
|
|
140
|
-
}
|
|
141
|
-
return null;
|
|
142
|
-
}, [codec, limitNumberOfGifLoops, numberOfGifLoopsSetting]);
|
|
143
|
-
const audioBitrate = (0, react_1.useMemo)(() => {
|
|
144
|
-
if (shouldHaveCustomTargetAudioBitrate) {
|
|
145
|
-
return customTargetAudioBitrate;
|
|
146
|
-
}
|
|
147
|
-
return null;
|
|
148
|
-
}, [customTargetAudioBitrate, shouldHaveCustomTargetAudioBitrate]);
|
|
149
|
-
const videoBitrate = (0, react_1.useMemo)(() => {
|
|
150
|
-
if (qualityControlType === 'bitrate') {
|
|
151
|
-
return customTargetVideoBitrate;
|
|
152
|
-
}
|
|
153
|
-
return null;
|
|
154
|
-
}, [customTargetVideoBitrate, qualityControlType]);
|
|
155
|
-
const { crf, maxCrf, minCrf, setCrf, shouldDisplayOption: shouldDisplayCrfOption, } = (0, CrfSetting_1.useCrfState)(codec);
|
|
156
104
|
const dispatchIfMounted = (0, react_1.useCallback)((payload) => {
|
|
157
105
|
if (isMounted.current === false)
|
|
158
106
|
return;
|
|
159
107
|
dispatch(payload);
|
|
160
108
|
}, []);
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
return false;
|
|
166
|
-
}, [mutedState, renderMode]);
|
|
167
|
-
const enforceAudioTrack = (0, react_1.useMemo)(() => {
|
|
168
|
-
if (renderMode === 'video') {
|
|
169
|
-
return enforceAudioTrackState;
|
|
170
|
-
}
|
|
171
|
-
return false;
|
|
172
|
-
}, [enforceAudioTrackState, renderMode]);
|
|
173
|
-
const proResProfile = (0, react_1.useMemo)(() => {
|
|
174
|
-
if (renderMode === 'video' && codec === 'prores') {
|
|
175
|
-
return proResProfileSetting;
|
|
176
|
-
}
|
|
177
|
-
return null;
|
|
178
|
-
}, [codec, proResProfileSetting, renderMode]);
|
|
109
|
+
const onValueChange = (0, react_1.useCallback)((e) => {
|
|
110
|
+
setOutName(e.target.value);
|
|
111
|
+
}, []);
|
|
179
112
|
const { compositions } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
|
|
180
113
|
const currentComposition = (0, react_1.useMemo)(() => {
|
|
181
114
|
for (const composition of compositions) {
|
|
@@ -188,55 +121,27 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
|
|
|
188
121
|
if (currentComposition === null) {
|
|
189
122
|
throw new Error('This composition does not exist');
|
|
190
123
|
}
|
|
191
|
-
const endFrame = (0, react_1.useMemo)(() => {
|
|
192
|
-
if (endFrameOrNull === null) {
|
|
193
|
-
return currentComposition.durationInFrames - 1;
|
|
194
|
-
}
|
|
195
|
-
return Math.max(0, Math.min(currentComposition.durationInFrames - 1, endFrameOrNull));
|
|
196
|
-
}, [currentComposition.durationInFrames, endFrameOrNull]);
|
|
197
|
-
const startFrame = (0, react_1.useMemo)(() => {
|
|
198
|
-
if (startFrameOrNull === null) {
|
|
199
|
-
return 0;
|
|
200
|
-
}
|
|
201
|
-
return Math.max(0, Math.min(endFrame - 1, startFrameOrNull));
|
|
202
|
-
}, [endFrame, startFrameOrNull]);
|
|
203
|
-
const frame = (0, react_1.useMemo)(() => {
|
|
204
|
-
const parsed = Math.floor(unclampedFrame);
|
|
205
|
-
return Math.max(0, Math.min(currentComposition.durationInFrames - 1, parsed));
|
|
206
|
-
}, [currentComposition.durationInFrames, unclampedFrame]);
|
|
207
124
|
const getStringBeforeSuffix = (0, react_1.useCallback)((fileName) => {
|
|
208
125
|
const dotPos = fileName.lastIndexOf('.');
|
|
209
126
|
const bitBeforeDot = fileName.substring(0, dotPos);
|
|
210
127
|
return bitBeforeDot;
|
|
211
128
|
}, []);
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
setOutName((prev) => {
|
|
221
|
-
const codecSuffix = client_1.BrowserSafeApis.getFileExtensionFromCodec(options.codec);
|
|
222
|
-
const newFileName = getStringBeforeSuffix(prev) + '.' + codecSuffix;
|
|
223
|
-
return newFileName;
|
|
224
|
-
});
|
|
225
|
-
}
|
|
129
|
+
const setCodec = (0, react_1.useCallback)((codec) => {
|
|
130
|
+
setVideoCodec(codec);
|
|
131
|
+
setOutName((prev) => {
|
|
132
|
+
// TODO: Check file extension for h264-mkv (render throws error atm)
|
|
133
|
+
const codecSuffix = client_1.BrowserSafeApis.getFileExtensionFromCodec(codec, 'final');
|
|
134
|
+
const newFileName = getStringBeforeSuffix(prev) + '.' + codecSuffix;
|
|
135
|
+
return newFileName;
|
|
136
|
+
});
|
|
226
137
|
}, [getStringBeforeSuffix]);
|
|
227
|
-
const setCodec = (0, react_1.useCallback)((newCodec) => {
|
|
228
|
-
if (renderMode === 'audio') {
|
|
229
|
-
setAudioSpecificalCodec(newCodec);
|
|
230
|
-
}
|
|
231
|
-
else {
|
|
232
|
-
setVideoSpecificalCodec(newCodec);
|
|
233
|
-
}
|
|
234
|
-
setDefaultOutName({ type: 'render', codec: newCodec });
|
|
235
|
-
}, [renderMode, setDefaultOutName]);
|
|
236
138
|
const setStillFormat = (0, react_1.useCallback)((format) => {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
139
|
+
setImageFormat(format);
|
|
140
|
+
setOutName((prev) => {
|
|
141
|
+
const newFileName = getStringBeforeSuffix(prev) + '.' + format;
|
|
142
|
+
return newFileName;
|
|
143
|
+
});
|
|
144
|
+
}, [getStringBeforeSuffix]);
|
|
240
145
|
const onClickStill = (0, react_1.useCallback)(() => {
|
|
241
146
|
var _a;
|
|
242
147
|
(_a = SidebarContent_1.leftSidebarTabs.current) === null || _a === void 0 ? void 0 : _a.selectRendersPanel();
|
|
@@ -244,8 +149,8 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
|
|
|
244
149
|
(0, actions_1.addStillRenderJob)({
|
|
245
150
|
compositionId,
|
|
246
151
|
outName,
|
|
247
|
-
imageFormat
|
|
248
|
-
quality:
|
|
152
|
+
imageFormat,
|
|
153
|
+
quality: imageFormat === 'jpeg' ? quality : null,
|
|
249
154
|
frame,
|
|
250
155
|
scale,
|
|
251
156
|
verbose,
|
|
@@ -261,20 +166,13 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
|
|
|
261
166
|
compositionId,
|
|
262
167
|
dispatchIfMounted,
|
|
263
168
|
frame,
|
|
264
|
-
|
|
169
|
+
imageFormat,
|
|
265
170
|
outName,
|
|
266
171
|
quality,
|
|
267
172
|
scale,
|
|
268
173
|
setSelectedModal,
|
|
269
174
|
verbose,
|
|
270
175
|
]);
|
|
271
|
-
const [everyNthFrameSetting, setEveryNthFrameSetting] = (0, react_1.useState)(() => initialEveryNthFrame);
|
|
272
|
-
const everyNthFrame = (0, react_1.useMemo)(() => {
|
|
273
|
-
if (codec === 'gif') {
|
|
274
|
-
return everyNthFrameSetting;
|
|
275
|
-
}
|
|
276
|
-
return 1;
|
|
277
|
-
}, [codec, everyNthFrameSetting]);
|
|
278
176
|
const onClickVideo = (0, react_1.useCallback)(() => {
|
|
279
177
|
var _a;
|
|
280
178
|
(_a = SidebarContent_1.leftSidebarTabs.current) === null || _a === void 0 ? void 0 : _a.selectRendersPanel();
|
|
@@ -283,23 +181,11 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
|
|
|
283
181
|
compositionId,
|
|
284
182
|
outName,
|
|
285
183
|
imageFormat: videoImageFormat,
|
|
286
|
-
quality:
|
|
184
|
+
quality: imageFormat === 'jpeg' ? quality : null,
|
|
287
185
|
scale,
|
|
288
186
|
verbose,
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
crf: qualityControlType === 'crf' ? crf : null,
|
|
292
|
-
endFrame,
|
|
293
|
-
startFrame,
|
|
294
|
-
muted,
|
|
295
|
-
enforceAudioTrack,
|
|
296
|
-
proResProfile,
|
|
297
|
-
pixelFormat,
|
|
298
|
-
audioBitrate,
|
|
299
|
-
videoBitrate,
|
|
300
|
-
everyNthFrame,
|
|
301
|
-
numberOfGifLoops,
|
|
302
|
-
delayRenderTimeout,
|
|
187
|
+
// TODO: Make this configurable
|
|
188
|
+
codec: videoCodec,
|
|
303
189
|
})
|
|
304
190
|
.then(() => {
|
|
305
191
|
dispatchIfMounted({ type: 'succeed' });
|
|
@@ -309,36 +195,70 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
|
|
|
309
195
|
dispatchIfMounted({ type: 'fail' });
|
|
310
196
|
});
|
|
311
197
|
}, [
|
|
312
|
-
dispatchIfMounted,
|
|
313
198
|
compositionId,
|
|
199
|
+
dispatchIfMounted,
|
|
200
|
+
imageFormat,
|
|
314
201
|
outName,
|
|
315
|
-
videoImageFormat,
|
|
316
|
-
stillImageFormat,
|
|
317
202
|
quality,
|
|
318
203
|
scale,
|
|
319
|
-
verbose,
|
|
320
|
-
codec,
|
|
321
|
-
concurrency,
|
|
322
|
-
qualityControlType,
|
|
323
|
-
crf,
|
|
324
|
-
endFrame,
|
|
325
|
-
startFrame,
|
|
326
|
-
muted,
|
|
327
|
-
enforceAudioTrack,
|
|
328
|
-
proResProfile,
|
|
329
|
-
pixelFormat,
|
|
330
|
-
audioBitrate,
|
|
331
|
-
videoBitrate,
|
|
332
|
-
everyNthFrame,
|
|
333
|
-
numberOfGifLoops,
|
|
334
|
-
delayRenderTimeout,
|
|
335
204
|
setSelectedModal,
|
|
205
|
+
verbose,
|
|
206
|
+
videoCodec,
|
|
207
|
+
videoImageFormat,
|
|
336
208
|
]);
|
|
209
|
+
const onQualityChangedDirectly = (0, react_1.useCallback)((newQuality) => {
|
|
210
|
+
setQuality(newQuality);
|
|
211
|
+
}, []);
|
|
212
|
+
const onQualityChanged = (0, react_1.useCallback)((e) => {
|
|
213
|
+
setQuality((q) => {
|
|
214
|
+
const newQuality = parseInt(e, 10);
|
|
215
|
+
if (Number.isNaN(newQuality)) {
|
|
216
|
+
return q;
|
|
217
|
+
}
|
|
218
|
+
const newQualityClamped = Math.min(MAX_QUALITY, Math.max(newQuality, MIN_QUALITY));
|
|
219
|
+
return newQualityClamped;
|
|
220
|
+
});
|
|
221
|
+
}, []);
|
|
222
|
+
const onScaleSetDirectly = (0, react_1.useCallback)((newScale) => {
|
|
223
|
+
setScale(newScale);
|
|
224
|
+
}, []);
|
|
225
|
+
const onScaleChanged = (0, react_1.useCallback)((e) => {
|
|
226
|
+
setScale((q) => {
|
|
227
|
+
const newScale = parseFloat(e);
|
|
228
|
+
if (Number.isNaN(newScale)) {
|
|
229
|
+
return q;
|
|
230
|
+
}
|
|
231
|
+
const newScaleClamped = Math.min(MAX_SCALE, Math.max(newScale, MIN_SCALE));
|
|
232
|
+
return newScaleClamped;
|
|
233
|
+
});
|
|
234
|
+
}, []);
|
|
235
|
+
const onFrameSetDirectly = (0, react_1.useCallback)((newFrame) => {
|
|
236
|
+
console.log(newFrame);
|
|
237
|
+
if (newFrame > currentComposition.durationInFrames - 1) {
|
|
238
|
+
setFrame(currentComposition.durationInFrames - 1);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
setFrame(newFrame);
|
|
242
|
+
}
|
|
243
|
+
}, [currentComposition.durationInFrames, setFrame]);
|
|
244
|
+
const onFrameChanged = (0, react_1.useCallback)((e) => {
|
|
245
|
+
setFrame((q) => {
|
|
246
|
+
const newFrame = parseFloat(e);
|
|
247
|
+
if (Number.isNaN(newFrame)) {
|
|
248
|
+
return q;
|
|
249
|
+
}
|
|
250
|
+
// TODO: User could change frame inbetween 😈
|
|
251
|
+
return newFrame > currentComposition.durationInFrames - 1
|
|
252
|
+
? currentComposition.durationInFrames - 1
|
|
253
|
+
: newFrame;
|
|
254
|
+
});
|
|
255
|
+
}, []);
|
|
337
256
|
(0, react_1.useEffect)(() => {
|
|
338
257
|
return () => {
|
|
339
258
|
isMounted.current = false;
|
|
340
259
|
};
|
|
341
260
|
}, []);
|
|
261
|
+
const existence = (0, use_file_existence_1.useFileExistence)(outName);
|
|
342
262
|
const imageFormatOptions = (0, react_1.useMemo)(() => {
|
|
343
263
|
return [
|
|
344
264
|
{
|
|
@@ -348,7 +268,7 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
|
|
|
348
268
|
: () => setVideoImageFormat('png'),
|
|
349
269
|
key: 'png',
|
|
350
270
|
selected: renderMode === 'still'
|
|
351
|
-
?
|
|
271
|
+
? imageFormat === 'png'
|
|
352
272
|
: videoImageFormat === 'png',
|
|
353
273
|
},
|
|
354
274
|
{
|
|
@@ -358,23 +278,32 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
|
|
|
358
278
|
: () => setVideoImageFormat('jpeg'),
|
|
359
279
|
key: 'jpeg',
|
|
360
280
|
selected: renderMode === 'still'
|
|
361
|
-
?
|
|
281
|
+
? imageFormat === 'jpeg'
|
|
362
282
|
: videoImageFormat === 'jpeg',
|
|
363
283
|
},
|
|
364
284
|
];
|
|
365
|
-
}, [
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
285
|
+
}, [imageFormat, renderMode, setStillFormat, videoImageFormat]);
|
|
286
|
+
const videoCodecOptions = (0, react_1.useMemo)(() => {
|
|
287
|
+
const codecs = [
|
|
288
|
+
'h264',
|
|
289
|
+
'h265',
|
|
290
|
+
'vp8',
|
|
291
|
+
'vp9',
|
|
292
|
+
'mp3',
|
|
293
|
+
'aac',
|
|
294
|
+
'wav',
|
|
295
|
+
'prores',
|
|
296
|
+
'gif',
|
|
297
|
+
];
|
|
298
|
+
return codecs.map((codec) => {
|
|
299
|
+
return {
|
|
300
|
+
label: codec,
|
|
301
|
+
onClick: () => setCodec(codec),
|
|
302
|
+
key: codec,
|
|
303
|
+
selected: videoCodec === codec,
|
|
304
|
+
};
|
|
305
|
+
});
|
|
306
|
+
}, [setCodec, videoCodec]);
|
|
378
307
|
const renderTabOptions = (0, react_1.useMemo)(() => {
|
|
379
308
|
if ((currentComposition === null || currentComposition === void 0 ? void 0 : currentComposition.durationInFrames) < 2) {
|
|
380
309
|
return [
|
|
@@ -382,6 +311,7 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
|
|
|
382
311
|
label: 'Still',
|
|
383
312
|
onClick: () => {
|
|
384
313
|
setRenderMode('still');
|
|
314
|
+
setStillFormat(imageFormat);
|
|
385
315
|
},
|
|
386
316
|
key: 'still',
|
|
387
317
|
selected: renderMode === 'still',
|
|
@@ -393,6 +323,7 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
|
|
|
393
323
|
label: 'Still',
|
|
394
324
|
onClick: () => {
|
|
395
325
|
setRenderMode('still');
|
|
326
|
+
setStillFormat(imageFormat);
|
|
396
327
|
},
|
|
397
328
|
key: 'still',
|
|
398
329
|
selected: renderMode === 'still',
|
|
@@ -401,21 +332,46 @@ const RenderModal = ({ compositionId, initialFrame, initialVideoImageFormat, ini
|
|
|
401
332
|
label: 'Video',
|
|
402
333
|
onClick: () => {
|
|
403
334
|
setRenderMode('video');
|
|
335
|
+
setCodec(videoCodec);
|
|
404
336
|
},
|
|
405
337
|
key: 'video',
|
|
406
338
|
selected: renderMode === 'video',
|
|
407
339
|
},
|
|
408
|
-
{
|
|
409
|
-
label: 'Audio',
|
|
410
|
-
onClick: () => {
|
|
411
|
-
setRenderMode('audio');
|
|
412
|
-
},
|
|
413
|
-
key: 'audio',
|
|
414
|
-
selected: renderMode === 'audio',
|
|
415
|
-
},
|
|
416
340
|
];
|
|
417
|
-
}, [
|
|
418
|
-
|
|
419
|
-
|
|
341
|
+
}, [
|
|
342
|
+
currentComposition === null || currentComposition === void 0 ? void 0 : currentComposition.durationInFrames,
|
|
343
|
+
imageFormat,
|
|
344
|
+
renderMode,
|
|
345
|
+
setCodec,
|
|
346
|
+
setStillFormat,
|
|
347
|
+
videoCodec,
|
|
348
|
+
]);
|
|
349
|
+
const onVerboseLoggingChanged = (0, react_1.useCallback)((e) => {
|
|
350
|
+
setVerboseLogging(e.target.checked);
|
|
351
|
+
}, []);
|
|
352
|
+
if (renderMode === 'still') {
|
|
353
|
+
return ((0, jsx_runtime_1.jsxs)(ModalContainer_1.ModalContainer, { onOutsideClick: onQuit, onEscape: onQuit, children: [(0, jsx_runtime_1.jsx)(ModalHeader_1.NewCompHeader, { title: `Render ${compositionId}` }), (0, jsx_runtime_1.jsx)("div", { style: container, children: (0, jsx_runtime_1.jsx)(SegmentedControl_1.SegmentedControl, { items: renderTabOptions, needsWrapping: false }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(layout_1.Spacing, { block: true, y: 0.5 }), (0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Format" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)(SegmentedControl_1.SegmentedControl, { items: imageFormatOptions, needsWrapping: true }) })] }), (0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Output name" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(RemInput_1.RemotionInput
|
|
354
|
+
// TODO: Validate and reject folders or weird file names
|
|
355
|
+
, {
|
|
356
|
+
// TODO: Validate and reject folders or weird file names
|
|
357
|
+
warning: existence, style: input, type: "text", value: outName, onChange: onValueChange }), existence ? ((0, jsx_runtime_1.jsx)(ValidationMessage_1.ValidationMessage, { align: "flex-end", message: "Will be overwritten" })) : null] }) })] }), currentComposition.durationInFrames > 1 ? ((0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Frame" }), (0, jsx_runtime_1.jsxs)("div", { style: rightRow, children: [(0, jsx_runtime_1.jsx)(InputDragger_1.InputDragger, { value: frame, onTextChange: onFrameChanged,
|
|
358
|
+
// TODO: Actual frame
|
|
359
|
+
placeholder: "0-100",
|
|
360
|
+
// TODO: Debug the number input field
|
|
361
|
+
onValueChange: onFrameSetDirectly, name: "frame", step: 1, min: 0,
|
|
362
|
+
// TODO: Add actual frame
|
|
363
|
+
max: currentComposition.durationInFrames - 1 }), ' '] })] })) : null, (0, jsx_runtime_1.jsxs)(CollapsableOptions_1.CollapsableOptions, { showLabel: "Show advanced settings", hideLabel: "Hide advanced settings", children: [(0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Scale" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)(InputDragger_1.InputDragger, { value: scale, onTextChange: onScaleChanged, placeholder: "0.1-10",
|
|
364
|
+
// TODO: Does not allow non-integer steps
|
|
365
|
+
// TODO: Cannot click and type in 0.2
|
|
366
|
+
onValueChange: onScaleSetDirectly, name: "scale", step: 0.1, min: MIN_SCALE, max: MAX_SCALE }) })] }), (0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Verbose logging" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)("input", { type: 'checkbox', checked: verbose, onChange: onVerboseLoggingChanged }) })] }), imageFormat === 'jpeg' && ((0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "JPEG Quality" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)(InputDragger_1.InputDragger, { value: quality, onTextChange: onQualityChanged, placeholder: "0-100", onValueChange: onQualityChangedDirectly, name: "quality", step: 1, min: MIN_QUALITY, max: MAX_QUALITY }) })] }))] }), (0, jsx_runtime_1.jsx)(layout_1.Spacing, { block: true, y: 0.5 }), (0, jsx_runtime_1.jsx)("div", { style: buttonRow, children: (0, jsx_runtime_1.jsx)(Button_1.Button, { autoFocus: true, onClick: onClickStill, disabled: state.type === 'load', children: state.type === 'idle' ? 'Render still' : 'Rendering...' }) })] })] }));
|
|
367
|
+
}
|
|
368
|
+
return ((0, jsx_runtime_1.jsxs)(ModalContainer_1.ModalContainer, { onOutsideClick: onQuit, onEscape: onQuit, children: [(0, jsx_runtime_1.jsx)(ModalHeader_1.NewCompHeader, { title: `Render ${compositionId}` }), (0, jsx_runtime_1.jsx)("div", { style: container, children: (0, jsx_runtime_1.jsx)(SegmentedControl_1.SegmentedControl, { items: renderTabOptions, needsWrapping: false }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(layout_1.Spacing, { block: true, y: 0.5 }), (0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Codec" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)(SegmentedControl_1.SegmentedControl, { items: videoCodecOptions, needsWrapping: true }) })] }), (0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Output name" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(RemInput_1.RemotionInput
|
|
369
|
+
// TODO: Validate and reject folders or weird file names
|
|
370
|
+
, {
|
|
371
|
+
// TODO: Validate and reject folders or weird file names
|
|
372
|
+
warning: existence, style: input, type: "text", value: outName, onChange: onValueChange }), existence ? ((0, jsx_runtime_1.jsx)(ValidationMessage_1.ValidationMessage, { align: "flex-end", message: "Will be overwritten" })) : null] }) })] }), (0, jsx_runtime_1.jsxs)(CollapsableOptions_1.CollapsableOptions, { showLabel: "Show advanced settings", hideLabel: "Hide advanced settings", children: [(0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Scale" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)(InputDragger_1.InputDragger, { value: scale, onTextChange: onScaleChanged, placeholder: "0.1-10",
|
|
373
|
+
// TODO: Direct input does not allow non-integer steps
|
|
374
|
+
// TODO: Cannot click and type in 0.2
|
|
375
|
+
onValueChange: onScaleSetDirectly, name: "scale", step: 0.1, min: MIN_SCALE, max: MAX_SCALE }) })] }), (0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Verbose logging" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)("input", { type: 'checkbox', checked: verbose, onChange: onVerboseLoggingChanged }) })] }), (0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "Image Format" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)(SegmentedControl_1.SegmentedControl, { items: imageFormatOptions, needsWrapping: false }) })] }), videoImageFormat === 'jpeg' && ((0, jsx_runtime_1.jsxs)("div", { style: optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: label, children: "JPEG Quality" }), (0, jsx_runtime_1.jsx)("div", { style: rightRow, children: (0, jsx_runtime_1.jsx)(InputDragger_1.InputDragger, { value: quality, onTextChange: onQualityChanged, placeholder: "0-100", onValueChange: onQualityChangedDirectly, name: "quality", step: 1, min: MIN_QUALITY, max: MAX_QUALITY }) })] }))] }), (0, jsx_runtime_1.jsx)(layout_1.Spacing, { block: true, y: 0.5 }), (0, jsx_runtime_1.jsx)("div", { style: buttonRow, children: (0, jsx_runtime_1.jsx)(Button_1.Button, { autoFocus: true, onClick: onClickVideo, disabled: state.type === 'load', children: state.type === 'idle' ? 'Render video' : 'Rendering...' }) })] })] }));
|
|
420
376
|
};
|
|
421
377
|
exports.RenderModal = RenderModal;
|
|
@@ -2,10 +2,26 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ScaleSetting = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
-
const
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const InputDragger_1 = require("../NewComposition/InputDragger");
|
|
7
|
+
const RemInput_1 = require("../NewComposition/RemInput");
|
|
8
|
+
const layout_1 = require("./layout");
|
|
6
9
|
const MIN_SCALE = 0.1;
|
|
7
10
|
const MAX_SCALE = 10;
|
|
8
11
|
const ScaleSetting = ({ scale, setScale }) => {
|
|
9
|
-
|
|
12
|
+
const onScaleSetDirectly = (0, react_1.useCallback)((newScale) => {
|
|
13
|
+
setScale(newScale);
|
|
14
|
+
}, [setScale]);
|
|
15
|
+
const onScaleChanged = (0, react_1.useCallback)((e) => {
|
|
16
|
+
setScale((q) => {
|
|
17
|
+
const newScale = parseFloat(e);
|
|
18
|
+
if (Number.isNaN(newScale)) {
|
|
19
|
+
return q;
|
|
20
|
+
}
|
|
21
|
+
const newScaleClamped = Math.min(MAX_SCALE, Math.max(newScale, MIN_SCALE));
|
|
22
|
+
return newScaleClamped;
|
|
23
|
+
});
|
|
24
|
+
}, [setScale]);
|
|
25
|
+
return ((0, jsx_runtime_1.jsxs)("div", { style: layout_1.optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: layout_1.label, children: "Scale" }), (0, jsx_runtime_1.jsx)("div", { style: layout_1.rightRow, children: (0, jsx_runtime_1.jsx)(RemInput_1.RightAlignInput, { children: (0, jsx_runtime_1.jsx)(InputDragger_1.InputDragger, { value: scale, onTextChange: onScaleChanged, placeholder: `${MIN_SCALE}-${MAX_SCALE}`, onValueChange: onScaleSetDirectly, name: "scale", step: 0.1, min: MIN_SCALE, max: MAX_SCALE }) }) })] }));
|
|
10
26
|
};
|
|
11
27
|
exports.ScaleSetting = ScaleSetting;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.rightRow = exports.label = exports.optionRow = void 0;
|
|
4
4
|
const colors_1 = require("../../helpers/colors");
|
|
5
5
|
exports.optionRow = {
|
|
6
6
|
display: 'flex',
|
|
@@ -9,12 +9,10 @@ exports.optionRow = {
|
|
|
9
9
|
minHeight: 40,
|
|
10
10
|
paddingLeft: 16,
|
|
11
11
|
paddingRight: 16,
|
|
12
|
-
paddingTop: 8,
|
|
13
|
-
paddingBottom: 8,
|
|
14
12
|
};
|
|
15
13
|
exports.label = {
|
|
16
14
|
width: 150,
|
|
17
|
-
fontSize:
|
|
15
|
+
fontSize: 14,
|
|
18
16
|
lineHeight: '40px',
|
|
19
17
|
color: colors_1.LIGHT_TEXT,
|
|
20
18
|
};
|
|
@@ -25,7 +23,3 @@ exports.rightRow = {
|
|
|
25
23
|
alignSelf: 'center',
|
|
26
24
|
flex: 1,
|
|
27
25
|
};
|
|
28
|
-
exports.input = {
|
|
29
|
-
minWidth: 250,
|
|
30
|
-
textAlign: 'right',
|
|
31
|
-
};
|
|
@@ -4,12 +4,11 @@ exports.RenderQueueRemoveItem = void 0;
|
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const react_1 = require("react");
|
|
6
6
|
const InlineAction_1 = require("../InlineAction");
|
|
7
|
-
const NotificationCenter_1 = require("../Notifications/NotificationCenter");
|
|
8
7
|
const actions_1 = require("./actions");
|
|
9
8
|
const RenderQueueRemoveItem = ({ job }) => {
|
|
10
9
|
const onClick = (0, react_1.useCallback)(() => {
|
|
11
10
|
(0, actions_1.removeRenderJob)(job).catch((err) => {
|
|
12
|
-
|
|
11
|
+
// TODO: Handle error
|
|
13
12
|
console.log(err);
|
|
14
13
|
});
|
|
15
14
|
}, [job]);
|