Files
loki f2761549f9
CI / backend-tests (push) Successful in 10s
Docker images / version (push) Successful in 2s
CI / docker (push) Successful in 1m4s
Docker images / backend (push) Successful in 12s
Docker images / frontend (push) Successful in 7s
fix: HOME=/tmp im backend-image (user-overrides, streamrip-import)
2026-10-04 14:20:02 +02:00

4.6 KiB

streamrip-ui — Projektwissen

Diese Datei hält jede neue Session auf Stand (Architektur, Entscheidungen, Umgebungs-Eigenheiten, offene Punkte). Bei größeren Schritten aktualisieren.

Was es ist

Webapp für das CLI-Tool streamrip (Musik-Downloader). Nur Deezer, lokal Single-User ohne Login (bewusst, nur Heimnetz — nicht ins Internet stellen).

Architektur

  • backend/ — FastAPI, importiert streamrip als Library (kein Subprocess). Endpoints: search (track/album/artist/playlist/podcast), artist/album/playlist/ podcast-Detail, discover (charts/genres/radios), downloads (Queue + SSE + Cancel
    • Batch + URL-Import), history, config (voll/maskiert/arl/ui), files (+download).
  • frontend/ — Next.js 14 (App Router), dunkel/hell, Space Grotesk, Bernstein-Akzent. Seiten: Suche, Entdecken, Artist, Album, Playlist, Podcast, Korb, Queue, Verlauf, Dateien, Einstellungen.
  • Betrieb per docker-compose.yml (2 Services). UI: Host-Port 3002, API: 8000.

Getroffene Entscheidungen (mit Begründung)

  • Zwei Container statt einem (Diskussion Okt 2026): Python/Node brauchen je eigene Laufzeit; HTMX-Rewrite und Single-Stack verworfen (Rewrite-Aufwand, schlechtere UX). Bleibt so.
  • streamrip exakt v2.2.0 vom Git-Tag (git+https://...@v2.2.0): PyPI kennt nur bis 2.1.0; 2.1.0 vs. lokale config.toml (2.2.0) wirft „Need to update config“.
  • Python 3.12-slim als Basis: Pillow 10.4 (streamrip-Pin) hat kein 3.14-Wheel.
  • Non-Root: Backend appuser (UID/GID per Build-Arg), Frontend node-User.
  • Version: automatisch CalVer DATUM.Commitanzahl via scripts/version.sh (APP_VERSION-Build-Arg → GET /api/version → Header-Anzeige).
  • ARL wird in GET /api/config maskiert (__MASKED__, Roundtrip-sicher).
  • Kein echter %-Fortschritt: nur Job-Status + Logs (streamrip-Rich-Bars sind terminalgebunden).
  • Podcasts: nur Suche + Episodenliste + Deezer-Link — streamrip kann keine Podcasts laden (technisch unmöglich ohne Fork).
  • Deezer-Lücken mit Fallbacks: Chart-Tracks/Artists + Editorial-Selection sind leer (total: 0, auch eingeloggt) → Fallback Top-Playlist-Tracks bzw. Charts-Alben (neueste zuerst). get_editorial_releases ist tot, Ersatz ist get_editorial_selection. Echte Sub-Genres gibt Deezer nicht (flache Genreliste) → Ersatz: Playlists-Tab + Genre-Sender mit aufklappbarer Trackliste.
  • Ports: UI Host 3002 (Host-3000 ist von einem bun-Prozess belegt, nicht anfassen). Alle Ports per Env umstellbar (*_HOST_PORT, BACKEND_PORT, FRONTEND_PORT); BACKEND_URL wird daraus abgeleitet.
  • Volumes per .env (.env.example): CONFIG_DIR, DOWNLOAD_DIR, UID/GID.
  • Git/Gitea: Repo loki/streamrip-ui (privat), main, Commits pro Feature. CI (.gitea/workflows/ci.yml): Tests + Docker-Builds. docker.yml baut und pusht beide Images (:latest + :Version) an gitea.lokcal.de. Registry-Login nutzt Secret ACTION_BOT (von Lokman per Web gesetzt — nicht zurückändern).
  • Kein lokaler venv-Push: .venv/, node_modules/, .next/ sind ignoriert. Lokaler Backend-venv braucht --system-site-packages (streamrip 2.2.0 nur systemweit).

Bekannte Stolpersteine

  • ~/.local/bin/rip (pipx-Shim) ist kaputt — irrelevant, Library-Import genutzt.
  • DB-Pfade aus gemounteter config.toml zeigen auf Host-Pfade → Service biegt sie auf das Config-Verzeichnis um (_load_config).
  • Fehlende config.toml wird automatisch aus streamrip-Defaults angelegt (_ensure_config_exists, Ordner = STREAMRIP_DOWNLOAD_DIR); ARL danach per UI.
  • Image setzt HOME=/tmp: streamrip erstellt beim Import ~/.config — mit user:-UID-Overrides gäbe es sonst Permission denied: '/.config'.
  • Next-14-Fallen: keine use(params)-Promise-Params; keine fremden Exporte aus page.tsx (z. B. sortByDate → app/lib/sort.ts); public/-Copy im Standalone-Dockerfile nötig; Healthcheck mit 127.0.0.1 statt localhost (BusyBox-wget/IPv6); Frontend braucht HOSTNAME=0.0.0.0 (sonst DNS-Crash).
  • TestClient erzeugt pro Request einen Loop → Background-Tasks darin sofort gecancelt; Cancel-Tests laufen deshalb auf einem Loop (asyncio.run).
  • Radio-ID 1 liefert leere Tracks (Deezer-seitig); Upload-Album 3451971451 ist tot (no data) — beides kein App-Fehler.

Offen / Später

  • Auth + HTTPS (Caddy) nur falls je über Heimnetz hinaus gehostet wird.
  • Echte %-Fortschritte (Downloadable-Hooks in streamrip).
  • Personalisierte Vorschläge aus downloads.db-Verlauf (Discover ist nur Genre/Charts).
  • Ordner als ZIP laden; Download-Links mit Ablauf.
  • Queue nutzt Polling (SSE-Endpoint existiert, wird nicht verwendet).