Na domácím serveru provozuji běžné věci z homelabu: zálohy, média a občas i „stáhnu si to a vyřeším později“. Dlouho jsem na to používal JDownloader2. Je to silný a osvědčený nástroj, který řeší spoustu reálných problémů.

Moje konkrétní nasazení ale bylo od začátku trochu zvláštní: JDownloader2 běžel v Dockeru na serveru a já ho ovládal přes VNC v prohlížeči. Fungovalo to, jenže z praktického pohledu to často zbytečně zpomalovalo práci. Sdílení schránky bylo dvoufázové (nejdřív do VNC klienta v prohlížeči, pak do vzdálené plochy a teprve potom do JD2). Měnění velikosti desktopového UI v okně prohlížeče nikdy nebylo stabilní. Na mobilu to bylo téměř nepoužitelné. A hlavně: neexistovala čistá cesta, jak přidávat odkazy z CLI, pokud jsem se nechtěl opírat o MyJDownloader (online služba + účet), což jsem pro server v racku nechtěl.

Postupně mi došlo, že nepotřebuji „download manager jako desktop aplikaci“. Potřebuji malou službu, která vystaví frontu.

Tak vzniklo DLQ (Download Queue): minimalistický headless Download Queue daemon + CLI, inspirovaný základní myšlenkou JDownloaderu, ale navržený pro Docker a terminál. A upřímně, byl v tom i další důvod: chtěl jsem se naučit Go a SvelteKit, takže jsem je zvolil pro backend/CLI a volitelné web UI.

Repo projektu: github.com/Witriol/dlq-download-queue

Stručně

  • DLQ je perzistentní fronta: přidávání odkazů, sledování průběhu, pause/resume, retry.
  • Běží headless, stav ukládá do SQLite a samotné stahování deleguje na aria2.
  • Ovládá se přes CLI (automatizace/SSH) nebo přes volitelné SvelteKit web UI.

Přehled

DLQ je daemon (dlqd), který ukládá joby do SQLite, resolvuje URL adresy a spouští stahování přes aria2 pomocí JSON-RPC. Nad tím je malý CLI klient (dlq) a volitelné web UI. Princip je jednoduchý: po restartu serveru fronta zůstane; když stahování selže, je vidět proč; a přidání odkazu je otázkou jednoho příkazu v terminálu.

Co to není

DLQ není „download manager na všechno na internetu“. Nesnaží se kopírovat plugin ekosystém JD2 ani celý desktopový workflow (link grabber, pravidla, hluboké parsování, captcha flow). Cíl je užší: aby cyklus „zařadit do fronty + stáhnout + pozorovat + retry“ působil přirozeně v headless prostředí.

Možnosti

DLQ v praxi převádí „odkaz“ na „soubory na disku“ a přitom si uchovává dost historie, aby se daly chyby a retry řešit bez dohadování.

Fronta je perzistentní (SQLite, typicky /state/dlq.db), joby umí pause/resume, retry a soft delete. Průběh je viditelný tak, jak by člověk čekal (rychlost, ETA, jasné stavy) a každý job má události, díky kterým jde zpětně zjistit, co se stalo.

Resolver je pluggable. Aktuálně je k dispozici jednoduchý HTTP/HTTPS passthrough, Webshare resolver, který se snaží běžet anonymně a vrací konkrétní důvody blokace, a MEGA resolver pro veřejné file odkazy (nejdřív vyřešit dočasné download URL, potom stahovat).

Po úspěšném stažení může DLQ volitelně spustit post-processing pro šifrované/archivované soubory (decrypt/extract). Když to selže (špatné heslo, chyba nástroje), job skončí ve zvláštním stavu a důvod je v logu událostí.

DLQ je rozdělené na malé části s jasnými hranicemi: dlqd (Go) je daemon a HTTP API (default :8099), dlq (Go) je tenký CLI klient nad tím API, aria2 je downloader ovládaný přes JSON-RPC a dlq-webui (volitelně, SvelteKit) je web UI, které server-side proxyuje volání na dlqd (default :8098).

Zjednodušeně to vypadá takto:

Prohlížeč -> dlq-webui (:8098) -> dlqd API (:8099) -> resolver -> fronta -> aria2 RPC -> /data
                                     |-> SQLite (/state/dlq.db)
                                     |-> události/logy
Terminal -> dlq CLI ---------------/

Proč aria2

Nechtěl jsem znovu vymýšlet „jak spolehlivě stahovat soubory“. aria2 umí ty těžké části dobře (resume, paralelismus, reporting) a má čisté JSON-RPC rozhraní. DLQ se soustředí na orchestraci: perzistentní stav fronty, retry, URL resolving a post-processing.

Volumes a „kam se smí zapisovat“

DLQ má běžet v kontejneru, takže výstupní cesty musí být omezené. Daemon validuje out_dir vůči namountovaným DATA_* cestám, aby joby zapisovaly jen tam, kam je úložiště explicitně připojené. Vedlejší efekt je, že web UI umí nabídnout smysluplné presety cílových složek právě z těchto mountů.

Workflow

Workflow je záměrně jednoduchý. Přidáte URL, job se vyřeší (host-specific logika), stahování běží v aria2 a daemon průběžně ukládá průběh a události. Když dojde k chybě, není potřeba hádat: stačí se podívat na log jobu a spustit retry.

Z CLI to vypadá například takto:

# Přidat job
docker exec -it dlq dlq add "<url>" --out /data/downloads

# Sledovat frontu
docker exec -it dlq dlq status --watch

# Prohlédnout chyby
docker exec -it dlq dlq logs 12 --tail 80

Web UI

Web UI není „středem systému“, je to komfortní vrstva. Běží jako samostatný kontejner, mluví se stejným API jako CLI a dělá server-side proxy, takže není potřeba řešit CORS. Pro mě je jeho hodnota jednoduchá: hromadné přidávání a rychlý přehled i na zařízeních, kde SSH není pohodlné.

Bezpečnost

DLQ je navržené pro důvěryhodné sítě (domácí LAN, Docker networking). HTTP API nemá autentizaci.

Prakticky to znamená: nevystavovat :8099 přímo do internetu; pokud je potřeba vzdálený přístup, dát službu za reverse proxy s autentizací; a nastavit ARIA2_SECRET, aby JSON-RPC k aria2 nebyly otevřené dveře.

Volba technologií

Část motivace bylo učení. Chtěl jsem něco dostatečně reálného, aby to mělo okrajové případy, ale zároveň dost malého, aby se to dalo dokončit.

Go mi dávalo smysl pro daemon a CLI: jedna statická binárka, rozumná práce s konkurencí pro zpracování fronty a ekosystém, ve kterém je „vydat malý server“ relativně přímočaré. Pro web UI jsem vybral SvelteKit, protože jsem se ho chtěl naučit a zároveň se hodí na tenký dashboard, který hlavně proxyuje API a zobrazuje aktuální stav.