From Medium Post to Audio. How I converted my medium post to…
By: Okoh Anita
Tags: Machine Learning, Ｍedium, AWS Lambda, Aws Cli, Médium

From Medium Post to Audio

Image by James McKinven from Unsplash

Introduction

I recently discovered that I retain more information when I read a book in audio format than in written format. This discovery instigated the need to find ways to read Medium posts in audio formats like a podcast. My first plan was to gather all my medium read lists and convert them to audio. Unfortunately, Medium API technically does not support this. What it does support, however, is getting article feeds by username. Therefore I decided to streamline my goal, for now, to convert one of my old medium posts to audio. Here is the sample below

You can also listen to it along with the original post here.

Overview of the project

Just like every other data-related project, this project’s data needed to be sourced, cleaned, and converted. Below is a diagram of the architecture.

Image created by Author

Based on the above diagram, this project can be broken down into two broad phases

The Extract, Transform, and Load (ETL) Phase. The Converting Text to Audio Phase.

1 . The ETL Phase

Extract

This phase focused on sourcing the post content and getting it ready to be converted. Like I hinted earlier, the medium posts were sourced using the Medium API with this code below

import requests

import json MEDIUM_API = "https://api.rss2json.com/v1/api.json"

MEDIUM_HANDLER = "@anitaokoh" # get the article repsonse from the API

rss_url ="https://medium.com/feed/{}".format(MEDIUM_HANDLER)

response = requests.get(url=MEDIUM_API, params={"rss_url": rss_url})

json_response = json.loads(response.text)

By adding my username to the API, I was able to get a JSON response that contained all my previous Medium post titles, contents, number of claps, etc. Since I was only focused on this particular article, I parsed the JSON output to extract just that particular title and content.

Transform

One of the less simple parts of this project was cleaning the text. Please note that the content of the post that is gotten from the Medium API comes along with the HTML tags. Below is a slice of the text data format.

Screenshot code from Author

This means I needed to find a way to remove or use the tags. To be honest, I used some tags and removed other tags. Why? you wonder. Before I answer this question, let's take a step back to understand briefly Amazon Web Service (AWS) POLLY and what format of text it accepts.

According to the AWS website, Amazon Polly is a service that turns text into lifelike speech, allowing you to create applications that talk, and build entirely new categories of speech-enabled products.

That about sums up its purpose. Let’s take a look at the AWS console interface

Screenshot from AWS console

Looking at the blue box area, AWS Polly supports two types of Text-To-Speech(TTS) voices: Standard and Neural. The Neural TTS has an edge over the Standard voices because its output voices are less robotic and more realistic. This is why for this project, I used the neural TTS option.

However, to answer the previous question raised, let's focus on the elements inside the red-box annotation above. AWS Polly can read text input in two formats: The regular text format and the SSML format (which means Speech Synthesis Markup Language).

Screenshot from AWS Console

This SSML format above, just HTML, has different types of opening and closing tags and one of them being <p> and </p> . This is why I didn’t completely remove all the HTML tags in the text data.

I identified the HTML tags present in the text data that can either be left as it is or changed to a relevant SSML tag. For example, the <p> tags were left as they are because these tags indicate taking a pause while reading each paragraph. However, the <blockquote> tags which indicate word quotes in Medium HTML were converted to Newscaster effect tags. Also, the <h3> tags which indicate new subtopics were replaced with <break> tags to indicate a timed talking pause. These changes in intonations can be observed throughout the audio duration. The reason for these specifics was to make it even more realistic and less robotic.

Other things that were done to the data as a form of transformation were

Removing unnecessary tasks like <img> tags, <li> tags etc.

tags, tags etc. Removing publication-related words or information that can be seen on the actual post.

Striping off extra spaces and removing weird characters etc.

Add special SSML tags to characters that by default were omitted by AWS Polly.

Load/Save

After getting the text data ready for AWS Polly, I stored the data in a text file locally.

2. Converting Text to Audio Phase

This part was the most straightforward in this project. All I needed to do was get the AWS Polly object from the Boto3 library and passing in my locally saved text file as an argument along with other configuration arguments like the TextType which is SSML, the engine which is Neural, the output format which is mp3, and the voice name. As you can see from the previous console interface image, AWS Polly has about 9 voices in total to choose from. I chose the Joanna voice because that voice has more SSML tag options accessible to it. Here is the code used

import boto3

from botocore.config import Config poly = boto3.client('polly',config=Config(region_name='us-east-1')) # read local text file

with open('polly_file.txt', 'r') as file_name:

file_text = file_name.read() def convert_text_to_audio(text):

"""

Convert the text read using the start_speech_synthesis_task method using Joanna, SSML format etc and store output to s3 bucket

"""

response = poly.start_speech_synthesis_task( \

Text= text,VoiceId='Joanna', OutputFormat='mp3', \

TextType='ssml', LanguageCode='en-US', \

Engine='neural', \

OutputS3BucketName='practice-buckets')

As you can see from the above code, I extracted the audio output from the JSON response from AWS Polly and stored it in an mp3 file (but in S3 due to the Polly object method I used which is advised for relatively long audio files).

Please note that you can use the console interface to execute most of the above process however there is a “number of character limit” to consider. You can check the documentation for more on this. I solely used the AWS CLI because of the size of my text file and for easier automation. There are also more automation options using the CLI for AWS Polly.

AWS WORKFLOW

After completing the project locally, I decided to automate it further using AWS Lambda where I created an AWS Lambda function to run the extract and transform python scripts and then load the clean text data in AWS S3. Still using that lambda function, I invoked another AWS lambda function to extract the newly uploaded text from S3, sent it to AWS Polly for conversion, and then back to S3. This whole process looks something like this

Image created by Author

Here is the Lamdba function used to clean the text data and invoke another Lambda function.

import json

import boto3

from transform_post import return_total_clean_content def lambda_handler(event, context):

"""

To run the extract and transform py file and store clean text in s3. Then invoke the conversion lambda function

"""

content = return_total_clean_content()

s3 = boto3.client('s3')

bucket_name = 'practice-buckets'

key = 'input_files/clean_medium_data2.txt' s3.put_object(Bucket=bucket_name,Key=key,Body=content)

invokelam = boto3.client('lambda',region_name='us-east-1')

payload = {'message':'You have been invoked'} resp = invokelam.invoke(FunctionName = "text-to-speech",\

InvocationType = "Event", Payload = json.dumps(payload))



print('Done')

All codes used can be found in this Github repo.

An upgrade of this project

In retrospect, this was an impressive but not too complicated project. With AWS Polly, a lot of time was saved converting text to audio. A good next step is finding a way to convert interesting medium reads to audio versions so that I can read medium posts on the go.