diora-web/tui/README.md

82 lines
4.2 KiB
Markdown
Raw Normal View History

# diora-tui
Lokaler EPUB-Reader im Terminal — die erste Stufe einer TUI-Version von diora.
Läuft komplett offline gegen eine lokale Bibliothek aus `.epub`-Dateien und merkt sich
den Lesefortschritt pro Buch (`progress.json` im plattformüblichen Datenverzeichnis,
z.B. `~/.local/share/diora-tui/` unter Linux, via `platformdirs`). Der Fortschritt wird
— genau wie im Web-Reader von diora (siehe `books/models.py`, `save_progress`) — nur
vorwärts überschrieben ("furthest wins"), damit ein älterer/gestaffelter Lauf nie eine
bereits weiter gelesene Position zurücksetzt.
`diora-tui sync` kann Bücher jetzt vom diora-Server holen und lokal entschlüsseln (siehe
unten) — Lesefortschritt bleibt aber weiterhin rein lokal, siehe **Grenzen** unten.
## Setup
```bash
cd tui
pip install -e .
```
## Nutzung
```bash
diora-tui --library ~/Books # Standard: ~/Books
```
Tastenkürzel:
- `↑`/`k`, `↓`/`j` — zeilenweise scrollen
- `n` / `]` — nächstes Kapitel, `p` / `[` — vorheriges Kapitel
- `Enter` — markiertes Buch aus der Bibliothek öffnen
- `Escape` / `q` — zurück zur Bibliothek (im Reader) bzw. beenden (in der Bibliothek)
## Bücher vom Server holen (`diora-tui sync`)
```bash
diora-tui sync # nutzt gespeicherte Zugangsdaten, sonst interaktive Abfrage
diora-tui sync --server https://diora.creamfresh.xyz --save # einmalig einrichten + speichern
```
Lädt alle EPUBs des Accounts über `GET /api/sync/` + `GET /books/<id>/data/` herunter,
entschlüsselt sie lokal (AES-256-GCM, kompatibel zu `static/js/app.js`) und legt sie als
normale `.epub`-Dateien in `--library` ab (Dateiname `<id> - <Titel>.epub`) — von da an
funktionieren sie wie jedes andere lokale Buch. Bereits heruntergeladene Bücher werden
beim nächsten Lauf übersprungen (kein erneuter Download).
Dafür nötig, beim ersten Lauf abgefragt (danach optional lokal gespeichert unter
`~/.config/diora-tui/config.json`, `chmod 600`):
- **Server-URL** — z.B. `https://diora.creamfresh.xyz`.
- **API-Token** — diora → Einstellungen (`/accounts/settings/`) → "Personal Access Token".
- **Verschlüsselungs-Key** — der AES-256-Schlüssel, mit dem deine Bücher clientseitig
verschlüsselt wurden. Der `sync`-Prompt bietet zwei Wege:
1. **Aus Benutzername + Passwort ableiten** (Standard) — reproduziert exakt, was diora's
"Unlock with password"-Formular im Browser tut (PBKDF2-HMAC-SHA256, 200.000
Iterationen, Salt `"diora:" + username`; siehe `static/js/app.js:deriveAndStoreKey`).
Funktioniert nur, wenn der Account diesen Weg im Browser mindestens einmal benutzt
hat — sonst wurde der Key ursprünglich zufällig im Browser erzeugt, und diese
Ableitung trifft ihn nicht. `sync` meldet einen falschen Key als
Entschlüsselungsfehler (harmlos, kein Datenverlust), nie als falsches Ergebnis.
2. **Base64-Key direkt einfügen** — für den Fall, dass Weg 1 nicht passt. Der
Export-Button dafür ist im Browser-UI aktuell nicht verdrahtet (`exportEncKey()` in
`app.js` existiert, hat aber keinen sichtbaren Button); bis das nachgezogen ist, in
der Browser-Devtools-Konsole auf der diora-Seite (nicht `/accounts/settings/` — die
lädt `app.js` nicht) ausführen: `await exportEncKey()` — kopiert den Key ins
Clipboard, von dort ins `sync`-Prompt einfügen.
Der Key/Token wird genauso vertrauensvoll behandelt wie im Web-Client (dort liegt der
Schlüssel unverschlüsselt in `localStorage`): lokal als Klartext in einer 0600-Datei.
## Grenzen der aktuellen Version
- Nur EPUB, kein PDF — `sync` lädt PDFs im Account gar nicht erst herunter (übersprungen,
wird gemeldet), da der Reader sie ohnehin nicht darstellen kann.
- Text wird pro Kapitel als Fließtext ohne Bild-/Layout-Rendering dargestellt.
- Lesefortschritt bleibt rein lokal — `sync` holt nur Bücher, keinen Fortschritt. Der
Web-Reader verankert Position als `"blockIndex:innerFraction"` in seiner eigenen, über
das ganze Buch laufenden Absatz-Nummerierung; diese TUI zählt Position dagegen pro
Kapitel. Ohne eine echte Übersetzung zwischen beiden Schemata würde ein naiver Abgleich
falsche Positionen liefern — deshalb bewusst (noch) nicht gebaut.