- HTML 59.3%
- Python 23.9%
- CSS 8.4%
- JavaScript 7.2%
- Dockerfile 0.7%
- Other 0.5%
|
Some checks failed
Container bauen / build (push) Failing after 10m51s
Every runner carrying the 'docker' label is offline; the only idle ones are unraid-runner (ubuntu-latest) and flaming_battenberg (vps). As written the job would have queued indefinitely instead of failing visibly. unraid-runner also runs on the Unraid host, so the linux/amd64 build is native there rather than emulated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|---|---|---|
| .forgejo/workflows | ||
| app | ||
| design | ||
| unraid | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| entrypoint.sh | ||
| LICENSE | ||
| README.md | ||
| requirements.txt | ||
YouTubby
Link einfügen, Qualität wählen, Datei aufs iPad laden. Läuft als Docker-Container auf Unraid, speichert nichts dauerhaft und redet Deutsch.
A small, single-user YouTube downloader with a German interface, built to be used on an iPad. Runs as a Docker container on Unraid.
Was es tut
Sie fügt einen YouTube-Link ein. YouTubby schaut sich das Video an und bietet an, was es tatsächlich gibt: 1080p bis 360p als Video, dazu MP3 oder M4A für den Ton allein. Sie tippt auf eine Karte, wartet kurz, und speichert die Datei.
Dazwischen liegt kein Konto, kein Passwort und keine Mediathek. Fertige Dateien werden nach sechs Stunden gelöscht, verwaiste Ordner beim nächsten Start.
Installation auf Unraid
Unraid hat das Feld für Template-URLs in Version 6.10 entfernt, deshalb geht es über die Konsole. Einmalig, danach läuft alles über die Oberfläche.
wget -O /boot/config/plugins/dockerMan/templates-user/youtubby.xml https://git.flamingbattenberg.de/fubledde/youtubby/raw/branch/main/unraid/youtubby.xml
Danach Docker → Add Container, oben unter [ User templates ] youtubby
wählen. Alle Felder sind vorbelegt. Ein Wert lohnt einen zweiten Blick:
Arbeitsverzeichnis gehört auf den Cache-Pool, nicht auf das Array und nicht
auf /mnt/user. Während eines Downloads liegen dort kurzzeitig etwa doppelt so
viele Daten wie die fertige Datei groß ist, weil Bild und Ton getrennt geladen
und danach zusammengefügt werden.
Eine RAM-Disk wäre bei 64 GB verlockend, ist aber die schlechtere Wahl: Unraids
eigenes / liegt bereits im RAM, ein vollgelaufenes /tmp kann den Server
mitnehmen, und ein Neuversuch nach einem Verbindungsabbruch ist auf einer
RAM-Disk nicht möglich.
Alternativ mit dem Compose-Plugin:
docker compose up -d
Warum es so gebaut ist
Vier Entscheidungen sind nicht Geschmackssache, sondern Folge dessen, wie iPads und YouTube sich tatsächlich verhalten.
Die Datei wird als normaler Link ausgeliefert, nie als Blob. Eine Seite in
iOS-Safari darf ungefähr 100 bis 200 MB Speicher belegen. Ein 500-MB-Video über
fetch() in einen Blob zu holen killt den Tab. Also: gewöhnliche Navigation zu
einer stabilen Server-URL mit Content-Disposition: attachment.
Der Fortschritt wird gepollt, nicht nur gestreamt. Seit iOS 18 meldet ein
EventSource, der während des Ruhezustands gestorben ist, weiterhin
readyState: OPEN und wirft nie einen Fehler. Wer sich darauf verlässt, zeigt
für immer einen eingefrorenen Balken. GET /api/jobs/{id} ist die Wahrheit, der
SSE-Stream ist Komfort, und jede Rückkehr in den Tab fragt neu nach.
Der Codec steckt im Format-Selektor, nicht in merge_output_format. Letzteres
sortiert nur Container-Vorlieben. VP9 und Opus landen fröhlich in einer .mp4,
die im Safari-Tab läuft und sich in der Dateien-App dann nicht öffnen lässt. Der
Selektor verlangt ausdrücklich avc1 und mp4a und fällt erst danach zurück.
yt-dlp ist nirgends festgenagelt. YouTube ändert mehrmals im Jahr etwas, das yt-dlp bricht; zuletzt am 17. August 2026, als der bis dahin voreingestellte Client auf jedes Format mit HTTP 403 antwortete. Der Container zieht bei jedem Start die aktuelle Nightly. Geht trotzdem etwas kaputt, ist der Knopf yt-dlp aktualisieren in der Fußzeile die ganze Reparatur.
Deno steckt aus demselben Grund im Image. yt-dlp hat seinen eingebauten JavaScript-Interpreter für YouTube abgegeben; fehlt eine Laufzeit, schreibt es eine Warnung ins Log und liefert danach still nur noch niedrige Auflösungen.
Selbsttest
Ob iOS-Safari eine .mp4 wirklich speichert oder nur eine Vorschau zeigt, ist
je nach Version unterschiedlich beantwortet worden. /selbsttest bietet
dieselbe Datei in vier Kopfzeilen-Varianten an. Einmal auf dem iPad durchtippen
beantwortet die Frage.
Speichert Safari die Varianten 1 und 3 sauber, kann DELIVERY_MODE auf native
gestellt werden. Sonst bleibt es bei octet, der Voreinstellung.
Einstellungen
| Variable | Standard | Bedeutung |
|---|---|---|
PORT |
8488 |
Port der Weboberfläche |
SCRATCH_DIR |
/scratch |
Arbeitsverzeichnis für laufende Downloads |
RETENTION_MINUTES |
360 |
Wie lange eine fertige Datei liegen bleibt |
MAX_HEIGHT |
1080 |
Höchste angebotene Auflösung |
MAX_DURATION_MINUTES |
180 |
Längere Videos werden abgelehnt, 0 schaltet es ab |
DELIVERY_MODE |
octet |
octet oder native, siehe Selbsttest |
YTDL_UPDATE_ON_START |
true |
Nightly beim Start ziehen |
MAX_CONCURRENT |
1 |
Gleichzeitige Downloads |
PUID / PGID |
99 / 100 |
Unraid-Standardbenutzer |
Sicherheit
Kein Login, absichtlich. Der Container gehört ins Heimnetz und nicht ins Internet.
Ein Loch bleibt auch dort: jede beliebige Webseite, die im selben Netz geöffnet ist, kann eine Anfrage an diese App schicken. Antworten kann sie nicht lesen, aber Downloads auslösen schon. Deshalb verlangt jeder verändernde Endpunkt einen eigenen Header, den ein fremdes Formular nicht setzen kann. Das kostet in der Bedienung nichts und schließt die Lücke.
Ausserdem: nur http- und https-Links, nur YouTube-Hosts, keine Adressen ins
eigene Netz, und enable_file_urls, --exec sowie externe Downloader sind
nirgends eingeschaltet.
Soll die App von unterwegs erreichbar sein, gehört davor ein Tailscale- oder WireGuard-Tunnel, keine Portfreigabe.
Was nicht geht
Gekaufte oder geliehene Filme sind kopiergeschützt und lassen sich nicht laden.
Altersbeschränkte, private und Mitglieder-Videos brauchen eine Anmeldung bei
YouTube, die hier bewusst nicht eingebaut ist. Live-Streams werden abgelehnt,
solange sie laufen. Playlists werden auf das einzelne Video reduziert, auch wenn
der Link aus der Teilen-Funktion ein &list= mitbringt.
Über 1080p liefert YouTube nur noch VP9 und AV1. AV1 braucht auf Apple-Geräten Hardware-Dekodierung, die ältere iPads nicht haben, und WebM spielt zwar im Safari-Tab, nicht aber in der Dateien-App. Deshalb endet die Auswahl bei 1080p.
Entwicklung
python -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt && pip install -U --pre "yt-dlp[default]"
SCRATCH_DIR=./scratch python -m app
Braucht ffmpeg und deno im Pfad.
Der Ordner design/ enthält die Design-Vorlagen, aus denen die Oberfläche
gebaut ist: fünf Bildschirme, das Maskottchen in fünf Zuständen und die
Farb- und Schriftwerte.
Lizenz
MIT, siehe LICENSE.