@remotion/lambda 3.2.29 → 3.2.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/render-media-on-lambda.d.ts +7 -2
- package/dist/api/render-media-on-lambda.js +3 -1
- package/dist/api/validate-webhook-signature.d.ts +13 -0
- package/dist/api/validate-webhook-signature.js +32 -0
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/commands/render/render.js +7 -1
- package/dist/cli/helpers/determine-image-format.d.ts +10 -0
- package/dist/cli/helpers/determine-image-format.js +47 -0
- package/dist/cli/helpers/webhook-types.d.ts +6 -0
- package/dist/cli/helpers/webhook-types.js +2 -0
- package/dist/client.d.ts +4 -2
- package/dist/client.js +3 -1
- package/dist/functions/chunk-optimization/plan-frame-ranges.d.ts +1 -4
- package/dist/functions/helpers/write-lambda-error.d.ts +1 -1
- package/dist/functions/launch.js +136 -1
- package/dist/functions/renderer.js +7 -0
- package/dist/functions/start.js +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -1
- package/dist/shared/constants.d.ts +7 -0
- package/dist/shared/invoke-webhook.d.ts +44 -0
- package/dist/shared/invoke-webhook.js +89 -0
- package/package.json +6 -6
- package/remotionlambda.zip +0 -0
|
@@ -31,6 +31,10 @@ export declare type RenderMediaOnLambdaInput = {
|
|
|
31
31
|
downloadBehavior?: DownloadBehavior | null;
|
|
32
32
|
muted?: boolean;
|
|
33
33
|
overwrite?: boolean;
|
|
34
|
+
webhook?: {
|
|
35
|
+
url: string;
|
|
36
|
+
secret: string | null;
|
|
37
|
+
};
|
|
34
38
|
};
|
|
35
39
|
export declare type RenderMediaOnLambdaOutput = {
|
|
36
40
|
renderId: string;
|
|
@@ -53,10 +57,11 @@ export declare type RenderMediaOnLambdaOutput = {
|
|
|
53
57
|
* @param params.region The AWS region in which the media should be rendered.
|
|
54
58
|
* @param params.maxRetries How often rendering a chunk may fail before the media render gets aborted. Default "1"
|
|
55
59
|
* @param params.logLevel Level of logging that Lambda function should perform. Default "info".
|
|
60
|
+
* @param params.webhook Configuration for webhook called upon completion or timeout of the render.
|
|
56
61
|
* @returns {Promise<RenderMediaOnLambdaOutput>} See documentation for detailed structure
|
|
57
62
|
*/
|
|
58
|
-
export declare const renderMediaOnLambda: ({ functionName, serveUrl, inputProps, codec, imageFormat, crf, envVariables, pixelFormat, proResProfile, quality, region, maxRetries, composition, framesPerLambda, privacy, logLevel, frameRange, outName, timeoutInMilliseconds, chromiumOptions, scale, numberOfGifLoops, everyNthFrame, concurrencyPerLambda, downloadBehavior, muted, overwrite, }: RenderMediaOnLambdaInput) => Promise<RenderMediaOnLambdaOutput>;
|
|
63
|
+
export declare const renderMediaOnLambda: ({ functionName, serveUrl, inputProps, codec, imageFormat, crf, envVariables, pixelFormat, proResProfile, quality, region, maxRetries, composition, framesPerLambda, privacy, logLevel, frameRange, outName, timeoutInMilliseconds, chromiumOptions, scale, numberOfGifLoops, everyNthFrame, concurrencyPerLambda, downloadBehavior, muted, overwrite, webhook, }: RenderMediaOnLambdaInput) => Promise<RenderMediaOnLambdaOutput>;
|
|
59
64
|
/**
|
|
60
65
|
* @deprecated Renamed to renderMediaOnLambda()
|
|
61
66
|
*/
|
|
62
|
-
export declare const renderVideoOnLambda: ({ functionName, serveUrl, inputProps, codec, imageFormat, crf, envVariables, pixelFormat, proResProfile, quality, region, maxRetries, composition, framesPerLambda, privacy, logLevel, frameRange, outName, timeoutInMilliseconds, chromiumOptions, scale, numberOfGifLoops, everyNthFrame, concurrencyPerLambda, downloadBehavior, muted, overwrite, }: RenderMediaOnLambdaInput) => Promise<RenderMediaOnLambdaOutput>;
|
|
67
|
+
export declare const renderVideoOnLambda: ({ functionName, serveUrl, inputProps, codec, imageFormat, crf, envVariables, pixelFormat, proResProfile, quality, region, maxRetries, composition, framesPerLambda, privacy, logLevel, frameRange, outName, timeoutInMilliseconds, chromiumOptions, scale, numberOfGifLoops, everyNthFrame, concurrencyPerLambda, downloadBehavior, muted, overwrite, webhook, }: RenderMediaOnLambdaInput) => Promise<RenderMediaOnLambdaOutput>;
|
|
@@ -26,9 +26,10 @@ const validate_serveurl_1 = require("../shared/validate-serveurl");
|
|
|
26
26
|
* @param params.region The AWS region in which the media should be rendered.
|
|
27
27
|
* @param params.maxRetries How often rendering a chunk may fail before the media render gets aborted. Default "1"
|
|
28
28
|
* @param params.logLevel Level of logging that Lambda function should perform. Default "info".
|
|
29
|
+
* @param params.webhook Configuration for webhook called upon completion or timeout of the render.
|
|
29
30
|
* @returns {Promise<RenderMediaOnLambdaOutput>} See documentation for detailed structure
|
|
30
31
|
*/
|
|
31
|
-
const renderMediaOnLambda = async ({ functionName, serveUrl, inputProps, codec, imageFormat, crf, envVariables, pixelFormat, proResProfile, quality, region, maxRetries, composition, framesPerLambda, privacy, logLevel, frameRange, outName, timeoutInMilliseconds, chromiumOptions, scale, numberOfGifLoops, everyNthFrame, concurrencyPerLambda, downloadBehavior, muted, overwrite, }) => {
|
|
32
|
+
const renderMediaOnLambda = async ({ functionName, serveUrl, inputProps, codec, imageFormat, crf, envVariables, pixelFormat, proResProfile, quality, region, maxRetries, composition, framesPerLambda, privacy, logLevel, frameRange, outName, timeoutInMilliseconds, chromiumOptions, scale, numberOfGifLoops, everyNthFrame, concurrencyPerLambda, downloadBehavior, muted, overwrite, webhook, }) => {
|
|
32
33
|
var _a;
|
|
33
34
|
const actualCodec = (0, validate_lambda_codec_1.validateLambdaCodec)(codec);
|
|
34
35
|
(0, validate_serveurl_1.validateServeUrl)(serveUrl);
|
|
@@ -69,6 +70,7 @@ const renderMediaOnLambda = async ({ functionName, serveUrl, inputProps, codec,
|
|
|
69
70
|
muted: muted !== null && muted !== void 0 ? muted : false,
|
|
70
71
|
version: version_1.VERSION,
|
|
71
72
|
overwrite: overwrite !== null && overwrite !== void 0 ? overwrite : false,
|
|
73
|
+
webhook: webhook !== null && webhook !== void 0 ? webhook : null,
|
|
72
74
|
},
|
|
73
75
|
region,
|
|
74
76
|
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description Throws if the signature of the finish webhook is missing or inauthentic
|
|
3
|
+
* @link https://remotion.dev/docs/lambda/validate-webhook-signature
|
|
4
|
+
* @param params.secret The secret used for signing the webhook
|
|
5
|
+
* @param params.body The body that was received by the endpoint
|
|
6
|
+
* @param params.signatureHeader The `X-Remotion-Signature` header
|
|
7
|
+
* @returns {void}
|
|
8
|
+
*/
|
|
9
|
+
export declare const validateWebhookSignature: ({ secret, body, signatureHeader, }: {
|
|
10
|
+
secret: string;
|
|
11
|
+
body: string;
|
|
12
|
+
signatureHeader: string;
|
|
13
|
+
}) => void;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateWebhookSignature = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* @description Throws if the signature of the finish webhook is missing or inauthentic
|
|
6
|
+
* @link https://remotion.dev/docs/lambda/validate-webhook-signature
|
|
7
|
+
* @param params.secret The secret used for signing the webhook
|
|
8
|
+
* @param params.body The body that was received by the endpoint
|
|
9
|
+
* @param params.signatureHeader The `X-Remotion-Signature` header
|
|
10
|
+
* @returns {void}
|
|
11
|
+
*/
|
|
12
|
+
const validateWebhookSignature = ({ secret, body, signatureHeader, }) => {
|
|
13
|
+
if (!secret) {
|
|
14
|
+
throw new TypeError("No 'secret' was provided to validateWebhookSignature().");
|
|
15
|
+
}
|
|
16
|
+
if (!body) {
|
|
17
|
+
throw new TypeError("No 'body' was provided to validateWebhookSignature().");
|
|
18
|
+
}
|
|
19
|
+
if (typeof require === 'undefined') {
|
|
20
|
+
throw new Error('validateWebhookSignature can only be called from Node.JS');
|
|
21
|
+
}
|
|
22
|
+
const Crypto = require('crypto');
|
|
23
|
+
const hmac = Crypto.createHmac('sha512', secret);
|
|
24
|
+
const signature = `sha512=${hmac.update(JSON.stringify(body)).digest('hex')}`;
|
|
25
|
+
if (!signatureHeader || signatureHeader === 'NO_SECRET_PROVIDED') {
|
|
26
|
+
throw new Error('No webhook signature was provided');
|
|
27
|
+
}
|
|
28
|
+
if (signatureHeader !== signature) {
|
|
29
|
+
throw new Error('Signatures do not match');
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
exports.validateWebhookSignature = validateWebhookSignature;
|
package/dist/cli/args.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ declare type LambdaCommandLineOptions = {
|
|
|
24
24
|
['architecture']: LambdaArchitecture;
|
|
25
25
|
['custom-role-arn']: string | undefined;
|
|
26
26
|
privacy: Privacy;
|
|
27
|
+
webhook: string | undefined;
|
|
28
|
+
['webhook-secret']: string | undefined;
|
|
27
29
|
};
|
|
28
30
|
export declare const parsedLambdaCli: LambdaCommandLineOptions & minimist.ParsedArgs;
|
|
29
31
|
export declare const forceFlagProvided: boolean;
|
|
@@ -19,7 +19,7 @@ const log_1 = require("../../log");
|
|
|
19
19
|
const progress_1 = require("./progress");
|
|
20
20
|
exports.RENDER_COMMAND = 'render';
|
|
21
21
|
const renderCommand = async (args) => {
|
|
22
|
-
var _a, _b, _c, _d;
|
|
22
|
+
var _a, _b, _c, _d, _e;
|
|
23
23
|
const serveUrl = args[0];
|
|
24
24
|
if (!serveUrl) {
|
|
25
25
|
log_1.Log.error('No serve URL passed.');
|
|
@@ -82,6 +82,12 @@ const renderCommand = async (args) => {
|
|
|
82
82
|
concurrencyPerLambda: args_1.parsedLambdaCli['concurrency-per-lambda'],
|
|
83
83
|
muted,
|
|
84
84
|
overwrite,
|
|
85
|
+
webhook: args_1.parsedLambdaCli.webhook
|
|
86
|
+
? {
|
|
87
|
+
url: args_1.parsedLambdaCli.webhook,
|
|
88
|
+
secret: (_e = args_1.parsedLambdaCli['webhook-secret']) !== null && _e !== void 0 ? _e : null,
|
|
89
|
+
}
|
|
90
|
+
: undefined,
|
|
85
91
|
});
|
|
86
92
|
const totalSteps = downloadName ? 5 : 4;
|
|
87
93
|
const progressBar = cli_1.CliInternals.createOverwriteableCliOutput(cli_1.CliInternals.quietFlagProvided());
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ImageFormat, StillImageFormat } from '@remotion/renderer';
|
|
2
|
+
export declare const getImageFormat: ({ downloadName, outName, configImageFormat, cliFlag, }: {
|
|
3
|
+
downloadName: string | null;
|
|
4
|
+
outName: string | null;
|
|
5
|
+
configImageFormat: ImageFormat | null;
|
|
6
|
+
cliFlag: ImageFormat | null;
|
|
7
|
+
}) => {
|
|
8
|
+
format: StillImageFormat;
|
|
9
|
+
source: string;
|
|
10
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getImageFormat = void 0;
|
|
4
|
+
const deriveExtensionFromFilename = (filename) => {
|
|
5
|
+
if (filename === null || filename === void 0 ? void 0 : filename.endsWith('.png')) {
|
|
6
|
+
return 'png';
|
|
7
|
+
}
|
|
8
|
+
if (filename === null || filename === void 0 ? void 0 : filename.endsWith('.jpg')) {
|
|
9
|
+
return 'jpeg';
|
|
10
|
+
}
|
|
11
|
+
if (filename === null || filename === void 0 ? void 0 : filename.endsWith('.jpeg')) {
|
|
12
|
+
return 'jpeg';
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
};
|
|
16
|
+
const getImageFormat = ({ downloadName, outName, configImageFormat, cliFlag, }) => {
|
|
17
|
+
const outNameExtension = deriveExtensionFromFilename(outName);
|
|
18
|
+
const downloadNameExtension = deriveExtensionFromFilename(downloadName);
|
|
19
|
+
if (outNameExtension &&
|
|
20
|
+
downloadNameExtension &&
|
|
21
|
+
outNameExtension !== downloadNameExtension) {
|
|
22
|
+
throw new TypeError(`Image format mismatch: ${outName} was given as the S3 output name and ${downloadName} was given as the download name, but the extensions don't match.`);
|
|
23
|
+
}
|
|
24
|
+
if (downloadNameExtension) {
|
|
25
|
+
if (cliFlag && downloadNameExtension !== cliFlag) {
|
|
26
|
+
throw new TypeError(`Image format mismatch: ${downloadName} was given as the download name, but --image-format=${cliFlag} was passed. The image formats must match.`);
|
|
27
|
+
}
|
|
28
|
+
return { format: downloadNameExtension, source: 'Download name extension' };
|
|
29
|
+
}
|
|
30
|
+
if (outNameExtension) {
|
|
31
|
+
if (cliFlag && outNameExtension !== cliFlag) {
|
|
32
|
+
throw new TypeError(`Image format mismatch: ${outName} was given as the S3 out name, but --image-format=${cliFlag} was passed. The image formats must match.`);
|
|
33
|
+
}
|
|
34
|
+
return { format: outNameExtension, source: 'Out name extension' };
|
|
35
|
+
}
|
|
36
|
+
if (cliFlag === 'none') {
|
|
37
|
+
throw new TypeError('The --image-format flag must not be "none" for stills.');
|
|
38
|
+
}
|
|
39
|
+
if (cliFlag !== null) {
|
|
40
|
+
return { format: cliFlag, source: '--image-format flag' };
|
|
41
|
+
}
|
|
42
|
+
if (configImageFormat !== null && configImageFormat !== 'none') {
|
|
43
|
+
return { format: configImageFormat, source: 'Config file' };
|
|
44
|
+
}
|
|
45
|
+
return { format: 'png', source: 'Default' };
|
|
46
|
+
};
|
|
47
|
+
exports.getImageFormat = getImageFormat;
|
package/dist/client.d.ts
CHANGED
|
@@ -2,7 +2,9 @@ import { getFunctions } from './api/get-functions';
|
|
|
2
2
|
import { getRenderProgress } from './api/get-render-progress';
|
|
3
3
|
import { renderMediaOnLambda, renderVideoOnLambda } from './api/render-media-on-lambda';
|
|
4
4
|
import { renderStillOnLambda } from './api/render-still-on-lambda';
|
|
5
|
+
import { validateWebhookSignature } from './api/validate-webhook-signature';
|
|
5
6
|
import type { AwsRegion } from './pricing/aws-regions';
|
|
6
7
|
import type { RenderProgress } from './shared/constants';
|
|
7
|
-
|
|
8
|
-
export
|
|
8
|
+
import type { WebhookPayload } from './shared/invoke-webhook';
|
|
9
|
+
export { renderVideoOnLambda, renderMediaOnLambda, renderStillOnLambda, getRenderProgress, getFunctions, validateWebhookSignature, };
|
|
10
|
+
export type { AwsRegion, RenderProgress, WebhookPayload };
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getFunctions = exports.getRenderProgress = exports.renderStillOnLambda = exports.renderMediaOnLambda = exports.renderVideoOnLambda = void 0;
|
|
3
|
+
exports.validateWebhookSignature = exports.getFunctions = exports.getRenderProgress = exports.renderStillOnLambda = exports.renderMediaOnLambda = exports.renderVideoOnLambda = void 0;
|
|
4
4
|
const get_functions_1 = require("./api/get-functions");
|
|
5
5
|
Object.defineProperty(exports, "getFunctions", { enumerable: true, get: function () { return get_functions_1.getFunctions; } });
|
|
6
6
|
const get_render_progress_1 = require("./api/get-render-progress");
|
|
@@ -10,3 +10,5 @@ Object.defineProperty(exports, "renderMediaOnLambda", { enumerable: true, get: f
|
|
|
10
10
|
Object.defineProperty(exports, "renderVideoOnLambda", { enumerable: true, get: function () { return render_media_on_lambda_1.renderVideoOnLambda; } });
|
|
11
11
|
const render_still_on_lambda_1 = require("./api/render-still-on-lambda");
|
|
12
12
|
Object.defineProperty(exports, "renderStillOnLambda", { enumerable: true, get: function () { return render_still_on_lambda_1.renderStillOnLambda; } });
|
|
13
|
+
const validate_webhook_signature_1 = require("./api/validate-webhook-signature");
|
|
14
|
+
Object.defineProperty(exports, "validateWebhookSignature", { enumerable: true, get: function () { return validate_webhook_signature_1.validateWebhookSignature; } });
|
package/dist/functions/launch.js
CHANGED
|
@@ -12,6 +12,7 @@ const version_1 = require("remotion/version");
|
|
|
12
12
|
const aws_clients_1 = require("../shared/aws-clients");
|
|
13
13
|
const constants_1 = require("../shared/constants");
|
|
14
14
|
const docs_url_1 = require("../shared/docs-url");
|
|
15
|
+
const invoke_webhook_1 = require("../shared/invoke-webhook");
|
|
15
16
|
const make_s3_url_1 = require("../shared/make-s3-url");
|
|
16
17
|
const validate_frames_per_lambda_1 = require("../shared/validate-frames-per-lambda");
|
|
17
18
|
const validate_outname_1 = require("../shared/validate-outname");
|
|
@@ -68,6 +69,49 @@ const innerLaunchHandler = async (params, options) => {
|
|
|
68
69
|
throw new Error('Expected launch type');
|
|
69
70
|
}
|
|
70
71
|
const startedDate = Date.now();
|
|
72
|
+
let webhookInvoked = false;
|
|
73
|
+
const webhookDueToTimeout = setTimeout(async () => {
|
|
74
|
+
if (params.webhook && !webhookInvoked) {
|
|
75
|
+
try {
|
|
76
|
+
await (0, invoke_webhook_1.invokeWebhook)({
|
|
77
|
+
url: params.webhook.url,
|
|
78
|
+
secret: params.webhook.secret,
|
|
79
|
+
payload: {
|
|
80
|
+
type: 'timeout',
|
|
81
|
+
renderId: params.renderId,
|
|
82
|
+
expectedBucketOwner: options.expectedBucketOwner,
|
|
83
|
+
bucketName: params.bucketName,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
webhookInvoked = true;
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
if (process.env.NODE_ENV === 'test') {
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
await (0, write_lambda_error_1.writeLambdaError)({
|
|
93
|
+
bucketName: params.bucketName,
|
|
94
|
+
errorInfo: {
|
|
95
|
+
type: 'webhook',
|
|
96
|
+
message: err.message,
|
|
97
|
+
name: err.name,
|
|
98
|
+
stack: err.stack,
|
|
99
|
+
tmpDir: null,
|
|
100
|
+
frame: 0,
|
|
101
|
+
chunk: 0,
|
|
102
|
+
isFatal: false,
|
|
103
|
+
attempt: 1,
|
|
104
|
+
willRetry: false,
|
|
105
|
+
totalAttempts: 1,
|
|
106
|
+
},
|
|
107
|
+
renderId: params.renderId,
|
|
108
|
+
expectedBucketOwner: options.expectedBucketOwner,
|
|
109
|
+
});
|
|
110
|
+
console.log('Failed to invoke webhook:');
|
|
111
|
+
console.log(err);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}, Math.max(params.timeoutInMilliseconds - 1000, 1000));
|
|
71
115
|
const [browserInstance, optimization] = await Promise.all([
|
|
72
116
|
(0, get_browser_instance_1.getBrowserInstance)(renderer_1.RenderInternals.isEqualOrBelowLogLevel(params.logLevel, 'verbose'), params.chromiumOptions),
|
|
73
117
|
(0, s3_optimization_file_1.getOptimization)({
|
|
@@ -378,6 +422,7 @@ const innerLaunchHandler = async (params, options) => {
|
|
|
378
422
|
contents,
|
|
379
423
|
jobs,
|
|
380
424
|
});
|
|
425
|
+
const outputUrl = (0, get_output_url_from_metadata_1.getOutputUrlFromMetadata)(renderMetadata, params.bucketName, customCredentials);
|
|
381
426
|
const postRenderData = (0, create_post_render_data_1.createPostRenderData)({
|
|
382
427
|
expectedBucketOwner: options.expectedBucketOwner,
|
|
383
428
|
region: (0, get_current_region_1.getCurrentRegionInFunction)(),
|
|
@@ -391,7 +436,7 @@ const innerLaunchHandler = async (params, options) => {
|
|
|
391
436
|
outputFile: {
|
|
392
437
|
lastModified: Date.now(),
|
|
393
438
|
size: outputSize.size,
|
|
394
|
-
url:
|
|
439
|
+
url: outputUrl,
|
|
395
440
|
},
|
|
396
441
|
});
|
|
397
442
|
await finalEncodingProgressProm;
|
|
@@ -409,8 +454,54 @@ const innerLaunchHandler = async (params, options) => {
|
|
|
409
454
|
customCredentials: null,
|
|
410
455
|
});
|
|
411
456
|
await Promise.all([cleanupChunksProm, fs_1.default.promises.rm(outfile)]);
|
|
457
|
+
clearTimeout(webhookDueToTimeout);
|
|
458
|
+
if (params.webhook && !webhookInvoked) {
|
|
459
|
+
try {
|
|
460
|
+
await (0, invoke_webhook_1.invokeWebhook)({
|
|
461
|
+
url: params.webhook.url,
|
|
462
|
+
secret: params.webhook.secret,
|
|
463
|
+
payload: {
|
|
464
|
+
type: 'success',
|
|
465
|
+
renderId: params.renderId,
|
|
466
|
+
expectedBucketOwner: options.expectedBucketOwner,
|
|
467
|
+
bucketName: params.bucketName,
|
|
468
|
+
outputUrl,
|
|
469
|
+
lambdaErrors: postRenderData.errors,
|
|
470
|
+
outputFile: postRenderData.outputFile,
|
|
471
|
+
timeToFinish: postRenderData.timeToFinish,
|
|
472
|
+
},
|
|
473
|
+
});
|
|
474
|
+
webhookInvoked = true;
|
|
475
|
+
}
|
|
476
|
+
catch (err) {
|
|
477
|
+
if (process.env.NODE_ENV === 'test') {
|
|
478
|
+
throw err;
|
|
479
|
+
}
|
|
480
|
+
await (0, write_lambda_error_1.writeLambdaError)({
|
|
481
|
+
bucketName: params.bucketName,
|
|
482
|
+
errorInfo: {
|
|
483
|
+
type: 'webhook',
|
|
484
|
+
message: err.message,
|
|
485
|
+
name: err.name,
|
|
486
|
+
stack: err.stack,
|
|
487
|
+
tmpDir: null,
|
|
488
|
+
frame: 0,
|
|
489
|
+
chunk: 0,
|
|
490
|
+
isFatal: false,
|
|
491
|
+
attempt: 1,
|
|
492
|
+
willRetry: false,
|
|
493
|
+
totalAttempts: 1,
|
|
494
|
+
},
|
|
495
|
+
renderId: params.renderId,
|
|
496
|
+
expectedBucketOwner: options.expectedBucketOwner,
|
|
497
|
+
});
|
|
498
|
+
console.log('Failed to invoke webhook:');
|
|
499
|
+
console.log(err);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
412
502
|
};
|
|
413
503
|
const launchHandler = async (params, options) => {
|
|
504
|
+
var _a, _b;
|
|
414
505
|
if (params.type !== constants_1.LambdaRoutines.launch) {
|
|
415
506
|
throw new Error('Expected launch type');
|
|
416
507
|
}
|
|
@@ -440,6 +531,50 @@ const launchHandler = async (params, options) => {
|
|
|
440
531
|
expectedBucketOwner: options.expectedBucketOwner,
|
|
441
532
|
renderId: params.renderId,
|
|
442
533
|
});
|
|
534
|
+
if ((_a = params.webhook) === null || _a === void 0 ? void 0 : _a.url) {
|
|
535
|
+
try {
|
|
536
|
+
await (0, invoke_webhook_1.invokeWebhook)({
|
|
537
|
+
url: params.webhook.url,
|
|
538
|
+
secret: (_b = params.webhook.secret) !== null && _b !== void 0 ? _b : null,
|
|
539
|
+
payload: {
|
|
540
|
+
type: 'error',
|
|
541
|
+
renderId: params.renderId,
|
|
542
|
+
expectedBucketOwner: options.expectedBucketOwner,
|
|
543
|
+
bucketName: params.bucketName,
|
|
544
|
+
errors: [err].map((e) => ({
|
|
545
|
+
message: e.message,
|
|
546
|
+
name: e.name,
|
|
547
|
+
stack: e.stack,
|
|
548
|
+
})),
|
|
549
|
+
},
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
catch (error) {
|
|
553
|
+
if (process.env.NODE_ENV === 'test') {
|
|
554
|
+
throw error;
|
|
555
|
+
}
|
|
556
|
+
await (0, write_lambda_error_1.writeLambdaError)({
|
|
557
|
+
bucketName: params.bucketName,
|
|
558
|
+
errorInfo: {
|
|
559
|
+
type: 'webhook',
|
|
560
|
+
message: err.message,
|
|
561
|
+
name: err.name,
|
|
562
|
+
stack: err.stack,
|
|
563
|
+
tmpDir: null,
|
|
564
|
+
frame: 0,
|
|
565
|
+
chunk: 0,
|
|
566
|
+
isFatal: false,
|
|
567
|
+
attempt: 1,
|
|
568
|
+
willRetry: false,
|
|
569
|
+
totalAttempts: 1,
|
|
570
|
+
},
|
|
571
|
+
renderId: params.renderId,
|
|
572
|
+
expectedBucketOwner: options.expectedBucketOwner,
|
|
573
|
+
});
|
|
574
|
+
console.log('Failed to invoke webhook:');
|
|
575
|
+
console.log(error);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
443
578
|
}
|
|
444
579
|
};
|
|
445
580
|
exports.launchHandler = launchHandler;
|
|
@@ -136,6 +136,13 @@ const renderHandler = async (params, options, logs) => {
|
|
|
136
136
|
downloadMap,
|
|
137
137
|
muted: params.muted,
|
|
138
138
|
enforceAudioTrack: true,
|
|
139
|
+
onSlowestFrames: (slowestFrames) => {
|
|
140
|
+
console.log();
|
|
141
|
+
console.log(`Slowest frames:`);
|
|
142
|
+
slowestFrames.forEach(({ frame, time }) => {
|
|
143
|
+
console.log(`Frame ${frame} (${time.toFixed(3)}ms)`);
|
|
144
|
+
});
|
|
145
|
+
},
|
|
139
146
|
})
|
|
140
147
|
.then(() => resolve())
|
|
141
148
|
.catch((err) => reject(err));
|
package/dist/functions/start.js
CHANGED
|
@@ -63,6 +63,7 @@ const startHandler = async (params, options) => {
|
|
|
63
63
|
downloadBehavior: params.downloadBehavior,
|
|
64
64
|
muted: params.muted,
|
|
65
65
|
overwrite: params.overwrite,
|
|
66
|
+
webhook: params.webhook,
|
|
66
67
|
};
|
|
67
68
|
await (0, aws_clients_1.getLambdaClient)((0, get_current_region_1.getCurrentRegionInFunction)()).send(new client_lambda_1.InvokeCommand({
|
|
68
69
|
FunctionName: process.env.AWS_LAMBDA_FUNCTION_NAME,
|
package/dist/index.d.ts
CHANGED
|
@@ -32,11 +32,13 @@ import type { RenderMediaOnLambdaInput, RenderMediaOnLambdaOutput } from './api/
|
|
|
32
32
|
import { renderMediaOnLambda, renderVideoOnLambda } from './api/render-media-on-lambda';
|
|
33
33
|
import type { RenderStillOnLambdaInput, RenderStillOnLambdaOutput } from './api/render-still-on-lambda';
|
|
34
34
|
import { renderStillOnLambda } from './api/render-still-on-lambda';
|
|
35
|
+
import { validateWebhookSignature } from './api/validate-webhook-signature';
|
|
35
36
|
import type { LambdaLSInput, LambdaLsReturnType } from './functions/helpers/io';
|
|
36
37
|
import { LambdaInternals } from './internals';
|
|
37
38
|
import type { AwsRegion } from './pricing/aws-regions';
|
|
38
39
|
import type { CustomCredentials } from './shared/aws-clients';
|
|
39
40
|
import type { RenderProgress } from './shared/constants';
|
|
41
|
+
import type { WebhookPayload } from './shared/invoke-webhook';
|
|
40
42
|
import type { LambdaArchitecture } from './shared/validate-architecture';
|
|
41
|
-
export { deleteSite, deployFunction, deploySite, downloadMedia, downloadVideo, getFunctions, getUserPolicy, getRolePolicy, getSites, getOrCreateBucket, getRenderProgress, renderVideoOnLambda, renderMediaOnLambda, simulatePermissions, deleteFunction, getFunctionInfo, estimatePrice, LambdaInternals, renderStillOnLambda, getRegions, getAwsClient, presignUrl, deleteRender, };
|
|
42
|
-
export type { AwsRegion, RenderProgress, DeploySiteInput, DeploySiteOutput, LambdaLsReturnType, LambdaLSInput, DeleteSiteInput, DeleteSiteOutput, EstimatePriceInput, DeployFunctionInput, DeployFunctionOutput, DeleteFunctionInput, GetFunctionInfoInput, FunctionInfo, GetFunctionsInput, GetSitesInput, GetSitesOutput, DownloadMediaInput, DownloadMediaOutput, GetOrCreateBucketInput, GetOrCreateBucketOutput, GetRenderInput, RenderMediaOnLambdaInput, RenderMediaOnLambdaOutput, RenderStillOnLambdaInput, RenderStillOnLambdaOutput, SimulatePermissionsInput, SimulatePermissionsOutput, GetAwsClientInput, GetAwsClientOutput, LambdaArchitecture, CustomCredentials, };
|
|
43
|
+
export { deleteSite, deployFunction, deploySite, downloadMedia, downloadVideo, getFunctions, getUserPolicy, getRolePolicy, getSites, getOrCreateBucket, getRenderProgress, renderVideoOnLambda, renderMediaOnLambda, simulatePermissions, deleteFunction, getFunctionInfo, estimatePrice, LambdaInternals, renderStillOnLambda, getRegions, getAwsClient, presignUrl, deleteRender, validateWebhookSignature, };
|
|
44
|
+
export type { AwsRegion, RenderProgress, DeploySiteInput, DeploySiteOutput, LambdaLsReturnType, LambdaLSInput, DeleteSiteInput, DeleteSiteOutput, EstimatePriceInput, DeployFunctionInput, DeployFunctionOutput, DeleteFunctionInput, GetFunctionInfoInput, FunctionInfo, GetFunctionsInput, GetSitesInput, GetSitesOutput, DownloadMediaInput, DownloadMediaOutput, GetOrCreateBucketInput, GetOrCreateBucketOutput, GetRenderInput, RenderMediaOnLambdaInput, RenderMediaOnLambdaOutput, RenderStillOnLambdaInput, RenderStillOnLambdaOutput, SimulatePermissionsInput, SimulatePermissionsOutput, GetAwsClientInput, GetAwsClientOutput, LambdaArchitecture, CustomCredentials, WebhookPayload, };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.deleteRender = exports.presignUrl = exports.getAwsClient = exports.getRegions = exports.renderStillOnLambda = exports.LambdaInternals = exports.estimatePrice = exports.getFunctionInfo = exports.deleteFunction = exports.simulatePermissions = exports.renderMediaOnLambda = exports.renderVideoOnLambda = exports.getRenderProgress = exports.getOrCreateBucket = exports.getSites = exports.getRolePolicy = exports.getUserPolicy = exports.getFunctions = exports.downloadVideo = exports.downloadMedia = exports.deploySite = exports.deployFunction = exports.deleteSite = void 0;
|
|
3
|
+
exports.validateWebhookSignature = exports.deleteRender = exports.presignUrl = exports.getAwsClient = exports.getRegions = exports.renderStillOnLambda = exports.LambdaInternals = exports.estimatePrice = exports.getFunctionInfo = exports.deleteFunction = exports.simulatePermissions = exports.renderMediaOnLambda = exports.renderVideoOnLambda = exports.getRenderProgress = exports.getOrCreateBucket = exports.getSites = exports.getRolePolicy = exports.getUserPolicy = exports.getFunctions = exports.downloadVideo = exports.downloadMedia = exports.deploySite = exports.deployFunction = exports.deleteSite = void 0;
|
|
4
4
|
const delete_function_1 = require("./api/delete-function");
|
|
5
5
|
Object.defineProperty(exports, "deleteFunction", { enumerable: true, get: function () { return delete_function_1.deleteFunction; } });
|
|
6
6
|
const delete_render_1 = require("./api/delete-render");
|
|
@@ -42,5 +42,7 @@ Object.defineProperty(exports, "renderMediaOnLambda", { enumerable: true, get: f
|
|
|
42
42
|
Object.defineProperty(exports, "renderVideoOnLambda", { enumerable: true, get: function () { return render_media_on_lambda_1.renderVideoOnLambda; } });
|
|
43
43
|
const render_still_on_lambda_1 = require("./api/render-still-on-lambda");
|
|
44
44
|
Object.defineProperty(exports, "renderStillOnLambda", { enumerable: true, get: function () { return render_still_on_lambda_1.renderStillOnLambda; } });
|
|
45
|
+
const validate_webhook_signature_1 = require("./api/validate-webhook-signature");
|
|
46
|
+
Object.defineProperty(exports, "validateWebhookSignature", { enumerable: true, get: function () { return validate_webhook_signature_1.validateWebhookSignature; } });
|
|
45
47
|
const internals_1 = require("./internals");
|
|
46
48
|
Object.defineProperty(exports, "LambdaInternals", { enumerable: true, get: function () { return internals_1.LambdaInternals; } });
|
|
@@ -93,6 +93,10 @@ export declare enum LambdaRoutines {
|
|
|
93
93
|
renderer = "renderer",
|
|
94
94
|
still = "still"
|
|
95
95
|
}
|
|
96
|
+
declare type WebhookOption = null | {
|
|
97
|
+
url: string;
|
|
98
|
+
secret: string | null;
|
|
99
|
+
};
|
|
96
100
|
export declare type LambdaPayloads = {
|
|
97
101
|
info: {
|
|
98
102
|
type: LambdaRoutines.info;
|
|
@@ -125,6 +129,7 @@ export declare type LambdaPayloads = {
|
|
|
125
129
|
muted: boolean;
|
|
126
130
|
version: string;
|
|
127
131
|
overwrite: boolean;
|
|
132
|
+
webhook: WebhookOption;
|
|
128
133
|
};
|
|
129
134
|
launch: {
|
|
130
135
|
type: LambdaRoutines.launch;
|
|
@@ -155,6 +160,7 @@ export declare type LambdaPayloads = {
|
|
|
155
160
|
downloadBehavior: DownloadBehavior;
|
|
156
161
|
muted: boolean;
|
|
157
162
|
overwrite: boolean;
|
|
163
|
+
webhook: WebhookOption;
|
|
158
164
|
};
|
|
159
165
|
status: {
|
|
160
166
|
type: LambdaRoutines.status;
|
|
@@ -304,3 +310,4 @@ export declare type RenderProgress = {
|
|
|
304
310
|
export declare type Privacy = 'public' | 'private' | 'no-acl';
|
|
305
311
|
export declare const LAMBDA_CONCURRENCY_LIMIT_QUOTA = "L-B99A9384";
|
|
306
312
|
export declare const LAMBDA_BURST_LIMIT_QUOTA = "L-548AE339";
|
|
313
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import http from 'http';
|
|
4
|
+
import https from 'https';
|
|
5
|
+
import type { EnhancedErrorInfo } from '../functions/helpers/write-lambda-error';
|
|
6
|
+
/**
|
|
7
|
+
* @description Calculates cryptographically secure signature for webhooks using Hmac.
|
|
8
|
+
* @link https://remotion.dev/docs/lambda/webhooks#validate-webhooks
|
|
9
|
+
* @param payload Stringified request body to encode in the signature.
|
|
10
|
+
* @param secret User-provided webhook secret used to sign the request.
|
|
11
|
+
* @returns {string} Calculated signature
|
|
12
|
+
*/
|
|
13
|
+
export declare function calculateSignature(payload: string, secret: string | null): string;
|
|
14
|
+
declare type DynamicWebhookPayload = {
|
|
15
|
+
type: 'error';
|
|
16
|
+
errors: {
|
|
17
|
+
message: string;
|
|
18
|
+
name: string;
|
|
19
|
+
stack: string;
|
|
20
|
+
}[];
|
|
21
|
+
} | {
|
|
22
|
+
type: 'success';
|
|
23
|
+
lambdaErrors: EnhancedErrorInfo[];
|
|
24
|
+
outputUrl: string | undefined;
|
|
25
|
+
outputFile: string | undefined;
|
|
26
|
+
timeToFinish: number | undefined;
|
|
27
|
+
} | {
|
|
28
|
+
type: 'timeout';
|
|
29
|
+
};
|
|
30
|
+
export declare type WebhookPayload = {
|
|
31
|
+
renderId: string;
|
|
32
|
+
expectedBucketOwner: string;
|
|
33
|
+
bucketName: string;
|
|
34
|
+
} & DynamicWebhookPayload;
|
|
35
|
+
export declare const mockableHttpClients: {
|
|
36
|
+
http: typeof http.request;
|
|
37
|
+
https: typeof https.request;
|
|
38
|
+
};
|
|
39
|
+
export declare function invokeWebhook({ payload, secret, url, }: {
|
|
40
|
+
payload: WebhookPayload;
|
|
41
|
+
url: string;
|
|
42
|
+
secret: string | null;
|
|
43
|
+
}): Promise<void>;
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.invokeWebhook = exports.mockableHttpClients = exports.calculateSignature = void 0;
|
|
30
|
+
const Crypto = __importStar(require("crypto"));
|
|
31
|
+
const http_1 = __importDefault(require("http"));
|
|
32
|
+
const https_1 = __importDefault(require("https"));
|
|
33
|
+
/**
|
|
34
|
+
* @description Calculates cryptographically secure signature for webhooks using Hmac.
|
|
35
|
+
* @link https://remotion.dev/docs/lambda/webhooks#validate-webhooks
|
|
36
|
+
* @param payload Stringified request body to encode in the signature.
|
|
37
|
+
* @param secret User-provided webhook secret used to sign the request.
|
|
38
|
+
* @returns {string} Calculated signature
|
|
39
|
+
*/
|
|
40
|
+
function calculateSignature(payload, secret) {
|
|
41
|
+
if (!secret) {
|
|
42
|
+
return 'NO_SECRET_PROVIDED';
|
|
43
|
+
}
|
|
44
|
+
const hmac = Crypto.createHmac('sha512', secret);
|
|
45
|
+
const signature = 'sha512=' + hmac.update(payload).digest('hex');
|
|
46
|
+
return signature;
|
|
47
|
+
}
|
|
48
|
+
exports.calculateSignature = calculateSignature;
|
|
49
|
+
const getWebhookClient = (url) => {
|
|
50
|
+
if (url.startsWith('https://')) {
|
|
51
|
+
return exports.mockableHttpClients.https;
|
|
52
|
+
}
|
|
53
|
+
if (url.startsWith('http://')) {
|
|
54
|
+
return exports.mockableHttpClients.http;
|
|
55
|
+
}
|
|
56
|
+
throw new Error('Can only request URLs starting with http:// or https://');
|
|
57
|
+
};
|
|
58
|
+
exports.mockableHttpClients = {
|
|
59
|
+
http: http_1.default.request,
|
|
60
|
+
https: https_1.default.request,
|
|
61
|
+
};
|
|
62
|
+
function invokeWebhook({ payload, secret, url, }) {
|
|
63
|
+
const jsonPayload = JSON.stringify(payload);
|
|
64
|
+
return new Promise((resolve, reject) => {
|
|
65
|
+
const req = getWebhookClient(url)(url, {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
headers: {
|
|
68
|
+
'Content-Type': 'application/json',
|
|
69
|
+
'Content-Length': jsonPayload.length,
|
|
70
|
+
'X-Remotion-Mode': 'production',
|
|
71
|
+
'X-Remotion-Signature': calculateSignature(jsonPayload, secret),
|
|
72
|
+
'X-Remotion-Status': payload.type,
|
|
73
|
+
},
|
|
74
|
+
timeout: 5000,
|
|
75
|
+
}, (res) => {
|
|
76
|
+
if (res.statusCode && res.statusCode > 299) {
|
|
77
|
+
reject(new Error(`Sent a webhook but got a status code of ${res.statusCode} with message '${res.statusMessage}'`));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
resolve();
|
|
81
|
+
});
|
|
82
|
+
req.write(jsonPayload);
|
|
83
|
+
req.on('error', (err) => {
|
|
84
|
+
reject(err);
|
|
85
|
+
});
|
|
86
|
+
req.end();
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
exports.invokeWebhook = invokeWebhook;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remotion/lambda",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.30",
|
|
4
4
|
"description": "Distributed renderer for Remotion based on AWS Lambda",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"@aws-sdk/client-service-quotas": "3.58.0",
|
|
33
33
|
"@aws-sdk/lib-storage": "3.58.0",
|
|
34
34
|
"@aws-sdk/s3-request-presigner": "3.58.0",
|
|
35
|
-
"@remotion/bundler": "3.2.
|
|
36
|
-
"@remotion/cli": "3.2.
|
|
37
|
-
"@remotion/renderer": "3.2.
|
|
35
|
+
"@remotion/bundler": "3.2.30",
|
|
36
|
+
"@remotion/cli": "3.2.30",
|
|
37
|
+
"@remotion/renderer": "3.2.30",
|
|
38
38
|
"aws-policies": "^1.0.1",
|
|
39
39
|
"mime-types": "2.1.34",
|
|
40
|
-
"remotion": "3.2.
|
|
40
|
+
"remotion": "3.2.30"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"react": ">=16.8.0",
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"publishConfig": {
|
|
63
63
|
"access": "public"
|
|
64
64
|
},
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "5dc5f4c010c47186c7911b575998296d6a65f7cb"
|
|
66
66
|
}
|
package/remotionlambda.zip
CHANGED
|
Binary file
|