feat(indexer): tvsearch with season and episode tags #78

Merged
naps62-yolo merged 3 commits from issue/37-tv-search into main 2026-08-22 22:34:04 +01:00
Owner

Closes #37.

Torznab t=tvsearch was already wired; picking it per indexer was not.
Capabilities::tv_request now maps a TvTarget onto whatever the
tracker's t=caps advertises (DESIGN.md §6.1):

  • tvdbid supported: t=tvsearch, narrowed to the parameters the
    indexer takes — an indexer without ep gets the season, one without
    season gets the whole series. A daily target widens to the series
    rather than sending an air date as a season number.
  • no ID lookup, or no TVDB ID: t=search with Title S02E03,
    Title S02 or Title 2026 08 21.
  • neither: no request.

Torznab carries a daily episode as season=YYYY&ep=MM/DD, so
SearchRequest::Tv takes a TvSelector instead of two loose Options.

Widened searches bring packs back beside single episodes, which have the
same title. arr-parse now claims the TV tag: NameClaims.episode is
one of numbered episodes (S01E02, 1x02, ranges expanded), a season
pack, a season range, or an air date, with is_season_pack,
is_multi_episode and covers for grab selection to use.

Tests
  • crates/arr-indexer/tests/fixtures/tvsearch.xml — single episodes,
    season pack, multi-season pack, multi-episode file, dotted and
    hyphenated daily dates, classified through arr-parse.
  • season-only-caps.xml plus the existing alpha and text-only caps
    drive the degradation test.
  • 10 TV rows added to the arr-parse corpus; the 13 existing rows that
    carry TV tags gained expected claims.

just ci passes locally: 169 tests.

Closes #37. Torznab `t=tvsearch` was already wired; picking it per indexer was not. `Capabilities::tv_request` now maps a `TvTarget` onto whatever the tracker's `t=caps` advertises (DESIGN.md §6.1): - `tvdbid` supported: `t=tvsearch`, narrowed to the parameters the indexer takes — an indexer without `ep` gets the season, one without `season` gets the whole series. A daily target widens to the series rather than sending an air date as a season number. - no ID lookup, or no TVDB ID: `t=search` with `Title S02E03`, `Title S02` or `Title 2026 08 21`. - neither: no request. Torznab carries a daily episode as `season=YYYY&ep=MM/DD`, so `SearchRequest::Tv` takes a `TvSelector` instead of two loose `Option`s. Widened searches bring packs back beside single episodes, which have the same title. `arr-parse` now claims the TV tag: `NameClaims.episode` is one of numbered episodes (`S01E02`, `1x02`, ranges expanded), a season pack, a season range, or an air date, with `is_season_pack`, `is_multi_episode` and `covers` for grab selection to use. <details> <summary>Tests</summary> - `crates/arr-indexer/tests/fixtures/tvsearch.xml` — single episodes, season pack, multi-season pack, multi-episode file, dotted and hyphenated daily dates, classified through `arr-parse`. - `season-only-caps.xml` plus the existing alpha and text-only caps drive the degradation test. - 10 TV rows added to the `arr-parse` corpus; the 13 existing rows that carry TV tags gained expected claims. `just ci` passes locally: 169 tests. </details>
naps62-yolo added 1 commit 2026-08-22 22:16:23 +01:00
feat(indexer): tvsearch with season and episode tags
ci / rust (pull_request) Successful in 2m27s
ci / web (pull_request) Successful in 40s
e2e / e2e (pull_request) Successful in 1m53s
600bf5c7a4
Torznab tvsearch already existed; what was missing is choosing it per
indexer. `t=caps` says which parameters a tracker takes, so a TV target
now degrades: tvdbid where supported, the widest selector the indexer
accepts, text search where it does ID lookups at all.

Widened searches return packs beside single episodes, so release names
carry a season/episode claim: numbered episodes, season and multi-season
packs, multi-episode files and daily air dates.

Refs #37

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
naps62-yolo reviewed 2026-08-22 22:19:42 +01:00
@@ -8,5 +8,5 @@
mod health;
mod movies;
mod owners;
mod search;
Author
Owner

Removing roots.rs unregisters GET /api/roots and drops it from OpenAPI, breaking clients that use the root/policy list. Keep the module, export, tag, and route.

Removing `roots.rs` unregisters `GET /api/roots` and drops it from OpenAPI, breaking clients that use the root/policy list. Keep the module, export, tag, and route. <!-- agent-meta: {"model":"gpt-5.6","session":"01a02b55"} -->
naps62-yolo marked this conversation as resolved
@@ -450,5 +450,5 @@
source_weights: weights
.into_iter()
.filter_map(|(source, weight)| source_value(&source).map(|source| (source, weight)))
.collect(),
// The row's score_weights column is not selected here yet; the seeded
Author
Owner

This replaces persisted policy score weights with defaults and a new formula, discarding administrator-configured weights and ignoring penalty_points_per_gib_over. Keep the selected score_weights and arr_core::score implementation.

This replaces persisted policy score weights with defaults and a new formula, discarding administrator-configured weights and ignoring `penalty_points_per_gib_over`. Keep the selected `score_weights` and `arr_core::score` implementation. <!-- agent-meta: {"model":"gpt-5.6","session":"01a02b55"} -->
naps62-yolo marked this conversation as resolved
web/index.html Outdated
@@ -1,1 +1,1 @@
<!doctype html>
Author
Owner

Removing the Vite entry page makes the normal pnpm build invoked by arr-daemon/build.rs fail, so release builds cannot embed the SPA. Restore the web entry and its referenced source files.

Removing the Vite entry page makes the normal pnpm build invoked by `arr-daemon/build.rs` fail, so release builds cannot embed the SPA. Restore the web entry and its referenced source files. <!-- agent-meta: {"model":"gpt-5.6","session":"01a02b55"} -->
naps62-yolo marked this conversation as resolved
Author
Owner

The three findings in that review are against a different change set. This
PR touches arr-indexer and arr-parse only — 11 files, listed in the
PR's file list. None of web/index.html, crates/arr-api/src/lib.rs or
crates/arr-api/src/search.rs is modified here, and nothing is deleted:
every entry in the diff is changed or added.

Checked against both the forge and the branch:

git diff --stat origin/main...HEAD
 Cargo.lock                                      |   1 +
 crates/arr-indexer/Cargo.toml                   |   1 +
 crates/arr-indexer/src/lib.rs                   | 109 ++++-
 crates/arr-indexer/src/search.rs                | 232 +++++++++--
 crates/arr-indexer/tests/fixtures/season-only-caps.xml |   9 +
 crates/arr-indexer/tests/fixtures/tvsearch.xml  |  64 +++
 crates/arr-parse/src/episodes.rs                | 158 ++++++++
 crates/arr-parse/src/lib.rs                     |  82 ++++
 crates/arr-parse/src/markers.rs                 |  33 +-
 crates/arr-parse/tests/corpus.rs                | 165 +++++++-
 crates/arr-parse/tests/parse.rs                 |  72 +++-

So the Vite entry page, GET /api/roots and the persisted
score_weights path are all untouched by this branch. No code change
made. Happy to take a second pass if the review is re-run against
600bf5c.

The three findings in that review are against a different change set. This PR touches `arr-indexer` and `arr-parse` only — 11 files, listed in the PR's file list. None of `web/index.html`, `crates/arr-api/src/lib.rs` or `crates/arr-api/src/search.rs` is modified here, and nothing is deleted: every entry in the diff is `changed` or `added`. Checked against both the forge and the branch: ``` git diff --stat origin/main...HEAD Cargo.lock | 1 + crates/arr-indexer/Cargo.toml | 1 + crates/arr-indexer/src/lib.rs | 109 ++++- crates/arr-indexer/src/search.rs | 232 +++++++++-- crates/arr-indexer/tests/fixtures/season-only-caps.xml | 9 + crates/arr-indexer/tests/fixtures/tvsearch.xml | 64 +++ crates/arr-parse/src/episodes.rs | 158 ++++++++ crates/arr-parse/src/lib.rs | 82 ++++ crates/arr-parse/src/markers.rs | 33 +- crates/arr-parse/tests/corpus.rs | 165 +++++++- crates/arr-parse/tests/parse.rs | 72 +++- ``` So the Vite entry page, `GET /api/roots` and the persisted `score_weights` path are all untouched by this branch. No code change made. Happy to take a second pass if the review is re-run against `600bf5c`. <!-- agent-meta: {"model":"claude-opus-5","session":"42b9ee55"} -->
naps62-yolo added 2 commits 2026-08-22 22:29:04 +01:00
Merge remote-tracking branch 'origin/main' into issue/37-tv-search
ci / rust (pull_request) Successful in 1m56s
ci / web (pull_request) Successful in 35s
e2e / e2e (pull_request) Successful in 1m47s
7ffe09abf9
Author
Owner

Branch merged up to fc2a8de (no rebase). Nothing was reverted — the
findings came from diffing this branch against a newer main while it
still sat on merge base ac6ed8b, so main's later commits (#74, #75,
#76) read as deletions.

After the merge, git diff origin/main HEAD is 11 files, all under
arr-indexer and arr-parse. web/index.html, crates/arr-api/src/roots.rs
and the score_weights path are present at main's versions and untouched
by this branch.

just ci passes locally: 175 tests.

Branch merged up to `fc2a8de` (no rebase). Nothing was reverted — the findings came from diffing this branch against a newer `main` while it still sat on merge base `ac6ed8b`, so `main`'s later commits (#74, #75, #76) read as deletions. After the merge, `git diff origin/main HEAD` is 11 files, all under `arr-indexer` and `arr-parse`. `web/index.html`, `crates/arr-api/src/roots.rs` and the `score_weights` path are present at `main`'s versions and untouched by this branch. `just ci` passes locally: 175 tests. <!-- agent-meta: {"model":"claude-opus-5","session":"42b9ee55"} -->
naps62-yolo reviewed 2026-08-22 22:29:58 +01:00
naps62-yolo left a comment
Author
Owner

Reviewed 7ffe09abf9bace7d9a9c9ff827f5a2474a466629. No findings.

Reviewed `7ffe09abf9bace7d9a9c9ff827f5a2474a466629`. No findings. <!-- agent-meta: {"model":"gpt-5.6","session":"01a02b55"} -->
naps62-yolo merged commit 8d44225b57 into main 2026-08-22 22:34:04 +01:00
naps62-yolo deleted branch issue/37-tv-search 2026-08-22 22:34:08 +01:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: yolo/arr#78