-
Notifications
You must be signed in to change notification settings - Fork 135
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
1,690 additions
and
122 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'; | ||
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; | ||
import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3'; | ||
import { GetSignedUrlRequest } from 'generative-ai-use-cases-jp'; | ||
|
||
export const handler = async ( | ||
event: APIGatewayProxyEvent | ||
): Promise<APIGatewayProxyResult> => { | ||
try { | ||
const req: GetSignedUrlRequest = JSON.parse(event.body!); | ||
const mediaFormat = req.mediaFormat; | ||
const currentDateString = new Date() | ||
.toISOString() | ||
.replace(/[:T-]/g, '') | ||
.split('.')[0]; | ||
|
||
const client = new S3Client({}); | ||
const command = new PutObjectCommand({ | ||
Bucket: process.env.AUDIO_BUCKET_NAME, | ||
Key: `${currentDateString}.${mediaFormat}`, | ||
}); | ||
|
||
const signedUrl = await getSignedUrl(client, command, { expiresIn: 3600 }); | ||
|
||
return { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'Access-Control-Allow-Origin': '*', | ||
}, | ||
body: signedUrl, | ||
}; | ||
} catch (error) { | ||
console.log(error); | ||
return { | ||
statusCode: 500, | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'Access-Control-Allow-Origin': '*', | ||
}, | ||
body: JSON.stringify({ message: 'Internal Server Error' }), | ||
}; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'; | ||
import { | ||
TranscribeClient, | ||
GetTranscriptionJobCommand, | ||
} from '@aws-sdk/client-transcribe'; | ||
import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3'; | ||
|
||
function parseS3Url(s3Url: string) { | ||
const url = new URL(s3Url); | ||
|
||
const pathParts = url.pathname.split('/'); | ||
const bucket = pathParts[1]; | ||
const key = pathParts.slice(2).join('/'); | ||
|
||
return { bucket, key }; | ||
} | ||
|
||
export const handler = async ( | ||
event: APIGatewayProxyEvent | ||
): Promise<APIGatewayProxyResult> => { | ||
try { | ||
const transcribeClient = new TranscribeClient({}); | ||
const s3Client = new S3Client({}); | ||
const jobName = event.pathParameters!.jobName; | ||
|
||
const command = new GetTranscriptionJobCommand({ | ||
TranscriptionJobName: jobName, | ||
}); | ||
const res = await transcribeClient.send(command); | ||
if (res.TranscriptionJob?.TranscriptionJobStatus === 'COMPLETED') { | ||
const { bucket, key } = parseS3Url( | ||
res.TranscriptionJob.Transcript!.TranscriptFileUri! | ||
); | ||
const s3Result = await s3Client.send( | ||
new GetObjectCommand({ | ||
Bucket: bucket, | ||
Key: key, | ||
}) | ||
); | ||
const transcript = JSON.parse(await s3Result.Body!.transformToString()); | ||
|
||
return { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'Access-Control-Allow-Origin': '*', | ||
}, | ||
body: JSON.stringify({ | ||
status: res.TranscriptionJob?.TranscriptionJobStatus, | ||
transcript: transcript.results.transcripts | ||
.map((item: { transcript: string }) => item.transcript) | ||
.join(''), | ||
}), | ||
}; | ||
} else { | ||
return { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'Access-Control-Allow-Origin': '*', | ||
}, | ||
body: JSON.stringify({ | ||
status: res.TranscriptionJob?.TranscriptionJobStatus, | ||
}), | ||
}; | ||
} | ||
} catch (error) { | ||
console.log(error); | ||
return { | ||
statusCode: 500, | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'Access-Control-Allow-Origin': '*', | ||
}, | ||
body: JSON.stringify({ message: 'Internal Server Error' }), | ||
}; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'; | ||
import { | ||
TranscribeClient, | ||
StartTranscriptionJobCommand, | ||
} from '@aws-sdk/client-transcribe'; | ||
import { StartTranscriptionRequest } from 'generative-ai-use-cases-jp'; | ||
|
||
export const handler = async ( | ||
event: APIGatewayProxyEvent | ||
): Promise<APIGatewayProxyResult> => { | ||
try { | ||
const client = new TranscribeClient({}); | ||
const req: StartTranscriptionRequest = JSON.parse(event.body!); | ||
const { audioUrl, mediaFormat } = req; | ||
|
||
const currentDateString = new Date() | ||
.toISOString() | ||
.replace(/[:T-]/g, '') | ||
.split('.')[0]; | ||
|
||
const command = new StartTranscriptionJobCommand({ | ||
IdentifyLanguage: true, | ||
LanguageOptions: ['ja-JP', 'en-US'], | ||
MediaFormat: mediaFormat, | ||
Media: { MediaFileUri: audioUrl }, | ||
TranscriptionJobName: `job-${currentDateString}`, | ||
OutputBucketName: process.env.TRANSCRIPT_BUCKET_NAME, | ||
}); | ||
const res = await client.send(command); | ||
|
||
return { | ||
statusCode: 200, | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'Access-Control-Allow-Origin': '*', | ||
}, | ||
body: JSON.stringify({ | ||
jobName: res.TranscriptionJob!.TranscriptionJobName, | ||
}), | ||
}; | ||
} catch (error) { | ||
console.log(error); | ||
return { | ||
statusCode: 500, | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'Access-Control-Allow-Origin': '*', | ||
}, | ||
body: JSON.stringify({ message: 'Internal Server Error' }), | ||
}; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import { Duration, RemovalPolicy } from 'aws-cdk-lib'; | ||
import { | ||
AuthorizationType, | ||
CognitoUserPoolsAuthorizer, | ||
LambdaIntegration, | ||
RestApi, | ||
} from 'aws-cdk-lib/aws-apigateway'; | ||
import { UserPool } from 'aws-cdk-lib/aws-cognito'; | ||
import { Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam'; | ||
import { Runtime } from 'aws-cdk-lib/aws-lambda'; | ||
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; | ||
import { Bucket, BucketEncryption, HttpMethods } from 'aws-cdk-lib/aws-s3'; | ||
import { Construct } from 'constructs'; | ||
|
||
export interface TranscribeProps { | ||
userPool: UserPool; | ||
api: RestApi; | ||
} | ||
|
||
export class Transcribe extends Construct { | ||
constructor(scope: Construct, id: string, props: TranscribeProps) { | ||
super(scope, id); | ||
|
||
const audioBucket = new Bucket(this, 'AudioBucket', { | ||
encryption: BucketEncryption.S3_MANAGED, | ||
removalPolicy: RemovalPolicy.DESTROY, | ||
autoDeleteObjects: true, | ||
}); | ||
audioBucket.addCorsRule({ | ||
allowedOrigins: ['*'], | ||
allowedMethods: [HttpMethods.PUT], | ||
allowedHeaders: ['*'], | ||
exposedHeaders: [], | ||
maxAge: 3000, | ||
}); | ||
|
||
const transcriptBucket = new Bucket(this, 'TranscriptBucket', { | ||
encryption: BucketEncryption.S3_MANAGED, | ||
removalPolicy: RemovalPolicy.DESTROY, | ||
autoDeleteObjects: true, | ||
}); | ||
|
||
const getSignedUrlFunction = new NodejsFunction(this, 'GetSignedUrl', { | ||
runtime: Runtime.NODEJS_18_X, | ||
entry: './lambda/getSignedUrl.ts', | ||
timeout: Duration.minutes(15), | ||
environment: { | ||
AUDIO_BUCKET_NAME: audioBucket.bucketName, | ||
}, | ||
}); | ||
audioBucket.grantWrite(getSignedUrlFunction); | ||
|
||
const startTranscriptionFunction = new NodejsFunction( | ||
this, | ||
'StartTranscription', | ||
{ | ||
runtime: Runtime.NODEJS_18_X, | ||
entry: './lambda/startTranscription.ts', | ||
timeout: Duration.minutes(15), | ||
environment: { | ||
TRANSCRIPT_BUCKET_NAME: transcriptBucket.bucketName, | ||
}, | ||
initialPolicy: [ | ||
new PolicyStatement({ | ||
effect: Effect.ALLOW, | ||
actions: ['transcribe:*'], | ||
resources: ['*'], | ||
}), | ||
], | ||
} | ||
); | ||
audioBucket.grantRead(startTranscriptionFunction); | ||
transcriptBucket.grantWrite(startTranscriptionFunction); | ||
|
||
const getTranscriptionFunction = new NodejsFunction( | ||
this, | ||
'GetTranscription', | ||
{ | ||
runtime: Runtime.NODEJS_18_X, | ||
entry: './lambda/getTranscription.ts', | ||
timeout: Duration.minutes(15), | ||
initialPolicy: [ | ||
new PolicyStatement({ | ||
effect: Effect.ALLOW, | ||
actions: ['transcribe:*'], | ||
resources: ['*'], | ||
}), | ||
], | ||
} | ||
); | ||
transcriptBucket.grantRead(getTranscriptionFunction); | ||
|
||
// API Gateway | ||
const authorizer = new CognitoUserPoolsAuthorizer(this, 'Authorizer', { | ||
cognitoUserPools: [props.userPool], | ||
}); | ||
|
||
const commonAuthorizerProps = { | ||
authorizationType: AuthorizationType.COGNITO, | ||
authorizer, | ||
}; | ||
const transcribeResource = props.api.root.addResource('transcribe'); | ||
|
||
// POST: /transcribe/start | ||
transcribeResource | ||
.addResource('start') | ||
.addMethod( | ||
'POST', | ||
new LambdaIntegration(startTranscriptionFunction), | ||
commonAuthorizerProps | ||
); | ||
|
||
// POST: /transcribe/url | ||
transcribeResource | ||
.addResource('url') | ||
.addMethod( | ||
'POST', | ||
new LambdaIntegration(getSignedUrlFunction), | ||
commonAuthorizerProps | ||
); | ||
|
||
// GET: /transcribe/result/{jobName} | ||
transcribeResource | ||
.addResource('result') | ||
.addResource('{jobName}') | ||
.addMethod( | ||
'GET', | ||
new LambdaIntegration(getTranscriptionFunction), | ||
commonAuthorizerProps | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.