122 lines
6.5 KiB
Markdown
122 lines
6.5 KiB
Markdown
# 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** 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 progressiver Download via yt-dlp mit gleichzeitigem Streaming
|
|
- 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 werden auf dem Geraet gespeichert, lokal bevorzugt abgespielt
|
|
- Aufraeumen: loescht alle nicht lokal gespeicherten Videos des Profils (sendet lokale IDs als Ausnahme)
|
|
- Server-IP konfigurierbar in `ApiClient.kt` (aktuell `192.168.178.92`)
|
|
- Emulator: Android Studio → Device Manager → Pixel 6a, API 35
|
|
|
|
## 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
|
|
- `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 — Progressiver Download + Streaming via yt-dlp
|
|
Dockerfile — Python 3.12 + ffmpeg
|
|
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
|
|
- Progressiver Download via yt-dlp mit gleichzeitigem Streaming statt komplettem Download vor dem Abspielen
|
|
- WebSocket statt Polling — effiziente Echtzeit-Aktualisierung der Videoliste
|
|
- Sprache der Dokumentation: Deutsch
|