How Spotify-Saver Downloads and Embeds Synchronized Lyrics from LRC Lib
Spotify-Saver downloads synchronized lyrics from LRC Lib by querying the lrclib.net API with track metadata, saving the timed text to a .lrc file alongside the audio, and tracking the success state in the Track model.
The open-source tool Spotify-Saver automates the process of archiving Spotify tracks while preserving rich metadata, including word-level synchronized lyrics. When you enable the lyrics option, the application fetches timed lyric data from the LRC Lib web service and embeds it as a standard .lrc file next to your downloaded audio. This article examines the exact implementation across the API client, downloader, and CLI layers.
Querying the LRC Lib Web Service
The interaction with LRC Lib is encapsulated in the LrclibAPI class located in spotifysaver/services/lrclib_api.py. This client constructs a GET request to https://lrclib.net/api/get, passing the track’s title, artist, album name, and duration as query parameters.
The request executes through a requests.Session configured with a 10-second timeout to prevent hanging on network issues. When the caller requests synchronized lyrics by setting synced=True, the method extracts the syncedLyrics field from the JSON response; otherwise, it returns plainLyrics.
from spotifysaver.services import LrclibAPI
from spotifysaver.models import Track
track = Track(
number=1,
total_tracks=10,
name="Never Gonna Give You Up",
duration=213,
uri="spotify:track:...",
artists=["Rick Astley"],
album_artist=["Rick Astley"],
release_date="1987-07-27",
album_name="Whenever You Need Somebody",
)
lrc = LrclibAPI()
synced = lrc.get_lyrics(track, synced=True) # Returns .lrc content or None
plain = lrc.get_lyrics(track, synced=False) # Returns plain text
Fallback Handling and Error Management
To maximize availability, Spotify-Saver implements get_lyrics_with_fallback (lines 78-92 in lrclib_api.py). This helper first attempts to retrieve synchronized lyrics, and if that returns None, automatically falls back to the plain text version.
All network failures and HTTP errors are normalized into a custom APIError exception, allowing upstream components to handle connectivity problems uniformly without inspecting raw HTTP status codes.
Saving Lyrics to the Filesystem
Once lyrics are retrieved, the YouTubeDownloader._save_lyrics method (lines 210-233 in spotifysaver/downloader/youtube_downloader.py) handles persistence. This method receives a Track instance and the path of the downloaded audio file, then orchestrates the file write operation.
The implementation performs three critical checks:
- Instrumental detection: If the API returns
"[instrumental]", the method skips writing the file. - Path generation: It constructs the lyrics path using
audio_path.with_suffix(".lrc"), ensuring the.lrcfile shares the same base name as the audio. - Success signaling: The method returns a boolean
Trueonly when lyrics are successfully written, which downstream code uses to update the track’s metadata.
from spotifysaver.downloader import YouTubeDownloader
dl = YouTubeDownloader(base_dir="MyMusic")
audio_path, updated_track = dl.download_track(
track,
output_format=dl.string_to_audio_format("mp3"),
bitrate=dl.int_to_bitrate(192),
download_lyrics=True # Triggers .lrc file creation
)
if audio_path and updated_track.has_lyrics:
print(f"Lyrics saved at {audio_path.with_suffix('.lrc')}")
User Interface and API Integration
The feature is exposed through both the command-line interface and the high-level Python API.
CLI Activation
In spotifysaver/cli/commands/download/track.py, the --lyrics flag triggers the download flow. When present, the CLI forwards download_lyrics=True to the downloader:
spotifysaver download track https://open.spotify.com/track/6rqhFgbbKwnb9MLmUQDhG6 \
--format mp3 --bitrate 192 --lyrics
Programmatic Access
The service layer in spotifysaver/api/services/download_service.py accepts a download_lyrics boolean parameter (lines 22-38), propagating the flag through to YouTubeDownloader.download_track. This ensures consistent behavior whether you use the CLI or import the library directly.
Track Model State Management
The immutable Track dataclass in spotifysaver/models/track.py maintains the lyrics state through three key members:
has_lyrics: A boolean field indicating whether the.lrcfile was successfully created.lyrics_filename: A property generating a safe filename for the lyrics file based on track metadata.with_lyrics_status: A method returning a newTrackinstance with an updatedhas_lyricsvalue, preserving immutability.
After _save_lyrics returns True, the downloader invokes track.with_lyrics_status(True) to produce an updated model that reflects the successful lyrics download.
Summary
- LrclibAPI queries
https://lrclib.net/api/getwith a 10-second timeout, returning eithersyncedLyricsorplainLyricsbased on thesyncedparameter. get_lyrics_with_fallbackautomatically attempts synchronized lyrics first, falling back to plain text if unavailable._save_lyricswrites the content to a.lrcfile adjacent to the audio, skipping instrumental tracks and returning a success boolean.- The
Trackmodel tracks lyrics status viahas_lyricsand the immutablewith_lyrics_statusmethod. - Both the CLI (
--lyricsflag) and the Python API (download_lyricsparameter) expose this functionality uniformly.
Frequently Asked Questions
What file format does Spotify-Saver use for synchronized lyrics?
Spotify-Saver saves synchronized lyrics in the standard .lrc format, which stores time-stamped lines that music players can display in sync with the audio. The file is placed in the same directory as the downloaded track with an identical base filename.
Does the application fall back to plain text if synchronized lyrics are unavailable?
Yes. The get_lyrics_with_fallback method in spotifysaver/services/lrclib_api.py first attempts to retrieve synchronized lyrics from the LRC Lib API. If the syncedLyrics field is null or empty, it automatically requests the plainLyrics field instead, ensuring you receive lyric content even when timed data is missing.
How does Spotify-Saver handle instrumental tracks?
When the LRC Lib API returns the string "[instrumental]", the _save_lyrics method in spotifysaver/downloader/youtube_downloader.py detects this value and skips writing the .lrc file entirely. This prevents creating empty or placeholder lyric files for tracks without vocal content.
Can I download lyrics separately from the audio file?
While the primary workflow couples audio and lyrics downloads through YouTubeDownloader.download_track, you can use the LrclibAPI class directly to fetch lyric content programmatically without invoking the full download pipeline. Instantiate the client and call get_lyrics(track, synced=True) to retrieve the raw .lrc text for any Track object.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →