Examples · Programming
Subtitles from a recording, in Python
Transcribe a recording with the time of every sentence, and write an .srt file that VLC, YouTube and video editors can read.
Intermediate · 35 min
What you need
A key and the openai library, as in Your first API call in Python, and a recording: a voice memo from your phone, an MP3, a WAV or a short video, up to 25 MB.
No recording at hand? Let a voice of the school make one. Save it as make_test.py:
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.ai.itsincom.org/v1",
api_key=os.environ["API_KEY"],
)
talk = ("Good morning, everyone. Today we present our project for the Internet of Things course. "
"We built a weather station with a Raspberry Pi and three sensors. "
"Every five minutes it measures the temperature, the humidity and the air pressure. "
"The readings go to a database in the cloud, and a web page draws the charts. "
"Thank you for listening.")
speech = client.audio.speech.create(model="tts-1", voice="ryan", input=talk, response_format="mp3")
speech.write_to_file("talk.mp3")
print("talk.mp3 is ready")
The model
Two models transcribe: whisper-1, the faster, and whisper-1-hd, the more accurate. Subtitles are read word by word, so this program uses whisper-1-hd.
The program
Save it as subtitles.py:
import os
import sys
from openai import OpenAI
client = OpenAI(
base_url="https://api.ai.itsincom.org/v1",
api_key=os.environ["API_KEY"],
)
def stamp(seconds):
"""12.5 -> 00:00:12,500, the way SRT writes a time."""
ms = round(seconds * 1000)
return f"{ms // 3600000:02}:{ms // 60000 % 60:02}:{ms // 1000 % 60:02},{ms % 1000:03}"
path = sys.argv[1]
with open(path, "rb") as f:
result = client.audio.transcriptions.create(
model="whisper-1-hd",
file=f,
language="en",
response_format="verbose_json",
timestamp_granularities=["segment", "word"],
)
srt_path = os.path.splitext(path)[0] + ".srt"
with open(srt_path, "w", encoding="utf-8") as srt:
for n, segment in enumerate(result.segments, 1):
srt.write(f"{n}\n{stamp(segment.start)} --> {stamp(segment.end)}\n{segment.text.strip()}\n\n")
print(f"{len(result.segments)} subtitles in {srt_path}, {result.duration:.0f} seconds of audio")
Run it on your recording:
python3 subtitles.py talk.mp3
What you should see
10 subtitles in talk.srt, 17 seconds of audio
and talk.srt begins like this:
1
00:00:00,000 --> 00:00:00,960
Good morning, everyone.
2
00:00:01,340 --> 00:00:03,000
Today, we present our project
3
00:00:03,000 --> 00:00:04,480
for the Internet of Things course.
Every take of a voice is a little different, so your times will not be exactly these. When we ran it, subtitle 4 said “We built the weather station”, where the voice read “a weather station”: a small thing, but it is why you read subtitles before you publish them.
How it works
response_format="verbose_json"gives the text cut into segments, each with itsstartandendin seconds. One segment becomes one subtitle."word"intimestamp_granularitiesalso gives the time of every word, inresult.words, and makes each subtitle start at its first word. Without it, the first subtitle starts at 0:00 even when the recording opens with silence or music.- An SRT file is plain text: a number, the start and the end with a comma before the milliseconds, the text, an empty line.
- The API has no
srtformat:response_format="srt"is refused. You build the file from the segments, as here.
Good to know
- Formats: WAV, MP3, M4A, MP4, Ogg, WebM, FLAC and 3GPP, up to 25 MB. The check looks at what is inside the file, not at its name.
- A video works too: the transcription uses only its sound. To stay under 25 MB, send only the audio, small and mono:
ffmpeg -i video.mp4 -vn -ac 1 -ar 16000 -b:a 48k audio.mp3. - Long recordings: a request that takes more than five minutes is cut off. For a long lesson, cut the audio into parts of a few minutes (
ffmpeg -ssand-t) and add the start of each part to its times. - Lower case and no full stops? It can happen when a recording starts with a long silence: cut the silence, or try the other model.
- A silent file gives an empty text, not an error.
- Names and technical terms are where subtitles go wrong most often: put them in
prompt, as in Text to speech in Python.
Exercise
- Run it on a 10-second video from your phone. Open the video in VLC and drag the
.srtonto it. - Write a
.vttfile too, for the<track>of a web page: it starts withWEBVTTand an empty line, the numbers are optional, and the times have a dot before the milliseconds,00:00:01.340. - Long subtitles are hard to read: split the ones over 42 characters, using the times of the words in
result.words. - Translate your subtitles with a chat model, as in Subtitles for your video: the numbers and the times stay, only the text changes.
