- Bump version to 0.5.0-beta - Add Apache-2.0 LICENSE (official text) + NOTICE - Add Docker support: multi-stage Dockerfile + docker-compose * non-root user, tini PID 1, gunicorn + uvicorn workers * healthcheck, OCI labels, persistent /data volume - Add GitHub Actions CI (lint/import-check + Docker smoke-test) - Backend polish: * modern lifespan handler (replaces deprecated on_event) * explicit sort validation (400 instead of 500) * title min_length=1 schema validation * configurable data dir + http timeout via env * avg_rating helper extracted (DRY) - Frontend polish: * beta badge in title, footer with live version * loading spinner on initial fetch * updated README, badges, config table, roadmap
164 lines
6.3 KiB
Markdown
164 lines
6.3 KiB
Markdown
# WatchStack
|
||
|
||
> **v0.5.0-beta** — eine Watchlist für **Bücher, Serien & mehr** — inspiriert von MyAnimeList, aber mit eigenem Konzept.
|
||
|
||
[](https://www.apache.org/licenses/LICENSE-2.0)
|
||
[](https://git.pkop.de/Vibecode/watchlist)
|
||
[](https://www.python.org)
|
||
[](#-docker)
|
||
|
||
WatchStack ist eine **single-user-lokale** Watchlist-App. Bücher und Serien teilen sich eine Oberfläche, ein Datenmodell und eine Such- und Filterlogik. Keine Anmeldung, kein Cloud-Zwang — läuft komplett auf deiner Maschine oder in einem Docker-Container.
|
||
|
||
## ✨ Features
|
||
|
||
- 📚 **Bücher**: Titel, Autor, Bände / Kapitel / Seiten, ISBN, Cover via Open Library
|
||
- 📺 **Serien**: Staffeln, Episoden, Sender / Streaming, Sendezeitraum, Cover-URL
|
||
- 🗂 **Status**: Plan · Laufend · Abgeschlossen · Pausiert · Abgebrochen (5 Stufen, MAL-ähnlich)
|
||
- ⭐ **Bewertungen** 1–10 mit Auto-Status: 0% → Plan, 100% → Done (+ `end_date`)
|
||
- 🔎 **Suche & Filter**: Volltext, Status, Genre, Tag, Sortierung
|
||
- 📊 **Dashboard**: Verteilung pro Medium, Durchschnittsbewertung
|
||
- 🏷 **Genres & Tags** frei verwaltbar
|
||
- 🌙 **Dark Theme** Default, modernes UI mit Hero + Drawer + Modals
|
||
- 🐳 **Docker-ready**: Multi-stage Build, non-root, gunicorn + uvicorn-Worker, healthcheck
|
||
- 🌐 **CORS offen** für lokale Entwicklung; in Produktion via Reverse-Proxy einschränken
|
||
|
||
## ⚠️ Beta-Hinweis
|
||
|
||
**v0.5.0-beta** bedeutet:
|
||
- Kernfunktionalität (CRUD, Suche, Stats, Progress) ist stabil und getestet
|
||
- Datenmodell kann sich noch ändern (siehe `models.py` — additive Änderungen wahrscheinlich)
|
||
- Datenbank-Migrationen sind **nicht** enthalten — bei Major-Upgrades DB sichern
|
||
- Public-API-Pfade stabil; Sub-Ressourcen (Genres/Tags) noch in Bewegung
|
||
|
||
Bitte Issues und Wünsche im [Repo](https://git.pkop.de/Vibecode/watchlist) melden.
|
||
|
||
## 🚀 Quickstart
|
||
|
||
### Option A: Docker (empfohlen)
|
||
|
||
```bash
|
||
docker compose up -d
|
||
# → http://localhost:8000
|
||
|
||
# Oder direkt:
|
||
docker build -t watchstack:beta .
|
||
docker run -d --name watchstack -p 8000:8000 -v watchstack-data:/data watchstack:beta
|
||
```
|
||
|
||
Die SQLite-DB liegt im benannten Volume `watchstack-data` (Pfad `/data/watchstack.db` im Container). Backups: `docker run --rm -v watchstack-data:/data -v $PWD:/backup alpine tar czf /backup/ws-$(date +%F).tgz /data`.
|
||
|
||
### Option B: Lokal (Python 3.11+)
|
||
|
||
```bash
|
||
git clone https://git.pkop.de/Vibecode/watchlist.git
|
||
cd watchlist
|
||
./run.sh # legt venv an, installiert deps, startet auf 127.0.0.1:8000
|
||
```
|
||
|
||
### Option C: Manuell
|
||
|
||
```bash
|
||
python3 -m venv .venv
|
||
source .venv/bin/activate
|
||
pip install -r requirements.txt
|
||
uvicorn app.main:app --reload
|
||
```
|
||
|
||
## 🔧 Konfiguration (Umgebungsvariablen)
|
||
|
||
| Variable | Default | Zweck |
|
||
|---|---|---|
|
||
| `WATCHSTACK_DATA_DIR` | `./data` | Pfad für SQLite-DB (im Container: `/data`) |
|
||
| `WATCHSTACK_HOST` | `127.0.0.1` | Bind-Adresse (Docker: `0.0.0.0`) |
|
||
| `WATCHSTACK_PORT` | `8000` | HTTP-Port |
|
||
| `WATCHSTACK_WORKERS` | `2` | gunicorn-Worker (nur Docker) |
|
||
| `WATCHSTACK_HTTP_TIMEOUT` | `8` | Timeout für Open-Library-Lookup (Sek.) |
|
||
|
||
## 📚 API-Übersicht
|
||
|
||
| Methode | Pfad | Zweck |
|
||
|---|---|---|
|
||
| GET | `/api/health` | Service-Info + Version |
|
||
| GET | `/api/media` | Liste, Filter via Query-Params |
|
||
| POST | `/api/media` | Anlegen (201) |
|
||
| GET | `/api/media/{id}` | Detail |
|
||
| PATCH | `/api/media/{id}` | Teil-Update |
|
||
| DELETE | `/api/media/{id}` | Löschen (204) |
|
||
| POST | `/api/media/{id}/progress` | `{"delta":1}` oder `{"set_to":N}` |
|
||
| GET | `/api/genres`, `/api/tags` | Taxonomien |
|
||
| GET | `/api/stats` | Verteilungen + ⌀-Bewertung |
|
||
| GET | `/api/lookup/book?isbn=…` | Cover via Open Library |
|
||
|
||
**Beispiel** — neuen Eintrag anlegen:
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8000/api/media \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{
|
||
"kind": "book",
|
||
"title": "Der Name des Windes",
|
||
"author": "Patrick Rothfuss",
|
||
"total_pages": 662, "pages_read": 100,
|
||
"status": "reading", "rating": 9.0,
|
||
"genres": ["Fantasy"], "tags": ["Königsmörder"]
|
||
}'
|
||
```
|
||
|
||
Vollständige interaktive Doku: <http://localhost:8000/docs> (Swagger UI, von FastAPI generiert).
|
||
|
||
## 🏗 Projektstruktur
|
||
|
||
```
|
||
watchlist/
|
||
├── app/
|
||
│ ├── __init__.py # __version__
|
||
│ ├── main.py # FastAPI-App + Routen
|
||
│ ├── database.py # Engine + Session
|
||
│ ├── models.py # SQLAlchemy-Modelle
|
||
│ ├── schemas.py # Pydantic-Schemas
|
||
│ ├── crud.py # DB-Operationen
|
||
│ ├── external.py # Open-Library-Client
|
||
│ ├── seed.py # Demo-Daten
|
||
│ └── static/ # Frontend (HTML/CSS/JS)
|
||
├── data/ # SQLite-DB (gitignored, Volume im Container)
|
||
├── .github/workflows/ # CI
|
||
├── Dockerfile # Multi-stage Build
|
||
├── docker-compose.yml
|
||
├── LICENSE # Apache-2.0 (offizieller Volltext)
|
||
├── NOTICE # Copyright + Drittanbieter
|
||
└── README.md
|
||
```
|
||
|
||
## 🛠 Entwicklung
|
||
|
||
```bash
|
||
# Tests (bisher Smoke-Tests ad-hoc; pytest-Suite ist Roadmap)
|
||
python -c "from app.main import app; print(app.title, app.version)"
|
||
|
||
# Code-Style
|
||
ruff check app/ # (optional, nicht in requirements)
|
||
```
|
||
|
||
## 📜 Lizenz
|
||
|
||
**Apache License 2.0** — siehe [LICENSE](./LICENSE) (vollständiger Text) und [NOTICE](./NOTICE) (Drittanbieter-Hinweise).
|
||
|
||
Kurzfassung: Du darfst das Projekt privat und kommerziell nutzen, verändern, weitergeben — unter Beibehaltung des Copyright-Hinweises und der Lizenz. Es gibt **keine** Patent-Gewähr; Änderungen müssen markiert werden. Volltext in der LICENSE-Datei.
|
||
|
||
## 🤝 Contributing
|
||
|
||
PRs willkommen — am besten mit Issue vorab. Bitte:
|
||
- Coding-Style einhalten (PEP 8, KISS, DRY)
|
||
- Keine externen Tracker; Issues im Gitea-Repo
|
||
- Tests für neue Logik (pytest-Suite kommt)
|
||
|
||
## 🔮 Roadmap
|
||
|
||
- [ ] pytest-Suite mit Coverage-Report
|
||
- [ ] Backup-/Restore-Endpoint
|
||
- [ ] Import von MAL-XML / Trakt-Listen
|
||
- [ ] Manga als dritte Medienart (Volumes, Chapters)
|
||
- [ ] Multi-User mit OIDC-Login (optional, hinter Feature-Flag)
|
||
- [ ] Cover-Caching-Proxy
|
||
- [ ] Deutsche Übersetzungen der Status-Labels i18n-ready
|