{ "kind": "open-flow-workflow", "version": 1, "name": "Start Transcription Jobs for S3 Objects", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_99325637d9804c85": { "name": "Start transcription jobs", "kind": "start", "inputDefs": [] }, "n_2b4ab91868ef71ee": { "inputs": { "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2b4ab91868ef71ee", "output": "state" } ] }, "bucket": { "kind": "value", "value": "YOUR_S3_BUCKET" }, "region": { "kind": "value", "value": "us-east-1" } }, "name": "Read the next S3 object", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previous" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "bucket" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "region" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "hasObject" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "key" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "bucket" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "region" } ], "name": "Read the next S3 object", "moduleId": "n_4c9fc8b64800aca4", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [ { "action": "aws_s3.list_objects" } ] } ] } }, "n_fd175acae80a1817": { "inputs": {}, "name": "Object in page?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_2b4ab91868ef71ee", "output": "hasObject" } }, "operator": "isTrue" } ] } ] } ] }, "n_3ca3e5d33a11eb4b": { "inputs": { "bucket": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2b4ab91868ef71ee", "output": "bucket" } ] }, "key": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2b4ab91868ef71ee", "output": "key" } ] }, "region": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2b4ab91868ef71ee", "output": "region" } ] }, "accessKeyId": { "kind": "unset" }, "secretAccessKey": { "kind": "unset" }, "sessionToken": { "kind": "unset" } }, "name": "Start the Transcribe job", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "bucket" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "key" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "region" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "accessKeyId" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "secretAccessKey" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "sessionToken" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "jobName" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "mediaFileUri" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "status" } ], "name": "Start the Transcribe job", "moduleId": "n_394ccbf5e49c56f8", "capabilities": [ { "kind": "http", "hosts": [ "transcribe.us-east-1.amazonaws.com" ] } ] } }, "n_759f7ec781ba541f": { "inputs": {}, "name": "More S3 pages?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_2b4ab91868ef71ee", "output": "more" } }, "operator": "isTrue" } ] } ] } ] } }, "edges": [ { "source": "n_99325637d9804c85", "target": "n_2b4ab91868ef71ee" }, { "source": "n_2b4ab91868ef71ee", "target": "n_fd175acae80a1817" }, { "source": "n_fd175acae80a1817", "target": "n_3ca3e5d33a11eb4b", "sourceHandle": "yes" }, { "source": "n_fd175acae80a1817", "target": "n_759f7ec781ba541f", "sourceHandle": "otherwise" }, { "source": "n_3ca3e5d33a11eb4b", "target": "n_759f7ec781ba541f" }, { "source": "n_759f7ec781ba541f", "target": "n_2b4ab91868ef71ee", "sourceHandle": "yes" } ] } }, "modules": { "n_4c9fc8b64800aca4": { "name": "Read the next S3 object", "imports": [], "source": "export default async function next({previous,bucket,region},context){if(typeof bucket!=='string'||!bucket.trim()||bucket.startsWith('YOUR_'))throw Error('Configure S3 bucket');if(typeof region!=='string'||!/^[a-z]{2}(?:-[a-z]+){1,2}-[0-9]+$/.test(region))throw Error('Configure AWS region');const s=previous??{cursor:null,count:0};const r=await context.actions.call('aws_s3.list_objects',{bucket,region,maxKeys:1,...(s.cursor?{continuationToken:s.cursor}:{})});if(!Array.isArray(r?.objects)||r.objects.length>1||typeof r.isTruncated!=='boolean')throw Error('Invalid S3 page');const next=r.isTruncated?r.nextContinuationToken:null;if(r.isTruncated&&(typeof next!=='string'||!next||next===s.cursor))throw Error('Invalid S3 continuation token');const item=r.objects[0];if(item&&(typeof item.name!=='string'||!item.name||!Number.isSafeInteger(item.size)||item.size<0))throw Error('Invalid S3 object');const include=!!item&&item.size>0&&!item.name.endsWith('/');return{state:{cursor:next,count:s.count+(include?1:0)},hasObject:include,more:r.isTruncated,key:include?item.name:'-',bucket,region}}\n" }, "n_394ccbf5e49c56f8": { "name": "Start the Transcribe job", "imports": [], "source": "import{createHash,createHmac}from'node:crypto';export default async function start({bucket,key,region,accessKeyId,secretAccessKey,sessionToken},context){if(typeof bucket!=='string'||!bucket.trim()||typeof key!=='string'||!key)throw Error('Missing S3 bucket or object key');if(typeof region!=='string'||!/^[a-z]{2}(?:-[a-z]+){1,2}-[0-9]+$/.test(region))throw Error('Configure AWS region');const required=(v,n)=>{if(typeof v!=='string'||!v.trim())throw Error('Select AWS '+n+' secret Variable');return v.trim()},id=required(accessKeyId,'access key ID'),secret=required(secretAccessKey,'secret access key');if(sessionToken!=null&&typeof sessionToken!=='string')throw Error('Invalid AWS session token');const token=sessionToken?.trim()??'',service='transcribe',host=service+'.'+region+'.amazonaws.com'+(region.startsWith('cn-')?'.cn':''),hash=x=>createHash('sha256').update(x).digest('hex'),hmac=(k,v)=>createHmac('sha256',k).update(v).digest(),jobName='s3-'+hash(JSON.stringify([bucket,key])),mediaFileUri='s3://'+bucket+'/'+key,body=JSON.stringify({TranscriptionJobName:jobName,Media:{MediaFileUri:mediaFileUri},Settings:{},IdentifyLanguage:true}),timestamp=new Date().toISOString().replace(/[:-]|\\.\\d{3}/g,''),date=timestamp.slice(0,8),headers={'content-type':'application/x-amz-json-1.1',host,'x-amz-date':timestamp,'x-amz-target':'Transcribe.StartTranscriptionJob',...(token?{'x-amz-security-token':token}:{})},names=Object.keys(headers).sort(),signedHeaders=names.join(';'),canonicalHeaders=names.map(n=>n+':'+headers[n].trim().replace(/\\s+/g,' ')+'\\n').join(''),scope=date+'/'+region+'/'+service+'/aws4_request',canonical=['POST','/','',canonicalHeaders,signedHeaders,hash(body)].join('\\n'),toSign=['AWS4-HMAC-SHA256',timestamp,scope,hash(canonical)].join('\\n'),signingKey=hmac(hmac(hmac(hmac('AWS4'+secret,date),region),service),'aws4_request'),signature=createHmac('sha256',signingKey).update(toSign).digest('hex');delete headers.host;headers.authorization='AWS4-HMAC-SHA256 Credential='+id+'/'+scope+', SignedHeaders='+signedHeaders+', Signature='+signature;let response;try{response=await context.fetch('https://'+host+'/',{method:'POST',headers,body})}catch{throw Error('Transcription request failed; inspect AWS before retry')};if(!response.ok)throw Error('Transcription request failed; inspect AWS before retry');const data=await response.json(),job=data?.TranscriptionJob;if(job?.TranscriptionJobName!==jobName||typeof job.TranscriptionJobStatus!=='string')throw Error('Transcription result unconfirmed; inspect AWS before retry');return{jobName,mediaFileUri,status:job.TranscriptionJobStatus}}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }