Files
arnefandCopilot 4e1a89cb05 Skip empty tonie folders instead of pruning all chapters
Previously, an accidentally empty tonie folder combined with pruning
(the default) would delete every existing chapter on the tonie. Now:

- syncer.BuildPlan detects an empty local folder and returns a
  Plan{Skipped: true, SkipReason: ...} instead of computing a diff
- syncer.ApplyPlan is a no-op for a skipped plan (defense in depth)
- Plan.NeedsChanges() reports false for skipped plans
- cmd/toni-sync sync prints "Skipped: <reason>" for such folders and
  moves on, regardless of --no-prune
- Added a test covering the skip behavior end-to-end (BuildPlan +
  ApplyPlan no-op)
- README documents the safety behavior

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-14 19:27:58 +02:00

173 lines
5.2 KiB
Markdown

# toni-sync
Ein CLI-Tool zur Verwaltung von Kreativ-Tonies: Es synchronisiert lokale
Audio-Ordner (z. B. exportierte Deezer-Playlists) automatisch zu den
passenden Kreativ-Tonies über die inoffizielle TonieCloud-API. Geschrieben
in Go, kompiliert zu einer einzigen statischen Binary - keine Laufzeit-
Abhängigkeiten (kein Python/pip nötig).
> **Hinweis:** toni-sync lädt selbst keine Musik von Deezer herunter. Das
> Herunterladen/Umgehen von DRM-geschützten Streams verstößt gegen die
> Nutzungsbedingungen von Deezer und ggf. gegen Urheberrecht. Lege deine
> bereits legal exportierten Audiodateien einfach in die passenden
> lokalen Ordner - toni-sync kümmert sich nur um den Abgleich mit der
> TonieCloud.
## Keine Config-Datei - die Ordnerstruktur *ist* die Konfiguration
Statt einer separaten Config-Datei wird das Mapping Playlist ↔ Kreativ-Tonie
direkt über die Ordnerstruktur abgebildet:
```
<root>/
<household_id>/
<freier_name> [id=<tonie_id>]/
01 - Track.mp3
02 - Track.mp3
```
- `<household_id>`: die Household-ID deines TonieCloud-Accounts
- `<freier_name> [id=<tonie_id>]`: frei wählbarer Name + Tonie-ID in eckigen
Klammern - der Name dient nur der Lesbarkeit, für das Mapping zählt allein
die ID
- Die alphabetische Dateireihenfolge innerhalb eines Tonie-Ordners bestimmt
die Kapitelreihenfolge auf dem Tonie
## Installation
Benötigt wird nur eine Go-Toolchain (>= 1.21) zum Bauen - danach ist das
Ergebnis eine einzelne Binary ohne weitere Abhängigkeiten:
```bash
go build -o toni-sync ./cmd/toni-sync
./toni-sync --help
```
Oder direkt installieren (landet in `$(go env GOPATH)/bin`):
```bash
go install ./cmd/toni-sync
```
## Anmeldedaten
toni-sync benötigt deine TonieCloud-Zugangsdaten (dieselben wie in der
Tonies-App). Sie werden **nicht** gespeichert, sondern bei jedem Aufruf
über Umgebungsvariablen oder interaktiven Prompt abgefragt:
```bash
export TONI_SYNC_USERNAME="you@example.com"
export TONI_SYNC_PASSWORD="********"
```
Alternativ: `--username`/`--password` Flags bei `tonies list`, `init` und `sync`.
## Nutzung
### 1. Library-Ordner automatisch anlegen
```bash
toni-sync init --root ~/Musik/tonies
```
Legt für jede Household und jeden Kreativ-Tonie deines Accounts den
passenden Ordner an, z. B.:
```
[created] /home/you/Musik/tonies/abcd-1234/Peppa Wutz [id=ef01-5678]
[created] /home/you/Musik/tonies/abcd-1234/Gute-Nacht-Geschichten [id=9876-4321]
Library ready at /home/you/Musik/tonies
```
Bereits vorhandene Ordner (anhand der Tonie-ID erkannt, auch bei geändertem
Namen) werden nicht angefasst - deine Audiodateien bleiben unberührt.
Alternativ manuell: einfach die Ordner nach obigem Schema selbst anlegen.
### 2. Root-Ordner festlegen
Der Library-Root wird wie folgt bestimmt (erste zutreffende Option):
1. `--root PATH` Flag
2. Umgebungsvariable `TONI_SYNC_ROOT`
3. aktuelles Arbeitsverzeichnis
### 3. Audiodateien einsortieren & synchronisieren
Lege deine (legal exportierten) Audiodateien in den passenden Tonie-Ordner.
Die Kapitelreihenfolge wird wie folgt bestimmt:
- **Mit Playlist:** Liegt genau eine `.m3u`/`.m3u8`-Datei im Tonie-Ordner,
bestimmt deren Zeilenreihenfolge die Kapitelreihenfolge (Kommentare/`#`-Zeilen
und Leerzeilen werden ignoriert, Pfade relativ zum Ordner). Dateien, die
lokal existieren aber nicht in der Playlist stehen, werden trotzdem
synchronisiert und ans Ende angehängt (nichts geht verloren). Einträge in
der Playlist ohne passende Datei werden ignoriert. Liegen mehrere Playlist-
Dateien im selben Ordner, bricht `sync` mit einem Fehler ab (Reihenfolge
wäre sonst mehrdeutig).
- **Ohne Playlist:** alphabetische Dateireihenfolge, z. B. `01 - Track.mp3`,
`02 - Track.mp3`, ...
Beispiel `playlist.m3u`:
```
#EXTM3U
02 - Second Track.mp3
01 - First Track.mp3
03 - Third Track.mp3
```
```bash
# alle gefundenen Tonies synchronisieren
toni-sync sync --root ~/Musik/tonies
# nur Tonies syncen, deren Household-ID/Tonie-ID/Name den Filter enthalten
toni-sync sync peppa --root ~/Musik/tonies
# nur anzeigen, was sich ändern würde
toni-sync sync --root ~/Musik/tonies --dry-run
```
`sync` lädt neue Dateien hoch, entfernt Kapitel, die lokal nicht mehr
existieren (abschaltbar via `--no-prune`), und sortiert die Kapitel passend
zur lokalen Reihenfolge (Playlist-Datei falls vorhanden, sonst alphabetisch).
**Leere Tonie-Ordner werden übersprungen** - so verhindert toni-sync, dass
ein versehentlich leerer Ordner (z. B. noch nicht befüllt) beim Sync alle
vorhandenen Kapitel auf dem Tonie löscht.
### Household-/Tonie-IDs nachschlagen
Falls du die Struktur lieber manuell pflegen willst:
```bash
toni-sync tonies list
```
```
Household: Familie Müller [id=abcd-1234]
- Peppa Wutz [id=ef01-5678] (12 chapters, 3600s)
- Gute-Nacht-Geschichten [id=9876-4321] (0 chapters, 0s)
```
## Unterstützte Audioformate
`.mp3`, `.m4a`, `.aac`, `.ogg`, `.flac`, `.wav`
## Projektstruktur
```
cmd/toni-sync/ CLI-Einstiegspunkt (Cobra-Kommandos: tonies, init, sync)
internal/tonieapi/ Eigener, minimaler TonieCloud-API-Client
internal/library/ Erkennung & Scaffolding der Ordnerstruktur (Household/Tonie)
internal/syncer/ Diff-/Apply-Logik zwischen lokalem Ordner und Tonie
```
## Entwicklung / Tests
```bash
go build ./...
go vet ./...
go test ./...
```