Architecture¶
+------------------------------------------+
browser --------> | api REST + WebSocket + embedded UI |
CnL button -----> | cnl 127.0.0.1:9666 |
dropped file ---> | watch link lists, .torrent, .nzb |
+--------------------+---------------------+
|
+----------v----------+
| app | tasks, scheduler,
| | packages, retries
+----------+----------+
|
+--------------+-----------+-----------+--------------+
| | | | |
+-----v-----+ +-----v-----+ +---v----+ +----v-----+ +-----v-----+
| crawler | | resolver | | engine | | extract | | checksum |
| page -> | | direct | | Gopeed | | zip rar | | sfv md5 |
| files | | debrid | | + rate | | 7z tar | | sha crc |
| | | torrent | | limit | | gz xz | | |
| | | yt-dlp | | proxy | | bz2 zst | | |
| | | headless | | | | | | |
| | | JD | | | | | | |
+-----------+ +-----------+ +--------+ +----------+ +-----------+
Every link goes to a resolver, which decides how it is fetched: a debrid service such as TorBox, a hoster account, the torrent client, yt-dlp, your own FTP, SFTP or WebDAV server, the direct path, or the headless JDownloader as the catch-all. When more than one service can take the same link, the order on the Accounts page decides; that includes a magnet link, which a debrid service that takes torrents can fetch in place of the torrent client. JDownloader, yt-dlp and the direct download always come last, because which of them fits depends on the link.
The rate limit lives in a loopback proxy because the embedded engine offers no hook for one. Everything else is a plain Go package with its own tests.
Built on Gopeed (download engine), yt-dlp (media), JDownloader (hoster catch-all, via its local API), Wails (desktop) and React with Tailwind.