Add configurable seed ratio and seed time goals to request indexers, supporting both static goals and tracker-advertised ratio/seed-time limits via torznab attributes. Enforce indexer-specific seed policy during request fulfillment and display active seed targets in the request seed panel. Closes #1360
12 KiB
Book Requests
Book requests let readers ask for a book BookOrbit does not have. An approver decides on the request, a release is picked from the sources you configured, a download client fetches it, and the result is filed into a library.
Everything in this guide is operator configuration. Nothing here is on by default, and a fresh install has no sources, no download clients and no automation.
What BookOrbit ships
BookOrbit ships no sources. There is no bundled tracker, no default indexer, and no list of
sites to pick from. Two protocol adapters are compiled in: torznab for torrent indexers and
newznab for Usenet indexers. Both need an address and API key you supply. Everything else is an
indexer plugin you install yourself.
Which sources you point BookOrbit at, and what you search for and download through them, is your decision and your responsibility. BookOrbit does not provide or endorse indexer sources. Its URL, file, and path safety checks still apply to every configured source.
Permissions
| Permission | What it allows |
|---|---|
book_request_access |
File a request, see your own, join somebody else's |
manage_book_requests |
Approve, reject, and drive fulfilment on every request |
book_request_auto_approve |
Your own requests skip the approval queue |
book_request_self_fulfill |
Pick the release and download it yourself, with no approver |
The last three each imply book_request_access; granting one on its own would produce an account
that can approve requests but has no page to see them on.
Configuring sources, download clients and automation is separate again, and needs
manage_app_settings. Installing an indexer plugin needs a superuser: a plugin runs inside the
server process with the server's reach.
Before anything can be saved: the encryption key
Set BOOK_REQUEST_ENCRYPTION_KEY before configuring a download client or an indexer:
openssl rand -hex 32
It must be a 64-character hex string. Unlike MIGRATION_ENCRYPTION_KEY and the email key, this one
is not optional in practice: a tracker API key or seedbox password is refused rather than stored
in the clear when the key is missing, so the settings form will not save.
Rotating or removing the key leaves stored credentials unreadable. They are not silently dropped: the affected source refuses to be used and says why, so you can re-enter the credential rather than discover the loss during a download.
Sources
Settings > System > Requests > Sources.
Three kinds of row, added two ways, identical once they exist:
- Torznab: an address plus an API key. This is how you attach a Prowlarr or Jackett instance, and one such instance can expose many trackers behind one row per tracker.
- Newznab: an address plus an API key. BookOrbit uses generic searches, including an ISBN as the query when enabled, and fetches the selected NZB without exposing the key to the download client.
- Plugins: a single
.mjsfile installed through the page, loaded at boot from<APP_DATA_PATH>/plugins/indexers/<name>/index.mjs. Plugins live in their own repositories and are not distributed with BookOrbit.
A plugin may declare a signed update manifest. BookOrbit checks each declared channel at most once every 12 hours, supports a review-before-install flow, and can install verified updates automatically when a superuser enables that option for the plugin. The downloaded source must match both the manifest's SHA-256 and its Ed25519 signature. Installs are atomic, activate without a restart, and restore the previous plugin if activation fails.
Per source you can set the medium it is searched for, its categories, whether it is handed an ISBN when the request has one, and a colour that marks its releases in the release picker.
Private network addresses are refused unless you opt the row in with Allow private address. A self-hosted Prowlarr on the LAN is the common reason to; a public tracker resolving to a private address is not.
Two health facts are shown per row, and they answer different questions:
- Connected / Not reachable is the last time you pressed Test, which makes a capabilities call.
- Searches failing is how the last real searches went. A source can be reachable and still refuse every search, and the badge counts consecutive failures so one blip reads differently from a source that has been broken for a week.
Per-indexer seed goals
Torrent sources can set a manual seed ratio and total seed time. Each manual field is independent. Leaving one blank means inheritance; zero and negative values are not special modes. Apply tracker seed goals controls only whether a valid goal reported by Torznab or a torrent plugin fills a blank manual field. Turning it off does not disable manual values and does not request unlimited seeding.
For ratio, BookOrbit uses the manual source value, then the tracker value when tracker fallback is enabled, then omits the value so the download client uses its own default. Seed time follows the same order: manual source value, tracker value when tracker fallback is enabled, then the download client's default.
The policy is read when a new grab attempt starts, so a saved edit applies even when BookOrbit is reusing recently inspected torrent bytes. It applies only to releases selected from that configured source. A pasted magnet or uploaded torrent file has no indexer policy. Changes do not reconfigure torrents already in a client.
Download clients
Settings > System > Requests > Download clients. qBittorrent, Transmission and Deluge accept torrents; NZBGet accepts NZBs. A Newznab source therefore needs an enabled NZBGet client, while a Torznab source needs an enabled torrent client. Direct HTTP sources need no external client.
BookOrbit does not connect to an NNTP article server. Configure the article-server account in NZBGet, then give BookOrbit a separate NZBGet API account. BookOrbit fetches the credentialed NZB, sends its bytes to NZBGet, and monitors the item by a stable ownership key rather than a transient NZBGet queue ID.
Each client needs at least one path mapping, including when BookOrbit and the client run on the
same host. A mapping translates the download directory the client reports into a path BookOrbit can
open, and it is also the root the import is allowed to read out of. On a single host that mapping
is an identity, /downloads -> /downloads; it still has to be stated, because "the same path" and
"no path stated" are different things and only one of them is safe to import from.
Hardlinking from the download directory into the library avoids storing the imported bytes twice and keeps a torrent seeding after the book is filed. The mapping form can test whether a hardlink actually works between the two paths, which is worth doing before the first grab: a mapping across filesystems copies instead, silently doubling the space every book takes.
The same mount is necessary but is not sufficient. On Linux, fs.protected_hardlinks=1 normally
requires the BookOrbit process user to own the completed source file or be able to write it. The
download client and BookOrbit commonly run with the same UID, or with a shared group that grants
BookOrbit write access to completed files. The mapping test creates its own BookOrbit-owned source
file, so it proves that the paths and mount permit hardlinks but cannot prove that a completed file
owned by a different user will be linkable. If the real file cannot be linked, BookOrbit falls back
to copying it and the book uses space twice while the original keeps seeding.
BookOrbit never stops a seed on its own. Removing a client-managed download is an explicit action with its own confirmation, and removing one that is still working also fails the request.
qBittorrent accepts a per-torrent ratio and total seeding-time limit through fields documented since Web API 2.8.1. When both exist, its own stop rules can act when either limit is reached; the pair is not an AND condition and does not guarantee both tracker minimums. Deluge accepts the ratio with stop-at-ratio enabled and removal disabled. Transmission accepts the ratio in a best-effort call after adding the torrent; its idle-time option is not used as total seed time. A failure in that Transmission policy update leaves the tracked torrent in place and is logged as a warning.
Client-wide ratio, time, idle, and completion settings still apply. qBittorrent and Deluge can adopt a duplicate torrent without replacing its existing limits; Transmission attempts to apply an explicit ratio to an adopted torrent. BookOrbit does not delete and re-add an adopted torrent, reconcile limits across clients, or run a background seed stopper.
Automation
Settings > System > Requests > Automation. Everything here is off or unset by default.
- Auto-grab picks a release without an approver, but only above a minimum score. Below that the request waits, and the approver still sees the full ranked list.
- Auto-retry falls back to the next-best release when an automatic grab fails, bounded by a maximum number of attempts per request.
- Auto-search re-searches an approved request nothing was found for. Without it a request is searched exactly once, and a book whose first release is posted next month waits forever. The interval doubles for each week a request has been waiting, up to a cap, and stops entirely after a configurable age.
- Import checking scores what landed against what was asked for. Below the threshold the file waits in the Book Dock and the request reads "needs review", where a person either files it anyway or discards it. Switching this off files whatever was downloaded straight into the target library.
- Import formats decides what to keep when one release carries the same book several times over: every format, or only the target library's preferred one. A multipart audiobook is never affected, since its parts are one book rather than competing editions.
- Default destinations are per medium, and are the lowest rung of the ladder: a requester's own pinned destination and an explicit choice at request or approval time both win over them. Without one, a request that names no library can never be approved.
- Release profiles are an ordered tier list per medium describing the edition you want. Empty is the ordinary state and disengages the tier axis entirely, leaving auto-grab decided by score.
The lifecycle
pending -> approved -> searching -> grabbed -> downloading -> importing -> available
- pending: waiting on an approver. Auto-approval and self-serve both skip it.
- needs_review: the import scored below the threshold and is sitting in the Book Dock. Two ways out: file it anyway, or discard it, which removes the dock entry and fails the request.
- failed: nothing could be downloaded, or a person discarded the import. Still grabbable, so a different release can be tried.
- rejected / cancelled: settled by a person. A settled work can be requested again.
One live request per work. A second person asking for the same book is attached to the existing request as a subscriber rather than opening a second one, and can leave it again from the request's own menu.
Troubleshooting
"No sources are enabled" on the requests page: either nothing is configured, or every row is switched off. The notice says which.
Requests sit at approved and nothing happens: auto-grab is off, which is the default. Either
an approver opens the release picker, or auto-grab is switched on with a minimum score.
A grab is refused with a path error: the client has no path mapping, or the download directory resolved outside the one it has. Add or widen the mapping under Settings > System > Requests.
Credentials will not save: BOOK_REQUEST_ENCRYPTION_KEY is unset or is not 64 hex characters.
A source shows "Searches failing" but tests fine: the address answers a capabilities call and refuses searches. Usually a rate limit, an expired credential, or categories that do not exist on that indexer.