Vue 3 + FastAPI Webapp zur Anzeige von Filebot/n8n-Medienmetadaten aus PostgreSQL. Enthält Frontend, API, Nginx-Config, SQL-Migrationen und Docker-Compose-Setup. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
202 lines
6.4 KiB
Markdown
202 lines
6.4 KiB
Markdown
# Projekt-Kontext für KI-Assistenten (Filebot Media Browser)
|
||
|
||
Diese Datei fasst zusammen, **was gebaut wurde** und wie das Projekt betrieben wird. Bei weiteren Änderungen zuerst hier und in der `README.md` nachschlagen.
|
||
|
||
---
|
||
|
||
## Zweck
|
||
|
||
Interne Web-App im Heimnetz zum Anzeigen und Verwalten von **Filebot/n8n-Medienmetadaten** aus PostgreSQL. Schwerpunkt: **Original- vs. neuem Dateinamen** nebeneinander (Rename-Kontrolle), plus technische Metadaten (Codec, Bitrate, Auflösung, …).
|
||
|
||
**Produktiv nur auf dem Server** unter `/opt/nginx_filebot` – nicht lokal auf dem Mac entwickeln/ausführen.
|
||
|
||
---
|
||
|
||
## Infrastruktur
|
||
|
||
| Komponente | Details |
|
||
|------------|---------|
|
||
| Server-Pfad | `/opt/nginx_filebot` |
|
||
| Docker Compose | Projektname `nginx_filebot` |
|
||
| Web | Nginx, Port **8080** → Container 80 |
|
||
| API | FastAPI (Python 3.12), intern `api:8000` |
|
||
| PostgreSQL | Host typisch `192.168.30.186` |
|
||
| Datenbank | `n8n_filebot` (Name in config) |
|
||
| Schema | **`n8n`** (nicht `public`!) |
|
||
| Tabelle | **`movie_files`** (nicht `n8n_filebot`!) |
|
||
| Vollqualifizierter Name | `"n8n"."movie_files"` |
|
||
|
||
Config: `config/config.yaml` (nur auf Server, nicht committen). Beispiel: `config/config.example.yaml`.
|
||
|
||
**Wichtig:** Feld `schema` in YAML funktioniert, intern heißt es `db_schema` (Pydantic-Konflikt mit `BaseModel.schema`).
|
||
|
||
---
|
||
|
||
## Tech-Stack
|
||
|
||
- **Frontend:** Vue 3, TypeScript, Vite, TanStack Table
|
||
- **Backend:** FastAPI, asyncpg
|
||
- **Webserver:** Nginx (Multi-Stage-Build: Node baut Vue, Nginx liefert aus)
|
||
- **DB:** PostgreSQL
|
||
|
||
---
|
||
|
||
## Datenbank-Spalten (relevant)
|
||
|
||
Standard-Spalten aus n8n/Filebot (siehe `api/main.py` → `COLUMNS`), u. a.:
|
||
|
||
- `id`, `file_name_original`, `file_name_new`
|
||
- `movie_rating`, `movie_votes`
|
||
- Video/Audio: `standard_video_format`, `video_codec_library`, `audio_codec`, `container_format`, Bitraten, Auflösung, …
|
||
- `created_at`
|
||
|
||
**Zusätzlich per Migration:**
|
||
|
||
| Spalte | Typ | Bedeutung |
|
||
|--------|-----|-----------|
|
||
| `seen_at` | `TIMESTAMPTZ NULL` | `NULL` = ungesehen; gesetzt = in UI als gesehen markiert |
|
||
| `cover_url` | `TEXT NULL` | Poster-URL (z. B. TVMaze), erste Tabellenspalte |
|
||
|
||
SQL-Dateien: `sql/001_add_seen_at.sql`, `sql/002_add_cover_url.sql`
|
||
|
||
**n8n:** Bei INSERT/UPDATE `seen_at` und `cover_url` nicht ungewollt überschreiben.
|
||
|
||
---
|
||
|
||
## UI-Funktionen (Stand Projektabschluss)
|
||
|
||
### Layout
|
||
|
||
- **Desktop:** Tabelle mit vielen Spalten; **erste Spalte Cover** (ca. 76×114 px), dann Dateinamen-Paar
|
||
- **Mobile (≤900px):** Karten mit Cover links
|
||
- **Helles Dark-Theme** (`frontend/src/styles.css`, CSS-Variablen)
|
||
|
||
### Dateinamen
|
||
|
||
- Immer **Original** und **Neu** sichtbar (`NamePair.vue`)
|
||
- Kein „Rename prüfen“-Badge mehr (Heuristik entfernt)
|
||
|
||
### Filter (`FilterBar.vue`)
|
||
|
||
- Textsuche in Original + Neu
|
||
- **Buttons** (keine Dropdowns): Format, Codec, Container
|
||
- **Datum/Uhrzeit Von–Bis** mit Kalender-Popup + HH:MM-Dropdowns (`DateTimeRangeFilter.vue`)
|
||
- **Gesehen:** „Nur ungesehen“, „Alle als gesehen“, Zähler „X neu“
|
||
- Aktualisieren / Zurücksetzen
|
||
|
||
### Gesehen-Status
|
||
|
||
- Ungesehene Zeilen: blauer linker Rand + Hintergrund (`row-unseen` / `card-unseen`)
|
||
- **Tippen auf Zeile** (nicht Scroll): Toggle gesehen/ungesehen via `PATCH /api/records/{id}/seen`
|
||
- Mülleimer löscht Eintrag (kein Toggle)
|
||
|
||
### Tabelle
|
||
|
||
- Sortierung Standard: `created_at` absteigend (neueste oben)
|
||
- Schnellfilter in geladenen Daten
|
||
- **Löschen:** roter Mülleimer pro Zeile → `DELETE /api/records/{id}`
|
||
|
||
### Cover
|
||
|
||
- `CoverImage.vue`: lazy load, Platzhalter bei Fehler, URL-Bereinigung (extra `"` entfernen)
|
||
|
||
---
|
||
|
||
## API-Endpunkte
|
||
|
||
| Methode | Pfad | Beschreibung |
|
||
|---------|------|--------------|
|
||
| GET | `/api/health` | DB-Ping |
|
||
| GET | `/api/status` | Diagnose (Config, Tabellenliste bei Fehler) |
|
||
| GET | `/api/columns` | Spaltenliste |
|
||
| GET | `/api/records` | Daten; Query: `search`, `format`, `codec`, `container`, `created_from`, `created_to`, `unseen_only` |
|
||
| GET | `/api/filters` | Distinct-Werte für Filter-Buttons |
|
||
| PATCH | `/api/records/{id}/seen` | Body: `{ "seen": true \| false }` |
|
||
| POST | `/api/records/mark-all-seen` | Alle gefilterten als gesehen |
|
||
| DELETE | `/api/records/{id}` | Zeile löschen |
|
||
|
||
Nginx proxied `/api/` → `http://api:8000/api/`.
|
||
|
||
---
|
||
|
||
## Projektstruktur
|
||
|
||
```text
|
||
/opt/nginx_filebot/
|
||
├── docker-compose.yml # absolute Pfade für Server
|
||
├── config/
|
||
│ ├── config.example.yaml
|
||
│ └── config.yaml # nur Server
|
||
├── api/
|
||
│ ├── main.py
|
||
│ ├── config.py
|
||
│ ├── Dockerfile
|
||
│ └── scripts/list-tables.py
|
||
├── frontend/
|
||
│ └── src/
|
||
│ ├── App.vue
|
||
│ ├── api.ts
|
||
│ ├── components/ # DataTable, FilterBar, CoverImage, …
|
||
│ ├── composables/ # (useSeenObserver entfernt)
|
||
│ └── utils/
|
||
├── nginx/
|
||
│ ├── Dockerfile # Multi-Stage Frontend-Build
|
||
│ └── nginx.conf
|
||
├── sql/ # DB-Migrationen
|
||
├── PROMPT.md # diese Datei
|
||
└── README.md
|
||
```
|
||
|
||
---
|
||
|
||
## Deployment (Kurz)
|
||
|
||
```bash
|
||
cd /opt/nginx_filebot
|
||
docker compose up -d --build
|
||
# Browser: http://<server-ip>:8080
|
||
```
|
||
|
||
Nach Frontend-Änderungen: `docker compose up -d --build web`
|
||
Nach API-Änderungen: `docker compose up -d --build api`
|
||
Oft: komplett `--build`
|
||
|
||
---
|
||
|
||
## Typische Fehler (bereits aufgetreten)
|
||
|
||
1. **`relation "n8n_filebot" does not exist`** → Tabelle heißt `movie_files`, DB `n8n_filebot`
|
||
2. **`schema: n8n` ignoriert** → `db_schema` / API-Fix mit `validation_alias`
|
||
3. **Leere DB-Liste** → falsche Datenbank oder leere Tabelle
|
||
4. **API 500 ohne `seen_at`/`cover_url`** → SQL-Migration ausführen
|
||
5. **Design ändert sich nicht** → `web` neu bauen + Browser Hard-Reload
|
||
|
||
Diagnose:
|
||
|
||
```bash
|
||
docker compose logs api --tail 50
|
||
curl -s http://localhost:8080/api/status
|
||
docker compose exec api python /app/scripts/list-tables.py
|
||
```
|
||
|
||
---
|
||
|
||
## Konventionen für weitere Entwicklung
|
||
|
||
- Antworten an Nutzer oft **auf Deutsch**
|
||
- Keine Commits unless asked
|
||
- `config.yaml` nie ins Git
|
||
- Server-only Deployment kommunizieren
|
||
- Nach Änderungen: welche Dateien hochladen + `docker compose up -d --build`
|
||
- Minimale Diffs, bestehenden Stil beibehalten (Vue 3 Composition API, FastAPI async)
|
||
|
||
---
|
||
|
||
## Nutzer-Präferenzen (aus Session)
|
||
|
||
- Rename-Kontrolle: `file_name_original` + `file_name_new` immer sichtbar
|
||
- Filter als Buttons, nebeneinander (mobil gestapelt)
|
||
- Gesehen per **Tap**, nicht Scroll
|
||
- Cover in erster Spalte, etwas höhere Zeilen
|
||
- Etwas **helleres** UI-Theme
|