Skip to main content

sanitune

MIT License Python 3.11+ CI GitHub release (latest by date) GitHub last commit

sanitune logo

CLI that scans a music library and lists or deletes tracks that match your rules: DRM-locked files, low bitrates, and other quality bars. Point it at a file or folder; filters decide what matches.

Built for bulk library cleanup. Header and tag inspection only (via mutagen); it does not decode audio or strip DRM.

Contents

What it does

  • Scan files or trees. Recursively finds common audio extensions under the paths you pass.
  • Filter by DRM. Best-effort detection for Windows Media DRM (WMA/ASF encryption objects) and common FairPlay signals (.m4p, MP4 protection atoms).
  • Filter by quality. Bitrate caps for lossy codecs (e.g. MP3 under 192 kbps), plus sample rate and bit depth for lossless/PCM.
  • List or delete. Default action lists matches. --delete removes them; combine with --dry-run to preview.
  • Reacquisition report. --report FILE writes CSV or JSONL of every match with path, why it matched, DRM label, bitrate, and action (matched / would_delete / deleted / delete_failed) so you can track what to re-rip or re-buy.
  • Terminal UI. On an interactive terminal: discovery spinner, scan progress bar, results table, and a summary panel. Piping, --json, --plain, or -q stay script-friendly (paths or JSON only).
  • JSON output. --json emits one object per match on stdout for scripting.
  • No phoning home. No analytics or metrics collection; this tool does not call home in any way.

Quick Start

Once published to PyPI:

uvx sanitune ~/Music --max-bitrate 192 --format mp3

Local development (git checkout)

While hacking on this repo, do not rely on plain uvx --from .. That command builds a wheel and caches it; source edits will not show up until the project version bumps or the uv cache is cleared. Symptom: old UX or missing features after you just changed the code.

Use one of these instead (both always run the tree on disk):

uv sync
uv run sanitune ~/Music --max-bitrate 192 --format mp3 -v

# same idea via uvx + editable install
uvx --with-editable . --from . sanitune ~/Music --drm

Confirm you are on the code you think you are:

uv run sanitune --version
uv run python -c "import sanitune.display as d; print(d.__file__)"

If you specifically want to test the packaged (non-editable) install:

# after bumping version in pyproject.toml:
uvx --from . --refresh sanitune --version
# nuclear option:
uv cache clean
uvx --from . --refresh sanitune ~/Music --drm

Filters

Flag Meaning
--drm / --no-drm Match only DRM-locked files, or only non-DRM
--format mp3,m4a,... Restrict to logical formats
--max-bitrate KBPS Lossy files at or below this bitrate
--min-bitrate KBPS Lossy files at or above this bitrate
--max-sample-rate HZ Sample rate at or below
--min-sample-rate HZ Sample rate at or above
--max-bit-depth BITS Bit depth at or below
--min-bit-depth BITS Bit depth at or above
--lossy / --lossless Codec family

Filters combine with AND. Bitrate rules apply to lossy streams only; FLAC and friends are not matched by --max-bitrate alone.

Examples

# List MP3s at or under 192 kbps
uvx sanitune ~/Music --format mp3 --max-bitrate 192

# Find DRM-locked files (WMA FairPlay-era libraries, etc.)
uvx sanitune ~/Music --drm -v

# Preview deletes
uvx sanitune ~/Music --format mp3 --max-bitrate 128 --delete --dry-run

# Delete and keep a spreadsheet of what went away (and why)
uv run sanitune ~/Music --drm --delete --report ~/Music/sanitune-deleted.csv

# Dry-run with a JSONL audit trail before you commit
uv run sanitune ~/Music --max-bitrate 128 --format mp3 --delete --dry-run \
  --report ./would-delete.jsonl

# Scripting (stdout)
uvx sanitune ~/Music --drm --json

# Force plain text (CI, logs, no rich UI)
uvx sanitune ~/Music --max-bitrate 192 --plain

Report columns

CSV/JSONL fields include: timestamp, action, path, format, codec, bitrate_kbps, sample_rate, bit_depth, drm, drm_label, drm_reason, reasons (why the filters matched), error, sanitune_version. Format is taken from the file extension (.csv / .jsonl) or --report-format.

Interactive runs show a progress bar while probing and a results table (path, format, bitrate, DRM). Use -v for a Why column. --delete requires at least one filter (or --force) so a bare delete cannot wipe a tree by accident.

Security Notice

  • Deletes are permanent. There is no trash integration. Prefer --delete --dry-run first.
  • DRM detection is heuristic. Unknown protection schemes may be missed; see SECURITY.md.

Supported formats

MP3, M4A/AAC/ALAC (MP4), WMA/ASF, FLAC, WAV, AIFF, Ogg Vorbis, Opus, WavPack, APE, Musepack, and other types mutagen recognizes. Directory walks skip video containers (.mkv, .avi, …) unless --include-video.

Status

Early (0.2). Useful for library cleanup; treat DRM results as best-effort and review deletes before running without --dry-run.

Shell completions

# bash
eval "$(sanitune --print-completion bash)"

# zsh
eval "$(sanitune --print-completion zsh)"

# fish
sanitune --print-completion fish | source
# or install permanently:
sanitune --print-completion fish > ~/.config/fish/completions/sanitune.fish

Add the eval line to your shell rc if you want it every session.

Man page

# view without installing
sanitune --print-man | man -l -

# install for the local user (example)
mkdir -p ~/.local/share/man/man1
sanitune --print-man > ~/.local/share/man/man1/sanitune.1
# ensure ~/.local/share/man is on MANPATH, then:
man sanitune

Development

uv sync --group dev
uv run pytest -v          # enforces 100% coverage of the sanitune package
uv run ruff check .
uv run ruff format --check .

See CONTRIBUTING.md for the workflow.

License

MIT; see LICENSE. PRs and constructive feedback are welcome.

Runtime dependency mutagen is GPL-2.0-or-later; redistributors of binary bundles that include mutagen should account for that license on the combined work.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sanitune-0.2.0.tar.gz (511.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sanitune-0.2.0-py3-none-any.whl (27.9 kB view details)

Uploaded Python 3

File details

Details for the file sanitune-0.2.0.tar.gz.

File metadata

  • Download URL: sanitune-0.2.0.tar.gz
  • Upload date:
  • Size: 511.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for sanitune-0.2.0.tar.gz
Algorithm Hash digest
SHA256 b87416414fae7bd375781f9412e83b39e7c11f529b1313e01564d0f109eeec04
MD5 818f89e1232b7c7b68f03c6a85483df6
BLAKE2b-256 124551abc30a868a8d7a4cca3c4ce16a102dbd82bc7219f1cc7c58d6b2ca4137

See more details on using hashes here.

File details

Details for the file sanitune-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: sanitune-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 27.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for sanitune-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ca98a81cad4b711b7be120653a2f9115e7ef62ee589cfe21648517b5e001d674
MD5 6b0611f0b79e1a9678cbf8706c0a097a
BLAKE2b-256 17ffe7fcb4238cf528199c4349f48d4ba6abc8200fb54cf3128ea9ac61b75c6e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page