Skip to main content

coverart-cli

Fill the missing covers in your music library — embed and sidecar in one pass.

HTML coverage report

CI CodeQL CodeRabbit reviews PyPI PyPI downloads Python License: MIT

What it does

Most cover-art tools only embed or only sidecar. That breaks across players — Subsonic apps (Amperfy, Symfonium) read tags, Plex / Jellyfin prefer cover.jpg, Apple Music wants embedded. coverart-cli does both in one pass and ships an HTML coverage report so you can see what's still missing.

Install

pipx install coverart-cli

Requires Python 3.11 or newer. Upgrade an existing installation with pipx upgrade coverart-cli.

Quick start

# 1. Preview the exact library first; this performs no writes
coverart ~/Music --dry-run -v

# 2. Fetch + embed + sidecar (free providers, no key needed)
coverart ~/Music

# Optional: add Last.fm as the first provider
LASTFM_API_KEY=your-key coverart ~/Music

# Just generate the coverage report
coverart ~/Music --report-only --report-html report.html

# Only write cover.jpg files; leave audio tags unchanged
coverart ~/Music --no-embed

Run coverart --help for the full flag list.

--dry-run never writes files and therefore cannot be combined with --report-html or --missing-csv; generate those outputs in a separate run.

By default, an album is complete only when it has a usable sidecar and every supported audio file has embedded artwork. If a local sidecar already exists, it is reused to fill missing embeds without a network request. Symbolic links to album directories, audio files, or sidecars are ignored, so writes stay inside the selected library.

Before the first non-dry run, keep a backup of irreplaceable music files. Tag writes are handled by Mutagen, but any bulk metadata operation deserves a recovery path. Do not move or rename library directories concurrently with a write run. Root-anchored traversal rejects symlink swaps, but on POSIX a directory rename by another process does not revoke a directory descriptor that is already open.

Config file

Save your defaults so you don't have to repeat flags:

# ~/.config/coverart-cli/config.toml
lastfm_key      = "your-key"
min_bytes       = 30000
replace_smaller = true
no_musicbrainz  = false

Lookup order (later wins): built-in defaults → ~/.config/coverart-cli/config.toml → ./coverart.toml → --config PATH → CLI flags. LASTFM_API_KEY overrides the configured key unless --lastfm-key is supplied. Run coverart ~/Music afterwards with no repeated flags. An explicit CLI value always wins, even when it equals the built-in default. Use --embed, --sidecar, --no-dry-run, --no-report-html, or --no-missing-csv to temporarily clear the corresponding configured action.

Sources

Tried in order until a cover is found:

  1. Last.fm — album.getinfo (needs a free API key)
  2. iTunes — Apple Music's public search, no key
  3. Deezer — public API, no key
  4. MusicBrainz + Cover Art Archive — fallback for niche releases

Album and artist names are sent to the enabled providers over HTTPS. Download URLs and redirects are restricted to the providers' API and image hosts; arbitrary hosts and non-HTTPS URLs are rejected.

Supported formats

MP3 (ID3 APIC), M4A/M4B/MP4 (covr atom), FLAC (Picture block), Ogg Vorbis / Opus (metadata_block_picture).

Library traversal and mutation require directory-relative, no-follow file APIs available on Linux and macOS. The CLI rejects unsupported platforms before contacting any provider, rather than risk following a replaced path component.

Cover inputs are accepted only when Pillow can fully decode them as JPEG or PNG, up to 20 MiB and within the documented dimension limits. Report thumbnails also use a global size budget so very large libraries cannot make the self-contained HTML grow without bound. Malformed, oversized, or unrecognized provider payloads and local sidecars are ignored instead of being written into tags.

Common workflows

Goal Command
Preview all changes coverart ~/Music --dry-run -v
Upgrade small artwork coverart ~/Music --min-bytes 30000 --replace-smaller
Embed only coverart ~/Music --no-sidecar
Sidecars only coverart ~/Music --no-embed
Disable directory-name fallback coverart ~/Music --no-fallback-dirnames
Export misses coverart ~/Music --missing-csv missing.csv
Build an HTML report coverart ~/Music --report-only --report-html report.html

Use --workers 1 for deterministic serial processing or when a provider is rate-limiting heavily. MusicBrainz requests are always rate-limited internally.

Programmatic use

from pathlib import Path
from coverart_cli.core import RunOptions, run
from coverart_cli.providers import ITunesProvider, DeezerProvider

stats = run(
    RunOptions(
        root=Path("~/Music").expanduser(),
        providers=[ITunesProvider(), DeezerProvider()],
    )
)
print(stats.fetched_from, stats.not_found)

Alternatives

Tool When to pick it
sacad Best match rate; Rust binary, more sources
get-cover-art Battle-tested Python API
beets fetchart Already using beets for everything else
coverart-cli (this) You want an HTML report plus embed/sidecar dual output

Development

git clone https://github.com/buettgen-app/coverart-cli && cd coverart-cli
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
ruff check . && pyrefly check && pytest

Releases

Releases are prepared by release-please and published from an immutable Git tag via PyPI Trusted Publishing. Commits to main follow Conventional Commits:

Commit prefix Effect on next release
feat: … minor bump (0.3.0 → 0.4.0)
fix: … patch bump (0.3.0 → 0.3.1)
feat!: … / BREAKING CHANGE: minor before 1.0; major from 1.0
perf:, docs:, refactor: patch bump (0.3.0 → 0.3.1)
chore:, ci:, test: hidden in changelog

The Prepare release workflow uses the repository-scoped GITHUB_TOKEN to open and update one rolling Release PR. Merging that PR creates a mutable draft release and sends an authenticated repository dispatch to the isolated Publish release workflow. That workflow verifies the source run, exact commit, package metadata, changelog, and draft state before it creates a protected annotated version tag and publishes an immutable GitHub Release. It builds one wheel and source distribution, attaches those exact files to the release, publishes them to PyPI with OIDC attestations, and cryptographically verifies the exact local artifacts against PyPI's recorded provenance.

The normal release writes use only the repository-scoped GITHUB_TOKEN. Configure RELEASE_SETTINGS_TOKEN as a fine-grained token limited to this repository with Administration: read; it is used only to verify, immediately before publication, that GitHub Immutable Releases remain enabled. In Settings → Actions → General → Workflow permissions, enable Allow GitHub Actions to create and approve pull requests. GitHub may hold checks on a Release Please PR until a maintainer selects Approve workflows to run.

The PyPI Trusted Publisher must be configured for GitHub owner buettgen-app, repository coverart-cli, workflow release.yml, and environment pypi. Enable GitHub Immutable Releases for the repository. The active refs/tags/v* ruleset must block tag updates, deletion, and non-fast-forward changes without bypass actors; publication fails closed if either repository protection is missing or weakened. That filename is the stable publish identity even though Release Please itself runs in prepare-release.yml. Do not create release tags or upload distributions by hand. A failed publish can be retried without introducing a second release path: the workflow accepts only the exact mutable draft or immutable release, rebuilds wheel and sdist deterministically from the bound source and requires their SHA-256 values to match any immutable GitHub assets, uploads only missing PyPI files, and verifies provenance. The same workflow can be run manually from main for a named draft or a published version that already satisfies the protected annotated-tag contract; recovery refuses product changes beyond the explicit release repair. Pull requests and Release PRs remain subject to branch protection and their configured merge gates. GitHub Actions changes are gated by the repository's Zizmor security lint; third-party AI review remains advisory so availability limits cannot block security updates.

License

MIT

Metadata

Release files for coverart-cli 0.6.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for coverart-cli 0.6.2
File Size Uploaded
coverart_cli-0.6.2.tar.gz 122.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for coverart-cli 0.6.2
File Interpreter ABI Platform
coverart_cli-0.6.2-py3-none-any.whl Python 3 none any Details

Total release size: 166.9 kB

Release files / coverart_cli-0.6.2.tar.gz

Download URL coverart_cli-0.6.2.tar.gz
Size 122.9 kB
Tags Source
SHA-256 checksum
How to use checksums
22518ac1a23a6b358a66c476366633aac144cffaab6f1058060fa8f26bf4595c
BLAKE2b-256 checksum
How to use checksums
77481a84d989a9ef7cee86859de389a9dffeda7592630c3d149bcbbd510dd08c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.

Transparency log

Release files / coverart_cli-0.6.2-py3-none-any.whl

Download URL coverart_cli-0.6.2-py3-none-any.whl
Size 44.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3a14911616fb62e71ad55188c4645d3b91f7da3d99f3cfebece25bdbc577c596
BLAKE2b-256 checksum
How to use checksums
864a341809d3620fc68ad9d5467b68ce982b058143e4da41ec9473a455f7f99b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.

Transparency log

Release history Release notifications | RSS feed

0.7.3

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

This release

0.6.2 This release

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.5

2 release files

0.4.3

2 release files

0.4.1

2 release files

0.4.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page