# YouTube App Selbst-gehostete Anwendung: YouTube-Videos per Browser Extension erfassen, auf einem Server speichern und per Kotlin-App auf Android/Android TV streamen und herunterladen. ## Dokumentation - `systems.md` — Technologie-Stack pro Komponente - `commication.md` — Kommunikationsflüsse zwischen den Systemen - `features.md` — Konkrete Features und Benutzerinteraktionen - `architecture.md` — Detaillierter Systemaufbau (Endpoints, Services, Models, Screens) - `szenarios.md` — Benutzer-Szenarien ## Architektur Drei Komponenten: ### Browser Extension (`browser_extension/`) - **Manifest V2**, Firefox-kompatibel (`browser.*` API) - `content.js` — extrahiert Videodaten direkt aus dem YouTube-DOM: - `ytd-rich-item-renderer` (Homepage, Abos, Kanalseiten) - `ytd-video-renderer` (Suchergebnisse) - IntersectionObserver (threshold 50%) — nur sichtbare Cards erfassen - MutationObserver registriert neue Cards beim IntersectionObserver - `yt-navigate-finish` Event-Listener fuer SPA-Navigation - Deduplizierung ueber `sentUrls` Set, wird bei Navigation geleert - Batch-Versand: sammelt sichtbare Videos mit Profil-ID, sendet als Array - Selektoren ohne Klassen: nur Tags (`h3`, `img`) und Attribute (`href`, `src`) - `background.js` — empfaengt Batch vom Content Script, sendet POST an Server - `popup.html/popup.js` — Profil-Auswahl (holt Profile vom Server, speichert in browser.storage.local) - Laden via `about:debugging#/runtime/this-firefox` → "Temporaeres Add-on laden" → `manifest.json` ### Server (`backend/`) - **Python, FastAPI, SQLAlchemy, SQLite** (`videos/youtubeapp.db`) - **yt-dlp + ffmpeg + Deno** fuer Video-Download und Streaming - **WebSocket** (`/ws`) — benachrichtigt verbundene Clients bei neuen Videos - Dockerisiert: `docker compose up --build -d` im `backend/` Verzeichnis - Laeuft auf `http://localhost:8000` - Download-Service speichert Videos unter `/videos/{id}.mp4` - Stream-Service: heruntergeladene Videos von Datei, sonst ffmpeg Live-Muxing von Video+Audio mit gleichzeitigem Streaming und Speichern - Dedup: beim Batch-Import wird bestehender Eintrag mit gleicher Video-ID geloescht und neu eingefuegt - Sortierung: nach ID absteigend (erstes Video im Batch bekommt hoechste ID) - Profile: fest in DB definiert, Videos ueber Many-to-Many zugeordnet - Nach lokalem Download wird die Server-Datei geloescht (file_path auf null) ### App (`app/`) - **Kotlin, Jetpack Compose**, Android/Android TV - Gradle-Projekt, Modul `frontend` - Screens: AllVideos (Grid), Downloaded (lokal verfuegbare Videos), VideoDetail, VideoPlayer - Retrofit fuer API-Calls, Coil fuer Thumbnails, ExoPlayer fuer Streaming - OkHttp WebSocket-Client — automatisches Neuladen bei neuen Videos - Navigation mit TopBar (Profil-Auswahl, Aufraeumen-Icon) und Bottom Bar, Dark Theme - Profil-Auswahl wird in SharedPreferences persistiert, filtert Videos nach Profil - Lokaler Download: Videos und Metadaten werden auf dem Geraet gespeichert, lokal bevorzugt abgespielt, offline verfuegbar - Aufraeumen: loescht alle nicht lokal gespeicherten Videos des Profils (sendet lokale IDs als Ausnahme) - Server-IP konfigurierbar in `ApiClient.kt` (Emulator: `10.0.2.2`, echtes Geraet: `192.168.178.34`) - Emulator: Android Studio → Device Manager → Pixel 7a, API 36 ## API Endpoints - `GET /profiles` — alle Profile abrufen - `POST /videos` — Video-Batch von Extension empfangen (Dedup, Reverse-Insert, Profil-Zuordnung, WebSocket-Benachrichtigung) - `GET /videos` — alle Videos abrufen (optional `?profile_id=X`, sortiert nach ID absteigend) - `GET /videos/downloaded` — heruntergeladene Videos abrufen (optional `?profile_id=X`) - `DELETE /videos?profile_id=X&exclude_ids=` — Videos des Profils loeschen (ausser lokal gespeicherte) - `POST /videos/{id}/download` — Download auf Server triggern - `GET /videos/{id}/stream` — Video streamen (von Datei oder progressiver Download via yt-dlp) - `GET /videos/{id}/file` — Video-Datei zum Download auf Client ausliefern, setzt Download-Status zurueck wenn Datei fehlt - `DELETE /videos/{id}/file` — Server-Datei loeschen (nach lokalem Download) - `WS /ws` — WebSocket, sendet Profile-IDs bei neuen Videos ## Projektstruktur ``` backend/ main.py — FastAPI App, CORS, Startup, Seed-Profile, WebSocket database.py — SQLAlchemy Engine, Session, Base models.py — Video, Profile, video_profiles (Many-to-Many) schemas.py — Pydantic Schemas (VideoCreate, VideoResponse, ProfileResponse) routes/videos.py — Video- und Profil-Routen services/ video_service.py — CRUD-Operationen, Dedup, Profil-Filter download_service.py — yt-dlp Download stream_service.py — ffmpeg Live-Muxing + Streaming Dockerfile — Python 3.12 + ffmpeg + Deno docker-compose.yml — Service-Definition, Port 8000, Volume /videos .dockerignore — videos/, __pycache__/ .gitignore — videos/, __pycache__/ browser_extension/ manifest.json — Manifest V2, Permissions, browser_action, storage content.js — DOM-Extraktion + IntersectionObserver + Batch-Versand mit Profil background.js — Batch-POST an Server popup.html — Profil-Auswahl UI popup.js — Profile laden, Auswahl speichern app/ .gitignore — .gradle/, build/, .idea/, local.properties frontend/src/main/java/com/youtubeapp/ MainActivity.kt — Einstiegspunkt data/ — Video, Profile, ApiClient, VideoApi, VideoRepository, LocalStorageService ui/screens/ — AllVideos, Downloaded, VideoDetail, VideoPlayer ui/components/ — VideoCard ui/viewmodel/ — VideoViewModel (inkl. WebSocket-Client) ui/navigation/ — AppNavigation, Routes ui/theme/ — Theme (Dark) frontend/src/main/res/ layout/player_view.xml — PlayerView mit TextureView fuer TV-Kompatibilitaet drawable/tv_banner.png — Android TV Launcher-Banner ``` ## Entscheidungen - Kein Jellyfin — erfuellt nicht die Anforderung, Videos vor dem Download aufzulisten - Kein PostgreSQL/MySQL — SQLite reicht fuer den Prototyp - Profile fest in DB — kein UI zum Erstellen/Loeschen, werden direkt in der Datenbank verwaltet - Server-Datei wird nach lokalem Download geloescht — spart Speicherplatz auf dem Server - DOM-Extraktion statt ytInitialData-Parsing — funktioniert auch bei SPA-Navigation und Scrollen - IntersectionObserver statt blindem Scan — nur sichtbare Videos erfassen - ffmpeg Live-Muxing statt komplettem Download vor dem Abspielen - Deno als JavaScript-Runtime fuer yt-dlp — YouTube erfordert JS-Ausfuehrung zur URL-Extraktion - Videos ohne Profilzuweisung werden automatisch dem Standardprofil zugeordnet - WebSocket statt Polling — effiziente Echtzeit-Aktualisierung der Videoliste - Sprache der Dokumentation: Deutsch