feat(daemon): grab pipeline for wanted movies #77
Merged
naps62-yolo
merged 3 commits from 2026-08-22 22:38:31 +01:00
issue/22-grab-pipeline into main
Labels
Clear labels
area/api
area/ci
area/compat
area/core
area/daemon
area/db
area/dl
area/indexer
area/infra
area/meta
area/parse
area/probe
area/subs
area/web
difficulty/easy
difficulty/hard
difficulty/moderate
difficulty/trivial
phase/1-skeleton
phase/2-logic
phase/3-sourcing
phase/4-movies
phase/5-ui
phase/6-tv
phase/7-people
phase/8-compat
phase/9-subtitles
type/bug
type/chore
type/feature
type/test
arr-api
CI and test harness
arr-compat
arr-core
arr-daemon
arr-db
arr-dl
arr-indexer
workspace and tooling
arr-meta
arr-parse
arr-probe
arr-subs crate: providers, extraction, translation, sync
web/
bounded, obvious approach, few files
subtle correctness or cross-cutting
multiple files, judgement, an interface to design
one file, mechanical, no design decisions
workspace, CI, config, database, empty API and SPA
parsing and policy engine, pure, no network
TMDB and Prowlarr, read-only
movies end to end
search, buckets, library views, queues
seasons, episodes, tracking, derived status
owner tags and notifications
Jellyseerr shim
build order: subtitles, DESIGN.md §15
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: yolo/arr#77
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #22.
A wanted movie with no file is now searched, scored and sent to Transmission,
and the
Grabis recorded. There is no grab delay (§5.4), and the label plusboth seeding limits are set at add time (§7.1, §7.3).
Restarting mid-flight does not grab twice: a title with a live grab is not a
gap, selection is a total order over the candidate set, and
torrent-addiskeyed on the infohash — so a crash between the add and the insert heals on the
next tick rather than leaving a second torrent. There is a test for exactly
that.
The policy-row-to-
arr-coremapping moved intoarr-db, where both themanual-search API and the daemon read it, replacing the copy in
arr-api.Out of scope, each with its own issue: search backoff and release-date gating
(#26), RSS matching (#27), import (#23), blacklist and hard/soft fail (#24),
per-tracker seeding rules and the reaper (#25). Seeding limits come from
config defaults until #25 gives them a home, and the daemon scores with
arr-corewhilearr-apistill uses its placeholder (#68).Verification
just cilocally: fmt, clippy-D warnings,cargo machete, 175 testspassing,
biome ciandtsc.New tests in
crates/arr-daemon/src/grab.rsrun the action against a wiremockProwlarr and a Transmission fake that dedupes on the infohash like the real
one:
WEB-DL beats the 60 GB remux while the CAM is filtered out
one torrent and one grab
seedRatioLimit,seedIdleLimitand download dir are on the adddownloaded@@ -0,0 +91,4 @@return Ok(outcomes);}let indexers = self.prowlarr.indexers().await?;The reconcile lane has a 25 s timeout, but
indexers()probes capabilities sequentially with a 30 s HTTP timeout. One slow tracker cancels the whole action before any targeted search starts. Bound or parallelize discovery, or cache it outside this tick.Fixed in
4502abb. Discovery is now bounded and cached:searchable_indexers()wrapsindexers()in an 8 s timeout and reuses the result for 15 minutes, so a slow tracker costs at most 8 s of the 25 s budget and only on a cache refresh. A refresh that times out or fails keeps the previous list and logs, instead of failing the tick; only an empty cache turns it into an error.Two tests cover it:
indexer_discovery_is_cached_across_ticksasserts one enumeration across two ticks, anda_stalled_discovery_gives_up_instead_of_hangingasserts the timeout wins against a stalled Prowlarr.Parallelising the caps probes inside
arr-indexerwould help too, but that belongs to that crate rather than this issue.Merged
origin/main(#71, #74, #75) rather than rebasing.crates/arr-api/src/search.rsconflicted with #74; resolved onto main's version, then re-applied the move of the policy-row mapping intoarr-db. #74'spolicy_row_uses_persisted_score_weightsmoved with it ascolumns_map_without_a_row.Two failures came in from main and are fixed here, unrelated to this issue:
search.rsusedMovieOverrides::default()in a test after #71 renamed the type toTitleOverrides— main does not compile--all-targets.roots_carry_their_policy_namestill expected 2 roots after #71 seeded the two TV roots.Reviewed
4502abbb04edcc3db8bf77b1fb00ccae13e9d5a0. No findings.Reviewed
acda6c5d5e82dbf29b64d5c7ded9c1ca34ca6843. No findings.