- Open and read a text file in Python
- Use
split()to separate a timestamp from lyric text - Convert a time string like
"1:19"into a number of seconds - Build a list of
(seconds, text)pairs - Store that list inside a
Trackobject'slyricsattribute
In Lesson 2 you pulled information out of a filename using string methods. In this lesson you will do the same thing, but with the contents of a file β reading it line by line and extracting two pieces of data from each line.
Part 1 β What is in a lyrics file?
Each song on the SD card has a matching lyrics file in /sd/transcripts/.
The format looks like this:
0:04 [Intro]
1:19 Well, oh, they might wear classic Reeboks
1:22 Or knackered Converse, or track bottoms stucked in socks
1:26 But I'll be forever grateful for the name I've got
Each line has two parts separated by whitespace (spaces or a tab):
- A timestamp in
minutes:secondsformat β e.g.1:19 - The lyric text for that moment in the song
Our goal is to turn each of these lines into a pair of values: the timestamp converted to a number of seconds, and the lyric text. We will store all these pairs in a list.
Part 2 β Opening and reading a file
Python's built-in open() function opens a file. Using it inside a
with block ensures the file is automatically closed when we are done:
with open("/sd/transcripts/Wonderwall.txt") as f:
for line in f:
print(line) # prints every line in the file
with open(...)?
On a microcontroller, leaving files open wastes memory and can cause crashes.
The with block guarantees the file is closed even if something goes wrong
β you never need to call f.close() yourself.
Part 3 β Splitting a line into its parts
Each line contains a timestamp and some text. split() without any
arguments splits on any whitespace (spaces or tabs).
Passing a second argument of 1 tells it to split only at the
first whitespace it finds β which is exactly what we want:
line = "1:19 Well, oh, they might wear classic Reeboks\n"
parts = line.strip().split(None, 1)
print(parts)
# ['1:19', 'Well, oh, they might wear classic Reeboks']
timestamp = parts[0] # '1:19'
text = parts[1] # 'Well, oh, they might wear classic Reeboks'
Part 4 β Converting a timestamp to seconds
The timestamp "1:19" means 1 minute and 19 seconds.
We need to convert it to a single number (79 seconds) so we can compare it
against how long the song has been playing.
timestamp = "1:19"
minutes, seconds = timestamp.split(":") # ['1', '19']
total_seconds = int(minutes) * 60 + int(seconds)
print(total_seconds) # 79
int()?
split() always gives back strings. The string "1"
and the number 1 look the same but behave very differently:
"1" + "19" gives "119", not 20.
int() converts the string to a proper number.
Part 5 β Writing load_lyrics()
Now combine all of the above into a function. It takes a file path, reads every line,
and returns a list of (seconds, text) pairs:
def load_lyrics(path):
"""Read a lyrics file and return a list of (seconds, lyric) pairs."""
entries = []
with open(path) as f:
for line in f:
parts = line.strip().split(None, 1)
if len(parts) == 2: # skip blank lines
minutes, seconds = parts[0].split(":")
total = int(minutes) * 60 + int(seconds)
entries.append((total, parts[1]))
return entries
Part 6 β Storing lyrics inside a Track
The Track class already has a lyrics attribute.
Update your for loop from Lesson 2 to call load_lyrics()
when creating each track:
from pypod import Track, add_track, list_music_files, music_path, lyrics_path, start
def parse_filename(filename):
stem = filename[:-4]
position = stem.rfind("(")
title = stem[:position].strip()
artist = stem[position + 1 : -1].strip()
return title, artist
def load_lyrics(path):
entries = []
with open(path) as f:
for line in f:
parts = line.strip().split(None, 1)
if len(parts) == 2:
minutes, seconds = parts[0].split(":")
total = int(minutes) * 60 + int(seconds)
entries.append((total, parts[1]))
return entries
for filename in list_music_files():
title, artist = parse_filename(filename)
track = Track(
title = title,
artist = artist,
file_path = music_path(filename),
lyrics = load_lyrics(lyrics_path(title)),
)
add_track(track)
start()
- Add the
load_lyricsfunction to yourmain.py. - Update the
Track()call to includelyrics = load_lyrics(lyrics_path(title)). - Add a
printstatement inside the loop to show the first lyric entry for each track β confirm it is a(seconds, text)pair. - Remove the
printonce you are happy it works.
-
What happens if a lyrics file does not exist for a track?
Wrap the
open()call in atry / exceptblock so thatload_lyricsreturns an empty list[]instead of crashing. - How many total lyric lines does each song have? Print the song title and the length of its lyrics list inside the loop.