qBittorrent Shim and Automation - Kheopsian/Hydranos GitHub Wiki
Applies to Hydranos 4.4.
For anyone connecting Sonarr, Radarr, Lidarr, autobrr, cross-seed or a script to Hydranos as if it were qBittorrent: what each call really does, what is constant or ignored, and the settings that work per application (Application recipes below). Moving an existing qBittorrent setup? Follow Migrating from qBittorrent first.
The qBittorrent shim is the set of routes under /api/v2/* on the same port as the web UI, port 8199 by default ([daemon] api_port). It imitates the qBittorrent WebUI API closely enough for the common automation tools:
-
GET /api/v2/app/versionanswersv4.6.0(plain text). -
GET /api/v2/app/webapiVersionanswers2.9.3.
Both are fixed on purpose: tools pick their calls from them. Adds follow the normal placement rule (category → engine and save path, see Categories and Routing). The shim lists every engine (extra engines included), each torrent once, but adds only into race or hoard (by category): it has no field to name an engine.
While Hydranos is still loading its torrents at startup, every /api/v2/* route, login included, answers 503 with a JSON body and Retry-After: 5; clients retry.
POST /api/v2/auth/login takes a form body (application/x-www-form-urlencoded) with username and password. Two kinds of credentials are accepted:
| Username | Password | Accepted when |
|---|---|---|
the admin account name ([auth] username, default admin) |
the admin password | the bcrypt hash in [auth] password_hash matches |
| anything | the API key ([daemon] api_key) |
the key matches exactly |
The answer is always HTTP 200: Ok. with a Set-Cookie: SID=… header, or Fails. (same answer for any wrong credential). A JSON body is not read: send a form.
The SID session expires after 1 hour without a request (each authenticated request renews it), at most 256 exist at once (the oldest is dropped), and all are lost when Hydranos restarts, so clients log in again. The cookie is Path=/; HttpOnly; SameSite=Lax, without Secure, so it works over plain HTTP. It also opens the whole native REST API: treat it like the API key.
Every other shim route requires authentication; a script can instead send the API key as an X-Api-Key header or an apikey query parameter. An unauthenticated shim call gets 403 Forbidden. (plain text) like real qBittorrent (the native REST API answers 401). POST /api/v2/auth/logout ends the session and needs no credentials.
Account, API key and exposure rules are on Security and Access.
# Log in (admin account, or any username + YOUR_API_KEY as password), keep the cookie
curl -i -c jar.txt \
--data-urlencode 'username=admin' --data-urlencode 'password=YOUR_PASSWORD' \
http://hydranos.example.org:8199/api/v2/auth/login
# Or skip the login and send the key
curl -H 'X-Api-Key: YOUR_API_KEY' http://hydranos.example.org:8199/api/v2/app/versionfull = as qBittorrent; partial = works, differences listed; constant = fixed values; refused = answers 409 with the reason as plain text and changes nothing.
| Endpoint | Verbs | Fidelity | What Hydranos does |
|---|---|---|---|
auth/login |
POST | full | See Logging in. |
auth/logout |
POST | full | Revokes the session and expires the cookie. |
app/version |
any | constant |
v4.6.0. |
app/webapiVersion |
any | constant |
2.9.3. |
app/buildInfo |
any | constant | A real qBittorrent 4.6.0's library versions (qt 6.5.3, libtorrent 2.0.9.0…), none used by Hydranos. |
app/preferences |
any | partial | Real: dht, pex, listen_port, max_connec, max_uploads_per_torrent, queueing_enabled, max_active_downloads, the slow-torrent keys (dont_count_slow_torrents, slow_torrent_*) (all from [race]); max_active_uploads/max_active_torrents (from [race] when queueing is on, else 50/100); lsd; up_limit/dl_limit (client-wide caps, bytes/s, 0 = none); the share-limit keys (max_ratio_enabled, max_ratio, max_seeding_time*, max_inactive_seeding_time*, max_ratio_act) of the race engine; save_path (empty until a client sets it with setPreferences); web_ui_port (= api_port); create_subfolder_enabled (= [daemon] create_torrent_folder). Constants: encryption 1, locale en, *_enabled extras false. |
app/setPreferences |
POST | partial | Applies share limits, speed caps, dht/pex/lsd, active counts, the slow-torrent rule and save_path; other keys answer 200 and do nothing. Each of these is written (on every local engine) only when it differs from what app/preferences shows, so posting back the page unchanged changes nothing, [hoard] values included. See Limits. |
app/defaultSavePath |
any | full | The save_path set through setPreferences, empty otherwise. |
transfer/info |
any | partial |
dl_info_data/up_info_data = bytes moved this session; speeds = sum of all engines; dht_nodes real; *_rate_limit = the client-wide caps. connection_status always connected. |
transfer/uploadLimit, transfer/downloadLimit
|
any | full | The client-wide cap, bytes/s, 0 = none. |
transfer/setUploadLimit, transfer/setDownloadLimit
|
POST | full |
limit in bytes/s, 0 or negative = none; one cap over every engine of this host, kept across restarts. 400 without limit. |
sync/maindata |
any | partial | Always full_update: true (never a delta): torrents (the torrents/info rows keyed by hash), categories, tags, server_state; trackers is {}; rid grows for the whole process. |
torrents/info |
any | partial | See The torrent list as *arr sees it below. |
torrents/count |
any | full | How many rows the unfiltered torrents/info lists. |
torrents/properties |
any | partial | Real: transfer totals, share_ratio, seeding_time, eta, session totals, average speeds, peers (connected), peers_total/seeds_total (tracker), creation_date, created_by, comment (read from the stored .torrent, creation_date -1 if absent), up_limit/dl_limit, share-limit keys. seeds and total_wasted are 0. Full 40-character hash; 404 if unknown. |
torrents/files |
any | partial | Real: names (multi-file names start with the torrent's folder), sizes, piece_range, per-file progress, priority (1, 6 or 7, never 0), is_seed. availability is always 1. |
torrents/trackers |
any | partial |
** [DHT] **, ** [PeX] ** (status 2, counts 0), ** [LSD] ** (status 0 with This torrent is private or LSD is off for this engine, else 2 with the LAN peers found), then the real trackers with their tier. status is one value for the whole torrent: 1 not contacted yet, 2 working, 4 last announce failed (with msg); num_peers and num_downloaded 0. 404 for an unknown hash. |
torrents/export |
any | full | The stored .torrent (application/x-bittorrent); 404 if none. |
torrents/add |
POST | partial | See Adding torrents through the shim. |
torrents/delete |
POST | full |
hashes, deleteFiles=true|1. Removes every copy of the torrent, in every engine. Files shared with another loaded torrent are kept. Always 200. |
torrents/pause, torrents/stop
|
POST | full | Stop (persisted user intent) on every copy. pause and stop are the qBittorrent 4 and 5 names of the same call. |
torrents/resume, torrents/start
|
POST | full | Start every copy. |
torrents/recheck |
POST | full | Hash-checks every copy, guarded like Recheck in the right-click menu: if a file is also named by another torrent with other content, the copy is stopped and those files are hashed first; it is rechecked (and restarted if it was running) only if they verify, otherwise it stays stopped in error with the reason. Always 200; the log line qbit bulk recheck counts the copies checked and the ones refused (a copy removed in the meantime); a magnet still resolving has no copy yet and is skipped. |
torrents/reannounce |
POST | full | Asks the announce scheduler of every copy for an announce now. Always 200; the counts go to the log. |
torrents/setLocation |
POST | partial |
hashes, location: one move job per copy, as Set location… (Jobs tab). 400 without location; 409 with the reason when nothing could be queued. |
torrents/setShareLimits |
POST | partial |
ratioLimit and seedingTimeLimit (minutes) required (400 otherwise), inactiveSeedingTimeLimit optional; -2 = follow the engine, -1 = no limit. See Limits. |
torrents/setForceStart |
POST | partial |
value=true marks every copy forced (as Force download) and starts it: the queue runs it past any ceiling without counting it. value=false lifts the mark. Rows still report force_start: false. |
torrents/uploadLimit, torrents/downloadLimit
|
any | full |
{hash: bytes/s}, 0 = none. |
torrents/setUploadLimit, torrents/setDownloadLimit
|
POST | full |
hashes, limit (bytes/s, 0 or negative = none), on every copy. 400 without limit. |
torrents/filePrio |
POST | partial |
hash, id (|-separated), priority 1, 6 or 7: the order files are fetched in. Priority 0 is refused: every file is always downloaded. 400 for a missing or invalid priority, 409 for a file id out of range. |
torrents/toggleSequentialDownload, torrents/toggleFirstLastPiecePrio
|
POST | full | Flips the flag; every copy takes the new value of the first. |
torrents/rename |
POST | refused | The name is the .torrent's. 404 for an unknown hash. |
torrents/topPrio, bottomPrio, increasePrio, decreasePrio
|
POST | refused |
Torrent queueing is not enabled when no engine holding the torrents runs a queue; otherwise a reason saying Hydranos orders its queue itself and that force start runs a torrent past it. |
torrents/categories |
any | partial |
{name: {"name", "savePath"}}. The category's mode is not shown. |
torrents/createCategory |
POST | partial |
category, savePath. Always creates mode hoard; an existing name answers 409 Category already exists. |
torrents/editCategory |
POST | partial | Changes the save path only; creates a hoard category if the name is unknown. |
torrents/removeCategories |
POST | full |
categories, separated by newlines or commas. The torrents lose the label. |
torrents/setCategory |
POST | partial |
hashes, category (empty = clear). Label only: no file move, no engine change, no check that the category exists. |
torrents/tags |
any | full | The registered tags. |
torrents/createTags |
POST | full | Registers tags. |
torrents/deleteTags |
POST | full | Removes the tags from the list and from every torrent. |
torrents/addTags, torrents/removeTags
|
POST | full |
hashes, tags (comma-separated). Applied to every copy. |
torrents/addTrackers, torrents/removeTrackers
|
POST | partial | Form field hash (singular, full hash) and urls (newline- or |-separated). Each added URL becomes its own tier at the end, and the copies announce to it straight away. 400 if a field is empty or if the torrent was added seconds ago and its .torrent is not stored yet (retry shortly); 404 for an unknown torrent. |
rss/* (13 routes) |
POST; items, rules, matchingArticles any |
full | qBittorrent's RSS API: addFolder, addFeed, setFeedURL, removeItem, moveItem, refreshItem, markAsRead, items, setRule, renameRule, removeRule, rules, matchingArticles. 400 Missing parameter: X or a malformed value, 409 for a refusal. See RSS and Automatic Downloads. |
Notes:
-
hashesis split on|or,;allmeans every torrent of every engine. Write endpoints takinghashesaccept a hash prefix (the first stored torrent that matches wins) and answer an empty 200 like qBittorrent, even when no hash matched: check withtorrents/info, not the status code. Exceptions: the refused and 400 cases above,addTrackers/removeTrackers(hash, noall),createCategory(400 on an empty name, 409 on an existing one),torrents/add(Ok./Fails.). Calls taking a singlehash(properties,files,filePrio,rename) need the full hash. - Where parameters are read: the read calls (
torrents/info,properties,files,trackers,export) and the calls added in 4.4 (setLocation,setShareLimits,setForceStart, the limit calls,filePrio, the toggles,rss/*) take them from the query string or a form body (cross-seed sends everything as a POST form). The older write calls (stop, start, delete, recheck, reannounce, tags, categories, trackers) read the form body only. - Actions by hash reach every copy, extra engines included. To target one copy, use
?engine=on the REST API.
These routes are not registered. They answer HTTP 404 (a wrong verb on an existing route answers 405):
torrents/ setSuperSeeding, setAutoManagement, renameFile, renameFolder, editTracker, pieceStates, pieceHashes, webseeds; app/shutdown; transfer/ banPeers, toggleSpeedLimitsMode, speedLimitsMode; sync/torrentPeers; log/*, search/*.
The shim still reports WebAPI 2.9.3, so a tool may try them. For search, keep Prowlarr or autobrr.
torrents/info is the call Sonarr, Radarr, autobrr and cross-seed poll. It accepts, from the query string or a form body:
| Parameter | Behaviour |
|---|---|
category |
Exact match on the category a row is reported under (see category below). |
filter |
all, downloading, seeding, completed, paused = stopped, active, inactive, stalled, stalled_uploading, stalled_downloading, errored, resumed = running. An unknown value lists everything. |
tag |
One tag, exact match. |
hashes |
Full hashes separated by | or ,. all means no filter. |
sort, reverse
|
Any row field; default added_on. reverse=true inverts. |
limit, offset
|
Paging; 0 or absent = no limit. |
Every engine is listed, and each torrent once: the first engine holding it answers, in the order race, hoard, then the extra engines. A torrent held by both race and hoard is therefore reported as its race copy (race's save_path and content_path, and race as the fallback category). torrents/count and sync/maindata list the same rows.
| Field | Value |
|---|---|
hash, name, size/total_size, progress, dlspeed, upspeed, added_on, completion_on, tags
|
Real. |
num_seeds/num_complete, num_incomplete
|
The tracker's seeders and leechers (last announce or scrape). |
num_leechs |
Peers connected now. |
category |
The torrent's category; an uncategorised torrent is reported under its engine name (race, hoard or the extra engine's id). |
save_path |
The folder the engine holds the data in. |
content_path |
save_path + / + name. This is the path *arr imports from and cross-seed links from. |
state |
See the next table. |
uploaded, downloaded, ratio
|
The torrent's lifetime counters; ratio = uploaded / downloaded, two decimals. A complete torrent whose download counter is 0 (added in seed mode) reports downloaded = size. |
amount_left, completed
|
Complete: 0 and the size. Incomplete: size minus the bytes downloaded, and the bytes downloaded. |
eta |
Bytes left divided by the current download rate; 8640000 (qBittorrent's "infinite") when nothing is received. |
seeding_time |
Seconds of seeding. |
tracker |
The first URL of the torrent's tracker list (the list it announces to); empty for a torrent without trackers. |
up_limit, dl_limit
|
The torrent's own speed caps, bytes/s, -1 = none. |
seq_dl, f_l_piece_prio
|
Sequential download and first/last piece first, as set. |
ratio_limit, seeding_time_limit, inactive_seeding_time_limit
|
The torrent's own share limits (-2 = follow the engine). |
max_ratio, max_seeding_time, max_inactive_seeding_time
|
The limits in force for it (-1 = none). |
time_active |
Now minus added_on. |
availability, seen_complete, priority, auto_tmm, super_seeding, force_start, magnet_uri
|
0, false or empty. |
Absent: infohash_v1, private, trackers_count, last_activity.
Since 4.4 uploaded and ratio are the real figures (4.3 kept them at 0). A seed goal set in Sonarr, Radarr, Lidarr or an autobrr rule now acts on them, and can remove torrents: see Application recipes.
| Hydranos situation | state |
|---|---|
| You stopped it (persisted, survives restarts) |
stoppedUP (complete) / stoppedDL
|
Held by the engine without a stop from you (for example waiting for a download slot, or a seeding slot under queueing); moves on by itself |
queuedUP / queuedDL
|
| Seeding, uploading now / not uploading now |
uploading / stalledUP
|
| Downloading, receiving now / not receiving |
downloading / stalledDL
|
| Hash check running |
checkingDL (also for complete torrents) |
| Error | error |
| Anything else |
stalledUP / stalledDL
|
Never returned: pausedUP/pausedDL, forcedUP/forcedDL, metaDL, missingFiles, checkingUP, checkingResumeData, moving, allocating (a torrent being moved keeps its state). A magnet still waiting for metadata is not listed at all. The paused/stopped filter matches stopped* rows only.
POST /api/v2/torrents/add is a multipart form:
| Field | Read? | Meaning |
|---|---|---|
torrents, file, fileselect
|
yes |
.torrent file parts; several allowed. |
urls |
yes | One per line. magnet:? links are resolved in the background; http(s) links are downloaded (30 s timeout, 15 MiB max, no cookies). |
category |
yes | Picks the engine (category mode) and the save path. |
savepath |
yes | Overrides the category's save path. |
autoTMM |
yes |
true: when the category has a save path, savepath is ignored. |
tags |
yes | Comma-separated. |
paused, stopped
|
yes |
true or 1: add stopped. |
stopCondition |
yes |
MetadataReceived or FilesChecked: add stopped. |
skip_checking |
yes |
true or 1: seed mode, no hash check of data already on disk. Magnets too (applied when the metadata arrives). |
upLimit, dlLimit
|
yes | The torrent's speed caps, bytes/s; 0 or negative = none. |
ratioLimit, seedingTimeLimit, inactiveSeedingTimeLimit
|
yes | The torrent's own share limits, as setShareLimits (minutes; -2 follow the engine, -1 none). See Limits. |
contentLayout |
yes |
Original, Subfolder (a single file gets a folder) or NoSubfolder; wins over root_folder. NoSubfolder on a multi-file torrent is refused: the engine always writes it under its folder. |
root_folder |
yes | Older form: true = Subfolder, false = NoSubfolder. With neither field, [daemon] create_torrent_folder decides. |
sequentialDownload, firstLastPiecePrio
|
yes | Download order (Adding Torrents and Existing Data). |
| any other field | no | Ignored (callout below). |
Accepted but ignored in 4.4: the add fields
renameandcookie(and any other unknown field) look like per-torrent options — Hydranos ignores them and logsqbit add: these fields have no effect in Hydranos— the torrent keeps the name from its.torrent; a.torrentURL needing a cookie must be fetched by the tool and sent as a file.
What decides the answer:
-
Placement. A known category gives the engine (
hoardmode →hoard, anything else →race) and its save path; an unknown category goes torace. With no folder fromsavepathor the category, the folder a client set withsetPreferencessave_pathis used; if there is none either, the add is refused (log:no save path: category "X" is unknown and no savepath was given). The shim cannot name an engine. - Already added (same torrent already in that engine) counts as success.
-
Answer:
Ok.(200) if at least one item was accepted;Fails.(HTTP 400) if every item was refused;Bad request(400) if there was neither a file nor a URL. Exception: anhttp(s).torrentURL that downloads but whose add is then refused (for example no save path) still getsOk.; checktorrents/infoor the log. -
Magnets:
Ok.means resolution has started. The torrent enters the engine, andtorrents/info, only when its metadata arrives; until then it sits in the Magnets card of the + Add Torrent tab (retried a few times, then kept there with the reason). The add options (caps, share limits, download order, layout,skip_checking) are kept with the request and applied once it resolves; aNoSubfoldermagnet that turns out multi-file fails there with that reason. - Data already on disk is hash-checked unless
skip_checkingis set. "On add" workflows run (for magnets, once resolved). The request body is limited to 2 MB.
Magnets, seed mode, .torrent URLs and dedup are explained on Adding Torrents and Existing Data.
H=http://hydranos.example.org:8199
# A .torrent file into the category "tv"
curl -b jar.txt -F '[email protected]' -F 'category=tv' $H/api/v2/torrents/add
# A magnet (use --form-string so curl does not interpret ; or @)
curl -b jar.txt \
--form-string 'urls=magnet:?xt=urn:btih:0123456789abcdef0123456789abcdef01234567&tr=https://tracker.example.org/announce' \
-F 'category=tv' $H/api/v2/torrents/add
# Seed mode on data already in place, explicit folder
curl -b jar.txt -F '[email protected]' -F 'savepath=/data/movies' -F 'skip_checking=true' $H/api/v2/torrents/add
# List a category, remove a torrent with its files
curl -b jar.txt "$H/api/v2/torrents/info?category=tv&filter=downloading"
curl -b jar.txt -d 'hashes=0123456789abcdef0123456789abcdef01234567&deleteFiles=true' $H/api/v2/torrents/deleteThis is what Hydranos receives from autobrr when it sends a magnet:
sequenceDiagram
participant AB as autobrr
participant SH as Hydranos qBittorrent shim
participant MR as Magnet resolver BEP 9
participant EN as Engine race or hoard
AB->>SH: POST auth/login (username, password)
SH-->>AB: 200 Ok. and SID cookie
AB->>SH: POST torrents/add (urls = magnet link, category)
alt no savepath, no category save path, no preferences save_path
SH-->>AB: 400 Fails.
else placement found
SH->>MR: store the magnet request and start resolving
SH-->>AB: 200 Ok.
MR->>MR: fetch metadata from peers and check it against the info hash
MR->>EN: add the torrent at its save path (savepath, category or setPreferences), started unless added stopped
end
AB->>SH: POST torrents/reannounce (hashes)
Note over SH,EN: a magnet still resolving has no copy yet, nothing is announced
SH->>EN: announce now, every copy
SH-->>AB: 200 (empty body)
Create your categories in Hydranos (tab Categories, + New: Name, Mode Race/Hoard, Save Path) before you point a tool at them, and use the same names in the tool. The shim's category calls cannot set a mode:
-
createCategoryalways stores modehoard. On a name that already exists it answers 409Category already existsand leaves the category alone (mode, Graduate to and transit settings kept). - A category created by a tool with an empty
savePathis stored without a save path, and every later add into it is refused unless the tool sendssavepath(or has set a default folder withsetPreferencessave_path). -
setCategorychanges the label only; data and engine stay put. To move the data, use Change category + move files › in the web UI (see Categories and Routing).
Tags are labels with no effect on placement (Categories and Routing). deleteTags removes a tag from the list and from every torrent; removeCategories also clears that label from the torrents.
Each application uses its qBittorrent download-client type; unlisted settings stay at their defaults. Below, hydranos stands for the host, which depends on where Hydranos runs:
- same Docker network as the application: the service name,
hydranos; - behind a gluetun container (
network_mode: "service:gluetun"):gluetun, because Hydranos has no network of its own and the namehydranosresolves to nothing (VPN and Egress); - different networks, or not sure (for example Unraid's default bridge): the host machine's LAN address, e.g.
192.0.2.10, with port 8199 published.
Paths. The shim reports save_path and content_path as Hydranos sees them. Mount the same host folder at the same container path in both containers (Docker), or add a remote path mapping in the application (host = the host above, remote = Hydranos path, local = its own path).
Mode per category. If unsure, file your categories as Hoard. Race is for racing fresh uploads, and the race drain can delete race torrents with their data or move them (Race Drain and Graduation).
Settings › Download Clients › + › qBittorrent:
| Setting | Value |
|---|---|
| Host | hydranos |
| Port | 8199 |
| Use SSL | off (on only behind a TLS reverse proxy, with its port) |
| URL Base | empty |
| Username / Password | the admin account, or any username + the API key as password |
| Category | an existing Hydranos category, e.g. tv (Sonarr), movies (Radarr), music (Lidarr) |
| Post-Import Category | empty, or an existing category: it only relabels the torrent (no move) |
| Recent / Older Priority | Leave at the default, Last: the queue-position calls answer 409 (as qBittorrent with queueing off) |
| Initial State | Start or Stop. Force Start starts the torrent and runs it past the engine's queue (active_downloads, 10 in hoard as shipped) |
| Sequential Order, First and Last First | Applied (download order) |
| Content Layout | Applied; No subfolder is refused for a multi-file torrent, keep Original
|
Seed ratio and seed time (indexer settings) now work both ways:
- they arrive with the add as
ratioLimit/seedingTimeLimitand become the torrent's own share limits in Hydranos; - Sonarr/Radarr read the real
ratioandseeding_time, so their own seed-goal check can now be met (and remove the torrent if removal of completed downloads is enabled in the application; not tested by the maintainer).
Hydranos's own share limit, however, is held (log share limit reached, held) until the tracker's declared minimum seeding time is served, and forever for a tracker with no announce_min_seed_hours declared (a torrent with no tracker at all is not held); when it acts, it applies the engine's share_limit_action (stop by default), not a per-torrent action. See Queue, Share and Speed Limits. A workflow remains the other way to clean up after seeding.
If Test fails, check the host, port and credentials. Create the category in Hydranos first: Sonarr and Radarr usually call createCategory only for a category missing from torrents/categories, so an existing one is left alone, but one they create has no save path and every add into it is refused (not tested by the maintainer).
Settings › Clients › Add new: type qBittorrent, host http://hydranos:8199, username and password as above (or the API key as password). In a filter's action, choose this client and set:
| Action setting | Effect in Hydranos |
|---|---|
| Category | Must exist in Hydranos with a save path, or set Save Path. |
| Save Path | Sent as savepath; overrides the category's folder. |
| Tags | Applied. |
| Add paused (label may vary by version) | Applied (added stopped). |
| Skip hash check | Applied, magnets included (once resolved). |
| Reannounce (interval, attempts) | Works (torrents/reannounce). |
| Upload/download limits | Applied (the torrent's own caps). |
| Ratio limit, seed time limit | Stored as the torrent's share limits; same tracker-minimum hold as above. |
| Content layout | Applied; NoSubfolder refused for a multi-file torrent. |
autobrr sends magnets through urls (supported). Its client rules on speed, ratio or uploaded amount see the real values (transfer/info, torrents/info).
Point cross-seed at the shim with a URL that carries the credentials, then use injection:
// cross-seed config.js (v6 syntax; v5 uses qbittorrentUrl: "http://…" instead)
torrentClients: ["qbittorrent:http://admin:YOUR_PASSWORD@hydranos:8199"],
// or, with the API key: "qbittorrent:http://cross-seed:YOUR_API_KEY@hydranos:8199"
action: "inject",
skipRecheck: true,- cross-seed injects with an explicit
savepathand, for full matches,skip_checking=true: Hydranos adds the torrent in seed mode at that folder. Partial matches are sent for a recheck (not tested by the maintainer). -
The category cross-seed sends decides the engine. A category unknown to Hydranos still works because cross-seed sends a save path, but the torrent then lands in the race engine, where the drain can remove it. Create the categories cross-seed uses in Hydranos with mode
hoardand a save path; withduplicateCategoriesit sends<category>.cross-seed, so create those too. - Every engine is listed, so cross-seed also matches torrents held by extra engines (it injects into
raceorhoard, by category). A torrent held by race and hoard is seen once, as its race copy. - cross-seed sends its calls as POST forms:
torrents/files,properties,trackers,exportandinforead thehashfrom the body.torrents/filesgives names under the torrent's folder and real piece ranges, so its matching sees the true layout.
No direct role: Prowlarr feeds indexers to Sonarr, Radarr and Lidarr. To grab from its search page, add Hydranos as a download client with the Sonarr settings above.
-
Share limits (
setShareLimits,ratioLimit/seedingTimeLimitat add,setPreferencesmax_ratio*): stored and shown, but they act only once the tracker's declared minimum seeding time (announce_min_seed_hours) is served, and never for a tracker without one (a torrent with no tracker at all is not held); the action is the engine'sshare_limit_action. Details: Queue, Share and Speed Limits. -
app/setPreferencesneeds ajsonfield or a JSON body (400 without; a relativesave_pathis 400 before anything is written). Applied, to every local engine:max_ratio*,max_seeding_time*,max_inactive_seeding_time*(each value only with its*_enabled),max_ratio_act(2, super seeding, is logged and leaves the action unchanged),up_limit/dl_limit(live),max_active_downloads(live),max_active_uploads/max_active_torrents(only ifqueueingis already on),dont_count_slow_torrentsand theslow_torrent_*keys,save_path, anddht,pex,lsd(written to the file, used at the next start).queueingitself cannot be switched through the shim.
Accepted but ignored in 4.4: other
setPreferenceskeys (listen port, encryption, alternative speed limits…) look saved — they answer 200 and change nothing, logged assetPreferences: these keys have no effect in Hydranos— use the Config tab ordefault.toml.
Not implemented in 4.4: selective download —
filePrio0 (do not download) is refused with 409 and every file is always downloaded — priorities 1/6/7 only change the order. Also missing: a delta fromsync/maindata(always a full update), per-tracker status and peer counts intorrents/trackers,seeds/total_wastedinproperties,availabilityanywhere.
Other missing qBittorrent features: Migrating from qBittorrent.
Migrating from qBittorrent (step-by-step switch, Sonarr add sequence) · Categories and Routing · Adding Torrents and Existing Data · Security and Access · Trackers and Announces · Queue, Share and Speed Limits · RSS and Automatic Downloads · REST API