├── .gitignore ├── LICENSE ├── README.md ├── lambda_handler.py └── transcript.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | *.json 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jason Purdy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a port of purdy/aws-transcribe-transcript intended to run as an AWS Lambda function 2 | 3 | # aws-transcribe-transcript 4 | This is a simple utility script to convert the Amazon Transcribe .json transcript into a more readable transcript. This uses PHP, but if you're interested, there's [a Python port](https://github.com/trhr/aws-transcribe-transcript) of this repo. 5 | 6 | Amazon has a neat Transcription service and you can have the service identify speakers. And in their web interface, they show you a neat play-by-play transcript, but it's limited to the first 5,000 characters. If you want the full transcript, you have to download their JSON file. However, the JSON file only has the transcript as a big block and then some structured data below for the various speakers, start times, and text fragments. 7 | 8 | This script creates a transcript that's human-readable. 9 | 10 | ## Regular Directions 11 | 12 | 1. Download your Transcription from the Job Details page. The filename format is currently asrOutput.json. 13 | 2. Run the `transcript.py` program on the downloaded file, i.e. `python ./transcript.py asrOutput.json` 14 | 3. Results will be written in your current working directory as `[FILENAME]-transcript.txt` 15 | 16 | ## S3/Lambda Directions 17 | 0. Probably worth checking your lambda Memory/Execution time settings, depending on the size of the files you'll work with. I like ~256MB and ~15 seconds for general use. 18 | 1. Create an S3 bucket with two folders; input/ and output/ 19 | 2. Create a Lambda function that triggers on CreateObject in input/ (Triggers section of the UI) 20 | 3. Give the function access to write to S3/output (Resources section of the UI) 21 | 4. Place json file in S3/input and wait a few seconds for your transcript to show up in output/ 22 | -------------------------------------------------------------------------------- /lambda_handler.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | def convert_transcript(infile, outfile): 4 | speaker_start_times = {} 5 | lines = [] 6 | line = '' 7 | time = 0 8 | speaker = 'spk_1' 9 | recent_speaker = 'spk_1' 10 | 11 | import datetime 12 | print(f"Filename: {infile}") 13 | 14 | with open(outfile, "w+") as writeFile: 15 | with open(infile) as readFile: 16 | data = json.loads(readFile.read()) 17 | results = data.get("results") 18 | 19 | try: 20 | speaker_labels = results["speaker_labels"] 21 | except KeyError: # speaker labels are off; just return the transcript 22 | transcript = results.get("transcripts")[0].get("transcript") 23 | writeFile.write(f"{transcript}") 24 | return 25 | 26 | for label in speaker_labels.get("segments", []): 27 | for item in label.get("items", []): 28 | speaker_start_times.update({item.get("start_time"): item.get("speaker_label", "Anon")}) 29 | 30 | items = results.get("items", []) 31 | for idx, item in enumerate(items): 32 | 33 | if item.get("start_time"): # This is a spoken item 34 | speaker = speaker_start_times.get(item.get("start_time"), "Anon") 35 | if speaker == recent_speaker: 36 | line+=f" {item.get('alternatives')[0].get('content')}" # Append the content to line and repeat 37 | else: # New speaker 38 | lines.append({'speaker': recent_speaker, 'line': line, 'time': time}) 39 | print(f"[{time}] {recent_speaker}: {line}") 40 | recent_speaker = speaker 41 | line=item.get('alternatives')[0].get('content') 42 | time=item.get('start_time') 43 | elif item.get("type") == "punctuation": 44 | line+=item.get('alternatives')[0].get('content') 45 | 46 | lines.append({'speaker': speaker, 'line': line, 'time': time}) 47 | sorted_lines = sorted(lines, key=lambda k: float(k['time'])) 48 | for line_data in sorted_lines: 49 | line = '[' + str( 50 | datetime.timedelta(seconds=int(round(float(line_data['time']))))) + '] ' + line_data.get( 51 | 'speaker') + ': ' + line_data.get('line') 52 | writeFile.write(f"{line}\n\n") 53 | 54 | def lambda_handler(event, context): 55 | import os, boto3 56 | 57 | s3_client = boto3.client('s3') 58 | 59 | for record in event['Records']: 60 | bucket = record['s3']['bucket']['name'] 61 | key = record['s3']['object']['key'] 62 | localkey = os.path.basename(key) 63 | txtfile = f"output/{localkey}.txt" 64 | download_path = f"/tmp/{localkey}" 65 | upload_path = f"/tmp/{localkey}.txt" 66 | s3_client.download_file(bucket, key, download_path) 67 | convert_transcript(download_path, upload_path) 68 | s3_client.upload_file(upload_path, f'{bucket}', txtfile) 69 | 70 | return {'statusCode': 200, 'body': json.dumps('Transcription run.')} 71 | 72 | -------------------------------------------------------------------------------- /transcript.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | def main(): 3 | import sys 4 | import json 5 | import datetime 6 | import codecs 7 | 8 | filename=sys.argv[1] 9 | print ("Filename: ", filename) 10 | with codecs.open(filename+'.txt', 'w', 'utf-8') as w: 11 | with codecs.open(filename, 'r', 'utf-8') as f: 12 | data=json.loads(f.read()) 13 | labels = data['results']['speaker_labels']['segments'] 14 | speaker_start_times={} 15 | for label in labels: 16 | for item in label['items']: 17 | speaker_start_times[item['start_time']] =item['speaker_label'] 18 | items = data['results']['items'] 19 | lines=[] 20 | line='' 21 | time=0 22 | speaker='null' 23 | i=0 24 | for item in items: 25 | i=i+1 26 | content = item['alternatives'][0]['content'] 27 | if item.get('start_time'): 28 | current_speaker=speaker_start_times[item['start_time']] 29 | elif item['type'] == 'punctuation': 30 | line = line+content 31 | if current_speaker != speaker: 32 | if speaker: 33 | lines.append({'speaker':speaker, 'line':line, 'time':time}) 34 | line=content 35 | speaker=current_speaker 36 | time=item['start_time'] 37 | elif item['type'] != 'punctuation': 38 | line = line + ' ' + content 39 | lines.append({'speaker':speaker, 'line':line,'time':time}) 40 | sorted_lines = sorted(lines,key=lambda k: float(k['time'])) 41 | for line_data in sorted_lines: 42 | line='[' + str(datetime.timedelta(seconds=int(round(float(line_data['time']))))) + '] ' + line_data.get('speaker') + ': ' + line_data.get('line') 43 | w.write(line + '\n\n') 44 | 45 | 46 | if __name__ == '__main__': 47 | main() 48 | --------------------------------------------------------------------------------