@remotion/lambda-client 4.0.261

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.
Files changed (227) hide show
  1. package/.turbo/turbo-make.log +6 -0
  2. package/LICENSE.md +49 -0
  3. package/README.md +5 -0
  4. package/bundle.ts +20 -0
  5. package/dist/app-router-webhook.d.ts +10 -0
  6. package/dist/app-router-webhook.js +43 -0
  7. package/dist/apply-lifecycle.d.ts +8 -0
  8. package/dist/apply-lifecycle.js +18 -0
  9. package/dist/aws-clients.d.ts +11 -0
  10. package/dist/aws-clients.js +51 -0
  11. package/dist/aws-provider.d.ts +23 -0
  12. package/dist/aws-provider.js +94 -0
  13. package/dist/bucket-exists.d.ts +3 -0
  14. package/dist/bucket-exists.js +26 -0
  15. package/dist/call-lambda-async.d.ts +2 -0
  16. package/dist/call-lambda-async.js +20 -0
  17. package/dist/call-lambda-streaming.d.ts +6 -0
  18. package/dist/call-lambda-streaming.js +125 -0
  19. package/dist/call-lambda-sync.d.ts +2 -0
  20. package/dist/call-lambda-sync.js +30 -0
  21. package/dist/check-credentials.d.ts +1 -0
  22. package/dist/check-credentials.js +39 -0
  23. package/dist/clean-items.d.ts +16 -0
  24. package/dist/clean-items.js +25 -0
  25. package/dist/constants.d.ts +25 -0
  26. package/dist/constants.js +26 -0
  27. package/dist/content-disposition-header.d.ts +2 -0
  28. package/dist/content-disposition-header.js +50 -0
  29. package/dist/convert-to-serve-url.d.ts +6 -0
  30. package/dist/convert-to-serve-url.js +14 -0
  31. package/dist/create-bucket.d.ts +3 -0
  32. package/dist/create-bucket.js +45 -0
  33. package/dist/delete-file.d.ts +3 -0
  34. package/dist/delete-file.js +12 -0
  35. package/dist/delete-function.d.ts +4 -0
  36. package/dist/delete-function.js +15 -0
  37. package/dist/delete-render.d.ts +20 -0
  38. package/dist/delete-render.js +84 -0
  39. package/dist/encode-aws-url-params.d.ts +1 -0
  40. package/dist/encode-aws-url-params.js +7 -0
  41. package/dist/esm/constants.mjs +45 -0
  42. package/dist/esm/index.mjs +6516 -0
  43. package/dist/esm/regions.mjs +50 -0
  44. package/dist/estimate-price.d.ts +17 -0
  45. package/dist/estimate-price.js +45 -0
  46. package/dist/express-webhook.d.ts +3 -0
  47. package/dist/express-webhook.js +46 -0
  48. package/dist/get-account-id.d.ts +3 -0
  49. package/dist/get-account-id.js +15 -0
  50. package/dist/get-aws-client.d.ts +30 -0
  51. package/dist/get-aws-client.js +56 -0
  52. package/dist/get-aws-urls.d.ts +25 -0
  53. package/dist/get-aws-urls.js +31 -0
  54. package/dist/get-buckets.d.ts +9 -0
  55. package/dist/get-buckets.js +62 -0
  56. package/dist/get-compositions-on-lambda.d.ts +18 -0
  57. package/dist/get-compositions-on-lambda.js +59 -0
  58. package/dist/get-credentials.d.ts +9 -0
  59. package/dist/get-credentials.js +55 -0
  60. package/dist/get-env-variable.d.ts +1 -0
  61. package/dist/get-env-variable.js +15 -0
  62. package/dist/get-function-name.d.ts +8 -0
  63. package/dist/get-function-name.js +17 -0
  64. package/dist/get-function-version.d.ts +7 -0
  65. package/dist/get-function-version.js +32 -0
  66. package/dist/get-functions.d.ts +8 -0
  67. package/dist/get-functions.js +77 -0
  68. package/dist/get-output-url-from-metadata.d.ts +3 -0
  69. package/dist/get-output-url-from-metadata.js +18 -0
  70. package/dist/get-render-progress.d.ts +15 -0
  71. package/dist/get-render-progress.js +45 -0
  72. package/dist/get-s3-client.d.ts +9 -0
  73. package/dist/get-s3-client.js +14 -0
  74. package/dist/get-service-client.d.ts +23 -0
  75. package/dist/get-service-client.js +120 -0
  76. package/dist/get-sites.d.ts +29 -0
  77. package/dist/get-sites.js +79 -0
  78. package/dist/head-file.d.ts +3 -0
  79. package/dist/head-file.js +17 -0
  80. package/dist/index.d.ts +123 -0
  81. package/dist/index.js +104 -0
  82. package/dist/is-cli.d.ts +2 -0
  83. package/dist/is-cli.js +10 -0
  84. package/dist/is-flaky-error.d.ts +1 -0
  85. package/dist/is-flaky-error.js +77 -0
  86. package/dist/is-in-lambda.d.ts +1 -0
  87. package/dist/is-in-lambda.js +9 -0
  88. package/dist/is-likely-to-have-aws-profile.d.ts +1 -0
  89. package/dist/is-likely-to-have-aws-profile.js +50 -0
  90. package/dist/lambda-version-string.d.ts +1 -0
  91. package/dist/lambda-version-string.js +7 -0
  92. package/dist/lifecycle-rules.d.ts +10 -0
  93. package/dist/lifecycle-rules.js +61 -0
  94. package/dist/lifecycle.d.ts +7 -0
  95. package/dist/lifecycle.js +24 -0
  96. package/dist/list-objects.d.ts +3 -0
  97. package/dist/list-objects.js +67 -0
  98. package/dist/make-lambda-payload.d.ts +54 -0
  99. package/dist/make-lambda-payload.js +148 -0
  100. package/dist/make-s3-url.d.ts +6 -0
  101. package/dist/make-s3-url.js +7 -0
  102. package/dist/p-limit.d.ts +1 -0
  103. package/dist/p-limit.js +57 -0
  104. package/dist/pages-router-webhook.d.ts +5 -0
  105. package/dist/pages-router-webhook.js +48 -0
  106. package/dist/parse-function-name.d.ts +8 -0
  107. package/dist/parse-function-name.js +17 -0
  108. package/dist/presign-url.d.ts +14 -0
  109. package/dist/presign-url.js +64 -0
  110. package/dist/price-per-1s.d.ts +37 -0
  111. package/dist/price-per-1s.js +822 -0
  112. package/dist/random-hash.d.ts +1 -0
  113. package/dist/random-hash.js +13 -0
  114. package/dist/read-file.d.ts +9 -0
  115. package/dist/read-file.js +18 -0
  116. package/dist/regions.d.ts +3 -0
  117. package/dist/regions.js +48 -0
  118. package/dist/render-media-on-lambda.d.ts +61 -0
  119. package/dist/render-media-on-lambda.js +127 -0
  120. package/dist/render-still-on-lambda.d.ts +53 -0
  121. package/dist/render-still-on-lambda.js +118 -0
  122. package/dist/runtime-preference.d.ts +2 -0
  123. package/dist/runtime-preference.js +8 -0
  124. package/dist/speculate-function-name.d.ts +6 -0
  125. package/dist/speculate-function-name.js +20 -0
  126. package/dist/test/encode-aws-url.test.d.ts +1 -0
  127. package/dist/test/encode-aws-url.test.js +8 -0
  128. package/dist/test/price-calculation.test.d.ts +1 -0
  129. package/dist/test/price-calculation.test.js +61 -0
  130. package/dist/test/pricing.test.d.ts +1 -0
  131. package/dist/test/pricing.test.js +27 -0
  132. package/dist/test/validate-disk-size-in-mb.test.d.ts +1 -0
  133. package/dist/test/validate-disk-size-in-mb.test.js +14 -0
  134. package/dist/validate-aws-region.d.ts +2 -0
  135. package/dist/validate-aws-region.js +9 -0
  136. package/dist/validate-bucketname.d.ts +4 -0
  137. package/dist/validate-bucketname.js +15 -0
  138. package/dist/validate-disk-size-in-mb.d.ts +1 -0
  139. package/dist/validate-disk-size-in-mb.js +23 -0
  140. package/dist/validate-lambda-codec.d.ts +2 -0
  141. package/dist/validate-lambda-codec.js +21 -0
  142. package/dist/validate-memory-size.d.ts +1 -0
  143. package/dist/validate-memory-size.js +22 -0
  144. package/dist/validate-presign-expiration.d.ts +1 -0
  145. package/dist/validate-presign-expiration.js +29 -0
  146. package/dist/validate-serveurl.d.ts +1 -0
  147. package/dist/validate-serveurl.js +9 -0
  148. package/dist/validate-webhook-signature.d.ts +5 -0
  149. package/dist/validate-webhook-signature.js +28 -0
  150. package/dist/write-file.d.ts +5 -0
  151. package/dist/write-file.js +56 -0
  152. package/eslint.config.mjs +5 -0
  153. package/package.json +71 -0
  154. package/src/app-router-webhook.ts +64 -0
  155. package/src/apply-lifecycle.ts +30 -0
  156. package/src/aws-clients.ts +60 -0
  157. package/src/aws-provider.ts +135 -0
  158. package/src/bucket-exists.ts +28 -0
  159. package/src/call-lambda-async.ts +39 -0
  160. package/src/call-lambda-streaming.ts +219 -0
  161. package/src/call-lambda-sync.ts +55 -0
  162. package/src/check-credentials.ts +51 -0
  163. package/src/clean-items.ts +47 -0
  164. package/src/constants.ts +38 -0
  165. package/src/content-disposition-header.ts +64 -0
  166. package/src/convert-to-serve-url.ts +24 -0
  167. package/src/create-bucket.ts +67 -0
  168. package/src/delete-file.ts +30 -0
  169. package/src/delete-function.ts +24 -0
  170. package/src/delete-render.ts +107 -0
  171. package/src/encode-aws-url-params.ts +3 -0
  172. package/src/estimate-price.ts +95 -0
  173. package/src/express-webhook.ts +50 -0
  174. package/src/get-account-id.ts +22 -0
  175. package/src/get-aws-client.ts +59 -0
  176. package/src/get-aws-urls.ts +85 -0
  177. package/src/get-buckets.ts +81 -0
  178. package/src/get-compositions-on-lambda.ts +104 -0
  179. package/src/get-credentials.ts +81 -0
  180. package/src/get-env-variable.ts +15 -0
  181. package/src/get-function-name.ts +24 -0
  182. package/src/get-function-version.ts +43 -0
  183. package/src/get-functions.ts +103 -0
  184. package/src/get-output-url-from-metadata.ts +23 -0
  185. package/src/get-render-progress.ts +62 -0
  186. package/src/get-s3-client.ts +22 -0
  187. package/src/get-service-client.ts +178 -0
  188. package/src/get-sites.ts +128 -0
  189. package/src/head-file.ts +28 -0
  190. package/src/index.ts +147 -0
  191. package/src/is-cli.ts +7 -0
  192. package/src/is-flaky-error.ts +101 -0
  193. package/src/is-in-lambda.ts +5 -0
  194. package/src/is-likely-to-have-aws-profile.ts +55 -0
  195. package/src/lambda-version-string.ts +5 -0
  196. package/src/lifecycle-rules.ts +104 -0
  197. package/src/lifecycle.ts +44 -0
  198. package/src/list-objects.ts +83 -0
  199. package/src/make-lambda-payload.ts +317 -0
  200. package/src/make-s3-url.ts +13 -0
  201. package/src/p-limit.ts +75 -0
  202. package/src/pages-router-webhook.ts +58 -0
  203. package/src/parse-function-name.ts +24 -0
  204. package/src/presign-url.ts +110 -0
  205. package/src/price-per-1s.ts +863 -0
  206. package/src/random-hash.ts +10 -0
  207. package/src/read-file.ts +31 -0
  208. package/src/regions.ts +48 -0
  209. package/src/render-media-on-lambda.ts +216 -0
  210. package/src/render-still-on-lambda.ts +195 -0
  211. package/src/runtime-preference.ts +7 -0
  212. package/src/speculate-function-name.ts +27 -0
  213. package/src/test/encode-aws-url.test.ts +7 -0
  214. package/src/test/price-calculation.test.ts +61 -0
  215. package/src/test/pricing.test.ts +32 -0
  216. package/src/test/validate-disk-size-in-mb.test.ts +15 -0
  217. package/src/validate-aws-region.ts +14 -0
  218. package/src/validate-bucketname.ts +24 -0
  219. package/src/validate-disk-size-in-mb.ts +37 -0
  220. package/src/validate-lambda-codec.ts +28 -0
  221. package/src/validate-memory-size.ts +31 -0
  222. package/src/validate-presign-expiration.ts +46 -0
  223. package/src/validate-serveurl.ts +9 -0
  224. package/src/validate-webhook-signature.ts +42 -0
  225. package/src/write-file.ts +74 -0
  226. package/tsconfig.json +9 -0
  227. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,107 @@
1
+ import type {ProviderSpecifics} from '@remotion/serverless-client';
2
+ import {
3
+ getExpectedOutName,
4
+ getOverallProgressFromStorage,
5
+ rendersPrefix,
6
+ type CustomCredentials,
7
+ } from '@remotion/serverless-client';
8
+ import type {AwsProvider} from './aws-provider';
9
+ import {awsImplementation} from './aws-provider';
10
+ import {cleanItems} from './clean-items';
11
+ import {REMOTION_BUCKET_PREFIX} from './constants';
12
+ import type {AwsRegion} from './regions';
13
+
14
+ export type DeleteRenderInput = {
15
+ region: AwsRegion;
16
+ bucketName: string;
17
+ renderId: string;
18
+ customCredentials?: CustomCredentials<AwsProvider>;
19
+ forcePathStyle?: boolean;
20
+ };
21
+
22
+ export const internalDeleteRender = async (
23
+ input: DeleteRenderInput & {
24
+ providerSpecifics: ProviderSpecifics<AwsProvider>;
25
+ forcePathStyle: boolean;
26
+ },
27
+ ) => {
28
+ const expectedBucketOwner = await input.providerSpecifics.getAccountId({
29
+ region: input.region,
30
+ });
31
+ const progress = await getOverallProgressFromStorage({
32
+ bucketName: input.bucketName,
33
+ expectedBucketOwner,
34
+ region: input.region,
35
+ renderId: input.renderId,
36
+ providerSpecifics: input.providerSpecifics,
37
+ forcePathStyle: input.forcePathStyle,
38
+ });
39
+
40
+ // Render did not start yet
41
+ if (progress.renderMetadata === null) {
42
+ return {freedBytes: 0};
43
+ }
44
+
45
+ const {key, renderBucketName, customCredentials} = getExpectedOutName({
46
+ renderMetadata: progress.renderMetadata,
47
+ bucketName: input.bucketName,
48
+ customCredentials: input.customCredentials ?? null,
49
+ bucketNamePrefix: REMOTION_BUCKET_PREFIX,
50
+ });
51
+
52
+ await input.providerSpecifics.deleteFile({
53
+ bucketName: renderBucketName,
54
+ customCredentials,
55
+ key,
56
+ region: input.region,
57
+ forcePathStyle: input.forcePathStyle,
58
+ });
59
+
60
+ let files = await input.providerSpecifics.listObjects({
61
+ bucketName: input.bucketName,
62
+ prefix: rendersPrefix(input.renderId),
63
+ region: input.region,
64
+ expectedBucketOwner,
65
+ forcePathStyle: input.forcePathStyle,
66
+ });
67
+
68
+ let totalSize = 0;
69
+
70
+ while (files.length > 0) {
71
+ totalSize += files.reduce((a, b) => {
72
+ return a + (b.Size ?? 0);
73
+ }, 0);
74
+ await cleanItems({
75
+ list: files.map((f) => f.Key as string),
76
+ bucket: input.bucketName,
77
+ onAfterItemDeleted: () => undefined,
78
+ onBeforeItemDeleted: () => undefined,
79
+ region: input.region,
80
+ providerSpecifics: input.providerSpecifics,
81
+ forcePathStyle: input.forcePathStyle,
82
+ });
83
+ files = await input.providerSpecifics.listObjects({
84
+ bucketName: input.bucketName,
85
+ prefix: rendersPrefix(input.renderId),
86
+ region: input.region,
87
+ expectedBucketOwner,
88
+ forcePathStyle: input.forcePathStyle,
89
+ });
90
+ }
91
+
92
+ return {
93
+ freedBytes: totalSize,
94
+ };
95
+ };
96
+
97
+ /*
98
+ * @description Deletes a rendered video, audio or still and its associated metadata.
99
+ * @see [Documentation](https://remotion.dev/docs/lambda/deleterender)
100
+ */
101
+ export const deleteRender = (input: DeleteRenderInput) => {
102
+ return internalDeleteRender({
103
+ ...input,
104
+ providerSpecifics: awsImplementation,
105
+ forcePathStyle: false,
106
+ });
107
+ };
@@ -0,0 +1,3 @@
1
+ export const encodeAwsUrlParams = (input: string) => {
2
+ return encodeURIComponent(input).replace(/%/g, '$25');
3
+ };
@@ -0,0 +1,95 @@
1
+ import {MIN_EPHEMERAL_STORAGE_IN_MB} from './constants';
2
+ import {pricing} from './price-per-1s';
3
+ import type {AwsRegion} from './regions';
4
+ import {validateAwsRegion} from './validate-aws-region';
5
+ import {validateDiskSizeInMb} from './validate-disk-size-in-mb';
6
+ import {validateMemorySize} from './validate-memory-size';
7
+
8
+ type Miliseconds =
9
+ | {
10
+ /**
11
+ * @deprecated Typo in property name. Use `durationInMilliseconds` instead.
12
+ */
13
+ durationInMiliseconds: number;
14
+ }
15
+ | {
16
+ durationInMilliseconds: number;
17
+ };
18
+
19
+ export type EstimatePriceInput = {
20
+ region: AwsRegion;
21
+ memorySizeInMb: number;
22
+ diskSizeInMb: number;
23
+ lambdasInvoked: number;
24
+ } & Miliseconds;
25
+
26
+ /*
27
+ * @description Calculates the AWS costs incurred for AWS Lambda given the region, execution duration and memory size.
28
+ * @see [Documentation](https://remotion.dev/docs/lambda/estimateprice)
29
+ */
30
+ export const estimatePrice = ({
31
+ region,
32
+ memorySizeInMb,
33
+ diskSizeInMb,
34
+ lambdasInvoked,
35
+ ...other
36
+ }: EstimatePriceInput): number => {
37
+ validateMemorySize(memorySizeInMb);
38
+ validateAwsRegion(region);
39
+ validateDiskSizeInMb(diskSizeInMb);
40
+
41
+ const durationInMilliseconds =
42
+ 'durationInMiliseconds' in other
43
+ ? other.durationInMiliseconds
44
+ : other.durationInMilliseconds;
45
+
46
+ if (typeof durationInMilliseconds !== 'number') {
47
+ throw new TypeError(
48
+ `Parameter 'durationInMilliseconds' must be a number but got ${typeof durationInMilliseconds}`,
49
+ );
50
+ }
51
+
52
+ if (Number.isNaN(durationInMilliseconds)) {
53
+ throw new TypeError(
54
+ `Parameter 'durationInMilliseconds' must not be NaN but it is.`,
55
+ );
56
+ }
57
+
58
+ if (!Number.isFinite(durationInMilliseconds)) {
59
+ throw new TypeError(
60
+ `Parameter 'durationInMilliseconds' must be finite but it is ${durationInMilliseconds}`,
61
+ );
62
+ }
63
+
64
+ if (durationInMilliseconds < 0) {
65
+ throw new TypeError(
66
+ `Parameter 'durationInMilliseconds' must be over 0 but it is ${durationInMilliseconds}.`,
67
+ );
68
+ }
69
+
70
+ const durationPrice = pricing[region]['Lambda Duration-ARM'].price;
71
+
72
+ // In GB-second
73
+ const timeCostDollars =
74
+ Number(durationPrice) *
75
+ ((memorySizeInMb * durationInMilliseconds) / 1000 / 1024);
76
+
77
+ const diskSizePrice = pricing[region]['Lambda Storage-Duration-ARM'].price;
78
+
79
+ const chargedDiskSize = Math.max(
80
+ 0,
81
+ diskSizeInMb - MIN_EPHEMERAL_STORAGE_IN_MB,
82
+ );
83
+ // In GB-second
84
+ const diskSizeDollars =
85
+ chargedDiskSize *
86
+ Number(diskSizePrice) *
87
+ (durationInMilliseconds / 1000 / 1024);
88
+
89
+ const invocationCost =
90
+ Number(pricing[region]['Lambda Requests'].price) * lambdasInvoked;
91
+
92
+ return Number(
93
+ (timeCostDollars + diskSizeDollars + invocationCost).toFixed(5),
94
+ );
95
+ };
@@ -0,0 +1,50 @@
1
+ import type {Request, Response} from 'express';
2
+ import type {NextWebhookArgs} from './app-router-webhook';
3
+ import {addHeaders} from './pages-router-webhook';
4
+ import {validateWebhookSignature} from './validate-webhook-signature';
5
+
6
+ export const expressWebhook = (options: NextWebhookArgs) => {
7
+ const {testing, extraHeaders, secret, onSuccess, onTimeout, onError} =
8
+ options;
9
+ return (req: Request, res: Response) => {
10
+ // add headers to enable testing
11
+ if (testing) {
12
+ const testingheaders = {
13
+ 'Access-Control-Allow-Origin': 'https://www.remotion.dev',
14
+ 'Access-Control-Allow-Headers':
15
+ 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, X-Remotion-Status, X-Remotion-Signature, X-Remotion-Mode',
16
+ 'Access-Control-Allow-Methods': 'OPTIONS,POST',
17
+ };
18
+ addHeaders(res, testingheaders);
19
+ }
20
+
21
+ // add extra headers
22
+ addHeaders(res, extraHeaders || {});
23
+
24
+ // dont go forward if just testing
25
+ if (req.method === 'OPTIONS') {
26
+ res.status(200).end();
27
+ return;
28
+ }
29
+
30
+ // validate the webhook signature
31
+ validateWebhookSignature({
32
+ signatureHeader: req.header('X-Remotion-Signature') as string,
33
+ body: req.body,
34
+ secret,
35
+ });
36
+
37
+ // custom logic
38
+ const payload = req.body;
39
+ if (payload.type === 'success' && onSuccess) {
40
+ onSuccess(payload);
41
+ } else if (payload.type === 'error' && onError) {
42
+ onError(payload);
43
+ } else if (payload.type === 'timeout' && onTimeout) {
44
+ onTimeout(payload);
45
+ }
46
+
47
+ // send response
48
+ res.status(200).json({success: true});
49
+ };
50
+ };
@@ -0,0 +1,22 @@
1
+ import {GetCallerIdentityCommand} from '@aws-sdk/client-sts';
2
+ import type {GetAccountId} from '@remotion/serverless-client';
3
+ import {getStsClient} from './aws-clients';
4
+ import type {AwsProvider} from './aws-provider';
5
+ import type {AwsRegion} from './regions';
6
+ import {validateAwsRegion} from './validate-aws-region';
7
+
8
+ export const getAccountIdImplementation: GetAccountId<
9
+ AwsProvider
10
+ > = async (options: {region: AwsRegion}) => {
11
+ validateAwsRegion(options.region);
12
+
13
+ const callerIdentity = await getStsClient(options.region).send(
14
+ new GetCallerIdentityCommand({}),
15
+ );
16
+
17
+ if (!callerIdentity.Account) {
18
+ throw new Error('Cannot get account ID');
19
+ }
20
+
21
+ return callerIdentity.Account;
22
+ };
@@ -0,0 +1,59 @@
1
+ import * as CloudWatchSDK from '@aws-sdk/client-cloudwatch-logs';
2
+ import * as IamSdk from '@aws-sdk/client-iam';
3
+ import * as LambdaSDK from '@aws-sdk/client-lambda';
4
+ import * as S3SDK from '@aws-sdk/client-s3';
5
+ import * as ServiceQuotasSDK from '@aws-sdk/client-service-quotas';
6
+ import * as StsSdk from '@aws-sdk/client-sts';
7
+ import type {CustomCredentials} from '@remotion/serverless-client';
8
+ import type {AwsProvider} from './aws-provider';
9
+ import {getServiceClient, type ServiceMapping} from './get-service-client';
10
+ import type {AwsRegion} from './regions';
11
+
12
+ export type GetAwsClientInput<T extends keyof ServiceMapping> = {
13
+ region: AwsRegion;
14
+ service: T;
15
+ customCredentials?: CustomCredentials<AwsProvider> | null;
16
+ forcePathStyle?: boolean;
17
+ };
18
+
19
+ type SdkMapping = {
20
+ s3: typeof S3SDK;
21
+ cloudwatch: typeof CloudWatchSDK;
22
+ iam: typeof IamSdk;
23
+ lambda: typeof LambdaSDK;
24
+ servicequotas: typeof ServiceQuotasSDK;
25
+ sts: typeof StsSdk;
26
+ };
27
+
28
+ export type GetAwsClientOutput<T extends keyof ServiceMapping> = {
29
+ client: ServiceMapping[T];
30
+ sdk: SdkMapping[T];
31
+ };
32
+
33
+ /*
34
+ * @description Exposes full access to the AWS SDK used by Remotion, allowing interaction with AWS infrastructure beyond provided functionalities.
35
+ * @see [Documentation](https://remotion.dev/docs/lambda/getawsclient)
36
+ */
37
+ export const getAwsClient = <T extends keyof ServiceMapping>({
38
+ region,
39
+ service,
40
+ customCredentials,
41
+ forcePathStyle,
42
+ }: GetAwsClientInput<T>): GetAwsClientOutput<T> => {
43
+ return {
44
+ client: getServiceClient({
45
+ region,
46
+ service,
47
+ customCredentials: customCredentials ?? null,
48
+ forcePathStyle: forcePathStyle ?? false,
49
+ }),
50
+ sdk: {
51
+ lambda: LambdaSDK,
52
+ cloudwatch: CloudWatchSDK,
53
+ iam: IamSdk,
54
+ s3: S3SDK,
55
+ servicequotas: ServiceQuotasSDK,
56
+ sts: StsSdk,
57
+ }[service],
58
+ };
59
+ };
@@ -0,0 +1,85 @@
1
+ import type {
2
+ GetLoggingUrlForRendererFunction,
3
+ ServerlessRoutines,
4
+ } from '@remotion/serverless-client';
5
+ import type {AwsProvider} from './aws-provider';
6
+ import {encodeAwsUrlParams} from './encode-aws-url-params';
7
+ import type {AwsRegion} from './regions';
8
+
9
+ const cloudWatchUrlWithQuery = ({
10
+ region,
11
+ functionNameToUse,
12
+ query,
13
+ }: {
14
+ region: AwsRegion;
15
+ functionNameToUse: string;
16
+ query: string;
17
+ }) => {
18
+ return `https://${region}.console.aws.amazon.com/cloudwatch/home?region=${region}#logsV2:log-groups/log-group/$252Faws$252Flambda$252F${functionNameToUse}/log-events$3FfilterPattern$3D${encodeAwsUrlParams(
19
+ query,
20
+ )}`;
21
+ };
22
+
23
+ export const getCloudwatchMethodUrl = ({
24
+ region,
25
+ functionName,
26
+ renderId,
27
+ rendererFunctionName,
28
+ method,
29
+ }: {
30
+ region: AwsRegion;
31
+ functionName: string;
32
+ method: ServerlessRoutines;
33
+ rendererFunctionName: string | null;
34
+ renderId: string;
35
+ }) => {
36
+ const functionNameToUse = rendererFunctionName ?? functionName;
37
+ const query = `"method=${method},renderId=${renderId}"`;
38
+
39
+ return cloudWatchUrlWithQuery({region, functionNameToUse, query});
40
+ };
41
+
42
+ export const getLambdaInsightsUrl = ({
43
+ region,
44
+ functionName,
45
+ }: {
46
+ region: AwsRegion;
47
+ functionName: string;
48
+ }) => {
49
+ return `https://${region}.console.aws.amazon.com/cloudwatch/home?region=${region}#lambda-insights:functions/${functionName}`;
50
+ };
51
+
52
+ export const getCloudwatchRendererUrl: GetLoggingUrlForRendererFunction<
53
+ AwsProvider
54
+ > = ({region, functionName, renderId, rendererFunctionName, chunk}) => {
55
+ const functionNameToUse = rendererFunctionName ?? functionName;
56
+ const query = `"method=renderer,renderId=${renderId}${
57
+ chunk === null ? '' : `,chunk=${chunk},`
58
+ }"`;
59
+
60
+ return cloudWatchUrlWithQuery({region, functionNameToUse, query});
61
+ };
62
+
63
+ export const getS3RenderUrl = ({
64
+ renderId,
65
+ region,
66
+ bucketName,
67
+ }: {
68
+ renderId: string;
69
+ region: AwsRegion;
70
+ bucketName: string;
71
+ }) => {
72
+ return `https://s3.console.aws.amazon.com/s3/buckets/${bucketName}?region=${region}&prefix=renders/${renderId}/`;
73
+ };
74
+
75
+ export const getProgressJsonUrl = ({
76
+ region,
77
+ bucketName,
78
+ renderId,
79
+ }: {
80
+ region: AwsRegion;
81
+ bucketName: string;
82
+ renderId: string;
83
+ }) => {
84
+ return `https://${region}.console.aws.amazon.com/s3/object/${bucketName}?region=${region}&bucketType=general&prefix=renders/${renderId}/progress.json`;
85
+ };
@@ -0,0 +1,81 @@
1
+ import {GetBucketLocationCommand, ListBucketsCommand} from '@aws-sdk/client-s3';
2
+ import type {ProviderSpecifics} from '@remotion/serverless-client';
3
+ import type {AwsProvider} from './aws-provider';
4
+ import {REMOTION_BUCKET_PREFIX} from './constants';
5
+ import {getS3Client} from './get-s3-client';
6
+ import type {AwsRegion} from './regions';
7
+ import {parseBucketName} from './validate-bucketname';
8
+
9
+ export type BucketWithLocation = {
10
+ name: string;
11
+ creationDate: number;
12
+ region: AwsRegion;
13
+ };
14
+
15
+ export const getRemotionBuckets: ProviderSpecifics<AwsProvider>['getBuckets'] =
16
+ async ({
17
+ region,
18
+ forceBucketName,
19
+ forcePathStyle,
20
+ }): Promise<BucketWithLocation[]> => {
21
+ const {Buckets} = await getS3Client({
22
+ region,
23
+ customCredentials: null,
24
+ forcePathStyle,
25
+ }).send(new ListBucketsCommand({}));
26
+ if (!Buckets) {
27
+ return [];
28
+ }
29
+
30
+ const remotionBuckets = Buckets.filter((b) => {
31
+ if (forceBucketName) {
32
+ return b.Name === forceBucketName;
33
+ }
34
+
35
+ return b.Name?.startsWith(REMOTION_BUCKET_PREFIX);
36
+ });
37
+
38
+ const locations = await Promise.all(
39
+ remotionBuckets.map(async (bucket) => {
40
+ const {region: parsedRegion} = parseBucketName(bucket.Name as string);
41
+ if (parsedRegion) {
42
+ return parsedRegion;
43
+ }
44
+
45
+ try {
46
+ const result = await getS3Client({
47
+ region,
48
+ customCredentials: null,
49
+ forcePathStyle,
50
+ }).send(
51
+ new GetBucketLocationCommand({
52
+ Bucket: bucket.Name as string,
53
+ }),
54
+ );
55
+ // AWS docs: Buckets in Region us-east-1 have a LocationConstraint of null!!
56
+ return result.LocationConstraint ?? ('us-east-1' as AwsRegion);
57
+ } catch (err) {
58
+ // Sometimes the API returns a bucket even if it was deleted before
59
+ if ((err as Error).stack?.includes('NoSuchBucket')) {
60
+ return null;
61
+ }
62
+
63
+ throw err;
64
+ }
65
+ }),
66
+ );
67
+
68
+ const bucketsWithLocation = remotionBuckets
69
+ .map((bucket, i): BucketWithLocation => {
70
+ return {
71
+ creationDate: (bucket.CreationDate as Date).getTime(),
72
+ name: bucket.Name as string,
73
+ region: locations[i] as AwsRegion,
74
+ };
75
+ })
76
+ .filter((b) => b.region);
77
+
78
+ return bucketsWithLocation.filter((bucket) => {
79
+ return bucket.region === region;
80
+ });
81
+ };
@@ -0,0 +1,104 @@
1
+ import type {
2
+ BrowserSafeApis,
3
+ ChromiumOptions,
4
+ ToOptions,
5
+ VideoConfig,
6
+ } from '@remotion/serverless-client';
7
+ import {
8
+ ServerlessRoutines,
9
+ VERSION,
10
+ compressInputProps,
11
+ getNeedsToUpload,
12
+ serializeOrThrow,
13
+ } from '@remotion/serverless-client';
14
+ import {awsImplementation} from './aws-provider';
15
+ import type {AwsRegion} from './regions';
16
+
17
+ export type GetCompositionsOnLambdaInput = {
18
+ chromiumOptions?: ChromiumOptions;
19
+ region: AwsRegion;
20
+ inputProps: Record<string, unknown>;
21
+ functionName: string;
22
+ serveUrl: string;
23
+ envVariables?: Record<string, string>;
24
+ forceBucketName?: string;
25
+ /**
26
+ * @deprecated in favor of `logLevel`: true
27
+ */
28
+ dumpBrowserLogs?: boolean;
29
+ forcePathStyle?: boolean;
30
+ } & Partial<
31
+ ToOptions<typeof BrowserSafeApis.optionsMap.getCompositionsOnLambda>
32
+ >;
33
+
34
+ export type GetCompositionsOnLambdaOutput = VideoConfig[];
35
+
36
+ /*
37
+ * @description Gets the compositions inside a Lambda function.
38
+ * @see [Documentation](https://remotion.dev/docs/lambda/getcompositionsonlambda)
39
+ */
40
+ export const getCompositionsOnLambda = async ({
41
+ chromiumOptions,
42
+ serveUrl,
43
+ region,
44
+ inputProps,
45
+ functionName,
46
+ envVariables,
47
+ logLevel,
48
+ timeoutInMilliseconds,
49
+ forceBucketName: bucketName,
50
+ dumpBrowserLogs,
51
+ offthreadVideoCacheSizeInBytes,
52
+ forcePathStyle,
53
+ }: GetCompositionsOnLambdaInput): Promise<GetCompositionsOnLambdaOutput> => {
54
+ const stringifiedInputProps = serializeOrThrow(inputProps, 'input-props');
55
+
56
+ const serializedInputProps = await compressInputProps({
57
+ stringifiedInputProps,
58
+ region,
59
+ userSpecifiedBucketName: bucketName ?? null,
60
+ propsType: 'input-props',
61
+ needsToUpload: getNeedsToUpload({
62
+ type: 'video-or-audio',
63
+ sizes: [
64
+ stringifiedInputProps.length,
65
+ JSON.stringify(envVariables).length,
66
+ ],
67
+ providerSpecifics: awsImplementation,
68
+ }),
69
+ providerSpecifics: awsImplementation,
70
+ forcePathStyle: forcePathStyle ?? false,
71
+ skipPutAcl: false,
72
+ });
73
+
74
+ try {
75
+ const res = await awsImplementation.callFunctionSync({
76
+ functionName,
77
+ type: ServerlessRoutines.compositions,
78
+ payload: {
79
+ type: ServerlessRoutines.compositions,
80
+ chromiumOptions: chromiumOptions ?? {},
81
+ serveUrl,
82
+ envVariables,
83
+ inputProps: serializedInputProps,
84
+ logLevel: dumpBrowserLogs ? 'verbose' : (logLevel ?? 'info'),
85
+ timeoutInMilliseconds: timeoutInMilliseconds ?? 30000,
86
+ version: VERSION,
87
+ bucketName: bucketName ?? null,
88
+ offthreadVideoCacheSizeInBytes: offthreadVideoCacheSizeInBytes ?? null,
89
+ forcePathStyle: forcePathStyle ?? false,
90
+ },
91
+ region,
92
+ timeoutInTest: 120000,
93
+ });
94
+ return res.compositions;
95
+ } catch (err) {
96
+ if ((err as Error).stack?.includes('UnrecognizedClientException')) {
97
+ throw new Error(
98
+ 'UnrecognizedClientException: The AWS credentials provided were probably mixed up. Learn how to fix this issue here: https://remotion.dev/docs/lambda/troubleshooting/unrecognizedclientexception',
99
+ );
100
+ }
101
+
102
+ throw err;
103
+ }
104
+ };
@@ -0,0 +1,81 @@
1
+ import {fromIni} from '@aws-sdk/credential-providers';
2
+ import {getEnvVariable} from './get-env-variable';
3
+ import {isInsideLambda} from './is-in-lambda';
4
+
5
+ type CredentialPair = {
6
+ accessKeyId: string;
7
+ secretAccessKey: string;
8
+ sessionToken?: string;
9
+ };
10
+ type AwsCredentialIdentityProvider = ReturnType<typeof fromIni>;
11
+
12
+ export const getCredentials = ():
13
+ | CredentialPair
14
+ | AwsCredentialIdentityProvider
15
+ | undefined => {
16
+ if (isInsideLambda()) {
17
+ return undefined;
18
+ }
19
+
20
+ if (getEnvVariable('REMOTION_AWS_PROFILE')) {
21
+ return fromIni({
22
+ profile: getEnvVariable('REMOTION_AWS_PROFILE'),
23
+ });
24
+ }
25
+
26
+ if (
27
+ getEnvVariable('REMOTION_AWS_ACCESS_KEY_ID') &&
28
+ getEnvVariable('REMOTION_AWS_SECRET_ACCESS_KEY') &&
29
+ getEnvVariable('REMOTION_AWS_SESSION_TOKEN')
30
+ ) {
31
+ return {
32
+ accessKeyId: getEnvVariable('REMOTION_AWS_ACCESS_KEY_ID') as string,
33
+ secretAccessKey: getEnvVariable(
34
+ 'REMOTION_AWS_SECRET_ACCESS_KEY',
35
+ ) as string,
36
+ sessionToken: getEnvVariable('REMOTION_AWS_SESSION_TOKEN') as string,
37
+ };
38
+ }
39
+
40
+ if (
41
+ getEnvVariable('REMOTION_AWS_ACCESS_KEY_ID') &&
42
+ getEnvVariable('REMOTION_AWS_SECRET_ACCESS_KEY')
43
+ ) {
44
+ return {
45
+ accessKeyId: getEnvVariable('REMOTION_AWS_ACCESS_KEY_ID') as string,
46
+ secretAccessKey: getEnvVariable(
47
+ 'REMOTION_AWS_SECRET_ACCESS_KEY',
48
+ ) as string,
49
+ };
50
+ }
51
+
52
+ if (getEnvVariable('AWS_PROFILE')) {
53
+ return fromIni({
54
+ profile: getEnvVariable('AWS_PROFILE'),
55
+ });
56
+ }
57
+
58
+ if (
59
+ getEnvVariable('AWS_ACCESS_KEY_ID') &&
60
+ getEnvVariable('AWS_SECRET_ACCESS_KEY') &&
61
+ getEnvVariable('AWS_SESSION_TOKEN')
62
+ ) {
63
+ return {
64
+ accessKeyId: getEnvVariable('AWS_ACCESS_KEY_ID') as string,
65
+ secretAccessKey: getEnvVariable('AWS_SECRET_ACCESS_KEY') as string,
66
+ sessionToken: getEnvVariable('AWS_SESSION_TOKEN') as string,
67
+ };
68
+ }
69
+
70
+ if (
71
+ getEnvVariable('AWS_ACCESS_KEY_ID') &&
72
+ getEnvVariable('AWS_SECRET_ACCESS_KEY')
73
+ ) {
74
+ return {
75
+ accessKeyId: getEnvVariable('AWS_ACCESS_KEY_ID') as string,
76
+ secretAccessKey: getEnvVariable('AWS_SECRET_ACCESS_KEY') as string,
77
+ };
78
+ }
79
+
80
+ return undefined;
81
+ };