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>
6.4 KiB
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_newmovie_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_atabsteigend (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
/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)
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)
relation "n8n_filebot" does not exist→ Tabelle heißtmovie_files, DBn8n_filebotschema: n8nignoriert →db_schema/ API-Fix mitvalidation_alias- Leere DB-Liste → falsche Datenbank oder leere Tabelle
- API 500 ohne
seen_at/cover_url→ SQL-Migration ausführen - Design ändert sich nicht →
webneu bauen + Browser Hard-Reload
Diagnose:
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.yamlnie 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_newimmer sichtbar - Filter als Buttons, nebeneinander (mobil gestapelt)
- Gesehen per Tap, nicht Scroll
- Cover in erster Spalte, etwas höhere Zeilen
- Etwas helleres UI-Theme