Operate and automate a lossless music library with beets — intake/import loops, tagging policy, quality-ranked deduplication (WAV > FLAC > lossy), fingerprints, quarantine-not-delete policy, and library maintenance. Use whenever a task involves beet/beets commands, music library imports, tagging, dedup, fingerprinting, duplicate cleanup, lossless archive management, or beets configuration — even if the user just says "organize my music", "clean up duplicates", or "import these albums" without naming beets.
75
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Drive a beets-managed lossless music library with minimal human intervention: import, identify metadata, normalize tags, fingerprint, deduplicate with an explicit quality policy, quarantine (never delete) losers, validate, and report. The core discipline: every decision is recorded, every rejection is reversible, and the quality policy beats naive bitrate comparisons.
This skill was forged on a real 13k-track library migration (Debian 12 CT, beets 1.6.0) and every command/config claim in the references was verified against plugin source or executed live. Where behavior differs between beets versions, the reference files say so explicitly.
beet version # pin the version BEFORE trusting any config advice
beet config -p # where is the real config?
beet config -d # where is the real library DB?
beet stats # how many items, what stateRead references/version-notes.md next if the version is not 2.x — 1.6.0 has
silent traps (interactively-prompting badfiles, list-form duplicates keys that
match nothing, incremental disabling resume) that produce empty results
instead of errors.
Duplicate decisions happen only after establishing two files are the same recording. Then rank:
quality_rank — explicit numeric tiers, NEVER lexical $format sorting:WAV/BWF: 3 (beets calls this format "WAVE" — match both names!)
FLAC: 2
ALAC: 2 (same tier as FLAC; codec-inspect m4a before trusting extension)
MP3: 1 (and AAC/OGG/Opus/AIFF lossy-or-lesser: 1)
Unknown: 0A WAV made from an MP3 is not an upgrade. Bitrate never proves losslessness. If provenance is ambiguous after all of the above: keep both, defer the group — never guess.
Why numeric ranks: the duplicates plugin tiebreak sorts values lexically as
well as descending, so format sorts WAV below FLAC — the exact opposite
of the policy. Inject an integer quality_rank field instead (pattern in
references/bulk-injection.md).
Run dedup once, after all inputs are final — full import done, all fingerprints computed and injected. No incremental passes.
fpcalc
runs offline at ~1–2.5 files/sec. The capture loop MUST record fpcalc's
stdout even when it exits nonzero — trailing-junk files print a valid
fingerprint then error on final decode; || return 0 discards them and
silently skips ~14% of real-world libraries. See references/fingerprints.md.references/bulk-injection.md.
13,318 items in 2.5 s; without the wrapping transaction it is one fsync per
item and hours of disk storming.references/version-notes.md.--move into a dated quarantine dir. Never --delete.
Assert the quarantine shares the library's filesystem first
(stat -c %d on both) — a cross-filesystem --move silently becomes a
copy+delete at ~3–8 MB/s on spinning disks and can fill a small root volume.
This has already caused a disk-full incident once: see
references/quarantine-and-disk-safety.md. On the same filesystem the move is
an instant rename; collisions are handled by beets (unique_path). Keep the
loser manifest (-f '$path' output).
Then purge the quarantined rows from the DB (rows only, delete=False)
before the next pass — otherwise the next pass re-groups the same losers with
their own winners and moves them again.Two passes cover real libraries: fingerprint-keyed (-k acoustid_fingerprint -k acoustid_length -s) for audio-identical copies, and tag-keyed
(-k albumartist -k album -k title -s) for same-tags-different-audio. Pass
keys explicitly on the CLI — CLI keys always work; config keys: forms are
version-fragile (see version notes).
beet import --pretend CANDIDATE # preview grouping/matches, keep output in the batch log
beet import -q CANDIDATE # automated import; verify exit status + log afterquiet_fallback explicitly. skip (default) leaves unmatched candidates
in the inbox → the runner must move them to a dated deferred location; never
treat "left in inbox" as terminal. asis imports with existing tags — only
with deliberate policy.duplicate_action: skip. The importer must never decide upgrades: the
native upgrade action compares raw bitrate only, no lossless/lossy
distinction. All duplicate decisions belong to the dedup pass above.import.copy: yes leaves originals in the inbox — the runner archives them
(move, never delete) to a dated collision-resistant dir only after verified
import. Every candidate ends the run with a disposition: imported,
skipped-duplicate, deferred, quarantined, or archived.find -print0 + read -d ''/xargs -0,
-- before operands. Music filenames carry apostrophes, $, Unicode.Full annotated config template (with version-gated blocks):
references/config-template.md.
import.delete: yes (config key, not a flag — audit the config).duplicates.delete: yes or the plugin's --delete.beet import -L: it treats arguments as a library query and retags
matching existing items instead of importing.These come from a 2-core/5400-RPM-HDD host and apply anywhere I/O-bound:
nohup), niced/ioniced, single-worker, under an
flock lockfile so relaunches cannot stack. Foreground SSH launches that
time out orphan their pipelines — kill children in pipeline order and
verify with pgrep -c before relaunching.pgrep -f
patterns that can self-match the monitoring script.beet ls -f '$field'), not just raw SQLite — the two
views can disagree after external writes; re-run the write via ORM to
reconcile.ssh | pct exec | bash -c quoting burns hours. Write scripts to
files and push them (base64 over the transport), never inline-quote.findmnt -T PATH, compare stat -c %d), and when recovering from
a full disk use rsync -a --remove-source-files so space is reclaimed per
file — cp-then-rm holds the disk at 100% for the entire copy, which on a
slow disk can mean hours of hazard for the services sharing that volume.ssh root@192.168.2.242; beets 1.6.0 / Python 3.11.2 / Debian 12.15/root/.musiclibrary.db; config /root/.config/beets/config.yaml
(asis import: autotag/write/copy/move all no — zero file mutation)/opt/homelab/navidrome/music; Navidrome runs in Docker here
(production: rescan via Subsonic API, never restart without asking)PYTHONPATH=/usr/share/beets required for ORM scripts (non-standard install)sqlite3 CLI, no lsof, no ffprobe in this CTfpcalc from the chromaprint package; fingerprints cached at
/root/fingerprints.tsv (path⇥duration⇥fingerprint) with done-list
/root/fp-done.txtpvnkmnk/homelab-proxmox-ansible → docs/findings/2026-09-18-homelab-music-library-cleanup.md (canonical; that repo is the authority for homelab state), plus docs/LIBRARY_CLEANUP_WORKSTREAM.md for the live workstreamThe library currently runs beets 1.6.0. Upgrade to 2.x (2.13.1+, which adds
the badfiles automation keys) is scheduled after the current
dedup/merge pipeline completes — never mid-pipeline. The playbook, including
the fresh-Debian-13-CT option and post-upgrade revalidation checklist, is in
references/upgrade-path.md.
references/version-notes.md — read this on any non-2.x install;
silent-failure config traps verified against 1.6.0 sourcereferences/config-template.md — full annotated config, version-gatedreferences/fingerprints.md — fpcalc capture loop, resumable design, the
nonzero-exit bugreferences/bulk-injection.md — ORM single-transaction attribute injection,
WAVE naming, verification rulesreferences/quarantine-and-disk-safety.md — read before any
--move; the cross-device quarantine incident and the guard that prevents itreferences/upgrade-path.md — 1.6.0 → 2.x playbookff4471e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.