chore: beta release v0.5.0-beta
- 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
This commit is contained in:
@@ -1,75 +1,163 @@
|
||||
# WatchStack
|
||||
|
||||
Eine Watchlist für **Bücher, Serien & mehr** — inspiriert von MyAnimeList, aber mit eigenem Konzept.
|
||||
> **v0.5.0-beta** — eine Watchlist für **Bücher, Serien & mehr** — inspiriert von MyAnimeList, aber mit eigenem Konzept.
|
||||
|
||||
## Features
|
||||
[](https://www.apache.org/licenses/LICENSE-2.0)
|
||||
[](https://git.pkop.de/Vibecode/watchlist)
|
||||
[](https://www.python.org)
|
||||
[](#-docker)
|
||||
|
||||
- 📚 **Bücher**: Titel, Autor, Bände, Kapitel, Seiten, ISBN-Cover von Open Library, Status, Bewertung, Notizen, Genre, Tags
|
||||
- 📺 **Serien**: Titel, Staffeln, Episoden, Sendezeitraum, Sender/Streaming, Status, Bewertung, Notizen, Genre, Tags
|
||||
- 🔎 **Suche & Filter**: Volltext, Status, Genre, Tag, Bewertung, Sortierung
|
||||
- 📊 **Dashboard**: Statistik pro Medium (Anzahl, verteilte Status, Durchschnittsbewertung)
|
||||
- ⭐ **Bewertungen**: 1–10 Skala
|
||||
- 🏷️ **Genres & Tags**: frei verwaltbar, vielen Einträgen zuweisbar
|
||||
- 🌙 **Dark Theme** als Default
|
||||
- 💾 **Lokal**: SQLite, single-user, kein Login nötig
|
||||
- 🐳 Optional: 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.
|
||||
|
||||
## Tech-Stack
|
||||
## ✨ Features
|
||||
|
||||
- **Backend**: Python 3.11+, FastAPI, SQLAlchemy 2.x, SQLite
|
||||
- **Frontend**: Vanilla HTML + CSS + JavaScript (kein Build-Step), HTMX-light Pattern
|
||||
- **Cover**: Open Library API (Bücher), URL (Serien)
|
||||
- 📚 **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
|
||||
|
||||
## Quickstart
|
||||
## ⚠️ 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
|
||||
# 1) Backend-Env
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 2) Starten (legt data/watchstack.db automatisch an + seedet Beispieldaten)
|
||||
uvicorn app.main:app --reload --host 127.0.0.1 --port 8000
|
||||
|
||||
# 3) Browser öffnen
|
||||
xdg-open http://127.0.0.1:8000 # oder einfach manuell
|
||||
uvicorn app.main:app --reload
|
||||
```
|
||||
|
||||
## Projektstruktur
|
||||
## 🔧 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/
|
||||
│ ├── main.py # FastAPI-App + Routes
|
||||
│ ├── __init__.py # __version__
|
||||
│ ├── main.py # FastAPI-App + Routen
|
||||
│ ├── database.py # Engine + Session
|
||||
│ ├── models.py # SQLAlchemy-Modelle
|
||||
│ ├── schemas.py # Pydantic-Schemas
|
||||
│ ├── crud.py # DB-Logik
|
||||
│ ├── seed.py # Beispieldaten
|
||||
│ ├── external.py # Open-Library-API
|
||||
│ └── static/
|
||||
│ ├── css/style.css
|
||||
│ ├── js/app.js
|
||||
│ └── img/
|
||||
├── data/ # SQLite-DB (gitignored)
|
||||
├── tests/
|
||||
├── requirements.txt
|
||||
│ ├── 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
|
||||
```
|
||||
|
||||
## API-Übersicht (Auszug)
|
||||
## 🛠 Entwicklung
|
||||
|
||||
| Methode | Pfad | Zweck |
|
||||
| ------- | --------------------------------- | ------------------------------ |
|
||||
| GET | `/api/media?type=book\|series` | Liste mit Filter & Suche |
|
||||
| POST | `/api/media` | Neuen Eintrag anlegen |
|
||||
| GET | `/api/media/{id}` | Detail |
|
||||
| PATCH | `/api/media/{id}` | Ändern (Status, Bewertung …) |
|
||||
| DELETE | `/api/media/{id}` | Löschen |
|
||||
| GET | `/api/stats` | Aggregierte Stats |
|
||||
| GET | `/api/lookup/book?isbn=…` | Cover per Open Library |
|
||||
| GET | `/` | Web-UI |
|
||||
```bash
|
||||
# Tests (bisher Smoke-Tests ad-hoc; pytest-Suite ist Roadmap)
|
||||
python -c "from app.main import app; print(app.title, app.version)"
|
||||
|
||||
## Lizenz
|
||||
# Code-Style
|
||||
ruff check app/ # (optional, nicht in requirements)
|
||||
```
|
||||
|
||||
MIT
|
||||
## 📜 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
|
||||
|
||||
Reference in New Issue
Block a user