# How Spotify-Saver Downloads and Embeds Synchronized Lyrics from LRC Lib

> Discover how Spotify-Saver downloads synchronized lyrics from LRC Lib using its API. Learn how timed text is saved to.lrc files and tracked within the Track model for seamless integration.

- Repository: [Gabriel Baute/spotify-saver](https://github.com/gabrielbaute/spotify-saver)
- Tags: deep-dive
- Published: 2026-03-02

---

**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](https://github.com/gabrielbaute/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`](https://github.com/gabrielbaute/spotify-saver/blob/main/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`.

```python
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`](https://github.com/gabrielbaute/spotify-saver/blob/main/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`](https://github.com/gabrielbaute/spotify-saver/blob/main/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 `.lrc` file shares the same base name as the audio.
- **Success signaling**: The method returns a boolean `True` only when lyrics are successfully written, which downstream code uses to update the track’s metadata.

```python
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`](https://github.com/gabrielbaute/spotify-saver/blob/main/spotifysaver/cli/commands/download/track.py), the `--lyrics` flag triggers the download flow. When present, the CLI forwards `download_lyrics=True` to the downloader:

```bash
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`](https://github.com/gabrielbaute/spotify-saver/blob/main/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`](https://github.com/gabrielbaute/spotify-saver/blob/main/spotifysaver/models/track.py) maintains the lyrics state through three key members:

- **`has_lyrics`**: A boolean field indicating whether the `.lrc` file 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 new `Track` instance with an updated `has_lyrics` value, 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/get` with a 10-second timeout, returning either `syncedLyrics` or `plainLyrics` based on the `synced` parameter.
- **`get_lyrics_with_fallback`** automatically attempts synchronized lyrics first, falling back to plain text if unavailable.
- **`_save_lyrics`** writes the content to a `.lrc` file adjacent to the audio, skipping instrumental tracks and returning a success boolean.
- The **`Track`** model tracks lyrics status via `has_lyrics` and the immutable `with_lyrics_status` method.
- Both the CLI (`--lyrics` flag) and the Python API (`download_lyrics` parameter) 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`](https://github.com/gabrielbaute/spotify-saver/blob/main/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`](https://github.com/gabrielbaute/spotify-saver/blob/main/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.