@remotion/cloudrun 4.0.21 → 4.0.22
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/.turbo/turbo-build.log +3 -3
- package/dist/api/render-media-on-cloudrun.d.ts +4 -2
- package/dist/api/render-media-on-cloudrun.js +25 -9
- package/dist/api/render-still-on-cloudrun.d.ts +2 -2
- package/dist/api/render-still-on-cloudrun.js +50 -2
- package/dist/cli/commands/render/index.js +4 -51
- package/dist/cli/commands/still.js +21 -15
- package/dist/cli/helpers/cloudrun-crash-logs.d.ts +2 -0
- package/dist/cli/helpers/cloudrun-crash-logs.js +57 -0
- package/dist/cli/index.js +18 -2
- package/dist/functions/helpers/payloads.d.ts +33 -27
- package/dist/functions/helpers/payloads.js +8 -5
- package/dist/functions/helpers/write-cloudrun-error.d.ts +12 -0
- package/dist/functions/helpers/write-cloudrun-error.js +18 -0
- package/dist/functions/index.js +6 -8
- package/dist/functions/render-media-single-thread.js +106 -88
- package/dist/functions/render-still-single-thread.js +81 -65
- package/package.json +6 -6
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
|
|
2
|
-
> @remotion/cloudrun@4.0.
|
|
2
|
+
> @remotion/cloudrun@4.0.21 build /Users/jonathanburger/remotion/packages/cloudrun
|
|
3
3
|
> tsc -d && cp src/shared/sa-permissions.json dist/shared/sa-permissions.json && pnpm run buildContainer && pnpm run tarInstaller
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
> @remotion/cloudrun@4.0.
|
|
6
|
+
> @remotion/cloudrun@4.0.21 buildContainer /Users/jonathanburger/remotion/packages/cloudrun
|
|
7
7
|
> ts-node src/admin/bundle-renderLogic.ts
|
|
8
8
|
|
|
9
9
|
distribution bundled.
|
|
10
10
|
|
|
11
|
-
> @remotion/cloudrun@4.0.
|
|
11
|
+
> @remotion/cloudrun@4.0.21 tarInstaller /Users/jonathanburger/remotion/packages/cloudrun
|
|
12
12
|
> ts-node src/admin/bundle-installer.ts
|
|
13
13
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AudioCodec, ChromiumOptions, FrameRange, LogLevel, PixelFormat, ProResProfile, VideoImageFormat } from '@remotion/renderer';
|
|
1
|
+
import type { AudioCodec, ChromiumOptions, FrameRange, LogLevel, PixelFormat, ProResProfile, VideoImageFormat, X264Preset } from '@remotion/renderer';
|
|
2
2
|
import type { CloudRunCrashResponse, RenderMediaOnCloudrunOutput } from '../functions/helpers/payloads';
|
|
3
3
|
import type { GcpRegion } from '../pricing/gcp-regions';
|
|
4
4
|
import type { CloudrunCodec } from '../shared/validate-gcp-codec';
|
|
@@ -19,6 +19,7 @@ export type RenderMediaOnCloudrunInput = {
|
|
|
19
19
|
audioBitrate?: string | null;
|
|
20
20
|
videoBitrate?: string | null;
|
|
21
21
|
proResProfile?: ProResProfile;
|
|
22
|
+
x264Preset?: X264Preset;
|
|
22
23
|
crf?: number | undefined;
|
|
23
24
|
pixelFormat?: PixelFormat;
|
|
24
25
|
imageFormat?: VideoImageFormat;
|
|
@@ -56,6 +57,7 @@ export type RenderMediaOnCloudrunInput = {
|
|
|
56
57
|
* @param params.audioBitrate The target bitrate for the audio of the generated video.
|
|
57
58
|
* @param params.videoBitrate The target bitrate of the generated video.
|
|
58
59
|
* @param params.proResProfile Sets a ProRes profile. Only applies to videos rendered with prores codec.
|
|
60
|
+
* @param params.x264Preset Sets a Preset profile. Only applies to videos rendered with h.264 codec.
|
|
59
61
|
* @param params.crf Constant Rate Factor, controlling the quality.
|
|
60
62
|
* @param params.pixelFormat Custom pixel format to use. Usually used for special use cases like transparent videos.
|
|
61
63
|
* @param params.imageFormat Which image format the frames should be rendered in.
|
|
@@ -75,4 +77,4 @@ export type RenderMediaOnCloudrunInput = {
|
|
|
75
77
|
* @param params.preferLossless Uses a lossless audio codec, if one is available for the codec. If you set audioCodec, it takes priority over preferLossless.
|
|
76
78
|
* @returns {Promise<RenderMediaOnCloudrunOutput>} See documentation for detailed structure
|
|
77
79
|
*/
|
|
78
|
-
export declare const renderMediaOnCloudrun: ({ cloudRunUrl, serviceName, region, serveUrl, composition, inputProps, codec, forceBucketName, privacy, outName, updateRenderProgress, jpegQuality, audioCodec, audioBitrate, videoBitrate, proResProfile, crf, pixelFormat, imageFormat, scale, everyNthFrame, numberOfGifLoops, frameRange, envVariables, chromiumOptions, muted, forceWidth, forceHeight, logLevel, delayRenderTimeoutInMilliseconds, concurrency, enforceAudioTrack, preferLossless, }: RenderMediaOnCloudrunInput) => Promise<RenderMediaOnCloudrunOutput | CloudRunCrashResponse>;
|
|
80
|
+
export declare const renderMediaOnCloudrun: ({ cloudRunUrl, serviceName, region, serveUrl, composition, inputProps, codec, forceBucketName, privacy, outName, updateRenderProgress, jpegQuality, audioCodec, audioBitrate, videoBitrate, proResProfile, x264Preset, crf, pixelFormat, imageFormat, scale, everyNthFrame, numberOfGifLoops, frameRange, envVariables, chromiumOptions, muted, forceWidth, forceHeight, logLevel, delayRenderTimeoutInMilliseconds, concurrency, enforceAudioTrack, preferLossless, }: RenderMediaOnCloudrunInput) => Promise<RenderMediaOnCloudrunOutput | CloudRunCrashResponse>;
|
|
@@ -28,6 +28,7 @@ const get_cloudrun_endpoint_1 = require("./helpers/get-cloudrun-endpoint");
|
|
|
28
28
|
* @param params.audioBitrate The target bitrate for the audio of the generated video.
|
|
29
29
|
* @param params.videoBitrate The target bitrate of the generated video.
|
|
30
30
|
* @param params.proResProfile Sets a ProRes profile. Only applies to videos rendered with prores codec.
|
|
31
|
+
* @param params.x264Preset Sets a Preset profile. Only applies to videos rendered with h.264 codec.
|
|
31
32
|
* @param params.crf Constant Rate Factor, controlling the quality.
|
|
32
33
|
* @param params.pixelFormat Custom pixel format to use. Usually used for special use cases like transparent videos.
|
|
33
34
|
* @param params.imageFormat Which image format the frames should be rendered in.
|
|
@@ -47,7 +48,7 @@ const get_cloudrun_endpoint_1 = require("./helpers/get-cloudrun-endpoint");
|
|
|
47
48
|
* @param params.preferLossless Uses a lossless audio codec, if one is available for the codec. If you set audioCodec, it takes priority over preferLossless.
|
|
48
49
|
* @returns {Promise<RenderMediaOnCloudrunOutput>} See documentation for detailed structure
|
|
49
50
|
*/
|
|
50
|
-
const renderMediaOnCloudrun = async ({ cloudRunUrl, serviceName, region, serveUrl, composition, inputProps, codec, forceBucketName, privacy, outName, updateRenderProgress, jpegQuality, audioCodec, audioBitrate, videoBitrate, proResProfile, crf, pixelFormat, imageFormat, scale, everyNthFrame, numberOfGifLoops, frameRange, envVariables, chromiumOptions, muted, forceWidth, forceHeight, logLevel, delayRenderTimeoutInMilliseconds, concurrency, enforceAudioTrack, preferLossless, }) => {
|
|
51
|
+
const renderMediaOnCloudrun = async ({ cloudRunUrl, serviceName, region, serveUrl, composition, inputProps, codec, forceBucketName, privacy, outName, updateRenderProgress, jpegQuality, audioCodec, audioBitrate, videoBitrate, proResProfile, x264Preset, crf, pixelFormat, imageFormat, scale, everyNthFrame, numberOfGifLoops, frameRange, envVariables, chromiumOptions, muted, forceWidth, forceHeight, logLevel, delayRenderTimeoutInMilliseconds, concurrency, enforceAudioTrack, preferLossless, }) => {
|
|
51
52
|
const actualCodec = (0, validate_gcp_codec_1.validateCloudrunCodec)(codec);
|
|
52
53
|
(0, validate_serveurl_1.validateServeUrl)(serveUrl);
|
|
53
54
|
if (privacy)
|
|
@@ -76,6 +77,7 @@ const renderMediaOnCloudrun = async ({ cloudRunUrl, serviceName, region, serveUr
|
|
|
76
77
|
imageFormat: imageFormat !== null && imageFormat !== void 0 ? imageFormat : renderer_1.RenderInternals.DEFAULT_VIDEO_IMAGE_FORMAT,
|
|
77
78
|
scale: scale !== null && scale !== void 0 ? scale : 1,
|
|
78
79
|
proResProfile: proResProfile !== null && proResProfile !== void 0 ? proResProfile : null,
|
|
80
|
+
x264Preset: x264Preset !== null && x264Preset !== void 0 ? x264Preset : null,
|
|
79
81
|
everyNthFrame: everyNthFrame !== null && everyNthFrame !== void 0 ? everyNthFrame : 1,
|
|
80
82
|
numberOfGifLoops: numberOfGifLoops !== null && numberOfGifLoops !== void 0 ? numberOfGifLoops : null,
|
|
81
83
|
frameRange: frameRange !== null && frameRange !== void 0 ? frameRange : null,
|
|
@@ -107,13 +109,27 @@ const renderMediaOnCloudrun = async ({ cloudRunUrl, serviceName, region, serveUr
|
|
|
107
109
|
const startTime = Date.now();
|
|
108
110
|
const formattedStartTime = new Date().toISOString();
|
|
109
111
|
const stream = postResponse.data;
|
|
112
|
+
let accumulatedChunks = ''; // A buffer to accumulate chunks.
|
|
110
113
|
stream.on('data', (chunk) => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
accumulatedChunks += chunk.toString(); // Add the new chunk to the buffer.
|
|
115
|
+
let parsedData;
|
|
116
|
+
try {
|
|
117
|
+
parsedData = JSON.parse(accumulatedChunks.trim());
|
|
118
|
+
accumulatedChunks = ''; // Clear the buffer after successful parsing.
|
|
114
119
|
}
|
|
115
|
-
|
|
116
|
-
|
|
120
|
+
catch (e) {
|
|
121
|
+
// If parsing fails, it means we don't have a complete JSON string yet.
|
|
122
|
+
// We'll wait for more chunks.
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (parsedData.response) {
|
|
126
|
+
response = parsedData.response;
|
|
127
|
+
}
|
|
128
|
+
else if (parsedData.onProgress) {
|
|
129
|
+
updateRenderProgress === null || updateRenderProgress === void 0 ? void 0 : updateRenderProgress(parsedData.onProgress);
|
|
130
|
+
}
|
|
131
|
+
if (parsedData.type === 'error') {
|
|
132
|
+
reject(parsedData);
|
|
117
133
|
}
|
|
118
134
|
});
|
|
119
135
|
stream.on('end', () => {
|
|
@@ -122,7 +138,7 @@ const renderMediaOnCloudrun = async ({ cloudRunUrl, serviceName, region, serveUr
|
|
|
122
138
|
const formattedCrashTime = new Date().toISOString();
|
|
123
139
|
updateRenderProgress === null || updateRenderProgress === void 0 ? void 0 : updateRenderProgress(0, true);
|
|
124
140
|
resolve({
|
|
125
|
-
|
|
141
|
+
type: 'crash',
|
|
126
142
|
cloudRunEndpoint,
|
|
127
143
|
message: 'Service crashed without sending a response. Check the logs in GCP console.',
|
|
128
144
|
requestStartTime: formattedStartTime,
|
|
@@ -130,8 +146,8 @@ const renderMediaOnCloudrun = async ({ cloudRunUrl, serviceName, region, serveUr
|
|
|
130
146
|
requestElapsedTimeInSeconds: (crashTime - startTime) / 1000,
|
|
131
147
|
});
|
|
132
148
|
}
|
|
133
|
-
else if (response.
|
|
134
|
-
throw
|
|
149
|
+
else if (response.type !== 'success' && response.type !== 'crash') {
|
|
150
|
+
throw response;
|
|
135
151
|
}
|
|
136
152
|
resolve(response);
|
|
137
153
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ChromiumOptions, LogLevel, StillImageFormat } from '@remotion/renderer';
|
|
2
|
-
import type { RenderStillOnCloudrunOutput } from '../functions/helpers/payloads';
|
|
2
|
+
import type { CloudRunCrashResponse, ErrorResponsePayload, RenderStillOnCloudrunOutput } from '../functions/helpers/payloads';
|
|
3
3
|
import type { GcpRegion } from '../pricing/gcp-regions';
|
|
4
4
|
export type RenderStillOnCloudrunInput = {
|
|
5
5
|
cloudRunUrl?: string;
|
|
@@ -46,4 +46,4 @@ export type RenderStillOnCloudrunInput = {
|
|
|
46
46
|
* @param params.delayRenderTimeoutInMilliseconds A number describing how long the render may take to resolve all delayRender() calls before it times out.
|
|
47
47
|
* @returns {Promise<RenderStillOnCloudrunOutput>} See documentation for detailed structure
|
|
48
48
|
*/
|
|
49
|
-
export declare const renderStillOnCloudrun: ({ cloudRunUrl, serviceName, region, serveUrl, composition, inputProps, forceBucketName, privacy, outName, imageFormat, envVariables, frame, jpegQuality, chromiumOptions, scale, forceWidth, forceHeight, logLevel, delayRenderTimeoutInMilliseconds, }: RenderStillOnCloudrunInput) => Promise<RenderStillOnCloudrunOutput>;
|
|
49
|
+
export declare const renderStillOnCloudrun: ({ cloudRunUrl, serviceName, region, serveUrl, composition, inputProps, forceBucketName, privacy, outName, imageFormat, envVariables, frame, jpegQuality, chromiumOptions, scale, forceWidth, forceHeight, logLevel, delayRenderTimeoutInMilliseconds, }: RenderStillOnCloudrunInput) => Promise<RenderStillOnCloudrunOutput | ErrorResponsePayload | CloudRunCrashResponse>;
|
|
@@ -66,11 +66,59 @@ const renderStillOnCloudrun = async ({ cloudRunUrl, serviceName, region, serveUr
|
|
|
66
66
|
delayRenderTimeoutInMilliseconds: delayRenderTimeoutInMilliseconds !== null && delayRenderTimeoutInMilliseconds !== void 0 ? delayRenderTimeoutInMilliseconds : renderer_1.RenderInternals.DEFAULT_TIMEOUT,
|
|
67
67
|
};
|
|
68
68
|
const client = await (0, get_auth_client_for_url_1.getAuthClientForUrl)(cloudRunEndpoint);
|
|
69
|
-
const
|
|
69
|
+
const postResponse = await client.request({
|
|
70
70
|
url: cloudRunUrl,
|
|
71
71
|
method: 'POST',
|
|
72
72
|
data,
|
|
73
|
+
responseType: 'stream',
|
|
73
74
|
});
|
|
74
|
-
|
|
75
|
+
const renderResponse = await new Promise((resolve, reject) => {
|
|
76
|
+
let response;
|
|
77
|
+
const startTime = Date.now();
|
|
78
|
+
const formattedStartTime = new Date().toISOString();
|
|
79
|
+
const stream = postResponse.data;
|
|
80
|
+
let accumulatedChunks = ''; // A buffer to accumulate chunks.
|
|
81
|
+
stream.on('data', (chunk) => {
|
|
82
|
+
accumulatedChunks += chunk.toString(); // Add the new chunk to the buffer.
|
|
83
|
+
let parsedData;
|
|
84
|
+
try {
|
|
85
|
+
parsedData = JSON.parse(accumulatedChunks.trim());
|
|
86
|
+
accumulatedChunks = ''; // Clear the buffer after successful parsing.
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
// If parsing fails, it means we don't have a complete JSON string yet.
|
|
90
|
+
// We'll wait for more chunks.
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (parsedData.response) {
|
|
94
|
+
response = parsedData.response;
|
|
95
|
+
}
|
|
96
|
+
if (parsedData.type === 'error') {
|
|
97
|
+
reject(parsedData);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
stream.on('end', () => {
|
|
101
|
+
if (!response) {
|
|
102
|
+
const crashTime = Date.now();
|
|
103
|
+
const formattedCrashTime = new Date().toISOString();
|
|
104
|
+
resolve({
|
|
105
|
+
type: 'crash',
|
|
106
|
+
cloudRunEndpoint,
|
|
107
|
+
message: 'Service crashed without sending a response. Check the logs in GCP console.',
|
|
108
|
+
requestStartTime: formattedStartTime,
|
|
109
|
+
requestCrashTime: formattedCrashTime,
|
|
110
|
+
requestElapsedTimeInSeconds: (crashTime - startTime) / 1000,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
else if (response.type !== 'success' && response.type !== 'crash') {
|
|
114
|
+
throw response;
|
|
115
|
+
}
|
|
116
|
+
resolve(response);
|
|
117
|
+
});
|
|
118
|
+
stream.on('error', (error) => {
|
|
119
|
+
reject(error);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
return renderResponse;
|
|
75
123
|
};
|
|
76
124
|
exports.renderStillOnCloudrun = renderStillOnCloudrun;
|
|
@@ -6,12 +6,10 @@ const config_1 = require("@remotion/cli/config");
|
|
|
6
6
|
const renderer_1 = require("@remotion/renderer");
|
|
7
7
|
const remotion_1 = require("remotion");
|
|
8
8
|
const download_file_1 = require("../../../api/download-file");
|
|
9
|
-
const extract_mem_from_url_1 = require("../../../api/helpers/extract-mem-from-url");
|
|
10
|
-
const extract_time_from_url_1 = require("../../../api/helpers/extract-time-from-url");
|
|
11
|
-
const get_cloud_logging_client_1 = require("../../../api/helpers/get-cloud-logging-client");
|
|
12
9
|
const render_media_on_cloudrun_1 = require("../../../api/render-media-on-cloudrun");
|
|
13
10
|
const validate_serveurl_1 = require("../../../shared/validate-serveurl");
|
|
14
11
|
const args_1 = require("../../args");
|
|
12
|
+
const cloudrun_crash_logs_1 = require("../../helpers/cloudrun-crash-logs");
|
|
15
13
|
const log_1 = require("../../log");
|
|
16
14
|
const renderArgsCheck_1 = require("./helpers/renderArgsCheck");
|
|
17
15
|
exports.RENDER_COMMAND = 'render';
|
|
@@ -152,55 +150,10 @@ ${downloadName ? ` Downloaded File = ${downloadName}` : ''}
|
|
|
152
150
|
enforceAudioTrack,
|
|
153
151
|
preferLossless: false,
|
|
154
152
|
});
|
|
155
|
-
if (res.
|
|
156
|
-
|
|
157
|
-
const timeout = (0, extract_time_from_url_1.extractTimeoutFromURL)(res.cloudRunEndpoint);
|
|
158
|
-
const memoryLimit = (0, extract_mem_from_url_1.extractMemoryFromURL)(res.cloudRunEndpoint);
|
|
159
|
-
if (timeout && res.requestElapsedTimeInSeconds + 10 > timeout) {
|
|
160
|
-
timeoutPreMsg = `Render call likely timed out. Service timeout is ${timeout} seconds, and render took at least ${res.requestElapsedTimeInSeconds.toFixed(1)} seconds.\n`;
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
timeoutPreMsg = `Crash unlikely due to timeout. Render took ${res.requestElapsedTimeInSeconds.toFixed(1)} seconds, below the timeout of ${timeout} seconds.\n`;
|
|
164
|
-
}
|
|
165
|
-
log_1.Log.error(`Error rendering on Cloud Run. The Cloud Run service did not return a response.\n
|
|
166
|
-
${timeoutPreMsg}The crash may be due to the service exceeding its memory limit of ${memoryLimit}.
|
|
167
|
-
Full logs are available at https://console.cloud.google.com/run?project=${process.env.REMOTION_GCP_PROJECT_ID}\n`);
|
|
168
|
-
const cloudLoggingClient = (0, get_cloud_logging_client_1.getCloudLoggingClient)();
|
|
169
|
-
const listLogEntriesRequest = {
|
|
170
|
-
resourceNames: [`projects/${process.env.REMOTION_GCP_PROJECT_ID}`],
|
|
171
|
-
filter: `logName=projects/${process.env.REMOTION_GCP_PROJECT_ID}/logs/run.googleapis.com%2Fvarlog%2Fsystem AND (severity=WARNING OR severity=ERROR) AND timestamp >= "${res.requestStartTime}"`,
|
|
172
|
-
};
|
|
173
|
-
const logCheckCountdown = cli_1.CliInternals.createOverwriteableCliOutput({
|
|
174
|
-
quiet: cli_1.CliInternals.quietFlagProvided(),
|
|
175
|
-
cancelSignal: null,
|
|
176
|
-
updatesDontOverwrite: false,
|
|
177
|
-
indent: false,
|
|
178
|
-
});
|
|
179
|
-
await (() => {
|
|
180
|
-
return new Promise((resolve) => {
|
|
181
|
-
let timeLeft = 30;
|
|
182
|
-
const intervalId = setInterval(() => {
|
|
183
|
-
logCheckCountdown.update(`GCP Cloud Logging takes time to ingest and index logs.\nFetching recent error/warning logs in ${timeLeft} seconds`, false);
|
|
184
|
-
timeLeft--;
|
|
185
|
-
if (timeLeft < 0) {
|
|
186
|
-
logCheckCountdown.update('Fetching logs...\n\n', false);
|
|
187
|
-
clearInterval(intervalId);
|
|
188
|
-
resolve();
|
|
189
|
-
}
|
|
190
|
-
}, 1000);
|
|
191
|
-
});
|
|
192
|
-
})();
|
|
193
|
-
const iterableLogListEntries = await cloudLoggingClient.listLogEntriesAsync(listLogEntriesRequest);
|
|
194
|
-
for await (const logResponse of iterableLogListEntries) {
|
|
195
|
-
const responseDate = new Date(Number(logResponse.timestamp.seconds) * 1000 +
|
|
196
|
-
Number(logResponse.timestamp.nanos) / 1000000);
|
|
197
|
-
const convertedDate = responseDate.toLocaleString();
|
|
198
|
-
log_1.Log.info(convertedDate);
|
|
199
|
-
log_1.Log.info(logResponse.textPayload);
|
|
200
|
-
log_1.Log.info();
|
|
201
|
-
}
|
|
153
|
+
if (res.type === 'crash') {
|
|
154
|
+
(0, cloudrun_crash_logs_1.displayCrashLogs)(res);
|
|
202
155
|
}
|
|
203
|
-
else if (res.
|
|
156
|
+
else if (res.type === 'success') {
|
|
204
157
|
renderProgress.doneIn = Date.now() - renderStart;
|
|
205
158
|
updateProgress();
|
|
206
159
|
log_1.Log.info(`
|
|
@@ -8,6 +8,7 @@ const remotion_1 = require("remotion");
|
|
|
8
8
|
const download_file_1 = require("../../api/download-file");
|
|
9
9
|
const render_still_on_cloudrun_1 = require("../../api/render-still-on-cloudrun");
|
|
10
10
|
const validate_serveurl_1 = require("../../shared/validate-serveurl");
|
|
11
|
+
const cloudrun_crash_logs_1 = require("../helpers/cloudrun-crash-logs");
|
|
11
12
|
const log_1 = require("../log");
|
|
12
13
|
const renderArgsCheck_1 = require("./render/helpers/renderArgsCheck");
|
|
13
14
|
exports.STILL_COMMAND = 'still';
|
|
@@ -112,21 +113,26 @@ ${downloadName ? ` Downloaded File = ${downloadName}` : ''}
|
|
|
112
113
|
logLevel: config_1.ConfigInternals.Logging.getLogLevel(),
|
|
113
114
|
delayRenderTimeoutInMilliseconds: puppeteerTimeout,
|
|
114
115
|
});
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
log_1.Log.info(
|
|
123
|
-
log_1.Log.info(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
116
|
+
if (res.type === 'crash') {
|
|
117
|
+
(0, cloudrun_crash_logs_1.displayCrashLogs)(res);
|
|
118
|
+
}
|
|
119
|
+
else if (res.type === 'success') {
|
|
120
|
+
doneIn = Date.now() - renderStart;
|
|
121
|
+
updateProgress(true);
|
|
122
|
+
log_1.Log.info(cli_1.CliInternals.chalk.gray(`Cloud Storage Uri = ${res.cloudStorageUri}`));
|
|
123
|
+
log_1.Log.info(cli_1.CliInternals.chalk.gray(`Render ID = ${res.renderId}`));
|
|
124
|
+
log_1.Log.info(cli_1.CliInternals.chalk.gray(`${Math.round(Number(res.size) / 1000)} KB, Privacy: ${res.privacy}, Bucket: ${res.bucketName}`));
|
|
125
|
+
log_1.Log.info(cli_1.CliInternals.chalk.blue(`○ ${res.publicUrl}`));
|
|
126
|
+
if (downloadName) {
|
|
127
|
+
log_1.Log.info('');
|
|
128
|
+
log_1.Log.info('downloading file...');
|
|
129
|
+
const { outputPath: destination } = await (0, download_file_1.downloadFile)({
|
|
130
|
+
bucketName: res.bucketName,
|
|
131
|
+
gsutilURI: res.cloudStorageUri,
|
|
132
|
+
downloadName,
|
|
133
|
+
});
|
|
134
|
+
log_1.Log.info(cli_1.CliInternals.chalk.blueBright(`Downloaded file to ${destination}!`));
|
|
135
|
+
}
|
|
130
136
|
}
|
|
131
137
|
};
|
|
132
138
|
exports.stillCommand = stillCommand;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.displayCrashLogs = void 0;
|
|
4
|
+
const cli_1 = require("@remotion/cli");
|
|
5
|
+
const extract_mem_from_url_1 = require("../../api/helpers/extract-mem-from-url");
|
|
6
|
+
const extract_time_from_url_1 = require("../../api/helpers/extract-time-from-url");
|
|
7
|
+
const get_cloud_logging_client_1 = require("../../api/helpers/get-cloud-logging-client");
|
|
8
|
+
const log_1 = require("../log");
|
|
9
|
+
const displayCrashLogs = async (res) => {
|
|
10
|
+
let timeoutPreMsg = '';
|
|
11
|
+
const timeout = (0, extract_time_from_url_1.extractTimeoutFromURL)(res.cloudRunEndpoint);
|
|
12
|
+
const memoryLimit = (0, extract_mem_from_url_1.extractMemoryFromURL)(res.cloudRunEndpoint);
|
|
13
|
+
if (timeout && res.requestElapsedTimeInSeconds + 10 > timeout) {
|
|
14
|
+
timeoutPreMsg = `Render call likely timed out. Service timeout is ${timeout} seconds, and render took at least ${res.requestElapsedTimeInSeconds.toFixed(1)} seconds.\n`;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
timeoutPreMsg = `Crash unlikely due to timeout. Render took ${res.requestElapsedTimeInSeconds.toFixed(1)} seconds, below the timeout of ${timeout} seconds.\n`;
|
|
18
|
+
}
|
|
19
|
+
log_1.Log.error(`Error rendering on Cloud Run. The Cloud Run service did not return a response.\n
|
|
20
|
+
${timeoutPreMsg}The crash may be due to the service exceeding its memory limit of ${memoryLimit}.
|
|
21
|
+
Full logs are available at https://console.cloud.google.com/run?project=${process.env.REMOTION_GCP_PROJECT_ID}\n`);
|
|
22
|
+
const cloudLoggingClient = (0, get_cloud_logging_client_1.getCloudLoggingClient)();
|
|
23
|
+
const listLogEntriesRequest = {
|
|
24
|
+
resourceNames: [`projects/${process.env.REMOTION_GCP_PROJECT_ID}`],
|
|
25
|
+
filter: `logName=projects/${process.env.REMOTION_GCP_PROJECT_ID}/logs/run.googleapis.com%2Fvarlog%2Fsystem AND (severity=WARNING OR severity=ERROR) AND timestamp >= "${res.requestStartTime}"`,
|
|
26
|
+
};
|
|
27
|
+
const logCheckCountdown = cli_1.CliInternals.createOverwriteableCliOutput({
|
|
28
|
+
quiet: cli_1.CliInternals.quietFlagProvided(),
|
|
29
|
+
cancelSignal: null,
|
|
30
|
+
updatesDontOverwrite: false,
|
|
31
|
+
indent: false,
|
|
32
|
+
});
|
|
33
|
+
await (() => {
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
let timeLeft = 30;
|
|
36
|
+
const intervalId = setInterval(() => {
|
|
37
|
+
logCheckCountdown.update(`GCP Cloud Logging takes time to ingest and index logs.\nFetching recent error/warning logs in ${timeLeft} seconds`, false);
|
|
38
|
+
timeLeft--;
|
|
39
|
+
if (timeLeft < 0) {
|
|
40
|
+
logCheckCountdown.update('Fetching logs...\n\n', false);
|
|
41
|
+
clearInterval(intervalId);
|
|
42
|
+
resolve();
|
|
43
|
+
}
|
|
44
|
+
}, 1000);
|
|
45
|
+
});
|
|
46
|
+
})();
|
|
47
|
+
const iterableLogListEntries = await cloudLoggingClient.listLogEntriesAsync(listLogEntriesRequest);
|
|
48
|
+
for await (const logResponse of iterableLogListEntries) {
|
|
49
|
+
const responseDate = new Date(Number(logResponse.timestamp.seconds) * 1000 +
|
|
50
|
+
Number(logResponse.timestamp.nanos) / 1000000);
|
|
51
|
+
const convertedDate = responseDate.toLocaleString();
|
|
52
|
+
log_1.Log.info(convertedDate);
|
|
53
|
+
log_1.Log.info(logResponse.textPayload);
|
|
54
|
+
log_1.Log.info();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
exports.displayCrashLogs = displayCrashLogs;
|
package/dist/cli/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.executeCommand = void 0;
|
|
4
|
+
const cli_1 = require("@remotion/cli");
|
|
5
|
+
const config_1 = require("@remotion/cli/config");
|
|
6
|
+
const renderer_1 = require("@remotion/renderer");
|
|
4
7
|
const args_1 = require("./args");
|
|
5
8
|
const permissions_1 = require("./commands/permissions");
|
|
6
9
|
const regions_1 = require("./commands/regions");
|
|
@@ -43,13 +46,26 @@ const matchCommand = (args, remotionRoot) => {
|
|
|
43
46
|
(0, quit_1.quit)(1);
|
|
44
47
|
};
|
|
45
48
|
const executeCommand = async (args, remotionRoot) => {
|
|
49
|
+
var _a, _b;
|
|
46
50
|
try {
|
|
47
51
|
await matchCommand(args, remotionRoot);
|
|
48
52
|
}
|
|
49
53
|
catch (err) {
|
|
50
54
|
const error = err;
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
if (error instanceof renderer_1.RenderInternals.SymbolicateableError) {
|
|
56
|
+
await cli_1.CliInternals.handleCommonError(error, config_1.ConfigInternals.Logging.getLogLevel());
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
const frames = renderer_1.RenderInternals.parseStack((_b = (_a = error.stack) === null || _a === void 0 ? void 0 : _a.split('\n')) !== null && _b !== void 0 ? _b : []);
|
|
60
|
+
const errorWithStackFrame = new renderer_1.RenderInternals.SymbolicateableError({
|
|
61
|
+
message: error.message,
|
|
62
|
+
frame: null,
|
|
63
|
+
name: error.name,
|
|
64
|
+
stack: error.stack,
|
|
65
|
+
stackFrame: frames,
|
|
66
|
+
});
|
|
67
|
+
await cli_1.CliInternals.handleCommonError(errorWithStackFrame, config_1.ConfigInternals.Logging.getLogLevel());
|
|
68
|
+
}
|
|
53
69
|
(0, quit_1.quit)(1);
|
|
54
70
|
}
|
|
55
71
|
};
|
|
@@ -16,6 +16,7 @@ export declare const CloudRunPayload: z.ZodDiscriminatedUnion<"type", [z.ZodObje
|
|
|
16
16
|
imageFormat: z.ZodEnum<["png", "jpeg", "none"]>;
|
|
17
17
|
scale: z.ZodNumber;
|
|
18
18
|
proResProfile: z.ZodNullable<z.ZodEnum<["4444-xq", "4444", "hq", "standard", "light", "proxy"]>>;
|
|
19
|
+
x264Preset: z.ZodNullable<z.ZodEnum<["ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower", "veryslow", "placebo"]>>;
|
|
19
20
|
everyNthFrame: z.ZodNumber;
|
|
20
21
|
numberOfGifLoops: z.ZodNullable<z.ZodNumber>;
|
|
21
22
|
frameRange: z.ZodNullable<z.ZodUnion<[z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>, z.ZodNumber]>>;
|
|
@@ -49,8 +50,8 @@ export declare const CloudRunPayload: z.ZodDiscriminatedUnion<"type", [z.ZodObje
|
|
|
49
50
|
enforceAudioTrack: z.ZodBoolean;
|
|
50
51
|
preferLossless: z.ZodBoolean;
|
|
51
52
|
}, "strip", z.ZodTypeAny, {
|
|
52
|
-
serveUrl: string;
|
|
53
53
|
type: "media";
|
|
54
|
+
serveUrl: string;
|
|
54
55
|
composition: string;
|
|
55
56
|
codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif";
|
|
56
57
|
serializedInputPropsWithCustomSchema: string;
|
|
@@ -63,6 +64,7 @@ export declare const CloudRunPayload: z.ZodDiscriminatedUnion<"type", [z.ZodObje
|
|
|
63
64
|
imageFormat: "png" | "jpeg" | "none";
|
|
64
65
|
scale: number;
|
|
65
66
|
proResProfile: "4444-xq" | "4444" | "hq" | "standard" | "light" | "proxy" | null;
|
|
67
|
+
x264Preset: "ultrafast" | "superfast" | "veryfast" | "faster" | "fast" | "medium" | "slow" | "slower" | "veryslow" | "placebo" | null;
|
|
66
68
|
everyNthFrame: number;
|
|
67
69
|
numberOfGifLoops: number | null;
|
|
68
70
|
frameRange: ((number | [number, number]) & (number | [number, number] | undefined)) | null;
|
|
@@ -86,8 +88,8 @@ export declare const CloudRunPayload: z.ZodDiscriminatedUnion<"type", [z.ZodObje
|
|
|
86
88
|
outName?: string | undefined;
|
|
87
89
|
privacy?: "public" | "private" | undefined;
|
|
88
90
|
}, {
|
|
89
|
-
serveUrl: string;
|
|
90
91
|
type: "media";
|
|
92
|
+
serveUrl: string;
|
|
91
93
|
composition: string;
|
|
92
94
|
codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif";
|
|
93
95
|
serializedInputPropsWithCustomSchema: string;
|
|
@@ -100,6 +102,7 @@ export declare const CloudRunPayload: z.ZodDiscriminatedUnion<"type", [z.ZodObje
|
|
|
100
102
|
imageFormat: "png" | "jpeg" | "none";
|
|
101
103
|
scale: number;
|
|
102
104
|
proResProfile: "4444-xq" | "4444" | "hq" | "standard" | "light" | "proxy" | null;
|
|
105
|
+
x264Preset: "ultrafast" | "superfast" | "veryfast" | "faster" | "fast" | "medium" | "slow" | "slower" | "veryslow" | "placebo" | null;
|
|
103
106
|
everyNthFrame: number;
|
|
104
107
|
numberOfGifLoops: number | null;
|
|
105
108
|
frameRange: ((number | [number, number]) & (number | [number, number] | undefined)) | null;
|
|
@@ -159,8 +162,8 @@ export declare const CloudRunPayload: z.ZodDiscriminatedUnion<"type", [z.ZodObje
|
|
|
159
162
|
delayRenderTimeoutInMilliseconds: z.ZodNumber;
|
|
160
163
|
logLevel: z.ZodEnum<["verbose", "info", "warn", "error"]>;
|
|
161
164
|
}, "strip", z.ZodTypeAny, {
|
|
162
|
-
serveUrl: string;
|
|
163
165
|
type: "still";
|
|
166
|
+
serveUrl: string;
|
|
164
167
|
composition: string;
|
|
165
168
|
serializedInputPropsWithCustomSchema: string;
|
|
166
169
|
imageFormat: "png" | "jpeg" | "pdf" | "webp";
|
|
@@ -183,8 +186,8 @@ export declare const CloudRunPayload: z.ZodDiscriminatedUnion<"type", [z.ZodObje
|
|
|
183
186
|
} | undefined;
|
|
184
187
|
outName?: string | undefined;
|
|
185
188
|
}, {
|
|
186
|
-
serveUrl: string;
|
|
187
189
|
type: "still";
|
|
190
|
+
serveUrl: string;
|
|
188
191
|
composition: string;
|
|
189
192
|
serializedInputPropsWithCustomSchema: string;
|
|
190
193
|
imageFormat: "png" | "jpeg" | "pdf" | "webp";
|
|
@@ -208,20 +211,23 @@ export declare const CloudRunPayload: z.ZodDiscriminatedUnion<"type", [z.ZodObje
|
|
|
208
211
|
outName?: string | undefined;
|
|
209
212
|
}>]>;
|
|
210
213
|
declare const renderFailResponsePayload: z.ZodObject<{
|
|
211
|
-
|
|
212
|
-
|
|
214
|
+
type: z.ZodLiteral<"error">;
|
|
215
|
+
message: z.ZodString;
|
|
216
|
+
name: z.ZodString;
|
|
213
217
|
stack: z.ZodString;
|
|
214
218
|
}, "strip", z.ZodTypeAny, {
|
|
215
|
-
|
|
216
|
-
|
|
219
|
+
type: "error";
|
|
220
|
+
message: string;
|
|
221
|
+
name: string;
|
|
217
222
|
stack: string;
|
|
218
223
|
}, {
|
|
219
|
-
|
|
220
|
-
|
|
224
|
+
type: "error";
|
|
225
|
+
message: string;
|
|
226
|
+
name: string;
|
|
221
227
|
stack: string;
|
|
222
228
|
}>;
|
|
223
229
|
declare const renderStillOnCloudrunResponsePayload: z.ZodObject<{
|
|
224
|
-
|
|
230
|
+
type: z.ZodLiteral<"success">;
|
|
225
231
|
publicUrl: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
226
232
|
cloudStorageUri: z.ZodString;
|
|
227
233
|
size: z.ZodNumber;
|
|
@@ -229,24 +235,24 @@ declare const renderStillOnCloudrunResponsePayload: z.ZodObject<{
|
|
|
229
235
|
renderId: z.ZodString;
|
|
230
236
|
privacy: z.ZodEnum<["public-read", "project-private"]>;
|
|
231
237
|
}, "strip", z.ZodTypeAny, {
|
|
232
|
-
|
|
233
|
-
size: number;
|
|
234
|
-
status: "success";
|
|
238
|
+
type: "success";
|
|
235
239
|
privacy: "public-read" | "project-private";
|
|
236
240
|
cloudStorageUri: string;
|
|
241
|
+
size: number;
|
|
242
|
+
bucketName: string;
|
|
237
243
|
renderId: string;
|
|
238
244
|
publicUrl?: string | null | undefined;
|
|
239
245
|
}, {
|
|
240
|
-
|
|
241
|
-
size: number;
|
|
242
|
-
status: "success";
|
|
246
|
+
type: "success";
|
|
243
247
|
privacy: "public-read" | "project-private";
|
|
244
248
|
cloudStorageUri: string;
|
|
249
|
+
size: number;
|
|
250
|
+
bucketName: string;
|
|
245
251
|
renderId: string;
|
|
246
252
|
publicUrl?: string | null | undefined;
|
|
247
253
|
}>;
|
|
248
254
|
declare const renderMediaOnCloudrunResponsePayload: z.ZodObject<{
|
|
249
|
-
|
|
255
|
+
type: z.ZodLiteral<"success">;
|
|
250
256
|
publicUrl: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
251
257
|
cloudStorageUri: z.ZodString;
|
|
252
258
|
size: z.ZodNumber;
|
|
@@ -254,39 +260,39 @@ declare const renderMediaOnCloudrunResponsePayload: z.ZodObject<{
|
|
|
254
260
|
renderId: z.ZodString;
|
|
255
261
|
privacy: z.ZodEnum<["public-read", "project-private"]>;
|
|
256
262
|
}, "strip", z.ZodTypeAny, {
|
|
257
|
-
|
|
258
|
-
size: number;
|
|
259
|
-
status: "success";
|
|
263
|
+
type: "success";
|
|
260
264
|
privacy: "public-read" | "project-private";
|
|
261
265
|
cloudStorageUri: string;
|
|
266
|
+
size: number;
|
|
267
|
+
bucketName: string;
|
|
262
268
|
renderId: string;
|
|
263
269
|
publicUrl?: string | null | undefined;
|
|
264
270
|
}, {
|
|
265
|
-
|
|
266
|
-
size: number;
|
|
267
|
-
status: "success";
|
|
271
|
+
type: "success";
|
|
268
272
|
privacy: "public-read" | "project-private";
|
|
269
273
|
cloudStorageUri: string;
|
|
274
|
+
size: number;
|
|
275
|
+
bucketName: string;
|
|
270
276
|
renderId: string;
|
|
271
277
|
publicUrl?: string | null | undefined;
|
|
272
278
|
}>;
|
|
273
279
|
declare const cloudRunCrashResponse: z.ZodObject<{
|
|
274
|
-
|
|
280
|
+
type: z.ZodLiteral<"crash">;
|
|
275
281
|
cloudRunEndpoint: z.ZodString;
|
|
276
282
|
message: z.ZodLiteral<"Service crashed without sending a response. Check the logs in GCP console.">;
|
|
277
283
|
requestStartTime: z.ZodString;
|
|
278
284
|
requestCrashTime: z.ZodString;
|
|
279
285
|
requestElapsedTimeInSeconds: z.ZodNumber;
|
|
280
286
|
}, "strip", z.ZodTypeAny, {
|
|
287
|
+
type: "crash";
|
|
281
288
|
message: "Service crashed without sending a response. Check the logs in GCP console.";
|
|
282
|
-
status: "crash";
|
|
283
289
|
cloudRunEndpoint: string;
|
|
284
290
|
requestStartTime: string;
|
|
285
291
|
requestCrashTime: string;
|
|
286
292
|
requestElapsedTimeInSeconds: number;
|
|
287
293
|
}, {
|
|
294
|
+
type: "crash";
|
|
288
295
|
message: "Service crashed without sending a response. Check the logs in GCP console.";
|
|
289
|
-
status: "crash";
|
|
290
296
|
cloudRunEndpoint: string;
|
|
291
297
|
requestStartTime: string;
|
|
292
298
|
requestCrashTime: string;
|
|
@@ -10,6 +10,7 @@ const pixelFormat = zod_1.z.enum(renderer_1.RenderInternals.validPixelFormats);
|
|
|
10
10
|
const videoImageFormat = zod_1.z.enum(renderer_1.RenderInternals.validVideoImageFormats);
|
|
11
11
|
const stillImageFormat = zod_1.z.enum(renderer_1.RenderInternals.validStillImageFormats);
|
|
12
12
|
const proResProfile = zod_1.z.enum(client_1.BrowserSafeApis.proResProfileOptions).nullable();
|
|
13
|
+
const x264Preset = zod_1.z.enum(client_1.BrowserSafeApis.x264PresetOptions).nullable();
|
|
13
14
|
const chromiumOptions = zod_1.z.object({
|
|
14
15
|
ignoreCertificateErrors: zod_1.z.boolean().optional(),
|
|
15
16
|
disableWebSecurity: zod_1.z.boolean().optional(),
|
|
@@ -36,6 +37,7 @@ exports.CloudRunPayload = zod_1.z.discriminatedUnion('type', [
|
|
|
36
37
|
imageFormat: videoImageFormat,
|
|
37
38
|
scale: zod_1.z.number(),
|
|
38
39
|
proResProfile,
|
|
40
|
+
x264Preset,
|
|
39
41
|
everyNthFrame: zod_1.z.number(),
|
|
40
42
|
numberOfGifLoops: zod_1.z.number().nullable(),
|
|
41
43
|
frameRange: zod_1.z.tuple([zod_1.z.number(), zod_1.z.number()]).or(zod_1.z.number()).nullable(),
|
|
@@ -72,12 +74,13 @@ exports.CloudRunPayload = zod_1.z.discriminatedUnion('type', [
|
|
|
72
74
|
}),
|
|
73
75
|
]);
|
|
74
76
|
const renderFailResponsePayload = zod_1.z.object({
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
type: zod_1.z.literal('error'),
|
|
78
|
+
message: zod_1.z.string(),
|
|
79
|
+
name: zod_1.z.string(),
|
|
77
80
|
stack: zod_1.z.string(),
|
|
78
81
|
});
|
|
79
82
|
const renderStillOnCloudrunResponsePayload = zod_1.z.object({
|
|
80
|
-
|
|
83
|
+
type: zod_1.z.literal('success'),
|
|
81
84
|
publicUrl: zod_1.z.string().optional().nullable(),
|
|
82
85
|
cloudStorageUri: zod_1.z.string(),
|
|
83
86
|
size: zod_1.z.number(),
|
|
@@ -86,7 +89,7 @@ const renderStillOnCloudrunResponsePayload = zod_1.z.object({
|
|
|
86
89
|
privacy: zod_1.z.enum(['public-read', 'project-private']),
|
|
87
90
|
});
|
|
88
91
|
const renderMediaOnCloudrunResponsePayload = zod_1.z.object({
|
|
89
|
-
|
|
92
|
+
type: zod_1.z.literal('success'),
|
|
90
93
|
publicUrl: zod_1.z.string().optional().nullable(),
|
|
91
94
|
cloudStorageUri: zod_1.z.string(),
|
|
92
95
|
size: zod_1.z.number(),
|
|
@@ -95,7 +98,7 @@ const renderMediaOnCloudrunResponsePayload = zod_1.z.object({
|
|
|
95
98
|
privacy: zod_1.z.enum(['public-read', 'project-private']),
|
|
96
99
|
});
|
|
97
100
|
const cloudRunCrashResponse = zod_1.z.object({
|
|
98
|
-
|
|
101
|
+
type: zod_1.z.literal('crash'),
|
|
99
102
|
cloudRunEndpoint: zod_1.z.string(),
|
|
100
103
|
message: zod_1.z.literal('Service crashed without sending a response. Check the logs in GCP console.'),
|
|
101
104
|
requestStartTime: zod_1.z.string().datetime(),
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type CloudrunErrorInfo = {
|
|
2
|
+
type: 'renderer' | 'browser' | 'stitcher' | 'webhook';
|
|
3
|
+
message: string;
|
|
4
|
+
name: string;
|
|
5
|
+
stack: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const writeCloudrunError: ({ bucketName, renderId, errorInfo, publicUpload, }: {
|
|
8
|
+
bucketName: string;
|
|
9
|
+
renderId: string;
|
|
10
|
+
errorInfo: CloudrunErrorInfo;
|
|
11
|
+
publicUpload: boolean;
|
|
12
|
+
}) => Promise<void>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.writeCloudrunError = void 0;
|
|
7
|
+
const storage_1 = require("@google-cloud/storage");
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const writeCloudrunError = async ({ bucketName, renderId, errorInfo, publicUpload, }) => {
|
|
10
|
+
const storage = new storage_1.Storage();
|
|
11
|
+
const tempFilePath = '/tmp/errorInfo.txt';
|
|
12
|
+
node_fs_1.default.writeFileSync(tempFilePath, JSON.stringify(errorInfo));
|
|
13
|
+
await storage.bucket(bucketName).upload(tempFilePath, {
|
|
14
|
+
destination: `renders/${renderId}/error.txt`,
|
|
15
|
+
predefinedAcl: publicUpload ? 'publicRead' : 'projectPrivate',
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
exports.writeCloudrunError = writeCloudrunError;
|
package/dist/functions/index.js
CHANGED
|
@@ -2,14 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.renderOnCloudRun = void 0;
|
|
4
4
|
const renderer_1 = require("@remotion/renderer");
|
|
5
|
-
const log_1 = require("../cli/log");
|
|
6
5
|
const payloads_1 = require("./helpers/payloads");
|
|
7
6
|
const render_media_single_thread_1 = require("./render-media-single-thread");
|
|
8
7
|
const render_still_single_thread_1 = require("./render-still-single-thread");
|
|
9
8
|
const renderOnCloudRun = async (req, res) => {
|
|
10
|
-
var _a;
|
|
11
9
|
try {
|
|
12
|
-
log_1.Log.info('renderOnCloudRun', req.body);
|
|
13
10
|
const body = payloads_1.CloudRunPayload.parse(req.body);
|
|
14
11
|
const renderType = body.type;
|
|
15
12
|
renderer_1.RenderInternals.setLogLevel(body.logLevel);
|
|
@@ -27,13 +24,14 @@ const renderOnCloudRun = async (req, res) => {
|
|
|
27
24
|
}
|
|
28
25
|
}
|
|
29
26
|
catch (err) {
|
|
30
|
-
log_1.Log.error('Error while rendering', err);
|
|
31
27
|
const response = {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
type: 'error',
|
|
29
|
+
message: err.message,
|
|
30
|
+
name: err.name,
|
|
31
|
+
stack: err.stack,
|
|
35
32
|
};
|
|
36
|
-
res.
|
|
33
|
+
res.write(JSON.stringify(response));
|
|
34
|
+
res.end();
|
|
37
35
|
}
|
|
38
36
|
};
|
|
39
37
|
exports.renderOnCloudRun = renderOnCloudRun;
|
|
@@ -6,98 +6,116 @@ const renderer_1 = require("@remotion/renderer");
|
|
|
6
6
|
const remotion_1 = require("remotion");
|
|
7
7
|
const random_hash_1 = require("../shared/random-hash");
|
|
8
8
|
const get_composition_from_body_1 = require("./helpers/get-composition-from-body");
|
|
9
|
+
const write_cloudrun_error_1 = require("./helpers/write-cloudrun-error");
|
|
9
10
|
const renderMediaSingleThread = async (body, res) => {
|
|
10
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
11
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
11
12
|
if (body.type !== 'media') {
|
|
12
13
|
throw new Error('expected type media');
|
|
13
14
|
}
|
|
14
|
-
const composition = await (0, get_composition_from_body_1.getCompositionFromBody)(body);
|
|
15
|
-
const defaultOutName = `out.${renderer_1.RenderInternals.getFileExtensionFromCodec(body.codec, body.audioCodec)}`;
|
|
16
|
-
const tempFilePath = `/tmp/${defaultOutName}`;
|
|
17
15
|
const renderId = (0, random_hash_1.randomHash)({ randomInTests: true });
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
16
|
+
try {
|
|
17
|
+
const composition = await (0, get_composition_from_body_1.getCompositionFromBody)(body);
|
|
18
|
+
const defaultOutName = `out.${renderer_1.RenderInternals.getFileExtensionFromCodec(body.codec, body.audioCodec)}`;
|
|
19
|
+
const tempFilePath = `/tmp/${defaultOutName}`;
|
|
20
|
+
let previousProgress = 2;
|
|
21
|
+
const onProgress = ({ progress }) => {
|
|
22
|
+
if (previousProgress !== progress) {
|
|
23
|
+
res.write(JSON.stringify({ onProgress: progress }) + '\n');
|
|
24
|
+
previousProgress = progress;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
28
|
+
const actualChromiumOptions = {
|
|
29
|
+
...body.chromiumOptions,
|
|
30
|
+
// Override the `null` value, which might come from CLI with swANGLE
|
|
31
|
+
gl: (_b = (_a = body.chromiumOptions) === null || _a === void 0 ? void 0 : _a.gl) !== null && _b !== void 0 ? _b : 'swangle',
|
|
32
|
+
};
|
|
33
|
+
await renderer_1.RenderInternals.internalRenderMedia({
|
|
34
|
+
composition: {
|
|
35
|
+
...composition,
|
|
36
|
+
height: (_c = body.forceHeight) !== null && _c !== void 0 ? _c : composition.height,
|
|
37
|
+
width: (_d = body.forceWidth) !== null && _d !== void 0 ? _d : composition.width,
|
|
38
|
+
},
|
|
39
|
+
serveUrl: body.serveUrl,
|
|
40
|
+
codec: body.codec,
|
|
41
|
+
outputLocation: tempFilePath,
|
|
42
|
+
serializedInputPropsWithCustomSchema: body.serializedInputPropsWithCustomSchema,
|
|
43
|
+
serializedResolvedPropsWithCustomSchema: remotion_1.Internals.serializeJSONWithDate({
|
|
44
|
+
data: composition.props,
|
|
45
|
+
indent: undefined,
|
|
46
|
+
staticBase: null,
|
|
47
|
+
}).serializedString,
|
|
48
|
+
jpegQuality: body.jpegQuality,
|
|
49
|
+
audioCodec: body.audioCodec,
|
|
50
|
+
audioBitrate: body.audioBitrate,
|
|
51
|
+
videoBitrate: body.videoBitrate,
|
|
52
|
+
crf: body.crf,
|
|
53
|
+
pixelFormat: body.pixelFormat,
|
|
54
|
+
imageFormat: body.imageFormat,
|
|
55
|
+
scale: body.scale,
|
|
56
|
+
proResProfile: (_e = body.proResProfile) !== null && _e !== void 0 ? _e : undefined,
|
|
57
|
+
x264Preset: (_f = body.x264Preset) !== null && _f !== void 0 ? _f : undefined,
|
|
58
|
+
everyNthFrame: body.everyNthFrame,
|
|
59
|
+
numberOfGifLoops: body.numberOfGifLoops,
|
|
60
|
+
onProgress,
|
|
61
|
+
frameRange: body.frameRange,
|
|
62
|
+
envVariables: body.envVariables,
|
|
63
|
+
chromiumOptions: actualChromiumOptions,
|
|
64
|
+
muted: body.muted,
|
|
65
|
+
logLevel: body.logLevel,
|
|
66
|
+
browserExecutable: null,
|
|
67
|
+
timeoutInMilliseconds: body.delayRenderTimeoutInMilliseconds,
|
|
68
|
+
cancelSignal: undefined,
|
|
69
|
+
concurrency: (_g = body.concurrency) !== null && _g !== void 0 ? _g : '100%',
|
|
70
|
+
disallowParallelEncoding: false,
|
|
71
|
+
enforceAudioTrack: body.enforceAudioTrack,
|
|
72
|
+
ffmpegOverride: undefined,
|
|
73
|
+
indent: false,
|
|
74
|
+
onBrowserLog: null,
|
|
75
|
+
onCtrlCExit: () => undefined,
|
|
76
|
+
onDownload: () => undefined,
|
|
77
|
+
onStart: () => undefined,
|
|
78
|
+
overwrite: true,
|
|
79
|
+
port: null,
|
|
80
|
+
preferLossless: body.preferLossless,
|
|
81
|
+
puppeteerInstance: undefined,
|
|
82
|
+
server: undefined,
|
|
83
|
+
});
|
|
84
|
+
const storage = new storage_1.Storage();
|
|
85
|
+
const publicUpload = body.privacy === 'public' || !body.privacy;
|
|
86
|
+
const uploadedResponse = await storage
|
|
87
|
+
.bucket(body.outputBucket)
|
|
88
|
+
.upload(tempFilePath, {
|
|
89
|
+
destination: `renders/${renderId}/${(_h = body.outName) !== null && _h !== void 0 ? _h : defaultOutName}`,
|
|
90
|
+
predefinedAcl: publicUpload ? 'publicRead' : 'projectPrivate',
|
|
91
|
+
});
|
|
92
|
+
const uploadedFile = uploadedResponse[0];
|
|
93
|
+
const renderMetadata = await uploadedFile.getMetadata();
|
|
94
|
+
const responseData = {
|
|
95
|
+
type: 'success',
|
|
96
|
+
publicUrl: publicUpload ? uploadedFile.publicUrl() : null,
|
|
97
|
+
cloudStorageUri: uploadedFile.cloudStorageURI.href,
|
|
98
|
+
size: renderMetadata[0].size,
|
|
99
|
+
bucketName: body.outputBucket,
|
|
100
|
+
renderId,
|
|
101
|
+
privacy: publicUpload ? 'public-read' : 'project-private',
|
|
102
|
+
};
|
|
103
|
+
renderer_1.RenderInternals.Log.info('Render Completed:', responseData);
|
|
104
|
+
res.end(JSON.stringify({ response: responseData }));
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
await (0, write_cloudrun_error_1.writeCloudrunError)({
|
|
108
|
+
bucketName: body.outputBucket,
|
|
109
|
+
renderId,
|
|
110
|
+
errorInfo: {
|
|
111
|
+
name: err.name,
|
|
112
|
+
message: err.message,
|
|
113
|
+
stack: err.stack,
|
|
114
|
+
type: 'renderer',
|
|
115
|
+
},
|
|
116
|
+
publicUpload: body.privacy === 'public' || !body.privacy,
|
|
117
|
+
});
|
|
118
|
+
throw err;
|
|
119
|
+
}
|
|
102
120
|
};
|
|
103
121
|
exports.renderMediaSingleThread = renderMediaSingleThread;
|
|
@@ -7,76 +7,92 @@ const remotion_1 = require("remotion");
|
|
|
7
7
|
const log_1 = require("../cli/log");
|
|
8
8
|
const random_hash_1 = require("../shared/random-hash");
|
|
9
9
|
const get_composition_from_body_1 = require("./helpers/get-composition-from-body");
|
|
10
|
+
const write_cloudrun_error_1 = require("./helpers/write-cloudrun-error");
|
|
10
11
|
const renderStillSingleThread = async (body, res) => {
|
|
11
12
|
var _a, _b, _c, _d, _e, _f;
|
|
12
13
|
if (body.type !== 'still') {
|
|
13
14
|
throw new Error('expected type still');
|
|
14
15
|
}
|
|
15
|
-
log_1.Log.verbose('Rendering still frame', body);
|
|
16
|
-
const composition = await (0, get_composition_from_body_1.getCompositionFromBody)(body);
|
|
17
|
-
log_1.Log.verbose('Composition loaded', composition);
|
|
18
|
-
const tempFilePath = '/tmp/still.png';
|
|
19
16
|
const renderId = (0, random_hash_1.randomHash)({ randomInTests: true });
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
17
|
+
try {
|
|
18
|
+
log_1.Log.verbose('Rendering still frame', body);
|
|
19
|
+
const composition = await (0, get_composition_from_body_1.getCompositionFromBody)(body);
|
|
20
|
+
log_1.Log.verbose('Composition loaded', composition);
|
|
21
|
+
const tempFilePath = '/tmp/still.png';
|
|
22
|
+
const actualChromiumOptions = {
|
|
23
|
+
...body.chromiumOptions,
|
|
24
|
+
// Override the `null` value, which might come from CLI with swANGLE
|
|
25
|
+
gl: (_b = (_a = body.chromiumOptions) === null || _a === void 0 ? void 0 : _a.gl) !== null && _b !== void 0 ? _b : 'swangle',
|
|
26
|
+
};
|
|
27
|
+
await renderer_1.RenderInternals.internalRenderStill({
|
|
28
|
+
composition: {
|
|
29
|
+
...composition,
|
|
30
|
+
height: (_c = body.forceHeight) !== null && _c !== void 0 ? _c : composition.height,
|
|
31
|
+
width: (_d = body.forceWidth) !== null && _d !== void 0 ? _d : composition.width,
|
|
32
|
+
},
|
|
33
|
+
serveUrl: body.serveUrl,
|
|
34
|
+
output: tempFilePath,
|
|
35
|
+
serializedInputPropsWithCustomSchema: body.serializedInputPropsWithCustomSchema,
|
|
36
|
+
serializedResolvedPropsWithCustomSchema: remotion_1.Internals.serializeJSONWithDate({
|
|
37
|
+
data: composition.props,
|
|
38
|
+
indent: undefined,
|
|
39
|
+
staticBase: null,
|
|
40
|
+
}).serializedString,
|
|
41
|
+
jpegQuality: (_e = body.jpegQuality) !== null && _e !== void 0 ? _e : renderer_1.RenderInternals.DEFAULT_JPEG_QUALITY,
|
|
42
|
+
imageFormat: body.imageFormat,
|
|
43
|
+
scale: body.scale,
|
|
44
|
+
envVariables: body.envVariables,
|
|
45
|
+
chromiumOptions: actualChromiumOptions,
|
|
46
|
+
frame: body.frame,
|
|
47
|
+
logLevel: body.logLevel,
|
|
48
|
+
browserExecutable: null,
|
|
49
|
+
cancelSignal: null,
|
|
50
|
+
indent: false,
|
|
51
|
+
timeoutInMilliseconds: body.delayRenderTimeoutInMilliseconds,
|
|
52
|
+
onBrowserLog: null,
|
|
53
|
+
onDownload: null,
|
|
54
|
+
overwrite: true,
|
|
55
|
+
port: null,
|
|
56
|
+
puppeteerInstance: null,
|
|
57
|
+
server: undefined,
|
|
58
|
+
});
|
|
59
|
+
log_1.Log.info('Still rendered');
|
|
60
|
+
const storage = new storage_1.Storage();
|
|
61
|
+
const publicUpload = body.privacy === 'public' || !body.privacy;
|
|
62
|
+
const uploadedResponse = await storage
|
|
63
|
+
.bucket(body.outputBucket)
|
|
64
|
+
.upload(tempFilePath, {
|
|
65
|
+
destination: `renders/${renderId}/${(_f = body.outName) !== null && _f !== void 0 ? _f : 'out.png'}`,
|
|
66
|
+
predefinedAcl: publicUpload ? 'publicRead' : 'projectPrivate',
|
|
67
|
+
});
|
|
68
|
+
log_1.Log.info('Still uploaded');
|
|
69
|
+
const uploadedFile = uploadedResponse[0];
|
|
70
|
+
const renderMetadata = await uploadedFile.getMetadata();
|
|
71
|
+
const responseData = {
|
|
72
|
+
publicUrl: uploadedFile.publicUrl(),
|
|
73
|
+
cloudStorageUri: uploadedFile.cloudStorageURI.href,
|
|
74
|
+
size: renderMetadata[0].size,
|
|
75
|
+
bucketName: body.outputBucket,
|
|
76
|
+
renderId,
|
|
77
|
+
type: 'success',
|
|
78
|
+
privacy: publicUpload ? 'public-read' : 'project-private',
|
|
79
|
+
};
|
|
80
|
+
renderer_1.RenderInternals.Log.info('Render Completed:', responseData);
|
|
81
|
+
res.end(JSON.stringify({ response: responseData }));
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
await (0, write_cloudrun_error_1.writeCloudrunError)({
|
|
85
|
+
bucketName: body.outputBucket,
|
|
86
|
+
renderId,
|
|
87
|
+
errorInfo: {
|
|
88
|
+
name: err.name,
|
|
89
|
+
message: err.message,
|
|
90
|
+
stack: err.stack,
|
|
91
|
+
type: 'renderer',
|
|
92
|
+
},
|
|
93
|
+
publicUpload: body.privacy === 'public' || !body.privacy,
|
|
94
|
+
});
|
|
95
|
+
throw err;
|
|
96
|
+
}
|
|
81
97
|
};
|
|
82
98
|
exports.renderStillSingleThread = renderStillSingleThread;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remotion/cloudrun",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.22",
|
|
4
4
|
"description": "GCP Cloud Run alternative to lambda rendering",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"dependencies": {
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
"@google-cloud/logging": "^10.5.0",
|
|
13
13
|
"google-auth-library": "^8.7.0",
|
|
14
14
|
"zod": "^3.21.4",
|
|
15
|
-
"@remotion/bundler": "4.0.
|
|
16
|
-
"@remotion/cli": "4.0.
|
|
17
|
-
"
|
|
18
|
-
"remotion": "4.0.
|
|
15
|
+
"@remotion/bundler": "4.0.22",
|
|
16
|
+
"@remotion/cli": "4.0.22",
|
|
17
|
+
"remotion": "4.0.22",
|
|
18
|
+
"@remotion/renderer": "4.0.22"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@jonny/eslint-config": "3.0.266",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"prettier-plugin-organize-imports": "^3.2.2",
|
|
27
27
|
"ts-node": "^10.8.0",
|
|
28
28
|
"vitest": "0.24.3",
|
|
29
|
-
"@remotion/compositor-linux-x64-gnu": "4.0.
|
|
29
|
+
"@remotion/compositor-linux-x64-gnu": "4.0.22"
|
|
30
30
|
},
|
|
31
31
|
"exports": {
|
|
32
32
|
"./package.json": "./package.json",
|