- Python 99.4%
- Dockerfile 0.6%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
Test and Build ntfy-downloader Image / build-image (push) Successful in 32s
|
||
| .forgejo/workflows | ||
| ntfy_downloader | ||
| releases | ||
| tests | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| compose.image.yaml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| README.md | ||
| requirements.txt | ||
ntfy yt-dlp Downloader
Ein kleiner Python-Dienst, der auf einem ntfy-Kanal lauscht. Sobald eine Nachricht mit einer URL eintrifft, wird diese mit yt-dlp heruntergeladen. Erfolg und Fehler werden als Nachricht auf denselben Kanal zurueckgemeldet. Der Zugriff auf den ntfy-Server erfolgt mit HTTP Basic Auth (Username/Passwort).
Funktionsweise
ntfy Topic --(JSON-Stream, Basic Auth)--> Listener
Listener --(URL erkannt)--> yt-dlp --> DOWNLOAD_DIR
Listener --(POST Statusmeldung)--> ntfy Topic
- Abonniert
GET <NTFY_BASE_URL>/<NTFY_TOPIC>/jsonund verarbeitetmessage-Events. - Extrahiert die erste
http(s)://-URL aus dem Nachrichtentext. - Laedt sie per
yt-dlpins konfigurierte Verzeichnis. - Speichert Auftraege vor dem Download in einer persistenten SQLite-Warteschlange.
- Setzt einen unterbrochenen ntfy-Stream am letzten gespeicherten Zeitpunkt fort und dedupliziert wiederholte Nachrichten anhand ihrer ntfy-ID.
- Loest
pornkai.com-Links automatisch auf die eingebettete Video-URL (iframe) auf, bevoryt-dlpstartet. - Meldet
Download gestartet(optional),Download erfolgreichoderDownload fehlgeschlagenzurueck. Bei Erfolg optional ein Link zur Web-Galerie (/watch?f=...), wennWEBUI_PUBLIC_URLgesetzt ist. - Ignoriert seine eigenen Statusmeldungen (verhindert Rueckkopplungsschleifen).
- Laeuft dauerhaft weiter: Ein fehlgeschlagener Download wird gemeldet, der Dienst lauscht danach aber normal weiter (es wird nichts erneut versucht).
- Verbindet bei Stream-Abbruch automatisch mit Backoff neu. Nur bei falschen Zugangsdaten (401/403) wird der Dienst beendet.
Konfiguration
Alle Einstellungen erfolgen ueber Umgebungsvariablen (bzw. .env). Kopiere
.env.example nach .env und passe die Werte an:
cp .env.example .env
| Variable | Pflicht | Beschreibung |
|---|---|---|
NTFY_BASE_URL |
ja | URL des ntfy-Servers, z.B. https://ntfy.example.com |
NTFY_TOPIC |
ja | Kanal/Topic zum Lauschen und Zurueckmelden |
NTFY_USERNAME |
ja | Benutzername (Basic Auth) |
NTFY_PASSWORD |
ja | Passwort (Basic Auth) |
DOWNLOAD_DIR |
nein | Zielverzeichnis der Downloads. Bei Docker der Host-Pfad, der nach /downloads gemappt wird (z.B. ein SMB-Mount /mnt/smb/XXX); ohne Docker der direkte Pfad. Default ./downloads |
JOB_DATABASE |
nein | SQLite-Datei der persistenten Jobqueue. Default ./state/jobs.sqlite3; Docker verwendet /state/jobs.sqlite3 |
YTDLP_FORMAT |
nein | yt-dlp Format-String, leer = Default |
YTDLP_OUTPUT_TEMPLATE |
nein | Ausgabe-Template (Default %(title).120B [%(id)s].%(ext)s; begrenzt lange Dateinamen) |
YTDLP_IMPERSONATE |
nein | Browser-TLS-Fingerprint fuer yt-dlp --impersonate (z.B. chrome, firefox). Umgeht TLS-Fingerprinting (z.B. Pornhub). Leer = aus. Default chrome |
NOTIFY_ON_START |
nein | true/false, Startmeldung senden (Default true) |
DOWNLOAD_TIMEOUT |
nein | Max. Laufzeit pro Download in Sekunden (Default 3600) |
LOG_LEVEL |
nein | DEBUG/INFO/WARNING/ERROR (Default INFO) |
WEBUI_USERNAME |
fuer Web-UI | Login-Benutzer der Galerie (Basic Auth) |
WEBUI_PASSWORD |
fuer Web-UI | Login-Passwort der Galerie (Basic Auth) |
WEBUI_PORT |
nein | Host-Port der Galerie (Default 2310) |
WEBUI_PUBLIC_URL |
nein | Oeffentliche Basis-URL der Galerie fuer Links in Erfolgsmeldungen (z.B. http://server:2310). Leer = kein Link |
WEBUI_DEFAULT_TAG |
nein | Zeigt beim Aufruf der Startseite nur Videos mit diesem Tag. Leer = alle Videos |
THUMB_MAX_AGE_DAYS |
nein | Entfernt beim Web-UI-Start aeltere Thumbnail-Cachedateien. Default 30, 0 deaktiviert die Bereinigung |
CLIPS_SUBDIR |
nein | Unterverzeichnis fuer erzeugte Videoausschnitte. Default clips |
GIFS_SUBDIR |
nein | Unterverzeichnis fuer erzeugte GIFs. Default gifs |
GIF_TIMEOUT |
nein | Maximale Laufzeit je ffmpeg-Phase eines GIF-Exports in Sekunden. Default 600 |
WHATSAPP_SUBDIR |
nein | Unterverzeichnis fuer kompatibel codierte WhatsApp-Videos. Default whatsapp |
WHATSAPP_TIMEOUT |
nein | Maximale ffmpeg-Laufzeit fuer einen WhatsApp-Export in Sekunden. Default 120 |
WHATSAPP_SHARE_TTL |
nein | Gueltigkeit signierter oeffentlicher WhatsApp-Links in Sekunden. Default 604800 (7 Tage) |
CLIP_MAX_DURATION |
nein | Maximale Laenge eines Ausschnitts in Sekunden. Default 600 |
CLIP_TIMEOUT |
nein | Maximale ffmpeg-Laufzeit fuer einen Ausschnitt in Sekunden. Default 120 |
Lokaler Start mit Docker Build
Diese Variante baut das Image direkt aus dem Checkout und eignet sich fuer Entwicklung und lokale Tests:
cp .env.example .env # Werte anpassen
mkdir -p ./downloads # oder vorhandenen DOWNLOAD_DIR-Pfad vorbereiten
docker compose up -d --build
docker compose logs -f
Die heruntergeladenen Dateien landen im Host-Verzeichnis, das in DOWNLOAD_DIR
(in der .env) steht, und das nach /downloads im Container gemappt wird.
Ist DOWNLOAD_DIR nicht gesetzt, wird ./downloads verwendet. Beispiel fuer
einen SMB-Mount:
DOWNLOAD_DIR=/mnt/smb/XXX
Der Container laeuft ohne Root-Rechte als UID/GID 1000. Der konfigurierte
Host-Pfad muss bereits existieren und fuer diesen Benutzer beschreibbar sein.
SQLite-Zustand, Thumbnail-Cache und Web-UI-Metadaten liegen in den Docker-Volumes
ntfy-state, ntfy-thumbs und ntfy-metadata.
CPU- und Speicherlimits koennen ueber die Variablen in .env.example angepasst
werden. Beide Dienste besitzen einen Docker-Healthcheck.
Betrieb mit dem Registry-Image
Forgejo Actions baut bei jedem Push auf main zuerst die Test-Stage und
veroeffentlicht danach das Runtime-Image unter:
git.home.schumbi.de/ralf/ntfy-downloader
Es werden die Tags latest und der vollstaendige Commit-SHA veroeffentlicht.
Fuer einen reproduzierbaren Betrieb auf dem Host sollte ein Commit-SHA statt
latest verwendet werden.
Auf dem Zielhost werden nur diese Dinge benoetigt:
compose.image.yaml- eine ausgefuellte
.env - das in
DOWNLOAD_DIRangegebene, fuer die konfigurierte Downloader-UID/GID beschreibbare Verzeichnis
Der Quellcode, die Tests und der Dockerfile werden auf dem Host nicht benoetigt.
Falls das Downloadverzeichnis einer anderen numerischen Identitaet gehoert,
koennen DOWNLOADER_UID und DOWNLOADER_GID in .env passend gesetzt werden.
Downloader und Web-UI verwenden beide diese Identitaet. Bereits vorhandene
Volumes fuer Jobzustand und Thumbnails muessen bei einer abweichenden UID
einmalig passende Eigentumsrechte erhalten. Das neue Metadaten-Volume wird beim
ersten Start automatisch vorbereitet. Beispiel fuer UID/GID 1000:1000:
docker run --rm --user 0:0 \
-v ntfy-downloader_ntfy-thumbs:/thumbs \
git.home.schumbi.de/ralf/ntfy-downloader:<TAG> \
chown -R 1000:1000 /thumbs
Einmalige Einrichtung:
mkdir -p /opt/ntfy-downloader
cd /opt/ntfy-downloader
# compose.image.yaml und eine angepasste .env hier ablegen
mkdir -p ./downloads # falls DOWNLOAD_DIR=./downloads verwendet wird
docker volume create ntfy-downloader_ntfy-state
docker volume create ntfy-downloader_ntfy-thumbs
docker login git.home.schumbi.de
docker compose -f compose.image.yaml config -q
docker compose -f compose.image.yaml pull
docker compose -f compose.image.yaml up -d
docker compose -f compose.image.yaml ps
Falls die Registry fuer Downloads ohne Anmeldung freigegeben ist, kann
docker login entfallen. Status und Logs:
docker compose -f compose.image.yaml ps
docker compose -f compose.image.yaml logs -f
Fuer ein reproduzierbares Update zuerst NTFY_DOWNLOADER_IMAGE_TAG in .env
auf den neuen vollstaendigen Commit-SHA setzen und danach ausfuehren:
docker compose -f compose.image.yaml pull
docker compose -f compose.image.yaml up -d
docker compose -f compose.image.yaml ps
Zum Stoppen beziehungsweise Entfernen der Container:
docker compose -f compose.image.yaml stop
docker compose -f compose.image.yaml down
ntfy-state und ntfy-thumbs sind als externe Volumes deklariert und bleiben
deshalb auch bei down -v erhalten. Das von Compose verwaltete
ntfy-metadata bleibt bei down erhalten, wird mit down -v jedoch geloescht.
Start ohne Docker
Voraussetzungen: Python 3.12+, ffmpeg im PATH.
python -m venv .venv
. .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
# Konfiguration als Umgebungsvariablen setzen (oder .env per Tool laden)
export NTFY_BASE_URL=https://ntfy.example.com
export NTFY_TOPIC=downloads
export NTFY_USERNAME=meinuser
export NTFY_PASSWORD=meinpasswort
export DOWNLOAD_DIR=./downloads
python -m ntfy_downloader.listener
Verwendung / Test
Eine URL auf den Kanal senden (loest einen Download aus):
curl -u meinuser:meinpasswort \
-d "https://www.youtube.com/watch?v=dQw4w9WgXcQ" \
https://ntfy.example.com/downloads
Anschliessend erscheinen die Statusmeldungen (Download erfolgreich /
Download fehlgeschlagen) auf demselben Kanal, und die Datei liegt im
Downloadverzeichnis.
Die automatisierten Unit-Tests laufen mit:
python -m unittest discover -v
Web-UI / Video-Galerie
Das docker compose-Setup startet zusaetzlich den Dienst ntfy-webui: eine
kleine Galerie, die die heruntergeladenen Videos mit Thumbnails und einem
HTML5-Player im Browser anzeigt. Sie ist im internen Netz unter
http://<server>:2310 erreichbar (Port via WEBUI_PORT aenderbar) und per
Basic Auth geschuetzt (WEBUI_USERNAME / WEBUI_PASSWORD).
- Videos werden rekursiv auch aus Unterverzeichnissen angezeigt.
- Videos koennen mit frei waehlbaren Tags versehen und danach gefiltert werden.
WEBUI_DEFAULT_TAGbegrenzt die ungefilterte Startseite auf ein bestimmtes Tag; ueberAllebleiben saemtliche Videos erreichbar. - Die Detailseite kann Videos in vorhandene oder neue Unterverzeichnisse verschieben und nach Bestaetigung dauerhaft loeschen. Tags folgen einer verschobenen Datei automatisch.
- Zeitstempel-Links koennen direkt aus der aktuellen Abspielposition erstellt
werden. Ein Start-/Endbereich laesst sich ausserdem per ffmpeg als neue Datei
unter
CLIPS_SUBDIRextrahieren und anschliessend abspielen oder herunterladen. Die Extraktion verwendet Stream-Copy, ist daher schnell, kann aber je nach Videoformat am naechsten Keyframe beginnen. - Zusätzliche Player-Tasten verwenden abhängig von der Videolänge eine sichtbare
Sprungweite von 2, 5, 10, 30 oder 60 Sekunden. Der Schnittbereich zeigt die
gewählte Dauer an, kann direkt als Vorschau abgespielt und anschließend über
getrennte Aktionen exportiert werden. Derselbe markierte Start-/Endbereich
kann bis zu einer Länge von 10 Sekunden
als optimiertes GIF in
GIFS_SUBDIRexportiert werden und erscheint danach in der GIF-Galerie. GIFs werden mit 8 Bildern pro Sekunde in eine Begrenzungsbox von 640 x 480 Pixeln skaliert, wobei das Seitenverhaeltnis erhalten bleibt. Nach dem Export wird das neue GIF direkt in der Lightbox geoeffnet. WennWEBUI_PUBLIC_URLund die ntfy-Zugangsdaten gesetzt sind, erscheint im konfigurierten Topic zusaetzlich eine Nachricht mit einem Link auf diese Ansicht. Die Erzeugung laeuft als Hintergrundjob; eine Fortschrittsseite zeigt anhand der von ffmpeg verarbeiteten Videozeit Phase und Prozentwert an, wartet auf das Ergebnis und verhindert dadurch Gateway-Timeouts bei laengeren FFmpeg-Laufzeiten. - Derselbe maximal 10 Sekunden lange Bereich kann als WhatsApp-kompatibles MP4
exportiert werden. Das Video wird auf hoechstens 640 x 480 Pixel skaliert und
als H.264 Baseline mit
yuv420p, AAC-Tonspur und Faststart erzeugt. Danach oeffnet sich eine Vorschau mit direktem Teilen ueber die Web Share API sowie einem Download als Fallback. Die ntfy-Nachricht enthaelt einen signierten, zeitlich begrenzten Link, den WhatsApp ohne Basic-Auth-Anmeldung abrufen kann. Erzeugte WhatsApp-Dateien liegen unterWHATSAPP_SUBDIR, erscheinen in der Galerie und erhalten automatisch den TagWhatsAppsowie die Tags des Quellvideos. Beim Start werden auch bereits vorhandene Exporte nachgetaggt. - Fuer diese Dateioperationen wird das Download-Verzeichnis schreibbar in die Web-UI eingebunden. Alle Aenderungsaktionen verwenden authentifizierte POST-Formulare mit CSRF-Schutz.
- Thumbnails werden bei Bedarf per
ffmpegerzeugt und im konfigurierten Cacheverzeichnis beziehungsweise im Docker-Volumentfy-thumbsgespeichert. Um Last- und Speicherspitzen auf kleinen Hosts zu vermeiden, läuft höchstens eine Thumbnail-Erzeugung gleichzeitig. Parallele Anfragen für dieselbe Datei warten auf denselben Auftrag, statt weitereffmpeg-Prozesse zu starten. - Tags werden unabhaengig vom Download-Verzeichnis im Docker-Volume
ntfy-metadatagespeichert. .mp4/.webmlaufen nativ im Browser;.mkv/.avilassen sich evtl. nicht direkt abspielen, koennen aber heruntergeladen werden.
Sicherheit
- Basic Auth verschluesselt die Zugangsdaten nicht. ntfy und Galerie sollten ausserhalb eines vollstaendig vertrauenswuerdigen Netzes nur ueber HTTPS erreichbar sein, beispielsweise hinter einem Reverse Proxy.
- Das ntfy-Topic ist eine Ausfuehrungsschnittstelle fuer Downloads und muss so konfiguriert sein, dass nur vertrauenswuerdige Benutzer darauf schreiben duerfen.
- Akzeptierte URLs koennen ausgehende Verbindungen des Containers ausloesen, auch zu internen Netzwerkzielen. Falls das Topic nicht vollstaendig vertrauenswuerdig ist, muss der Container zusaetzlich per Firewall oder separatem Docker-Netz von internen Diensten isoliert werden.
- Die Beispiel-Zugangsdaten in
.env.examplemuessen vor dem Start ersetzt werden. - Der Web-UI-Container besitzt absichtlich Schreibrechte im Download-Verzeichnis. Das verwendete Basic-Auth-Passwort muss deshalb stark sein und die Galerie darf nur ueber HTTPS beziehungsweise ein vertrauenswuerdiges Netz erreichbar sein.
- Signierte Links unter
/shared/whatsappumgehen Basic Auth bis zum Ablauf vonWHATSAPP_SHARE_TTL. Jeder, der einen solchen Link kennt, kann die zugehoerige Datei waehrend dieser Zeit abrufen. Ein Wechsel vonWEBUI_PASSWORDmacht alle bereits erzeugten Links ungueltig.