docs: add re-apply, cleanup, and gen_json plans

- Re-apply: idempotent apply using track number prefix as stable anchor;
  album.json never touched; optional --rename-dir flag for dir renames
- Cleanup: auto-remove abcde.* temp dirs after ripping + manual command
- gen_json: reverse-engineer album.json from file tree using fixed naming
  convention; audio tags take priority over filenames for all fields

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-02-20 11:29:57 +01:00
commit 5de6caba3a

View file

@ -335,3 +335,189 @@ class DiscMetadata:
Nach dem letzten `rip_disc`-Aufruf ruft `interactive_rip` einmalig Nach dem letzten `rip_disc`-Aufruf ruft `interactive_rip` einmalig
`merge_album()` auf und schreibt album.json. `merge_album()` auf und schreibt album.json.
---
## Wiederholbares Apply (Re-Apply in-place)
### Ziel
`apply` soll beliebig oft wiederholbar sein — auch nachträglich im
Jellyfin-Zielverzeichnis. Der User editiert album.json und ruft apply
erneut auf; Dateinamen, Tags und Playlist werden aktualisiert.
### Invariante: album.json bleibt immer erhalten
`apply` berührt album.json nie. Sie ist die einzige persistente
Wahrheitsquelle für alle Metadaten. Selbst wenn Tags oder Dateinamen
abweichen, definiert album.json den Sollzustand.
### Stabilität durch Tracknummer-Präfix
Der stabile Anker beim Re-Apply ist die führende Tracknummer im
Dateinamen:
```
01_-_Alter_Titel.flac → nach Edit → 01_-_Neuer_Titel.flac
```
`discover_audio_files()` identifiziert Tracks bereits nach dem
numerischen Präfix (unabhängig vom Titelanteil). Re-Apply ist damit
robust gegenüber beliebigen Titeländerungen.
### Was apply bei jedem Aufruf tut (idempotent)
1. Audiodateien per Tracknummer-Präfix identifizieren
2. Umbenennen gemäß aktuellem album.json (in-place)
3. Alle Audio-Tags neu schreiben (überschreiben)
4. Cover neu einbetten (überschreiben)
5. Playlist neu generieren (überschreiben, gleicher Dateiname)
6. album.json unangetastet lassen
### Verzeichnisumbenennung bei Albumname-Änderung
Wenn sich `album` oder `year` in album.json ändern, ändert sich der
korrekte Verzeichnisname. Im Jellyfin-Ordner ist das heikel (Jellyfin
erkennt das Album ggf. als neu).
Vorschlag: Verzeichnisumbenennung nur mit explizitem Flag:
```
musiksammlung apply --in-place --rename-dir <album_dir>
```
Ohne Flag: Dateien und Tags werden aktualisiert, Verzeichnisname bleibt.
---
## Cleanup nach dem Ripping
abcde hinterlässt Arbeitsverzeichnisse unterhalb der CDn-Ordner:
```
Album/
CD1/
abcde.XXXXX/ ← Temp-Verzeichnis von abcde
track01.wav ← evtl. noch vorhandene WAV-Zwischendateien
status
mbid.1
```
### Wann aufräumen
**Automatisch am Ende von Phase B** (nach erfolgreichem Ripping):
```python
for abcde_dir in cd_dir.glob("abcde.*"):
shutil.rmtree(abcde_dir)
```
Alternativ als expliziter Befehl für manuelles Aufräumen:
```
musiksammlung cleanup <album_dir>
```
Beide Varianten sollten implementiert werden: automatisch als Default,
manuell als Fallback wenn Phase B mit Fehler abgebrochen wurde.
---
## Neuer CLI-Befehl: gen_json
### Zweck
Rekonstruiert album.json aus dem vorhandenen Dateibaum, wenn die Datei
versehentlich gelöscht wurde. Kann auch für Alben genutzt werden, die
mit anderen Tools gerippt wurden.
```
musiksammlung gen_json <album_dir>
```
### Namenskonvention (verbindlich festgelegt)
**Album-Verzeichnis:**
```
Sanitized_Artist_-_Sanitized_Album_Title_(YYYY)/
Sanitized_Album_Title_(YYYY)/ ← wenn Artist "Various Artists"
```
Beispiele:
```
Pink_Floyd_-_The_Wall_(1979)/
Bach_-_Brandenburg_Concertos_(1967)/
Various_Artists_-_Now_Thats_What_I_Call_Music_(2001)/
```
**CDn-Unterverzeichnis** (bei Multi-Disc):
```
CD1/ CD2/ ...
```
Bei Single-Disc: Audiodateien liegen direkt im Album-Verzeichnis oder
in `CD1/` — beides wird akzeptiert.
**Track-Dateiname:**
```
NN_-_Sanitized_Track_Title.flac
NN_-_Sanitized_Track_Title_-_Sanitized_Track_Artist.flac
```
`NN` ist zweistellig mit führender Null (`01`, `02`, ..., `99`).
`_-_` (Unterstrich-Bindestrich-Unterstrich) ist der strukturelle
Trenner — kein einfacher `-` innerhalb eines Feldes ist `_-_`.
### gen_json Algorithmus
```
1. Verzeichnisname parsen:
"Pink_Floyd_-_The_Wall_(1979)" →
artist = "Pink Floyd" (vor erstem _-_)
album = "The Wall" (zwischen _-_ und _(YYYY))
year = 1979 (aus _(YYYY))
2. CDn-Unterverzeichnisse → disc_number (Zahl aus "CD1", "CD2" etc.)
Keine CDn-Dirs → Single-Disc, disc_number = 1
3. Pro Disc: Audiodateien nach Tracknummer-Präfix sortieren:
"01_-_In_the_Flesh.flac" → track_number=1, title="In the Flesh"
"02_-_The_Thin_Ice_-_Roger_Waters.flac"
→ track_number=2, title="The Thin Ice",
artist="Roger Waters"
4. Audio-Tags lesen (mutagen) — höhere Priorität als Dateiname:
- title, artist: aus Tags übernehmen wenn vorhanden
(Tags enthalten Original-Strings ohne Sanitizing)
- duration_ms: aus Audiodatei (exakt, besser als MB oder TOC)
- genre: nur aus Tags verfügbar, nirgends im Dateinamen kodiert
5. album.json schreiben (fehlende Felder als null)
```
### Daten-Rangfolge in gen_json
| Feld | Quelle 1 (bevorzugt) | Quelle 2 (Fallback) |
|------|---------------------|---------------------|
| title | Audio-Tag | Dateiname (desanitized) |
| artist | Audio-Tag | Dateiname / Verzeichnis |
| album | Audio-Tag | Verzeichnisname |
| year | Audio-Tag | Verzeichnisname _(YYYY) |
| genre | Audio-Tag | — (bleibt null) |
| duration_ms | Audiodatei (mutagen) | — |
| disc_number | CDn-Verzeichnis | — |
| track_number | Dateiname-Präfix | Audio-Tag |
### Was gen_json nicht rekonstruieren kann
- `disc.disc_id` (physischer TOC-Fingerprint — nicht in Dateien)
- `disc.name` (z.B. "Live in Berlin" — nicht im Dateinamen kodiert)
- `genre` wenn Audio-Tags fehlen
### Klassik: Komponist vs. Interpret
Beide Felder werden in den Audio-Tags gespeichert (COMPOSER-Tag bei
FLAC/MP3). `gen_json` liest COMPOSER aus den Tags und kann Komponist
von Interpret trennen — ohne spezielle Namenskonvention im Dateinamen.
Der Dateinamen-Titel enthält bei Klassik typischerweise Werk + Satz:
```
01_-_Symphony_No_5_Op_67_I_Allegro_con_brio.flac
```
Komponist steht im Verzeichnisnamen (`Bach_-_...`) und im COMPOSER-Tag.