From 65d2a09319b6fd1859b6fde25e407e220837cd3f Mon Sep 17 00:00:00 2001 From: Archana Venkitaramanan Date: Wed, 16 Apr 2025 16:50:32 +0530 Subject: [PATCH 1/7] Commit apigw-lambda-transcribe --- apigw-lambda-transcribe/README.md | 94 ++++++++ apigw-lambda-transcribe/Transcribe.png | Bin 0 -> 21034 bytes apigw-lambda-transcribe/example-pattern.json | 58 +++++ .../generate_presigned_url.zip | Bin 0 -> 806 bytes apigw-lambda-transcribe/main.tf | 214 ++++++++++++++++++ apigw-lambda-transcribe/process_s3_event.zip | Bin 0 -> 1318 bytes 6 files changed, 366 insertions(+) create mode 100644 apigw-lambda-transcribe/README.md create mode 100644 apigw-lambda-transcribe/Transcribe.png create mode 100644 apigw-lambda-transcribe/example-pattern.json create mode 100644 apigw-lambda-transcribe/generate_presigned_url.zip create mode 100644 apigw-lambda-transcribe/main.tf create mode 100644 apigw-lambda-transcribe/process_s3_event.zip diff --git a/apigw-lambda-transcribe/README.md b/apigw-lambda-transcribe/README.md new file mode 100644 index 000000000..daa0c33d9 --- /dev/null +++ b/apigw-lambda-transcribe/README.md @@ -0,0 +1,94 @@ +# Audio transcription with AWS Lambda and Amazon Transcribe + +Using this sample pattern, users can securely upload images to an Amazon S3 bucket by requesting a pre-signed URL through Amazon API Gateway. This URL allows secure and temporary access for uploading files directly to S3. + +Once an audio file is uploaded, an S3 event invokes another Lambda function to start the Transcribe job using the StartTranscriptionJob API. Once the transcription is completed, the result will be stored in the output S3 bucket. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/apigw-lambda-transcribe + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [Terraform](https://learn.hashicorp.cxom/tutorials/terraform/install-cli?in=terraform/aws-get-started) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd apigw-lambda-transcribe + ``` +1. From the command line, initialize terraform to downloads and installs the providers defined in the configuration: + ``` + terraform init + ``` +1. From the command line, apply the configuration in the main.tf file: + ``` + terraform apply + ``` +1. During the prompts + #var.prefix + - Enter a value: {enter any prefix to associate with resources} + + #var.region + - Enter a value: {enter the region for deployment} + +## Testing + +1. Make a POST request to the API using the following cURL command: + + curl --location 'https://.execute-api..amazonaws.com/dev/generate-presigned-url' --header 'Content-Type: application/json' --data '{"object_name": "audio.mp3", "content_type": "audio/mpeg"}' + + Note: Replace 'api-id' with the generated API ID from Terraform, 'region' with the region where the API is deployed (refer to the Terraform Outputs section) 'object_name' with your desired name for the S3 object and 'content_type' with the content type of the audio, for ex, mp3 or m4a + +1. Get the pre-signed URL from the previous step and use the following cURL command to upload the object in S3: + + curl -v --location --request PUT '' --header 'Content-Type: application/json' --data '.mp3' + + Note: Replace 'presigned-url' with pre-signed URL generated in the previous step. 'Content-Type' should match the content type used to generate the pre-signed URL in the previous step. Make sure you are passing the correct path of the object in the --data parameter. + + Once this command is run successfully and the object is uploaded, HTTP 200 OK should be seen. You can also check the S3 bucket to see if the object is uploaded correctly. + +1. Once the object is uploaded successfully, the "process_s3_event" Lambda function is invoked. Lambda function will then invoke the StartTranscriptionJob API and Amazon Transcibe will upload the transcribed output to the output S3 bucket. + +## Cleanup + +1. Delete the Transcription jobs: + Go to Transcribe > Transcription jobs > Select your transcription jobs and choose Delete + +1. Delete all the objects from S3: + Go to S3 > Select all objects > choose Delete + + https://docs.aws.amazon.com/AmazonS3/latest/userguide/delete-objects.html + https://docs.aws.amazon.com/AmazonS3/latest/userguide/delete-multiple-objects.html + +1. Change directory to the pattern directory: + ``` + cd serverless-patterns/apigw-lambda-rekognition + ``` + +1. Delete all created resources + ``` + terraform destroy + ``` + +1. During the prompts: + ``` + Enter all details as entered during creation. + ``` + +1. Confirm all created resources has been deleted + ``` + terraform show + ``` +---- +Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 \ No newline at end of file diff --git a/apigw-lambda-transcribe/Transcribe.png b/apigw-lambda-transcribe/Transcribe.png new file mode 100644 index 0000000000000000000000000000000000000000..635c48dc3a269ce2bb5a8ec373f084124920c33d GIT binary patch literal 21034 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 F{||+E z^X*jE3woNn#H>YH7v9*i_}ny`&Uceoe}4DeH222cRLAArDwCA%FP?X#J4j3Ad{h_X z`s+d4S5I}Z%rxJ5#z=2d<6Osynb{mh2c*+K|2Za^k=!JHVy{`+z8{;uuyYydwOVSG zGCXS4)|7ks^LzfqyKl1g#avk4IM1cqn*L5}`g_Syhg)h%kSNdIUCZ9{3wd~+>k2)z$W+)f?}JnaQ@hGEro`qt z3x%{btA4O2s6;TXpAcsg6L;kqT-Gjm=}e&6>s zwVQ(WxnOL!F0FC z$Y(ZXcO_0rFy7A$Il%SmwqH%#lu*lWGq#85cKSV37G;glbSz_UmJLxp>=l2CCBwt6 z=lJ1$w=eQ!|MLu;a_e?rfR|SClGEp0XMEHA5n0H}dG~j_O7(W9t+tOeL^I}bdaP0M z73TY8I#(b;gdtG>$l}!2K;O=959gZX%}i>Y_wGe&@M^^i6W>aI+SVM`oEy6Q`+wz^ zskKMHhlwZp+y6FSGXLgwwko}4$HSk`Uo_?W|0PrI*-EA7|DW!aAne@9Hhb^+|I`$3&SZbwxPQ6&$6pnfxHG&;I{q*Qcr!A|G2=?I s62PR)zyM5%3`-h8EIbLA6_S9_5^sPvD=_VYj9_Gl2htmXxq*QJ04Fa|!2kdN literal 0 HcmV?d00001 diff --git a/apigw-lambda-transcribe/main.tf b/apigw-lambda-transcribe/main.tf new file mode 100644 index 000000000..b110ced25 --- /dev/null +++ b/apigw-lambda-transcribe/main.tf @@ -0,0 +1,214 @@ +variable "region" {} + +provider "aws" { + region = var.region +} + +variable "prefix" { + description = "Prefix to associate with the resources" + type = string +} + +resource "random_string" "suffix" { + length = 8 + special = false + upper = false +} + +resource "aws_s3_bucket" "upload_bucket" { + bucket = "${lower(var.prefix)}-s3-upload-${random_string.suffix.result}" +} + +resource "aws_s3_bucket" "output_bucket" { + bucket = "${lower(var.prefix)}-s3-output-${random_string.suffix.result}" +} + +resource "aws_iam_role" "lambda_role" { + name = "${lower(var.prefix)}-lambda-execution-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Principal = { + Service = "lambda.amazonaws.com" + }, + Action = "sts:AssumeRole" + } + ] + }) +} + +resource "aws_iam_role_policy" "lambda_policy" { + name = "${lower(var.prefix)}-lambda-policy" + role = aws_iam_role.lambda_role.id + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Action = [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + Resource = "*" + }, + { + Effect = "Allow", + Action = [ + "s3:PutObject", + "s3:GetObject", + "s3:ListBucket" + ], + Resource = [ + aws_s3_bucket.upload_bucket.arn, + "${aws_s3_bucket.upload_bucket.arn}/*", + aws_s3_bucket.output_bucket.arn, + "${aws_s3_bucket.output_bucket.arn}/*" + ] + }, + { + Effect = "Allow", + Action = "transcribe:StartTranscriptionJob", + Resource = "*" + } + ] + }) +} + +resource "aws_cloudwatch_log_group" "lambda_log_group" { + count = 2 + name = "/aws/lambda/${lower(var.prefix)}-${element(["generate-presigned-url", "process-s3-event"], count.index)}" + retention_in_days = 14 +} + +resource "aws_lambda_function" "generate_presigned_url" { + filename = "generate_presigned_url.zip" + function_name = "${lower(var.prefix)}-generate-presigned-url" + role = aws_iam_role.lambda_role.arn + handler = "generate_presigned_url.lambda_handler" + runtime = "python3.12" + timeout = 30 + environment { + variables = { + BUCKET_NAME = aws_s3_bucket.upload_bucket.id + } + } +} + +resource "aws_lambda_function" "process_s3_event" { + filename = "process_s3_event.zip" + function_name = "${lower(var.prefix)}-process-s3-event" + role = aws_iam_role.lambda_role.arn + handler = "process_s3_event.lambda_handler" + runtime = "python3.12" + timeout = 60 + environment { + variables = { + OUTPUT_BUCKET = aws_s3_bucket.output_bucket.id + } + } +} + +resource "aws_s3_bucket_notification" "s3_bucket_notification" { + bucket = aws_s3_bucket.upload_bucket.id + lambda_function { + lambda_function_arn = aws_lambda_function.process_s3_event.arn + events = ["s3:ObjectCreated:*"] + } +} + +resource "aws_lambda_permission" "allow_s3" { + statement_id = "${lower(var.prefix)}-allow-s3-invoke-lambda" + action = "lambda:InvokeFunction" + function_name = aws_lambda_function.process_s3_event.function_name + principal = "s3.amazonaws.com" + source_arn = aws_s3_bucket.upload_bucket.arn +} + +resource "aws_api_gateway_rest_api" "api" { + name = "${lower(var.prefix)}-presigned-url-api" + description = "API for generating presigned URLs" +} + +resource "aws_api_gateway_resource" "resource" { + rest_api_id = aws_api_gateway_rest_api.api.id + parent_id = aws_api_gateway_rest_api.api.root_resource_id + path_part = "generate-presigned-url" +} + +resource "aws_api_gateway_method" "method" { + rest_api_id = aws_api_gateway_rest_api.api.id + resource_id = aws_api_gateway_resource.resource.id + http_method = "POST" + authorization = "NONE" +} + +resource "aws_api_gateway_integration" "integration" { + rest_api_id = aws_api_gateway_rest_api.api.id + resource_id = aws_api_gateway_resource.resource.id + http_method = aws_api_gateway_method.method.http_method + integration_http_method = "POST" + type = "AWS_PROXY" + uri = "arn:aws:apigateway:${var.region}:lambda:path/2015-03-31/functions/${aws_lambda_function.generate_presigned_url.arn}/invocations" +} + +resource "aws_lambda_permission" "allow_api_gateway" { + statement_id = "${lower(var.prefix)}-allow-api-gateway-invoke" + action = "lambda:InvokeFunction" + function_name = aws_lambda_function.generate_presigned_url.function_name + principal = "apigateway.amazonaws.com" + source_arn = "${aws_api_gateway_rest_api.api.execution_arn}/*/*" +} + +resource "aws_api_gateway_deployment" "deployment" { + depends_on = [aws_api_gateway_integration.integration] + rest_api_id = aws_api_gateway_rest_api.api.id + stage_name = "dev" +} + +output "api_id" { + description = "The ID of the API Gateway REST API" + value = aws_api_gateway_rest_api.api.id +} + +output "region" { + description = "The AWS region where resources are deployed" + value = var.region +} + +output "upload_bucket_name" { + description = "The name of the S3 upload bucket" + value = aws_s3_bucket.upload_bucket.bucket +} + +output "output_bucket_name" { + description = "The name of the S3 output bucket" + value = aws_s3_bucket.output_bucket.bucket +} + +output "lambda_generate_presigned_url_arn" { + description = "The ARN of the generate_presigned_url Lambda function" + value = aws_lambda_function.generate_presigned_url.arn +} + +output "lambda_process_s3_event_arn" { + description = "The ARN of the process_s3_event Lambda function" + value = aws_lambda_function.process_s3_event.arn +} + +output "lambda_generate_presigned_url_log_group" { + description = "The name of the CloudWatch log group for the generate_presigned_url Lambda function" + value = aws_cloudwatch_log_group.lambda_log_group[0].name +} + +output "lambda_process_s3_event_log_group" { + description = "The name of the CloudWatch log group for the process_s3_event Lambda function" + value = aws_cloudwatch_log_group.lambda_log_group[1].name +} + +output "api_endpoint" { + description = "The API Gateway endpoint URL" + value = "${aws_api_gateway_deployment.deployment.invoke_url}/${aws_api_gateway_resource.resource.path_part}" +} diff --git a/apigw-lambda-transcribe/process_s3_event.zip b/apigw-lambda-transcribe/process_s3_event.zip new file mode 100644 index 0000000000000000000000000000000000000000..3eba40f32b1774eeb5b4d750e2ba0f2bf33a2162 GIT binary patch literal 1318 zcmWIWW@Zs#-~hrcJ`*0UWr~oWoZRB10%~1Mg|6; zJ_d%+5MBm$9^L=xFg!IhHvh7LNbULc8;&&#uRY5p%D-Af#f;a4O=ROV_C-QZAD4Zd zcI#l-F|Vut&y{Y{Dcs!j!MQqnes#^e_WYZ>1cD|mE8*3%5VG zR1_?orrYYWcI5=g#5o~9Ce*Dp6N%kou{mzSca6tYKFdz(E}0ecY3T-rtrt3uXA2kl zO3#WAQ(Sw#C$%rI;QB{Cvlkc6E{^{+f9VAQ(aM!Ev%NyDOLPBy!6clz`VZSAUGpVv zf)xo~+T2O03nH`LtXDX<=+;@=)nC}=PGEb&WyToy@@ozE$qiYj7iWGy#Tn%1=eJF^ z>+VUhzvb>`%r;8`GKvCg4G(&*;rPO8Ha*6p)73kC!ac*f&l1mCa;6?@s{Z{X?c@y2 zjGxg9XEM0%s0jHJesHhf1BGPXly5gqZvDisq&WZL`MmB3$J-+Nw1mSieK?*`A;n+6 zU$*Z1;)hLq9K9cy^FN19G_GN``+Df&p}uEfD!jp`t9*Q3s98P>{q^kd*FDXBy^?)u z`u_gnEe|&{zB9WmS1CLpe6e7o%;alt-W{*kvxtjI;`_>VLj6R>kKYb<~Siw(hb3Ysq_#L@Z@641n+`^as%FK@Z8FMf5dF9(I+y4@7 zrT!PQ*aGwOch7UU_y5S(UjZ?u;qG_k1H9Qe)~()?{ho<|VGcV3LjcbFBnQk-@$tTn z&i=s>`g-vsW~~yv|LMsIqRHtANeLf(eZoHQhjj!rFintPRu_i=a&t{pq(do^X0wXJ+;U)C|_Lw9_^_WwJtZ8~FC#vZv;Yv-H- z^Yd*>ZhG6zH{?}4_7~`7c8)Elj+adb76&50av{K*kx7IZQK%pXk1&G-yog!S2x1Zw o4y+7}pyCDHU}Q_>Pz(-WW*{}J;dZdHftFbjwW00qDaJ^%m! literal 0 HcmV?d00001 From d6a564f1fe2456fd6e88a15824144ff050ed0f22 Mon Sep 17 00:00:00 2001 From: Archana Venkitaramanan Date: Thu, 17 Apr 2025 18:04:34 +0530 Subject: [PATCH 2/7] Updated --- apigw-lambda-transcribe/README.md | 8 +------- apigw-lambda-transcribe/main.tf | 2 ++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/apigw-lambda-transcribe/README.md b/apigw-lambda-transcribe/README.md index daa0c33d9..972dd65f4 100644 --- a/apigw-lambda-transcribe/README.md +++ b/apigw-lambda-transcribe/README.md @@ -63,15 +63,9 @@ Important: this application uses various AWS services and there are costs associ 1. Delete the Transcription jobs: Go to Transcribe > Transcription jobs > Select your transcription jobs and choose Delete -1. Delete all the objects from S3: - Go to S3 > Select all objects > choose Delete - - https://docs.aws.amazon.com/AmazonS3/latest/userguide/delete-objects.html - https://docs.aws.amazon.com/AmazonS3/latest/userguide/delete-multiple-objects.html - 1. Change directory to the pattern directory: ``` - cd serverless-patterns/apigw-lambda-rekognition + cd serverless-patterns/apigw-lambda-transcribe ``` 1. Delete all created resources diff --git a/apigw-lambda-transcribe/main.tf b/apigw-lambda-transcribe/main.tf index b110ced25..4b5911fee 100644 --- a/apigw-lambda-transcribe/main.tf +++ b/apigw-lambda-transcribe/main.tf @@ -17,10 +17,12 @@ resource "random_string" "suffix" { resource "aws_s3_bucket" "upload_bucket" { bucket = "${lower(var.prefix)}-s3-upload-${random_string.suffix.result}" + force_destroy = true } resource "aws_s3_bucket" "output_bucket" { bucket = "${lower(var.prefix)}-s3-output-${random_string.suffix.result}" + force_destroy = true } resource "aws_iam_role" "lambda_role" { From 52b7460cad3459ffa67c105c68d9467e11bcd0bc Mon Sep 17 00:00:00 2001 From: Archana Venkitaramanan Date: Fri, 18 Apr 2025 13:17:25 +0530 Subject: [PATCH 3/7] Updates --- apigw-lambda-transcribe/example-pattern.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apigw-lambda-transcribe/example-pattern.json b/apigw-lambda-transcribe/example-pattern.json index fc31b4f40..e94ae9690 100644 --- a/apigw-lambda-transcribe/example-pattern.json +++ b/apigw-lambda-transcribe/example-pattern.json @@ -50,7 +50,7 @@ "authors": [ { "name": "Archana V", - "image": "https://media.licdn.com/dms/image/v2/D5603AQHfEnMkJSFiGQ/profile-displayphoto-shrink_200_200/profile-displayphoto-shrink_200_200/0/1731306594216?e=1750291200&v=beta&t=LkZ1hyXT40DiX6CnWPb-KslCdZ0pXMRJXOwys_NpLCE", + "image": "https://media.licdn.com/dms/image/v2/D5603AQGhkVtEhllFEw/profile-displayphoto-shrink_400_400/B56ZZH3LL6H0Ag-/0/1744962369913?e=1750291200&v=beta&t=R0hX6jzWC03OyoWKvYJ0jDDTuPocobPSy0lAJY-3XfA", "bio": "Solutions Architect at AWS", "linkedin": "archana-venkat-9b80b7184" } From 908529d27f79ef36ab8f8b4af975591e94f0f536 Mon Sep 17 00:00:00 2001 From: Archana Venkitaramanan Date: Tue, 22 Apr 2025 16:15:56 +0530 Subject: [PATCH 4/7] Updated with the requested changes --- apigw-lambda-transcribe/README.md | 20 ++++++++++++++------ apigw-lambda-transcribe/main.tf | 15 ++++++++++++--- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/apigw-lambda-transcribe/README.md b/apigw-lambda-transcribe/README.md index 972dd65f4..0ef62ea3b 100644 --- a/apigw-lambda-transcribe/README.md +++ b/apigw-lambda-transcribe/README.md @@ -34,29 +34,37 @@ Important: this application uses various AWS services and there are costs associ terraform apply ``` 1. During the prompts + ``` #var.prefix - Enter a value: {enter any prefix to associate with resources} #var.region - Enter a value: {enter the region for deployment} - + ``` + ## Testing 1. Make a POST request to the API using the following cURL command: - curl --location 'https://.execute-api..amazonaws.com/dev/generate-presigned-url' --header 'Content-Type: application/json' --data '{"object_name": "audio.mp3", "content_type": "audio/mpeg"}' + ``` + curl --location 'API_ENDPOINT' --header 'Content-Type: application/json' --data '{"object_name": "audio.mp3", "content_type": "audio/mpeg"}' + ``` - Note: Replace 'api-id' with the generated API ID from Terraform, 'region' with the region where the API is deployed (refer to the Terraform Outputs section) 'object_name' with your desired name for the S3 object and 'content_type' with the content type of the audio, for ex, mp3 or m4a + Note: Replace `API_ENDPOINT` with the generated `api_endpoint` from Terraform (refer to the Terraform Outputs section) `object_name` with your desired name for the S3 object and `content_type` with the content type of the audio, for ex, mp3 or m4a 1. Get the pre-signed URL from the previous step and use the following cURL command to upload the object in S3: - curl -v --location --request PUT '' --header 'Content-Type: application/json' --data '.mp3' + ``` + curl -v --location -T "audio.mp3" \ + 'PRESIGNED_URL' \ + --header 'Content-Type: audio/mpeg' + ``` - Note: Replace 'presigned-url' with pre-signed URL generated in the previous step. 'Content-Type' should match the content type used to generate the pre-signed URL in the previous step. Make sure you are passing the correct path of the object in the --data parameter. + Note: Replace `PRESIGNED_URL` with pre-signed URL generated in the previous step. `Content-Type` should match the content type used to generate the pre-signed URL in the previous step. Once this command is run successfully and the object is uploaded, HTTP 200 OK should be seen. You can also check the S3 bucket to see if the object is uploaded correctly. -1. Once the object is uploaded successfully, the "process_s3_event" Lambda function is invoked. Lambda function will then invoke the StartTranscriptionJob API and Amazon Transcibe will upload the transcribed output to the output S3 bucket. +1. Once the object is uploaded successfully, the `process_s3_event` Lambda function is invoked. Lambda function will then invoke the `StartTranscriptionJob` API and Amazon Transcribe will upload the transcribed output to the output S3 bucket (Refer to the Terraform Outputs section under `output_bucket_name`). ## Cleanup diff --git a/apigw-lambda-transcribe/main.tf b/apigw-lambda-transcribe/main.tf index 4b5911fee..6486fb864 100644 --- a/apigw-lambda-transcribe/main.tf +++ b/apigw-lambda-transcribe/main.tf @@ -167,7 +167,16 @@ resource "aws_lambda_permission" "allow_api_gateway" { resource "aws_api_gateway_deployment" "deployment" { depends_on = [aws_api_gateway_integration.integration] rest_api_id = aws_api_gateway_rest_api.api.id - stage_name = "dev" + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_api_gateway_stage" "stage" { + deployment_id = aws_api_gateway_deployment.deployment.id + rest_api_id = aws_api_gateway_rest_api.api.id + stage_name = "dev" } output "api_id" { @@ -212,5 +221,5 @@ output "lambda_process_s3_event_log_group" { output "api_endpoint" { description = "The API Gateway endpoint URL" - value = "${aws_api_gateway_deployment.deployment.invoke_url}/${aws_api_gateway_resource.resource.path_part}" -} + value = "${aws_api_gateway_stage.stage.invoke_url}/${aws_api_gateway_resource.resource.path_part}" +} \ No newline at end of file From 50dfc56fd97be3b0ab0a22dd94f522d4dce45971 Mon Sep 17 00:00:00 2001 From: Archana Venkitaramanan Date: Thu, 24 Apr 2025 17:55:54 +0530 Subject: [PATCH 5/7] Changed audio file to video --- apigw-lambda-transcribe/README.md | 14 +++++++------- apigw-lambda-transcribe/example-pattern.json | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apigw-lambda-transcribe/README.md b/apigw-lambda-transcribe/README.md index 0ef62ea3b..6a3225cbd 100644 --- a/apigw-lambda-transcribe/README.md +++ b/apigw-lambda-transcribe/README.md @@ -1,8 +1,8 @@ -# Audio transcription with AWS Lambda and Amazon Transcribe +# Subtitle generation with AWS Lambda and Amazon Transcribe -Using this sample pattern, users can securely upload images to an Amazon S3 bucket by requesting a pre-signed URL through Amazon API Gateway. This URL allows secure and temporary access for uploading files directly to S3. +Using this sample pattern, users can securely upload videos to an Amazon S3 bucket by requesting a pre-signed URL through Amazon API Gateway. This URL allows secure and temporary access for uploading files directly to S3. -Once an audio file is uploaded, an S3 event invokes another Lambda function to start the Transcribe job using the StartTranscriptionJob API. Once the transcription is completed, the result will be stored in the output S3 bucket. +Once a video file is uploaded, an S3 event invokes another Lambda function to start the Transcribe job using the StartTranscriptionJob API. Once the transcription is completed, the generated subtitles will be stored in the output S3 bucket. Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/apigw-lambda-transcribe @@ -47,17 +47,17 @@ Important: this application uses various AWS services and there are costs associ 1. Make a POST request to the API using the following cURL command: ``` - curl --location 'API_ENDPOINT' --header 'Content-Type: application/json' --data '{"object_name": "audio.mp3", "content_type": "audio/mpeg"}' + curl --location 'API_ENDPOINT' --header 'Content-Type: application/json' --data '{"object_name": "video.mp4", "content_type": "video/mp4"}' ``` - Note: Replace `API_ENDPOINT` with the generated `api_endpoint` from Terraform (refer to the Terraform Outputs section) `object_name` with your desired name for the S3 object and `content_type` with the content type of the audio, for ex, mp3 or m4a + Note: Replace `API_ENDPOINT` with the generated `api_endpoint` from Terraform (refer to the Terraform Outputs section) `object_name` with your desired name for the S3 object and `content_type` with the content type of the video, for ex, mp4. 1. Get the pre-signed URL from the previous step and use the following cURL command to upload the object in S3: ``` - curl -v --location -T "audio.mp3" \ + curl -v --location -T "video.mp4" \ 'PRESIGNED_URL' \ - --header 'Content-Type: audio/mpeg' + --header 'Content-Type: video/mp4' ``` Note: Replace `PRESIGNED_URL` with pre-signed URL generated in the previous step. `Content-Type` should match the content type used to generate the pre-signed URL in the previous step. diff --git a/apigw-lambda-transcribe/example-pattern.json b/apigw-lambda-transcribe/example-pattern.json index e94ae9690..bcefe836f 100644 --- a/apigw-lambda-transcribe/example-pattern.json +++ b/apigw-lambda-transcribe/example-pattern.json @@ -1,5 +1,5 @@ { - "title": "Audio Transcription using AWS API Gateway and AWS Lambda", + "title": "Subtitle generation using AWS API Gateway and AWS Lambda", "description": "This pattern creates an AWS Lambda function which will invoke Amazon Transcribe for speech-to-text conversion, and stores results in Amazon S3", "language": "Python", "level": "200", @@ -7,7 +7,7 @@ "introBox": { "headline": "How it works", "text": [ - "This sample pattern is an automated serverless solution for audio transcription using AWS services. This system securely handles audio file uploads via pre-signed URLs, automatically triggers Amazon Transcribe for speech-to-text conversion, and stores results in S3." + "This sample pattern is an automated serverless solution for subtitle generation using AWS services. This system securely handles video file uploads via pre-signed URLs, automatically triggers Amazon Transcribe for speech-to-text conversion, and stores results in S3." ] }, "gitHub": { From f98e4379efa7305b008bcd1fe8915ffb9db7445b Mon Sep 17 00:00:00 2001 From: Archana Venkitaramanan Date: Fri, 25 Apr 2025 15:12:04 +0530 Subject: [PATCH 6/7] Readme Updates --- apigw-lambda-transcribe/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apigw-lambda-transcribe/README.md b/apigw-lambda-transcribe/README.md index 6a3225cbd..c985a4052 100644 --- a/apigw-lambda-transcribe/README.md +++ b/apigw-lambda-transcribe/README.md @@ -55,9 +55,7 @@ Important: this application uses various AWS services and there are costs associ 1. Get the pre-signed URL from the previous step and use the following cURL command to upload the object in S3: ``` - curl -v --location -T "video.mp4" \ - 'PRESIGNED_URL' \ - --header 'Content-Type: video/mp4' + curl -v --location -T "video.mp4" 'PRESIGNED_URL' --header 'Content-Type: video/mp4' ``` Note: Replace `PRESIGNED_URL` with pre-signed URL generated in the previous step. `Content-Type` should match the content type used to generate the pre-signed URL in the previous step. From 4a586a01dbf72c9ac3eaa95f0c17de8e951037d8 Mon Sep 17 00:00:00 2001 From: Marco Date: Fri, 25 Apr 2025 14:02:16 +0200 Subject: [PATCH 7/7] Create apigw-lambda-transcribe.json --- .../apigw-lambda-transcribe.json | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 apigw-lambda-transcribe/apigw-lambda-transcribe.json diff --git a/apigw-lambda-transcribe/apigw-lambda-transcribe.json b/apigw-lambda-transcribe/apigw-lambda-transcribe.json new file mode 100644 index 000000000..dad6e1f7e --- /dev/null +++ b/apigw-lambda-transcribe/apigw-lambda-transcribe.json @@ -0,0 +1,91 @@ +{ + "title": "Subtitle generation using AWS API Gateway and AWS Lambda", + "description": "This pattern creates an AWS Lambda function which will invoke Amazon Transcribe for speech-to-text conversion, and stores results in Amazon S3", + "language": "Python", + "level": "200", + "framework": "Terraform", + "introBox": { + "headline": "How it works", + "text": [ + "This sample pattern is an automated serverless solution for subtitle generation using AWS services. This system securely handles video file uploads via pre-signed URLs, automatically triggers Amazon Transcribe for speech-to-text conversion, and stores results in S3." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-lambda-transcribe", + "templateURL": "serverless-patterns/apigw-lambda-transcribe", + "projectFolder": "apigw-lambda-transcribe", + "templateFile": "main.tf" + } + }, + "resources": { + "bullets": [ + { + "text": "Uploading objects with presigned URLs", + "link": "https://docs.aws.amazon.com/AmazonS3/latest/userguide/PresignedUrlUploadObject.html" + }, + { + "text": "StartTranscriptionJob", + "link": "https://docs.aws.amazon.com/transcribe/latest/APIReference/API_StartTranscriptionJob.html" + } + ] + }, + "deploy": { + "text": ["terraform init", "terraform apply"] + }, + "testing": { + "text": ["See the GitHub repo for detailed testing instructions."] + }, + "cleanup": { + "text": ["terraform destroy", "terraform show"] + }, + "authors": [ + { + "name": "Archana V", + "image": "https://media.licdn.com/dms/image/v2/D5603AQGhkVtEhllFEw/profile-displayphoto-shrink_400_400/B56ZZH3LL6H0Ag-/0/1744962369913?e=1750291200&v=beta&t=R0hX6jzWC03OyoWKvYJ0jDDTuPocobPSy0lAJY-3XfA", + "bio": "Solutions Architect at AWS", + "linkedin": "archana-venkat-9b80b7184" + } + ], + "patternArch": { + "icon1": { + "x": 15, + "y": 50, + "service": "s3", + "label": "Amazon S3" + }, + "icon2": { + "x": 40, + "y": 50, + "service": "lambda", + "label": "AWS Lambda" + }, + "icon3": { + "x": 65, + "y": 50, + "service": "transcribe", + "label": "Amazon Transcribe" + }, + "icon4": { + "x": 90, + "y": 50, + "service": "s3", + "label": "Amazon S3" + }, + "line1": { + "from": "icon1", + "to": "icon2", + "label": "" + }, + "line2": { + "from": "icon2", + "to": "icon3", + "label": "" + }, + "line3": { + "from": "icon3", + "to": "icon4", + "label": "" + } + } +}