diff options
| author | Paul Buetow <paul@buetow.org> | 2026-06-19 22:05:28 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-06-19 22:05:28 +0300 |
| commit | 8e092ee1ecf429d7f5b6e15851bbccbc74bbb3ae (patch) | |
| tree | 95e09a39e6920e91ca0bd7f16de7b649b6abef1a /prompts | |
| parent | 55a2b349727f527acc3e4294539e35a012c15784 (diff) | |
music-collection skill: split oversized SKILL.md into references/
Reorganize the 217-line SKILL.md into a slim 50-line entry point
(purpose, principles, core workflow, reference index, quick reference)
plus four focused references, matching the f3s / llm-benchmark-comparison
pattern. Pure reorganization; all content preserved.
References:
- beets-layout-and-dedup.md (layout, sorting, duplicates plugin)
- navidrome-rescan.md (full scan on host and in Kubernetes)
- cmus-reindex.md (lib.pl/cache, offline and live re-index)
- tiddl-downloads.md (auth, batch loops, quality/output config)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'prompts')
5 files changed, 188 insertions, 182 deletions
diff --git a/prompts/skills/music-collection/SKILL.md b/prompts/skills/music-collection/SKILL.md index 8bdca34..5f0051d 100644 --- a/prompts/skills/music-collection/SKILL.md +++ b/prompts/skills/music-collection/SKILL.md @@ -9,194 +9,33 @@ description: >- # Music collection (beets, Navidrome, cmus, tiddl) +Maintain a local music library end to end: organize and tag with **beets**, +deduplicate, refresh the **Navidrome** server (on a host or in Kubernetes), +re-index **cmus**, and pull new tracks from **Tidal** with **tiddl**. + ## Principles - **Sort/organize** = consistent tags + path templates + `beet move` (or import with `move`). - **Dedup** = find candidates by audio fingerprint or metadata keys, then delete or merge deliberately—never bulk-delete without confirming criteria. - **Navidrome** picks up changes after a **library scan** (paths and tags on disk). -- **cmus** stores paths in `lib.pl` (text, not Perl) and tags in `cache`; re-index after path churn (see **cmus** section). +- **cmus** stores paths in `lib.pl` (text, not Perl) and tags in `cache`; re-index after path churn. - **tiddl** is third-party; personal-use only; respect Tidal ToS and local law. ---- - -## Beets: layout and “sorting” - -1. Point `directory` at the library root (the folder Navidrome mounts as music). -2. Define `paths` (e.g. genre → album artist → album → track). Use `$albumartist`, `$album%aunique{}`, `$track`, `$title`. -3. **Stable genre folders**: use the **inline** plugin with a computed field (e.g. `safe_genre`) that returns `Miscellaneous` when `genre` is empty and applies `.title()` so `electronic` and `Electronic` share one folder on case-sensitive filesystems. -4. **Import** (non-interactive batch): `beet import -A -m -q --quiet-fallback=asis PATH` (`-m` move, `-A` skip autotag for speed, `-q` no prompts). For MusicBrainz tagging, omit `-A` (slower). -5. After changing `paths` or inline fields: `beet move` to apply templates to items already in the DB. -6. **Permissions**: if the music volume is not writable as the current user, run `beet` with elevated permissions or fix ownership; keep the **beets database** under the user’s home (e.g. `-c /path/to/config.yaml` and `library:` in that file). - ---- - -## Beets: deduplication - -Enable the **duplicates** plugin in config (`plugins: duplicates`) and configure how “same track” is defined. - -- **By audio content (strong)**: use `duplicates: checksum: ffmpeg` (or another backend per beets docs) so identical audio is detected even if tags differ. -- **By metadata (weaker)**: keys such as `mb_trackid` or `fingerprint`—fast but misses true dupes with bad tags. - -Workflow: - -1. `beet duplicates -c` (or equivalent) to **count** / preview groups—consult `beet duplicates --help` for the installed beets version. -2. Inspect a few groups (paths, tags, bitrate). -3. Remove or merge only after explicit criteria (e.g. keep highest bitrate, or keep file already under the canonical path template). Prefer `beet remove` / `beet modify` on a **narrow query** over blind `-d` flags. - -Optional: **import** with `duplicate_action` / incremental import settings to reduce future dupes. - ---- - -## Navidrome: full rescan - -Navidrome reflects **on-disk** paths and embedded tags after a scan. - -### Same host as the music volume - -If the `navidrome` binary is available and paths match the server: - -```bash -navidrome scan --full --datafolder /path/to/data --musicfolder /path/to/music --cachefolder /path/to/cache -l info -n -``` - -Use the same `--datafolder`, `--musicfolder`, and `--cachefolder` as the running server (see process env e.g. `ND_DATAFOLDER`, `ND_MUSICFOLDER`, or deployment mounts). - -### Kubernetes (exec into the pod) - -Long scans can **outlive `kubectl exec`**; run **detached** inside the container so API disconnects do not kill the scan: - -```bash -kubectl exec -n NAMESPACE POD -- sh -c \ - 'nohup /app/navidrome scan --full --datafolder /data --musicfolder /music --cachefolder /data/cache -l info -n >> /data/scan-manual.log 2>&1 &' -``` - -Monitor: - -```bash -kubectl exec -n NAMESPACE POD -- tail -f /data/scan-manual.log -``` - -**Caveat:** scanner and server share the SQLite DB; if corruption or locking appears, run scan with Navidrome stopped (deployment scaled to 0), then start again. - ---- - -## cmus: re-index the library - -cmus is a **C** program; it does **not** use Perl. **`lib.pl`** is only a **plain-text list of absolute paths** (one per line)—the suffix is cmus naming, not Perl source. - -Library state lives under **`${XDG_CONFIG_HOME:-$HOME/.config}/cmus/`**: - -| File | Role | -|------|------| -| `lib.pl` | Every track path cmus knows about | -| `cache` | Binary tag/metadata cache (safe to delete to force rescan) | -| `autosave` | Options and bindings (do not delete casually) | - -After **beets `move`**, **tiddl** downloads, or any **tree reshuffle**, `lib.pl` can still list old paths until you re-index. Use the **same music root** as Navidrome (e.g. `/data/nfs/k3svolumes/navidrome/music`). - -### Check before rewriting files +## Core workflow -```bash -cmus-remote -Q 2>/dev/null || true # expect "cmus is not running" for offline rewrite -``` +A full library maintenance pass runs in this order: -### Offline re-index (cmus stopped) +**beets** (organize/tags + dedup) → **Navidrome** full scan → **cmus** re-index. +After **tiddl** downloads, rerun the same chain (import → scan → re-index). -Timestamped backup, regenerate `lib.pl`, remove `cache`: +## Reference Files -```bash -set -e -MUSIC=/data/nfs/k3svolumes/navidrome/music -CMUS="${XDG_CONFIG_HOME:-$HOME/.config}/cmus" -TS=$(date +%Y%m%d-%H%M%S) +Detailed reference documentation is in the `references/` subfolder: -cp -a "$CMUS/lib.pl" "$CMUS/lib.pl.bak.$TS" -test -f "$CMUS/cache" && cp -a "$CMUS/cache" "$CMUS/cache.bak.$TS" || true - -find "$MUSIC" -type f \( \ - -iname '*.flac' -o -iname '*.mp3' -o -iname '*.m4a' -o -iname '*.ogg' \ - -o -iname '*.opus' -o -iname '*.wav' -o -iname '*.wv' -o -iname '*.mpc' \ - -o -iname '*.aac' \ -\) 2>/dev/null | LC_ALL=C sort -u >"$CMUS/lib.pl.tmp" -mv "$CMUS/lib.pl.tmp" "$CMUS/lib.pl" -rm -f "$CMUS/cache" -``` - -Start cmus afterward; the **first launch** may take a while while the cache rebuilds. - -### Live re-index (`cmus-remote`, cmus already running) - -In the cmus `:` prompt, or: - -```bash -cmus-remote -C 'clear -l' -cmus-remote -C 'add -l /data/nfs/k3svolumes/navidrome/music' -cmus-remote -C 'update-cache -f' -``` - -`update-cache -f` forces a full metadata refresh (same idea as deleting `cache`). - -**Note:** the cmus UI wants a real TTY; `cmus-remote` only works when cmus is already running. - -### After a full library maintenance pass - -Typical order: **beets** (organize/tags) → **Navidrome** full scan → **cmus** re-index (offline script above, or `clear -l` / `add -l` / `update-cache -f`). - ---- - -## tiddl: Tidal downloads - -[tiddl](https://github.com/oskvr37/tiddl) is a Python CLI (PyPI: `tiddl`). Requires **ffmpeg** for transcoding. - -**Install:** `uv tool install tiddl` or `pip install tiddl` (see project README for current Python version requirements). - -**Auth (once):** - -```bash -tiddl auth login -``` - -**Download by URL or short id:** - -```bash -tiddl download url 'https://tidal.com/browse/track/...' -tiddl download url 'album/123456789' -tiddl download url 'playlist/...' --skip-errors -``` - -### Batch download from a list file (`tidal.txt`) - -Put one playlist/album/track URL or short id per line (e.g. `playlist/…`, `https://listen.tidal.com/…`). - -**Fish outer, bash inner:** The interactive shell is **fish**; each line runs in **`bash -c`** so `source …/bin/activate` works (Python venv activation is bash/POSix-oriented). Fish owns the loop and closes with **`end`**; there is no `do`/`done` in fish—only the inner bash sees a one-shot `-c` script. - -```fish -cat tidal.txt | while read -l playlist - bash -c 'source ~/git/upstream/tiddl/.venv/bin/activate && tiddl download url "$1" --skip-errors' _ "$playlist" -end -``` - -- Use **`"$1"`** with a dummy **`$0`** (`_`) so the URL/id is one argument even with spaces. -- Add `--skip-errors` inside the single-quoted `bash -c` string if you want the batch to continue past bad playlist entries. -- Optionally skip blanks or `#` lines before calling `bash -c` (e.g. `string trim`, `string match -q '#*'`). - -**Pure bash/zsh (optional):** If you are already in bash, activating the venv **once** then looping avoids one bash subprocess per line: - -```bash -source ~/git/upstream/tiddl/.venv/bin/activate -while IFS= read -r playlist || [[ -n "$playlist" ]]; do - [[ -z "${playlist// }" || "$playlist" =~ ^[[:space:]]*# ]] && continue - tiddl download url "$playlist" --skip-errors -done < tidal.txt -``` - -**Quality:** configure in `~/.tiddl/config.toml` (LOW / NORMAL / HIGH / MAX → m4a or flac per docs). - -**Output layout:** set templates in config so downloads land under the same root beets/Navidrome use (e.g. artist/album/track), reducing later `beet import` churn. - -After downloading, run **beets import** (and/or **Navidrome scan**) and **cmus re-index** as above. - ---- +- [Beets: layout & dedup](references/beets-layout-and-dedup.md) — `directory`/`paths` templates, the inline `safe_genre` field for stable genre folders, non-interactive `beet import`, `beet move`, permissions; the `duplicates` plugin (audio checksum vs metadata keys) and a careful remove/merge workflow. +- [Navidrome: full rescan](references/navidrome-rescan.md) — `navidrome scan --full` with matching `--datafolder`/`--musicfolder`/`--cachefolder`; the detached `nohup … &` pattern for scans inside a Kubernetes pod via `kubectl exec`, log monitoring, and the shared-SQLite locking caveat. +- [cmus: re-index](references/cmus-reindex.md) — what `lib.pl`/`cache`/`autosave` are, the offline backup-and-regenerate script (`find` → `lib.pl`, `rm cache`), and the live `cmus-remote` path (`clear -l` / `add -l` / `update-cache -f`). +- [tiddl: Tidal downloads](references/tiddl-downloads.md) — install/auth, `tiddl download url …`, the fish-outer/bash-inner batch loop over `tidal.txt` (and a pure-bash variant), quality and output-layout config. ## Quick reference @@ -209,9 +48,3 @@ After downloading, run **beets import** (and/or **Navidrome scan**) and **cmus r | Refresh Navidrome DB | navidrome | `navidrome scan --full` with correct folders | | Re-index cmus | cmus | Offline: backup `lib.pl`/`cache`, `find` → `lib.pl`, `rm cache`; live: `cmus-remote -C 'clear -l'` then `add -l ROOT` then `update-cache -f` | | Download from Tidal | tiddl | `tiddl auth login` → `tiddl download url …` | - ---- - -## Optional deep dive - -For beets duplicate plugin options and checksum backends, read the official beets documentation for the installed version when tuning `duplicates:` in config. diff --git a/prompts/skills/music-collection/references/beets-layout-and-dedup.md b/prompts/skills/music-collection/references/beets-layout-and-dedup.md new file mode 100644 index 0000000..9427901 --- /dev/null +++ b/prompts/skills/music-collection/references/beets-layout-and-dedup.md @@ -0,0 +1,29 @@ +# Beets: layout, sorting, and deduplication + +## Beets: layout and "sorting" + +1. Point `directory` at the library root (the folder Navidrome mounts as music). +2. Define `paths` (e.g. genre → album artist → album → track). Use `$albumartist`, `$album%aunique{}`, `$track`, `$title`. +3. **Stable genre folders**: use the **inline** plugin with a computed field (e.g. `safe_genre`) that returns `Miscellaneous` when `genre` is empty and applies `.title()` so `electronic` and `Electronic` share one folder on case-sensitive filesystems. +4. **Import** (non-interactive batch): `beet import -A -m -q --quiet-fallback=asis PATH` (`-m` move, `-A` skip autotag for speed, `-q` no prompts). For MusicBrainz tagging, omit `-A` (slower). +5. After changing `paths` or inline fields: `beet move` to apply templates to items already in the DB. +6. **Permissions**: if the music volume is not writable as the current user, run `beet` with elevated permissions or fix ownership; keep the **beets database** under the user's home (e.g. `-c /path/to/config.yaml` and `library:` in that file). + +## Beets: deduplication + +Enable the **duplicates** plugin in config (`plugins: duplicates`) and configure how "same track" is defined. + +- **By audio content (strong)**: use `duplicates: checksum: ffmpeg` (or another backend per beets docs) so identical audio is detected even if tags differ. +- **By metadata (weaker)**: keys such as `mb_trackid` or `fingerprint`—fast but misses true dupes with bad tags. + +Workflow: + +1. `beet duplicates -c` (or equivalent) to **count** / preview groups—consult `beet duplicates --help` for the installed beets version. +2. Inspect a few groups (paths, tags, bitrate). +3. Remove or merge only after explicit criteria (e.g. keep highest bitrate, or keep file already under the canonical path template). Prefer `beet remove` / `beet modify` on a **narrow query** over blind `-d` flags. + +Optional: **import** with `duplicate_action` / incremental import settings to reduce future dupes. + +## Optional deep dive + +For beets duplicate plugin options and checksum backends, read the official beets documentation for the installed version when tuning `duplicates:` in config. diff --git a/prompts/skills/music-collection/references/cmus-reindex.md b/prompts/skills/music-collection/references/cmus-reindex.md new file mode 100644 index 0000000..9f67802 --- /dev/null +++ b/prompts/skills/music-collection/references/cmus-reindex.md @@ -0,0 +1,61 @@ +# cmus: re-index the library + +cmus is a **C** program; it does **not** use Perl. **`lib.pl`** is only a **plain-text list of absolute paths** (one per line)—the suffix is cmus naming, not Perl source. + +Library state lives under **`${XDG_CONFIG_HOME:-$HOME/.config}/cmus/`**: + +| File | Role | +|------|------| +| `lib.pl` | Every track path cmus knows about | +| `cache` | Binary tag/metadata cache (safe to delete to force rescan) | +| `autosave` | Options and bindings (do not delete casually) | + +After **beets `move`**, **tiddl** downloads, or any **tree reshuffle**, `lib.pl` can still list old paths until you re-index. Use the **same music root** as Navidrome (e.g. `/data/nfs/k3svolumes/navidrome/music`). + +## Check before rewriting files + +```bash +cmus-remote -Q 2>/dev/null || true # expect "cmus is not running" for offline rewrite +``` + +## Offline re-index (cmus stopped) + +Timestamped backup, regenerate `lib.pl`, remove `cache`: + +```bash +set -e +MUSIC=/data/nfs/k3svolumes/navidrome/music +CMUS="${XDG_CONFIG_HOME:-$HOME/.config}/cmus" +TS=$(date +%Y%m%d-%H%M%S) + +cp -a "$CMUS/lib.pl" "$CMUS/lib.pl.bak.$TS" +test -f "$CMUS/cache" && cp -a "$CMUS/cache" "$CMUS/cache.bak.$TS" || true + +find "$MUSIC" -type f \( \ + -iname '*.flac' -o -iname '*.mp3' -o -iname '*.m4a' -o -iname '*.ogg' \ + -o -iname '*.opus' -o -iname '*.wav' -o -iname '*.wv' -o -iname '*.mpc' \ + -o -iname '*.aac' \ +\) 2>/dev/null | LC_ALL=C sort -u >"$CMUS/lib.pl.tmp" +mv "$CMUS/lib.pl.tmp" "$CMUS/lib.pl" +rm -f "$CMUS/cache" +``` + +Start cmus afterward; the **first launch** may take a while while the cache rebuilds. + +## Live re-index (`cmus-remote`, cmus already running) + +In the cmus `:` prompt, or: + +```bash +cmus-remote -C 'clear -l' +cmus-remote -C 'add -l /data/nfs/k3svolumes/navidrome/music' +cmus-remote -C 'update-cache -f' +``` + +`update-cache -f` forces a full metadata refresh (same idea as deleting `cache`). + +**Note:** the cmus UI wants a real TTY; `cmus-remote` only works when cmus is already running. + +## After a full library maintenance pass + +Typical order: **beets** (organize/tags) → **Navidrome** full scan → **cmus** re-index (offline script above, or `clear -l` / `add -l` / `update-cache -f`). diff --git a/prompts/skills/music-collection/references/navidrome-rescan.md b/prompts/skills/music-collection/references/navidrome-rescan.md new file mode 100644 index 0000000..63703df --- /dev/null +++ b/prompts/skills/music-collection/references/navidrome-rescan.md @@ -0,0 +1,30 @@ +# Navidrome: full rescan + +Navidrome reflects **on-disk** paths and embedded tags after a scan. + +## Same host as the music volume + +If the `navidrome` binary is available and paths match the server: + +```bash +navidrome scan --full --datafolder /path/to/data --musicfolder /path/to/music --cachefolder /path/to/cache -l info -n +``` + +Use the same `--datafolder`, `--musicfolder`, and `--cachefolder` as the running server (see process env e.g. `ND_DATAFOLDER`, `ND_MUSICFOLDER`, or deployment mounts). + +## Kubernetes (exec into the pod) + +Long scans can **outlive `kubectl exec`**; run **detached** inside the container so API disconnects do not kill the scan: + +```bash +kubectl exec -n NAMESPACE POD -- sh -c \ + 'nohup /app/navidrome scan --full --datafolder /data --musicfolder /music --cachefolder /data/cache -l info -n >> /data/scan-manual.log 2>&1 &' +``` + +Monitor: + +```bash +kubectl exec -n NAMESPACE POD -- tail -f /data/scan-manual.log +``` + +**Caveat:** scanner and server share the SQLite DB; if corruption or locking appears, run scan with Navidrome stopped (deployment scaled to 0), then start again. diff --git a/prompts/skills/music-collection/references/tiddl-downloads.md b/prompts/skills/music-collection/references/tiddl-downloads.md new file mode 100644 index 0000000..753cafb --- /dev/null +++ b/prompts/skills/music-collection/references/tiddl-downloads.md @@ -0,0 +1,53 @@ +# tiddl: Tidal downloads + +[tiddl](https://github.com/oskvr37/tiddl) is a Python CLI (PyPI: `tiddl`). Requires **ffmpeg** for transcoding. + +**Note:** tiddl is third-party; personal-use only; respect Tidal ToS and local law. + +**Install:** `uv tool install tiddl` or `pip install tiddl` (see project README for current Python version requirements). + +**Auth (once):** + +```bash +tiddl auth login +``` + +**Download by URL or short id:** + +```bash +tiddl download url 'https://tidal.com/browse/track/...' +tiddl download url 'album/123456789' +tiddl download url 'playlist/...' --skip-errors +``` + +## Batch download from a list file (`tidal.txt`) + +Put one playlist/album/track URL or short id per line (e.g. `playlist/…`, `https://listen.tidal.com/…`). + +**Fish outer, bash inner:** The interactive shell is **fish**; each line runs in **`bash -c`** so `source …/bin/activate` works (Python venv activation is bash/POSix-oriented). Fish owns the loop and closes with **`end`**; there is no `do`/`done` in fish—only the inner bash sees a one-shot `-c` script. + +```fish +cat tidal.txt | while read -l playlist + bash -c 'source ~/git/upstream/tiddl/.venv/bin/activate && tiddl download url "$1" --skip-errors' _ "$playlist" +end +``` + +- Use **`"$1"`** with a dummy **`$0`** (`_`) so the URL/id is one argument even with spaces. +- Add `--skip-errors` inside the single-quoted `bash -c` string if you want the batch to continue past bad playlist entries. +- Optionally skip blanks or `#` lines before calling `bash -c` (e.g. `string trim`, `string match -q '#*'`). + +**Pure bash/zsh (optional):** If you are already in bash, activating the venv **once** then looping avoids one bash subprocess per line: + +```bash +source ~/git/upstream/tiddl/.venv/bin/activate +while IFS= read -r playlist || [[ -n "$playlist" ]]; do + [[ -z "${playlist// }" || "$playlist" =~ ^[[:space:]]*# ]] && continue + tiddl download url "$playlist" --skip-errors +done < tidal.txt +``` + +**Quality:** configure in `~/.tiddl/config.toml` (LOW / NORMAL / HIGH / MAX → m4a or flac per docs). + +**Output layout:** set templates in config so downloads land under the same root beets/Navidrome use (e.g. artist/album/track), reducing later `beet import` churn. + +After downloading, run **beets import** (and/or **Navidrome scan**) and **cmus re-index** as above. |
