Commit Graph

86 Commits

Author SHA1 Message Date
Miguel Palhas 71c2741d08 feat(web): settings view for roots and policies
Own route beside library and queues. Roots edit kind, audience, path
and their policy; policies get a form over the real fields — size
bands as number inputs, score and source weights as numbers,
resolution preference and audio/HDR rules as validated lists. Delete
arms before firing; a 409 from a referenced policy or occupied root
surfaces its message.
2026-08-23 15:14:34 +01:00
Miguel Palhas 9fa1f0cc51 feat(api): policy and root CRUD endpoints
Policies and roots were editable only via SQL inside the container.
Full CRUD over both, validated against the vocabulary the policy
engine knows — unknown resolutions or sources answer 422 naming the
field, malformed JSON answers 422, deleting a referenced policy or an
occupied root answers 409. Closes #116 (API half).
2026-08-23 14:56:51 +01:00
Miguel Palhas 951669749b Merge remote-tracking branch 'origin/main' into issue/114-read-score
ci / web (push) Successful in 53s
ci / rust (push) Successful in 2m10s
e2e / e2e (push) Successful in 2m20s
2026-08-23 12:25:08 +01:00
Miguel Palhas 8f8431eed0 fix(api): rescore release decks at read time
GET .../releases returned the stored score column, frozen at
search time. Policy tuning never reached an existing deck. Recompute
from parsed/size/seeders against the current policy and re-sort by
bucket then score; the stored column stays for the daemon's grab-time
winner pick.

Closes #114
2026-08-23 12:25:04 +01:00
Miguel Palhas 401afe5aad Merge branch 'issue/115-avail-search'
ci / web (push) Successful in 34s
e2e / e2e (push) Successful in 1m6s
ci / rust (push) Successful in 1m11s
2026-08-23 12:23:10 +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 a5b3680746 feat(core): rank resolutions in the score
ci / web (push) Successful in 42s
e2e / e2e (push) Successful in 1m13s
ci / rust (push) Successful in 1m18s
`resolution_pref` only gated eligibility, so every release was scored
against its own resolution's size band and a 23 GB 4K lost to an
at-target 1080p. Each step up the list is now worth `resolution_step`
points, seeded at 300: five gibibytes of 4K overshoot, so a 4K up to
27 GB wins and a bloated one still does not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 12:01:24 +01:00
Miguel Palhas 532eee7dbe Merge branch 'issue/112-name-truncate'
ci / web (push) Successful in 38s
e2e / e2e (push) Successful in 1m33s
ci / rust (push) Successful in 1m42s
2026-08-23 11:26:20 +01:00
Miguel Palhas 24be8eb8d2 fix(web): keep grab on the row at every width 2026-08-23 11:26:07 +01:00
Miguel Palhas 9f0ccb0416 feat(web): score heat scale, drop release row expand
ci / web (push) Successful in 1m28s
ci / rust (push) Successful in 2m33s
e2e / e2e (push) Successful in 3m13s
Score cells interpolate red-to-green relative to the scores in the
current deck; the click-to-expand panel only repeated row content.
2026-08-23 11:13:11 +01:00
Miguel Palhas d6345c5305 fix(api,web): movie delete always removes files
ci / web (push) Successful in 1m5s
ci / rust (push) Successful in 1m30s
e2e / e2e (push) Successful in 1m27s
Drop the delete_files choice: removing a movie now always unlinks
its §7.4 library folder. Removes the query flag from DELETE
/api/movies/{id} and the OpenAPI params, and the toggle from the UI
confirmation panel — the confirmation itself stays. Path-safety and
torrent-untouched behaviour are unchanged.

Closes #110
2026-08-23 10:21:03 +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 4c73b3e7c6 feat(api,web): remove a movie, files optional
ci / web (push) Successful in 28s
ci / rust (push) Successful in 1m24s
e2e / e2e (push) Successful in 1m19s
`DELETE /api/movies/{id}` took the row and left the files, and no
surface exposed it. It now accepts `delete_files`, which unlinks the
title's §7.4 folder — atomic, so sidecars go with the feature. Targets
come from `media_files`, never from re-deriving the folder name, and a
path outside its root is never touched. The torrent is untouched (§7.3):
it keeps seeding and the reaper owns it.

The release deck grows a quiet REMOVE control opening one inline
confirmation: file count, size and folder first, then a delete-files
toggle that starts off, then what the choice costs.

Closes #104
2026-08-23 09:08:52 +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 Palhas ca8a891347 chore(web): drop probe button and footer rail
ci / web (push) Successful in 28s
ci / rust (push) Successful in 1m0s
e2e / e2e (push) Successful in 1m10s
The 15 s health poll stays; the manual trigger and the rtt/poll-note
footer earned nothing over it.

Closes #106

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 08:44:41 +01:00
Miguel Palhas eed7094d1b fix(web): keep release deck aligned on phones
Under 40rem the seven-column line cannot fit: hdr and audio move
into the expand row, the surviving five columns tighten, and the
column header never wraps at any width.

Closes #105

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 08:43:04 +01:00
Miguel Palhas e36c73ca75 feat(web): add client-side routes for views
ci / web (push) Successful in 27s
ci / rust (push) Successful in 53s
e2e / e2e (push) Successful in 1m1s
Deep-link, refresh and back/forward now land on the view the operator
was on instead of the board. Vanilla History API (pushState/popstate),
no router dependency: /library, /queues, /movies/{id}/releases and
/search?q= all restore on load.

Closes #102
2026-08-23 08:24:36 +01:00
Miguel Palhas 1ce9f11e62 Merge remote-tracking branch 'origin/main' into issue/101-search-feedback
ci / web (push) Successful in 30s
ci / rust (push) Successful in 58s
e2e / e2e (push) Successful in 1m17s
2026-08-23 08:07:53 +01:00
Miguel Palhas 6d78cacce0 fix(web): show sweep in flight, not empty deck
An empty release list has two meanings the deck conflated: a sweep
that has not landed yet and a sweep that found nothing. The movie's
last_searched_at tells them apart, so the deck now shows a probing
'sweeping indexers' state and polls the movie (5 s, 150 s budget)
until the sweep lands, then renders buckets or an honest empty
verdict with the sweep's age. A sweep held by the §6.2 backoff times
out to a message that says so.

Closes #101

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 08:07:33 +01:00
Miguel Palhas 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
Miguel Palhas 5db3a8d7a4 chore: add Dockerfile for Dokploy deploy
ci / web (push) Successful in 44s
e2e / e2e (push) Successful in 1m27s
ci / rust (push) Successful in 2m58s
2026-08-23 07:33:19 +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
Miguel Palhas 3082568171 fix(db): renumber colliding 0011 migration to 0012
ci / web (push) Successful in 45s
e2e / e2e (push) Successful in 1m2s
ci / rust (push) Successful in 2m59s
PRs #97 and #98 each added a migration numbered 0011; merged together
sqlx hits UNIQUE on _sqlx_migrations.version.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 02:33:23 +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 22024d4be2 Attention queues: no-PT-source and needs-decision (#89)
ci / web (push) Successful in 24s
ci / rust (push) Successful in 1m3s
e2e / e2e (push) Successful in 53s
2026-08-22 23:57:42 +01:00
naps62-yolo 01af397a40 Hard/soft fail handling and release blacklist (#87)
ci / web (push) Successful in 26s
ci / rust (push) Successful in 1m6s
e2e / e2e (push) Successful in 52s
2026-08-22 23:42:36 +01:00
naps62-yolo 38bd4102bb Sonarr v3 shim for Jellyseerr (#84)
ci / web (push) Successful in 55s
ci / rust (push) Successful in 1m36s
e2e / e2e (push) Successful in 1m24s
2026-08-22 23:33:09 +01:00
naps62-yolo f38fb1277b Library views with derived status (#83)
ci / web (push) Successful in 33s
ci / rust (push) Successful in 1m29s
e2e / e2e (push) Successful in 1m12s
2026-08-22 23:33:05 +01:00
Miguel Palhas 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 d3cea8693b feat(api): series, season and episode endpoints (#81)
ci / web (push) Successful in 26s
e2e / e2e (push) Successful in 47s
ci / rust (push) Successful in 2m39s
2026-08-22 23:02:34 +01:00
naps62-yolo e650a762f9 Manual search buckets and attribute chips (#79)
ci / rust (push) Successful in 1m54s
ci / web (push) Successful in 27s
e2e / e2e (push) Successful in 1m21s
2026-08-22 22:42:50 +01:00
naps62-yolo f26f278a75 feat(core): derive series status (#80)
ci / rust (push) Successful in 1m49s
ci / web (push) Successful in 31s
e2e / e2e (push) Successful in 2m1s
2026-08-22 22:38:36 +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 8d44225b57 feat(indexer): tvsearch with season and episode tags (#78)
ci / web (push) Successful in 50s
ci / rust (push) Successful in 1m18s
e2e / e2e (push) Successful in 1m46s
2026-08-22 22:34:04 +01:00
naps62-yolo fc2a8de58d Propagate auto-track intent to new seasons (#76)
ci / web (push) Successful in 33s
ci / rust (push) Successful in 1m1s
e2e / e2e (push) Successful in 1m41s
2026-08-22 22:26:23 +01:00
naps62-yolo b5413eef46 Use policy scoring weights in release search (#74)
ci / rust (push) Failing after 56s
ci / web (push) Successful in 37s
e2e / e2e (push) Successful in 1m51s
2026-08-22 22:12:17 +01:00
naps62-yolo bf526b9198 Unified search box (#30) (#75)
ci / rust (push) Failing after 1m26s
ci / web (push) Successful in 29s
e2e / e2e (push) Successful in 1m55s
2026-08-22 22:08:32 +01:00
naps62-yolo ac6ed8b798 feat(db): add series, season and episode model (#71)
ci / rust (push) Successful in 1m36s
ci / web (push) Successful in 34s
e2e / e2e (push) Successful in 1m41s
2026-08-22 22:04:27 +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 403593cda1 test(probe): add HDR fixture coverage (#72)
ci / rust (push) Successful in 1m23s
ci / web (push) Successful in 29s
e2e / e2e (push) Successful in 1m33s
2026-08-22 21:50:13 +01:00
naps62-yolo 0b7565189e feat(api): owner tags and filtered movie views (#70)
ci / web (push) Successful in 52s
ci / rust (push) Successful in 1m21s
e2e / e2e (push) Successful in 1m41s
2026-08-22 21:43:03 +01:00