How to Download and Save Album Covers Alongside Music Files in Spotify-Saver

Spotify-Saver downloads album covers by extracting high-resolution artwork URLs from Spotify metadata and processing them through the ImageDownloader service, either embedding the image bytes directly into audio file metadata or saving separate cover.jpg files alongside track collections.

When using the open-source Spotify-Saver tool to build your local music library, proper album artwork handling ensures your collection displays correctly across media players. This article examines how the gabrielbaute/spotify-saver repository implements cover art retrieval and storage, detailing the two distinct workflows for handling album covers alongside downloaded audio files.

How Album Cover Downloading Works

The application retrieves cover art through a coordinated pipeline involving Spotify's API, a dedicated image download service, and metadata embedding utilities. Two primary flows handle artwork depending on whether you download individual tracks or complete collections.

Extracting Cover Art URLs from Spotify Metadata

The process begins in spotifysaver/services/spotify_api.py, where the SpotifyAPI class constructs data models containing high-resolution cover art URLs. When processing tracks, albums, or playlists, the code extracts the first (highest resolution) image from Spotify's metadata response:


# spotifysaver/services/spotify_api.py

cover_url = raw_data["images"][0]["url"] if raw_data["images"] else None

This cover_url field populates the Track, Album, and Playlist model classes defined in spotifysaver/models/, making the artwork address available throughout the download pipeline.

The ImageDownloader Service

All HTTP image fetching logic resides in spotifysaver/downloader/image_downloader.py. This thin wrapper around the requests library provides two key methods:

  • download_image(url, output_path): Writes image bytes directly to disk (lines 15-38)
  • get_image_from_url(url): Returns raw bytes for in-memory processing (lines 45-61)

The implementation handles directory creation and timeout configuration:


# spotifysaver/downloader/image_downloader.py

response = requests.get(url, timeout=Config.DOWNLOAD_TIMEOUT)
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_bytes(response.content)          # saved to disk

# or return response.content for in-memory use

Embedding Covers into Audio Files

For single track downloads, Spotify-Saver embeds artwork directly into the audio file's metadata tags. In spotifysaver/downloader/youtube_downloader.py, the YouTubeDownloader._download_cover() method (lines 197-208) retrieves image bytes via ImageDownloader.get_image_from_url(), then passes them to MusicFileMetadata:


# youtube_downloader.py

cover_data = self._download_cover(track)          # bytes or None

metadata = MusicFileMetadata(file_path=output_path,
                               track=track,
                               cover_data=cover_data)
metadata.add_metadata()                           # embeds image

The MusicFileMetadata class in spotifysaver/metadata/music_file_metadata.py handles writing these bytes into ID3 tags (for MP3) or MP4 metadata (for M4A), ensuring the cover displays in any standard media player.

Saving Separate Cover Image Files

When downloading complete albums or playlists, the application saves a standalone cover.jpg file alongside the audio tracks. The YouTubeDownloader._save_cover_album() method (lines 267-282) orchestrates this process:


# youtube_downloader.py

def _save_cover_album(self, url: str, output_path: Path):
    image = self.image_downloader.download_image(url, output_path)
    if image:
        self.logger.info(f"Cover saved in: {output_path}")

CLI commands in spotifysaver/cli/commands/download/album.py (lines 145-148) and playlist.py (lines 113-116) invoke this helper, passing output_dir / "cover.jpg" as the target path. This creates a folder structure like:


Music/
  Artist/
    Album (2023)/
      01 - Artist - Track.m4a          # audio file with embedded art

      cover.jpg                        # separate image for album view

Code Implementation Examples

Downloading a Single Track with Embedded Cover Art

When downloading individual tracks, the cover art is automatically embedded into the audio file metadata:

from spotifysaver.downloader.youtube_downloader import YouTubeDownloader
from spotifysaver.models.track import Track
from spotifysaver.enums.audio_formats_enum import AudioFormat
from spotifysaver.enums.bitrates_enum import Bitrate
from pathlib import Path

# Track instance populated by SpotifyAPI

track = Track(...)

yt = YouTubeDownloader(base_dir=Path("Music"))
audio_path, updated_track = yt.download_track(
    track,
    output_format=AudioFormat.M4A,
    bitrate=Bitrate.B256,
    download_lyrics=True,
)

print(f"Saved audio: {audio_path}")          # contains embedded cover art

Key call chain: YouTubeDownloader.download_track()_download_cover()ImageDownloader.get_image_from_url()MusicFileMetadata.add_metadata().

Downloading an Album with Separate Cover File

For complete collections, use the cover=True parameter to save artwork as a separate file:

from spotifysaver.downloader.youtube_downloader import YouTubeDownloader
from spotifysaver.models.album import Album
from pathlib import Path

album = Album(...)                     # populated by SpotifyAPI

yt = YouTubeDownloader(base_dir=Path("Music"))
yt.download_album(album, cover=True)   # triggers _save_cover_album

Key call chain: YouTubeDownloader.download_album()_save_cover_album()ImageDownloader.download_image().

CLI Usage for Cover Art Preservation

Command-line users can trigger cover saving using the --cover flag:


# Album download with cover.jpg saved alongside tracks

spotifysaver download album "My Favorite Album" --cover

# Playlist download with cover art preservation

spotifysaver download playlist "Chill Vibes" --cover

These commands execute the same underlying methods as the Python API, ensuring consistent artwork handling across interfaces.

Key Components and File Locations

Summary

  • Spotify-Saver extracts high-resolution cover URLs from Spotify's metadata API during the initial track/album lookup phase.
  • Two distinct workflows handle artwork: embedding bytes directly into audio file metadata for single tracks, or saving separate cover.jpg files for album/playlist downloads.
  • The ImageDownloader service in image_downloader.py handles all HTTP image retrieval, supporting both disk writes and in-memory byte returns.
  • YouTubeDownloader coordinates the process, calling _download_cover() for embedding and _save_cover_album() for standalone files.
  • CLI commands support the --cover flag to trigger standalone cover saving for complete collections.

Frequently Asked Questions

Does Spotify-Saver always download album covers automatically?

Individual track downloads automatically embed cover art into the audio file metadata, but saving separate cover.jpg files requires explicitly setting cover=True when downloading albums or playlists via the API, or using the --cover flag in CLI commands.

What image format does Spotify-Saver use for saved covers?

When saving standalone album artwork via ImageDownloader.download_image(), the application preserves the original format provided by Spotify's CDN (typically JPEG) and writes it directly to disk as cover.jpg without transcoding.

Can I disable cover art embedding to save bandwidth?

The current implementation in youtube_downloader.py always attempts to download cover art for single tracks via _download_cover(), but you can skip standalone cover file generation for albums by omitting the cover=True parameter or --cover flag.

Where is the cover art stored when downloading playlists?

According to the source code in spotifysaver/cli/commands/download/playlist.py (lines 113-116), playlist covers are saved as cover.jpg in the root playlist directory, using the same YouTubeDownloader._save_cover_album() method employed for album downloads.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →