Skip to content

feat(serve): per-torrent seed time over the API - #186

Open
ralyodio wants to merge 1 commit into
baairon:mainfrom
ralyodio:feat/per-torrent-seed-time
Open

feat(serve): per-torrent seed time over the API#186
ralyodio wants to merge 1 commit into
baairon:mainfrom
ralyodio:feat/per-torrent-seed-time

Conversation

@ralyodio

@ralyodio ralyodio commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Why

--seed-time is one policy for the whole daemon. A seedbox that drops seeds after a couple of hours has no way to keep one release alive for a month short of restarting the daemon with a different flag or running a second one. The headless API grew per-torrent buttons in #105; this gives it a per-torrent seed limit to match.

What

  • POST /add takes an optional seedTime in the --seed-time grammar ("30d", "2h", a bare number is seconds; 0 = never stop this one). It belongs to that torrent alone and wins over the daemon-wide flag. An unusable value is a 400 and nothing is added.
  • New seed-time control action sets or clears it later (seedTime: "" clears), including on a download still in flight.
  • The value rides on the queue item into its history entry, so it survives restarts the same way completedAt does.
  • The reaper now runs in serve and watch even without --seed-time; with no daemon-wide limit it only acts on torrents carrying their own, and with neither it does nothing. seed (the one-shot local seeder) is unchanged.
  • GET /downloads reports seedTimeMs on downloads and seedTimeMs + seedUntil on seeds that have their own limit, so a caller can verify the setting took. Torrents inheriting the daemon default report nothing new, since that default is the caller's to know.

Watch mode is deliberately untouched: a .magnet file has nowhere to carry a limit, and the API is where a seedbox sets one anyway.

Tests

npm run typecheck clean, npm test 392 passing (373 before). New coverage: reaper precedence (own limit over daemon-wide in both directions, 0 = never, per-torrent with no daemon limit, nothing with no limit at all), extractSeedTime on every input shape, /add forwarding and rejecting, the seed-time control action end to end, the status payload, and setSeedTime on both a history entry and an in-flight download.

馃 Generated with Claude Code

https://claude.ai/code/session_01SKAohrRkqLKVQL2cGCAkR5

A seedbox rarely wants one seed policy for everything. --seed-time is
daemon-wide, so keeping a single release alive for a month on a box that
drops seeds after two hours meant restarting the daemon or running two.

POST /add now takes an optional seedTime (the --seed-time grammar: "30d",
"2h"; 0 = never stop) that belongs to that torrent alone, and a new
seed-time control action sets or clears it later, including on a download
still in flight. The value rides on the queue item into its history entry,
so it survives restarts the same way completedAt does.

The reaper prefers a torrent's own limit over the daemon-wide one and now
runs in serve and watch even without --seed-time, where it only acts on
torrents that carry a limit of their own. GET /downloads reports seedTimeMs
and seedUntil on anything that has one, so a caller can see the setting
took.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SKAohrRkqLKVQL2cGCAkR5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant