Skip to main content

coverart-cli

Album artwork in your audio tags, beside your tracks, and in a searchable report.

CI CodeQL PyPI Python License: MIT

Fill missing artwork from iTunes, Deezer, MusicBrainz / Cover Art Archive, and optionally Last.fm. One run embeds artwork into supported audio files and writes a sidecar for players that read covers from the album folder. Existing local artwork is reused where possible.

Coverart HTML report with album artwork, coverage statistics, search, and filters

Quick start · Workflows · Configuration · Reports · Development · Changelog

Install

Requires Python 3.11+ on Linux, macOS, or Windows 10 (1709+) / 11. Windows runs natively without WSL on local NTFS volumes. Junctions, symbolic links and other reparse points are skipped; directory-mounted volumes, UNC/network shares and non-NTFS volumes and SUBST drive mappings are unsupported. EFS-encrypted and multiply linked audio files are rejected before copying.

Install in an isolated tool environment with either pipx or uv:

pipx install coverart-cli
# Or:
uv tool install coverart-cli

coverart --version

Upgrade with pipx upgrade coverart-cli or uv tool upgrade coverart-cli. PyPI contains the latest published release; this README also documents repository changes that may be awaiting release.

Windows (PowerShell)

Install Python 3.15 (recommended; 3.11+ supported) and pipx, then open a new PowerShell window after ensurepath:

py -m pip install --user pipx
py -m pipx ensurepath
# Until the next PyPI release, install the Windows implementation from main
# (requires Git for Windows):
pipx install "git+https://github.com/buettgen-app/coverart-cli.git@main"
coverart --version
coverart "$env:USERPROFILE\Music" --dry-run -v
coverart "$env:USERPROFILE\Music"
coverart "$env:USERPROFILE\Music" --report-only --report-html .\report.html
Start-Process .\report.html

Quote paths containing spaces. Set a Last.fm key with $env:LASTFM_API_KEY = "YOUR_KEY". Windows defaults are read from %APPDATA%\coverart-cli\config.toml; an absolute XDG_CONFIG_HOME overrides that location. Use TOML literal strings for Windows paths, for example report_html = 'C:\Reports\coverage.html'.

Close players or other applications holding files open before embedding artwork. Windows denies a conflicting write or rename while library handles are held; locked or read-only audio files produce an error and keep their original bytes. Write directories must be controlled by your account, SYSTEM or local Administrators. Directories granting other principals file creation, deletion or ACL control are refused before staging and checked again before replacement; read-only sharing and report generation remain available. Existing destinations are held against in-place writes (including encryption changes) through the atomic replacement. Audio staging files receive the source owner, group, DACL and SACL at creation, before private audio is copied; if the complete security policy cannot be read or retained, replacement fails safely. Embedding therefore requires an already assigned SeSecurityPrivilege (typically available in an elevated Windows session); the tool never grants or elevates privileges. It enables this privilege only on a temporary private thread token while cloning security metadata, then restores the original token. Creation, access and modification times are retained. Named NTFS streams are copied through the verified source handle. A handle-bound atomic rename replaces the destination entry without following a substituted symlink. Staging handles remain open through commit. Hidden, system, indexing and compression attributes are retained; unsupported source attributes fail safely. Failed renames leave the original intact.

Quick start

# Preview fetching and planned changes without writing files.
coverart ~/Music --dry-run -v

# Fill missing embedded artwork and album-folder sidecars.
coverart ~/Music

# Inspect coverage locally without contacting any provider.
coverart ~/Music --report-only --report-html report.html

Open report.html in your browser. The default providers require no API key. To enable Last.fm as the first provider, set LASTFM_API_KEY in your environment; get a key from Last.fm.

Dry runs still contact providers and download candidate images. They write neither tags nor sidecars and cannot be combined with --report-html or --missing-csv. Use a separate report-only run for a local coverage check.

How it works

  1. Discover supported audio files in album directories, including the selected root when it is itself an album folder.
  2. Read album artist and album tags; fall back to Artist/Album/ directory names when readable tags are absent.
  3. Reuse a suitable local sidecar to fill missing embeds, or try enabled providers in order until valid artwork is found.
  4. Write the requested outputs, retaining existing artwork unless an upgrade threshold is configured.
Music/
└── Artist/
    └── Album/
        ├── 01 - Track.mp3
        ├── 02 - Track.mp3
        └── cover.jpg

A root with conflicting album identities, or a mix of readable and unreadable identities, is excluded from writing and reports. Compilations may have different track artists when their album identity agrees. Organize loose tracks into album directories before processing them. By default, processing skips an album only when a usable sidecar exists and every supported track has embedded artwork. Disabling an output removes that requirement from the completion check.

Common workflows

Goal Command
Preview a library coverart ~/Music --dry-run -v
Process one album coverart "$HOME/Music/Artist/Album"
Write sidecars only coverart ~/Music --no-embed
Embed artwork only coverart ~/Music --no-sidecar
Upgrade artwork below 30,000 bytes coverart ~/Music --min-bytes 30000
Use tags without directory fallback coverart ~/Music --no-fallback-dirnames
Export misses and processing failures coverart ~/Music --missing-csv missing.csv
Generate a report after processing coverart ~/Music --report-html report.html
Generate a report without fetching coverart ~/Music --report-only --report-html report.html
Omit report thumbnails coverart ~/Music --report-only --report-html report.html --no-thumbs
Process albums serially coverart ~/Music --workers 1

--min-bytes applies to embedded artwork and sidecars. Byte size is an upgrade heuristic, not a resolution or visual-quality score. Larger existing artwork is kept by default; --replace-smaller disables that protection, allowing a below-threshold cover to be replaced even by a smaller downloaded image.

Four workers run by default. MusicBrainz requests are rate-limited internally. Use -v for progress, -vv for debugging, and coverart --help for all options.

HTML report

The self-contained HTML report includes album search, source filters, coverage statistics, and light/dark themes. Viewing it requires no server. Thumbnails come from local sidecars and have a shared size budget; embedded-only albums can have coverage without a thumbnail.

Report coverage means an album has a usable sidecar or artwork embedded in every supported track. This is broader than the default processing completion check, which requires both. Provider provenance is not stored per album: existing artwork is classified as covered with an unknown origin. Reports contain library, artist and album names plus optional artwork; review their contents before sharing. For a directly selected album directory, reports use verified tag identities and show Unknown Artist when the tags cannot be read, avoiding disclosure of the parent directory name. Hidden tracks are excluded from counts and coverage.

Configuration

Store defaults in ~/.config/coverart-cli/config.toml (or under $XDG_CONFIG_HOME/coverart-cli/config.toml):

workers = 4
min_bytes = 30000
no_embed = false
no_sidecar = false
no_musicbrainz = false
# Optional: identify yourself when contacting MusicBrainz.
user_agent = "coverart-cli (mailto:you@example.com)"

Configuration is merged in this order; later values win:

Built-in defaults → user config → ./coverart.toml → --config PATH
                  → LASTFM_API_KEY (key only) → explicit CLI flags

Keys match CLI option names with underscores: no_embed, min_bytes, report_html, and so on. Unknown keys and invalid values are ignored with a warning. Keep API keys out of committed project configuration.

Explicit flags override configured values even when equal to the built-in default. Use --embed, --sidecar, --no-dry-run, --no-report-html, or --no-missing-csv to clear the corresponding configured action.

Providers and formats

Providers are tried in the following order; Last.fm is skipped without a key.

Provider API key Disable with
Last.fm Required --no-lastfm
iTunes Search None --no-itunes
Deezer None --no-deezer
MusicBrainz / Cover Art Archive None --no-musicbrainz
Audio formats Embedded artwork
MP3 ID3 APIC
M4A, M4B, MP4 covr atom
FLAC Picture block
Ogg Vorbis, Opus metadata_block_picture

Recognized local sidecars are cover.jpg, cover.png, folder.jpg, and folder.png. Artwork is validated as JPEG or PNG before use: at most 20 MiB, 4,096 pixels per dimension, and 16,777,216 total pixels. The normal minimum usable cover size is 2,000 bytes. Sidecar output follows the validated image format (cover.jpg or cover.png).

File safety and privacy

Album and artist names are sent to enabled providers over HTTPS. Provider downloads and redirects are restricted to their permitted API/image hosts. Report-only mode makes no provider requests.

Library traversal skips hidden entries and does not follow symbolic links to album folders, tracks, or covers. Tag writes use verified file identities and copy-on-write replacement. Keep a backup before bulk metadata changes and avoid moving or renaming the library while a run is writing: a POSIX directory rename does not revoke an already-open descriptor.

Read SECURITY.md for trust boundaries and vulnerability reporting.

MP3 artwork writes retain existing ID3v2.3/v2.4 versions and tag values; legacy ID3v1/v2.2 tags are upgraded to v2.3 and new tags use v2.4.

Automation and Python API

CLI exit codes are 0 for a completed run (ordinary cover misses are allowed), 1 for processing or I/O errors, and 2 for invalid options or unsupported platform capabilities. Use --missing-csv to assess missing artwork separately.

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

stats = run(
    RunOptions(
        root=Path.home() / "Music",
        providers=[ITunesProvider(), DeezerProvider()],
        dry_run=True,
    )
)
print(stats.fetched_from, stats.not_found, stats.errors)

Development

Dependency updates are configured through Renovate, including Python hash locks, GitHub Actions and pre-commit hooks.

Use the checked-in hash locks and the same editable-install approach as CI:

git clone https://github.com/buettgen-app/coverart-cli.git
cd coverart-cli
python3.15 -m venv .venv
source .venv/bin/activate
python -m pip install --require-hashes -r requirements-ci.txt
python -m pip install --no-deps --no-build-isolation -e .

ruff check .
ruff format --check .
pyrefly check
pytest --cov=coverart_cli --cov=scripts --cov-report=term-missing --cov-fail-under=87 \
  --cov-report=json:coverage-results/report.json \
  --cov-report=xml:coverage-results/coverage.xml \
  --cov-report=html:coverage-results/html
python scripts/check_coverage.py coverage-results/report.json --profile common

Install FFmpeg to enable the audio-stream preservation oracle used in CI. The Linux test matrix covers Python 3.11–3.15; additional jobs check macOS metadata preservation and native Windows processing, handle locking and junction rejection. CodeQL, Zizmor, dependency review, fuzzing, and reproducible artifact builds provide separate checks.

Test coverage measures executed code, separately from the report's album-art coverage. Line coverage counts executed statements; branch coverage checks both outcomes of decisions, including failures and rejected inputs. Neither proves that a test asserts the right behavior or that the program has no vulnerabilities. CI requires at least 90% lines and 78% branches for shared code, with a separate native Windows measurement. Download coverage-* artifacts from a CI run and open html/index.html to see missed lines and branches. See the coverage policy for scopes and gates.

Run python scripts/check_repository.py to check tracked files for private contact details and local artifacts. See repository hygiene and CI gates for privacy-check limits and the required-check configuration.

Report bugs or request features in GitHub Issues. For releases, protected tags, PyPI Trusted Publishing, and recovery procedures, see the release and publishing guide.

License

MIT

Metadata

Release files for coverart-cli 0.7.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.7.2
File Size Uploaded
coverart_cli-0.7.2.tar.gz 187.0 kB Details

Built distribution (wheel)

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

Total release size: 247.3 kB

Release files / coverart_cli-0.7.2.tar.gz

Download URL coverart_cli-0.7.2.tar.gz
Size 187.0 kB
Tags Source
SHA-256 checksum
How to use checksums
1abe10fc12f9060bde33a39b28da13c0ae90bc431af1c9a520a7943d0b129834
BLAKE2b-256 checksum
How to use checksums
b16adbb248e1839fc79f68cf8b03456c304c94ebc2bbf36ee7213d8c3b95d3e2
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 Oct 11, 2026.

Transparency log

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

Download URL coverart_cli-0.7.2-py3-none-any.whl
Size 60.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ffcefc81bede6eec1b3d49e45bed9e516170d3ff49be9453dfc75adeee7d8f7b
BLAKE2b-256 checksum
How to use checksums
813bcd7f8a56dd42371840659abe003b62fd4272007aea055fe7199597be0902
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 Oct 11, 2026.

Transparency log

Release history Release notifications | RSS feed

0.7.3

2 release files

This release

0.7.2 This release

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.2

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