@@ -15,20 +15,20 @@ import {
1515 GetAsyncInvokeCommand ,
1616 ValidationException ,
1717} from '@aws-sdk/client-bedrock-runtime' ;
18- import {
19- S3Client ,
20- GetObjectCommand ,
21- PutObjectCommand ,
22- DeleteObjectsCommand ,
23- ListObjectsV2Command ,
24- } from '@aws-sdk/client-s3' ;
2518import { initBedrockClient } from './utils/bedrockApi' ;
26- import { Readable } from 'stream' ;
19+ import { CopyVideoJobParams } from './copyVideoJob' ;
20+ import {
21+ LambdaClient ,
22+ InvokeCommand ,
23+ InvocationType ,
24+ } from '@aws-sdk/client-lambda' ;
2725
2826const BUCKET_NAME : string = process . env . BUCKET_NAME ! ;
2927const TABLE_NAME : string = process . env . TABLE_NAME ! ;
28+ const COPY_VIDEO_JOB_FUNCTION_ARN = process . env . COPY_VIDEO_JOB_FUNCTION_ARN ! ;
3029const dynamoDb = new DynamoDBClient ( { } ) ;
3130const dynamoDbDocument = DynamoDBDocumentClient . from ( dynamoDb ) ;
31+ const lambda = new LambdaClient ( { } ) ;
3232
3333export const createJob = async (
3434 _userId : string ,
@@ -67,63 +67,28 @@ export const createJob = async (
6767 return item ;
6868} ;
6969
70- const copyAndDeleteObject = async (
71- jobId : string ,
72- srcBucket : string ,
73- srcRegion : string ,
74- dstBucket : string ,
75- dstRegion : string
76- ) => {
77- const srcS3 = new S3Client ( { region : srcRegion } ) ;
78- const dstS3 = new S3Client ( { region : dstRegion } ) ;
79-
80- const { Body, ContentType, ContentLength } = await srcS3 . send (
81- new GetObjectCommand ( {
82- Bucket : srcBucket ,
83- Key : `${ jobId } /output.mp4` ,
84- } )
85- ) ;
86-
87- const chunks = [ ] ;
88- for await ( const chunk of Body as Readable ) {
89- chunks . push ( chunk ) ;
90- }
91- const fileBuffer = Buffer . concat ( chunks ) ;
92-
93- await dstS3 . send (
94- new PutObjectCommand ( {
95- Bucket : dstBucket ,
96- Key : `${ jobId } /output.mp4` ,
97- Body : fileBuffer ,
98- ContentType,
99- ContentLength,
100- } )
101- ) ;
102-
103- const listRes = await srcS3 . send (
104- new ListObjectsV2Command ( {
105- Bucket : srcBucket ,
106- Prefix : jobId ,
107- } )
108- ) ;
109-
110- const objects = listRes . Contents ?. map ( ( object ) => ( {
111- Key : object . Key ,
112- } ) ) ;
113-
114- await srcS3 . send (
115- new DeleteObjectsCommand ( {
116- Bucket : srcBucket ,
117- Delete : {
118- Objects : objects ,
119- } ,
120- } )
121- ) ;
70+ export const updateJobStatus = async ( job : VideoJob , status : string ) => {
71+ const updateCommand = new UpdateCommand ( {
72+ TableName : TABLE_NAME ,
73+ Key : {
74+ id : job . id ,
75+ createdDate : job . createdDate ,
76+ } ,
77+ UpdateExpression : 'set #status = :status' ,
78+ ExpressionAttributeNames : {
79+ '#status' : 'status' ,
80+ } ,
81+ ExpressionAttributeValues : {
82+ ':status' : status ,
83+ } ,
84+ } ) ;
85+
86+ await dynamoDbDocument . send ( updateCommand ) ;
12287} ;
12388
12489const checkAndUpdateJob = async (
12590 job : VideoJob
126- ) : Promise < 'InProgress' | 'Completed' | 'Failed' > => {
91+ ) : Promise < 'InProgress' | 'Completed' | 'Failed' | 'Finalizing' > => {
12792 try {
12893 const client = await initBedrockClient ( job . region ) ;
12994 const command = new GetAsyncInvokeCommand ( {
@@ -145,45 +110,29 @@ const checkAndUpdateJob = async (
145110 }
146111 }
147112
148- if ( res . status !== 'InProgress' ) {
149- if ( res . status === 'Completed' ) {
150- const jobId = job . jobId ;
151- const dstBucket = BUCKET_NAME ;
152- const dstRegion = process . env . AWS_DEFAULT_REGION ! ;
153- const srcRegion = job . region ;
154- const videoBucketRegionMap = JSON . parse (
155- process . env . VIDEO_BUCKET_REGION_MAP ?? '{}'
156- ) ;
157- const srcBucket = videoBucketRegionMap [ srcRegion ] ;
158-
159- await copyAndDeleteObject (
160- jobId ,
161- srcBucket ,
162- srcRegion ,
163- dstBucket ,
164- dstRegion
165- ) ;
166- }
167-
168- const updateCommand = new UpdateCommand ( {
169- TableName : TABLE_NAME ,
170- Key : {
171- id : job . id ,
172- createdDate : job . createdDate ,
173- } ,
174- UpdateExpression : 'set #status = :status' ,
175- ExpressionAttributeNames : {
176- '#status' : 'status' ,
177- } ,
178- ExpressionAttributeValues : {
179- ':status' : res . status ,
180- } ,
181- } ) ;
182-
183- await dynamoDbDocument . send ( updateCommand ) ;
113+ // Video generation is complete, but the video copying is not finished.
114+ // We will run the copy job to set the status to "Finalizing".
115+ if ( res . status === 'Completed' ) {
116+ const params : CopyVideoJobParams = { job } ;
117+
118+ await lambda . send (
119+ new InvokeCommand ( {
120+ FunctionName : COPY_VIDEO_JOB_FUNCTION_ARN ,
121+ InvocationType : InvocationType . Event ,
122+ Payload : JSON . stringify ( params ) ,
123+ } )
124+ ) ;
125+
126+ await updateJobStatus ( job , 'Finalizing' ) ;
127+ return 'Finalizing' ;
128+ } else if ( res . status === 'Failed' ) {
129+ // Since video generation has failed, we will not copy the video and will terminate with a Failed status.
130+ await updateJobStatus ( job , 'Failed' ) ;
131+ return 'Failed' ;
132+ } else {
133+ // This res.status will be InProgress only.
134+ return res . status ! ;
184135 }
185-
186- return res . status ! ;
187136 } catch ( e ) {
188137 console . error ( e ) ;
189138 return job . status ;
0 commit comments