@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.
@@ -1,13 +1,13 @@
1
1
 
2
- > @remotion/cloudrun@4.0.20 build /Users/jonathanburger/remotion/packages/cloudrun
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.20 buildContainer /Users/jonathanburger/remotion/packages/cloudrun
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.20 tarInstaller /Users/jonathanburger/remotion/packages/cloudrun
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
- const chunkResponse = JSON.parse(chunk.toString().trim());
112
- if (chunkResponse.response) {
113
- response = chunkResponse.response;
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
- else if (chunkResponse.onProgress) {
116
- updateRenderProgress === null || updateRenderProgress === void 0 ? void 0 : updateRenderProgress(chunkResponse.onProgress);
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
- status: 'crash',
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.status !== 'success' && response.status !== 'crash') {
134
- throw new Error(response.stack);
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 renderResponse = await client.request({
69
+ const postResponse = await client.request({
70
70
  url: cloudRunUrl,
71
71
  method: 'POST',
72
72
  data,
73
+ responseType: 'stream',
73
74
  });
74
- return renderResponse.data;
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.status === 'crash') {
156
- let timeoutPreMsg = '';
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.status === 'success') {
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
- doneIn = Date.now() - renderStart;
116
- updateProgress(true);
117
- log_1.Log.info(cli_1.CliInternals.chalk.gray(`Cloud Storage Uri = ${res.cloudStorageUri}`));
118
- log_1.Log.info(cli_1.CliInternals.chalk.gray(`Render ID = ${res.renderId}`));
119
- log_1.Log.info(cli_1.CliInternals.chalk.gray(`${Math.round(Number(res.size) / 1000)} KB, Privacy: ${res.privacy}, Bucket: ${res.bucketName}`));
120
- log_1.Log.info(cli_1.CliInternals.chalk.blue(`○ ${res.publicUrl}`));
121
- if (downloadName) {
122
- log_1.Log.info('');
123
- log_1.Log.info('downloading file...');
124
- const { outputPath: destination } = await (0, download_file_1.downloadFile)({
125
- bucketName: res.bucketName,
126
- gsutilURI: res.cloudStorageUri,
127
- downloadName,
128
- });
129
- log_1.Log.info(cli_1.CliInternals.chalk.blueBright(`Downloaded file to ${destination}!`));
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,2 @@
1
+ import type { CloudRunCrashResponse } from '../../functions/helpers/payloads';
2
+ export declare const displayCrashLogs: (res: CloudRunCrashResponse) => Promise<void>;
@@ -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
- // todo: catch errors and print a message. Check lambda cli for example
52
- log_1.Log.error(error.stack);
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
- status: z.ZodLiteral<"error">;
212
- error: z.ZodString;
214
+ type: z.ZodLiteral<"error">;
215
+ message: z.ZodString;
216
+ name: z.ZodString;
213
217
  stack: z.ZodString;
214
218
  }, "strip", z.ZodTypeAny, {
215
- error: string;
216
- status: "error";
219
+ type: "error";
220
+ message: string;
221
+ name: string;
217
222
  stack: string;
218
223
  }, {
219
- error: string;
220
- status: "error";
224
+ type: "error";
225
+ message: string;
226
+ name: string;
221
227
  stack: string;
222
228
  }>;
223
229
  declare const renderStillOnCloudrunResponsePayload: z.ZodObject<{
224
- status: z.ZodLiteral<"success">;
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
- bucketName: string;
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
- bucketName: string;
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
- status: z.ZodLiteral<"success">;
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
- bucketName: string;
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
- bucketName: string;
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
- status: z.ZodLiteral<"crash">;
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
- status: zod_1.z.literal('error'),
76
- error: zod_1.z.string(),
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
- status: zod_1.z.literal('success'),
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
- status: zod_1.z.literal('success'),
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
- status: zod_1.z.literal('crash'),
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;
@@ -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
- error: err.message,
33
- status: 'error',
34
- stack: (_a = err.stack) !== null && _a !== void 0 ? _a : '',
28
+ type: 'error',
29
+ message: err.message,
30
+ name: err.name,
31
+ stack: err.stack,
35
32
  };
36
- res.status(500).send(JSON.stringify(response));
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
- let previousProgress = 2;
19
- const onProgress = ({ progress }) => {
20
- if (previousProgress !== progress) {
21
- res.write(JSON.stringify({ onProgress: progress }) + '\n');
22
- previousProgress = progress;
23
- }
24
- };
25
- res.writeHead(200, { 'Content-Type': 'text/html' });
26
- const actualChromiumOptions = {
27
- ...body.chromiumOptions,
28
- // Override the `null` value, which might come from CLI with swANGLE
29
- gl: (_b = (_a = body.chromiumOptions) === null || _a === void 0 ? void 0 : _a.gl) !== null && _b !== void 0 ? _b : 'swangle',
30
- };
31
- await renderer_1.RenderInternals.internalRenderMedia({
32
- composition: {
33
- ...composition,
34
- height: (_c = body.forceHeight) !== null && _c !== void 0 ? _c : composition.height,
35
- width: (_d = body.forceWidth) !== null && _d !== void 0 ? _d : composition.width,
36
- },
37
- serveUrl: body.serveUrl,
38
- codec: body.codec,
39
- outputLocation: tempFilePath,
40
- serializedInputPropsWithCustomSchema: body.serializedInputPropsWithCustomSchema,
41
- serializedResolvedPropsWithCustomSchema: remotion_1.Internals.serializeJSONWithDate({
42
- data: composition.props,
43
- indent: undefined,
44
- staticBase: null,
45
- }).serializedString,
46
- jpegQuality: body.jpegQuality,
47
- audioCodec: body.audioCodec,
48
- audioBitrate: body.audioBitrate,
49
- videoBitrate: body.videoBitrate,
50
- crf: body.crf,
51
- pixelFormat: body.pixelFormat,
52
- imageFormat: body.imageFormat,
53
- scale: body.scale,
54
- proResProfile: (_e = body.proResProfile) !== null && _e !== void 0 ? _e : undefined,
55
- everyNthFrame: body.everyNthFrame,
56
- numberOfGifLoops: body.numberOfGifLoops,
57
- onProgress,
58
- frameRange: body.frameRange,
59
- envVariables: body.envVariables,
60
- chromiumOptions: actualChromiumOptions,
61
- muted: body.muted,
62
- logLevel: body.logLevel,
63
- browserExecutable: null,
64
- timeoutInMilliseconds: body.delayRenderTimeoutInMilliseconds,
65
- cancelSignal: undefined,
66
- concurrency: (_f = body.concurrency) !== null && _f !== void 0 ? _f : '100%',
67
- disallowParallelEncoding: false,
68
- enforceAudioTrack: body.enforceAudioTrack,
69
- ffmpegOverride: undefined,
70
- indent: false,
71
- onBrowserLog: null,
72
- onCtrlCExit: () => undefined,
73
- onDownload: () => undefined,
74
- onStart: () => undefined,
75
- overwrite: true,
76
- port: null,
77
- preferLossless: body.preferLossless,
78
- puppeteerInstance: undefined,
79
- server: undefined,
80
- });
81
- const storage = new storage_1.Storage();
82
- const publicUpload = body.privacy === 'public' || !body.privacy;
83
- const uploadedResponse = await storage
84
- .bucket(body.outputBucket)
85
- .upload(tempFilePath, {
86
- destination: `renders/${renderId}/${(_g = body.outName) !== null && _g !== void 0 ? _g : defaultOutName}`,
87
- predefinedAcl: publicUpload ? 'publicRead' : 'projectPrivate',
88
- });
89
- const uploadedFile = uploadedResponse[0];
90
- const renderMetadata = await uploadedFile.getMetadata();
91
- const responseData = {
92
- status: 'success',
93
- publicUrl: publicUpload ? uploadedFile.publicUrl() : null,
94
- cloudStorageUri: uploadedFile.cloudStorageURI.href,
95
- size: renderMetadata[0].size,
96
- bucketName: body.outputBucket,
97
- renderId,
98
- privacy: publicUpload ? 'public-read' : 'project-private',
99
- };
100
- renderer_1.RenderInternals.Log.info('Render Completed:', responseData);
101
- res.end(JSON.stringify({ response: responseData }));
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
- const actualChromiumOptions = {
21
- ...body.chromiumOptions,
22
- // Override the `null` value, which might come from CLI with swANGLE
23
- gl: (_b = (_a = body.chromiumOptions) === null || _a === void 0 ? void 0 : _a.gl) !== null && _b !== void 0 ? _b : 'swangle',
24
- };
25
- await renderer_1.RenderInternals.internalRenderStill({
26
- composition: {
27
- ...composition,
28
- height: (_c = body.forceHeight) !== null && _c !== void 0 ? _c : composition.height,
29
- width: (_d = body.forceWidth) !== null && _d !== void 0 ? _d : composition.width,
30
- },
31
- serveUrl: body.serveUrl,
32
- output: tempFilePath,
33
- serializedInputPropsWithCustomSchema: body.serializedInputPropsWithCustomSchema,
34
- serializedResolvedPropsWithCustomSchema: remotion_1.Internals.serializeJSONWithDate({
35
- data: composition.props,
36
- indent: undefined,
37
- staticBase: null,
38
- }).serializedString,
39
- jpegQuality: (_e = body.jpegQuality) !== null && _e !== void 0 ? _e : renderer_1.RenderInternals.DEFAULT_JPEG_QUALITY,
40
- imageFormat: body.imageFormat,
41
- scale: body.scale,
42
- envVariables: body.envVariables,
43
- chromiumOptions: actualChromiumOptions,
44
- frame: body.frame,
45
- logLevel: body.logLevel,
46
- browserExecutable: null,
47
- cancelSignal: null,
48
- indent: false,
49
- timeoutInMilliseconds: body.delayRenderTimeoutInMilliseconds,
50
- onBrowserLog: null,
51
- onDownload: null,
52
- overwrite: true,
53
- port: null,
54
- puppeteerInstance: null,
55
- server: undefined,
56
- });
57
- log_1.Log.info('Still rendered');
58
- const storage = new storage_1.Storage();
59
- const publicUpload = body.privacy === 'public' || !body.privacy;
60
- const uploadedResponse = await storage
61
- .bucket(body.outputBucket)
62
- .upload(tempFilePath, {
63
- destination: `renders/${renderId}/${(_f = body.outName) !== null && _f !== void 0 ? _f : 'out.png'}`,
64
- predefinedAcl: publicUpload ? 'publicRead' : 'projectPrivate',
65
- });
66
- log_1.Log.info('Still uploaded');
67
- const uploadedFile = uploadedResponse[0];
68
- const renderMetadata = await uploadedFile.getMetadata();
69
- const responseData = {
70
- publicUrl: uploadedFile.publicUrl(),
71
- cloudStorageUri: uploadedFile.cloudStorageURI.href,
72
- size: renderMetadata[0].size,
73
- bucketName: body.outputBucket,
74
- renderId,
75
- status: 'success',
76
- privacy: publicUpload ? 'public-read' : 'project-private',
77
- };
78
- renderer_1.RenderInternals.Log.info('Render Completed:', responseData);
79
- const jsonContent = JSON.stringify(responseData);
80
- res.end(jsonContent);
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.21",
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.21",
16
- "@remotion/cli": "4.0.21",
17
- "@remotion/renderer": "4.0.21",
18
- "remotion": "4.0.21"
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.21"
29
+ "@remotion/compositor-linux-x64-gnu": "4.0.22"
30
30
  },
31
31
  "exports": {
32
32
  "./package.json": "./package.json",