Commit Graph
49 Commits
Author SHA1 Message Date
Miguel PalhasandClaude Opus 5 d8797e1996 feat(daemon): refresh metadata on add
The metadata lane runs daily, so a series added a moment ago showed no
seasons for up to 24 hours and a movie had no digital release date —
the field §6.2 gates targeted search on.

AppState now carries a MetadataCommand channel alongside the movie,
episode and season ones. Both create handlers send on it after the row
is committed, and a new daemon lane drains it. Its own task rather than
an arm of manual::run: a refresh against TMDB can take a while and must
not sit in front of an operator's manual search.

The add never waits on TMDB and never fails because of it. A refresh
that fails leaves metadata_refreshed_at NULL, which is what the daily
sweep already treats as due, so the title is retried rather than lost.
A command naming a title deleted in between finds no row and does
nothing. METADATA_INTERVAL is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 18:12:10 +01:00
Miguel Palhas 8f7373d73d fix(core): auto-track skips the seeding refresh
A series' first metadata refresh reveals its whole back catalogue, and
apply_auto_track flagged every season as new — adding Rick and Morty
tracked S01-S09 and wanted 91 episodes (#160).

Per DESIGN.md 4.1 the rule applies from the second refresh onward.
The caller passes whether metadata_refreshed_at is set; arr-core stays
IO-free and decides. Wired through the daemon's daily refresh and the
API's create-season endpoint.
2026-08-24 11:40:44 +01:00
Miguel Palhas 08e3c7bce7 fix(meta): store unrated titles as NULL, not zero
TMDB reports vote_average: 0 where no rating exists (#156). Normalise
it to None at the arr-meta edge, like non_empty does for "", and let
the Option flow through the daemon refresh and API add paths so the
nullable columns from #145 do their job.
2026-08-23 22:21:57 +01:00
Miguel Palhas 8478c0f8a9 feat(db): store poster, backdrop and rating on titles
§9.6 keeps rich detail out of the database except for the three fields
pure-SQL views need. Adds poster_path, backdrop_path and vote_average to
movies and series, written by the daily metadata refresh in both lanes
and filled at add time from the TMDB response the create flows already
fetch.
2026-08-23 22:12:05 +01:00
Miguel Palhas 23f5781714 feat(meta): substitute TBA for unnamed episodes 2026-08-23 20:55:16 +01:00
Miguel Palhas 00ff4db26a style: cargo fmt 2026-08-23 19:35:07 +01:00
Miguel Palhas 409967eef6 feat(daemon): reconcile vanished seasons (#137) 2026-08-23 19:34:55 +01:00
Miguel Palhas a378b22306 Merge #134: satisfied titles leave the attention queue
Closes #134
2026-08-23 19:09:15 +01:00
Miguel Palhas a28b455fff fix: gate hard-fail attention on unsatisfied titles 2026-08-23 19:07:29 +01:00
Miguel Palhas 9df120b92a feat(daemon): match RSS results to wanted episodes
One feed pass now serves films and episodes alike. A
single-episode release grabs its open episode directly; a season
pack only grabs when season_grab_mode allows packs for that
season (§14, #117), and when it wins, the singles stand down.
Blocked series keep matching RSS (§6.3) and nothing here touches
the targeted-search backoff (§6.2).
2026-08-23 19:07:00 +01:00
Miguel Palhas 127338fd4d Merge #132: drain manual episode and season commands
Closes #132
2026-08-23 18:27:43 +01:00
Miguel Palhas 8a77049fdb feat(daemon): drain all manual command channels 2026-08-23 18:24:50 +01:00
Miguel Palhas 39e6bce2a0 feat(daemon): manual episode/season triggers 2026-08-23 18:24:50 +01:00
Miguel Palhas 69263da4f3 daemon: populate season_number on refreshed episodes 2026-08-23 18:18:26 +01:00
Miguel Palhas 34ccbeeb5a feat(api): expose vanished flag on episodes 2026-08-23 18:01:41 +01:00
Miguel Palhas 8b9a6d5a16 feat(daemon): reconcile episodes that vanish upstream 2026-08-23 17:59:45 +01:00
Miguel Palhas 0c58aae707 feat(daemon): daily series metadata refresh lane 2026-08-23 17:45:09 +01:00
Miguel Palhas 8b8e1dcec9 Merge #126: TV titles in the needs-a-decision queue
Closes #126
2026-08-23 16:58:08 +01:00
Miguel Palhas e3d41c24e1 refactor(daemon): extract tv queue roll-up 2026-08-23 16:53:52 +01:00
Miguel Palhas 8ec1506098 refactor: split long functions in tv attention work 2026-08-23 16:51:29 +01:00
Miguel Palhas b801bfbcc3 style: cargo fmt 2026-08-23 16:44:32 +01:00
Miguel Palhas a1971ed82c feat(daemon): tv series in the needs-a-decision queue 2026-08-23 16:39:06 +01:00
Miguel Palhas a5706cf1b4 feat(tv): search indexers by series tvdb id 2026-08-23 16:32:28 +01:00
Miguel Palhas aa6961f56c fix(daemon): search available movies refreshes deck
MovieCommand::Search loaded the movie through the gap filter, so an
available movie fell out and the search was a silent no-op. Split the
lane: a gap still searches and grabs, anything already satisfied
refreshes the release deck (DESIGN.md 9.3) without grabbing, and a
blocked movie still refuses.

Closes #115
2026-08-23 12:22:56 +01:00
Miguel Palhas 54b3ea8a4d fix(daemon): park vanished grabs instead of re-grabbing
ci / web (push) Successful in 42s
ci / rust (push) Successful in 1m22s
e2e / e2e (push) Successful in 1m39s
Overrides #86: removing a torrent by hand in Transmission is human
intent, not a gap to refill. Clear `wanted` and mark the target
`parked` (a new movies/episodes state) rather than reopening it as
`missing`, so neither targeted search nor RSS matching pick it back
up. Blacklist stays untouched, since the release never failed policy.

Closes #108
2026-08-23 09:13:37 +01:00
Miguel Palhas 83ca921501 fix(daemon): consume the MovieCommand channel
ci / web (push) Successful in 36s
ci / rust (push) Successful in 1m23s
e2e / e2e (push) Successful in 1m17s
Nothing drained AppState's movie_commands mpsc, so manual search and
one-click grab were accepted with 202 and then did nothing until the
64-slot buffer filled and the endpoint started 503ing.

A new daemon task drains it: Search resets the movie's backoff and
runs the targeted-search + grab lane immediately; Grab sends the
already-chosen release straight to Transmission.

Closes #107
2026-08-23 09:02:42 +01:00
Miguel PalhasandClaude Opus 5 4afe44c812 fix(dl): resolve download links in arr (#100)
ci / web (push) Successful in 36s
e2e / e2e (push) Successful in 1m56s
ci / rust (push) Successful in 4m11s
Transmission has no route to Prowlarr, and Prowlarr redirects a
.torrent link to a magnet that a file fetch cannot follow. arr fetches
the link itself and sends Transmission a magnet or the torrent bytes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 08:00:11 +01:00
naps62-yolo 02d99ae558 feat: match RSS results by IMDb id (#99)
ci / web (push) Successful in 28s
e2e / e2e (push) Successful in 53s
ci / rust (push) Successful in 2m15s
2026-08-23 02:44:42 +01:00
naps62-yolo bc2da22ea5 fix: reopen the gap for a grab whose torrent vanished (#98)
ci / web (push) Successful in 55s
ci / rust (push) Failing after 3m50s
e2e / e2e (push) Failing after 1m32s
2026-08-23 02:31:50 +01:00
naps62-yolo 55ae44fa7b fix(db): scale TV size bands to episode size (#97)
ci / web (push) Successful in 57s
e2e / e2e (push) Successful in 2m31s
ci / rust (push) Successful in 5m9s
2026-08-23 02:31:44 +01:00
naps62-yolo ad0eb0d9d3 feat: ntfy notifications for imported, needs-decision, broken (#96)
ci / web (push) Successful in 1m0s
e2e / e2e (push) Successful in 3m41s
ci / rust (push) Successful in 5m42s
2026-08-23 02:04:36 +01:00
naps62-yolo 9f812fac11 feat: season-pack vs per-episode grab selection (#94)
ci / web (push) Successful in 24s
e2e / e2e (push) Successful in 54s
ci / rust (push) Successful in 2m11s
2026-08-23 01:40:53 +01:00
naps62-yolo 35391f02fc feat: Jellyfin library refresh on import (#93)
ci / web (push) Successful in 28s
e2e / e2e (push) Successful in 55s
ci / rust (push) Successful in 2m12s
2026-08-23 01:14:46 +01:00
naps62-yolo 225be33b55 feat: RSS sync worker (#91)
ci / web (push) Successful in 26s
e2e / e2e (push) Successful in 53s
ci / rust (push) Successful in 2m13s
2026-08-23 01:04:15 +01:00
naps62-yolo e01505bd53 Targeted-search backoff and release-date gating (#90)
ci / web (push) Successful in 25s
e2e / e2e (push) Successful in 46s
ci / rust (push) Successful in 2m9s
2026-08-23 00:42:13 +01:00
naps62-yolo 0585811a0c Per-tracker seeding rules and torrent reaper (#88)
ci / web (push) Successful in 43s
e2e / e2e (push) Successful in 54s
ci / rust (push) Failing after 2m0s
2026-08-23 00:03:30 +01:00
naps62-yolo 01af397a40 Hard/soft fail handling and release blacklist (#87)
ci / rust (push) Successful in 1m6s
e2e / e2e (push) Successful in 52s
ci / web (push) Successful in 26s
2026-08-22 23:42:36 +01:00
Miguel PalhasandClaude Fable 5 0343783a57 fix(daemon): use canonical movie states in import
ci / web (push) Successful in 57s
ci / rust (push) Successful in 1m45s
e2e / e2e (push) Successful in 1m48s
PRs #82 and #85 were each green pre-merge; #85 renamed movie states
(grabbed->downloading, imported->available) and #82's import path
still wrote the old names, so main failed the movies CHECK constraint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-22 23:23:56 +01:00
naps62-yolo ffb8485939 Import pipeline: probe, hardlink, rename, layout (#82)
ci / rust (push) Failing after 1m43s
ci / web (push) Successful in 58s
e2e / e2e (push) Successful in 1m25s
2026-08-22 23:21:30 +01:00
naps62-yolo 62aba6315c feat(db): align movie state with canonical MediaState (#85)
ci / web (push) Successful in 29s
e2e / e2e (push) Successful in 47s
ci / rust (push) Successful in 2m59s
2026-08-22 23:21:09 +01:00
naps62-yolo f253e2755b feat(daemon): grab pipeline for wanted movies (#77)
ci / web (push) Successful in 53s
ci / rust (push) Successful in 1m47s
e2e / e2e (push) Successful in 2m1s
2026-08-22 22:38:31 +01:00
naps62-yolo e5f4e44925 Add reconcile loop scheduler (#69)
ci / rust (push) Successful in 1m42s
ci / web (push) Successful in 32s
e2e / e2e (push) Successful in 1m42s
2026-08-22 22:00:12 +01:00
naps62-yolo e9c055675b test(e2e): cross-process harness over the §12 seams (#66)
ci / rust (push) Failing after 1m36s
ci / web (push) Successful in 26s
e2e / e2e (push) Failing after 59s
2026-08-22 21:26:16 +01:00
naps62-yolo 514628f089 feat(web): SPA skeleton embedded in the binary (#60)
ci / web (push) Successful in 33s
ci / rust (push) Successful in 57s
e2e / e2e (push) Successful in 1m7s
2026-08-22 21:07:40 +01:00
naps62-yolo 94c334ffe9 Radarr v3 shim for Jellyseerr (#62)
ci / web (push) Successful in 12s
ci / rust (push) Successful in 1m44s
e2e / e2e (push) Successful in 1m21s
2026-08-22 21:00:35 +01:00
naps62-yolo 99ac79373d feat(api): add movie CRUD API (#56)
ci / web (push) Successful in 8s
e2e / e2e (push) Successful in 1m21s
ci / rust (push) Successful in 3m53s
2026-08-22 20:40:31 +01:00
naps62-yolo 58827da647 feat(api): axum skeleton with health and OpenAPI (#53)
ci / web (push) Successful in 6s
ci / rust (push) Successful in 45s
e2e / e2e (push) Successful in 1m31s
2026-08-22 20:09:52 +01:00
naps62-yolo 1529e9fdca Bootstrap configuration loading (#46)
ci / web (push) Successful in 14s
ci / rust (push) Successful in 39s
e2e / e2e (push) Successful in 1m15s
2026-08-22 19:44:50 +01:00
ClaudeandClaude Opus 5 f4e63f1ac5 chore: cargo workspace skeleton
Eleven empty crates per DESIGN.md §11, versions and lints pinned once in the
root manifest, Justfile carrying the §12 gate, nix dev shell.

The frontend steps in `just ci` no-op until web/ exists. arr-core carries one
placeholder test because `cargo nextest` fails a zero-test run, and relaxing
that would let a broken filter go green later.

Closes #1

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 19:20:47 +01:00