Add interactive release search (M6)

Release listing and grab in the shared arr client, a full-screen release
list (sort, hide rejected, rejection reasons, confirmation before grabbing
rejected releases; Usenet only), and a season/episode picker for Sonarr.
Overlays are now a stack so dialogs can open above the release list.
Add a fake Newznab indexer to the dev setup.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-20 18:09:19 +02:00
co-authored by Claude Sonnet 5.5
parent 446cbdd6c5
commit f928427993
18 changed files with 1002 additions and 23 deletions
+5
View File
@@ -145,3 +145,8 @@ func TMDBTerm(tmdbID int) string { return "tmdb:" + strconv.Itoa(tmdbID) }
func (c *Client) Queue(ctx context.Context) ([]arr.QueueItem, error) {
return c.QueueWith(ctx, url.Values{"includeMovie": {"true"}})
}
// Releases runs an interactive search for a library movie.
func (c *Client) Releases(ctx context.Context, movieID int) ([]arr.Release, error) {
return c.Client.Releases(ctx, url.Values{"movieId": {strconv.Itoa(movieID)}})
}
+13
View File
@@ -148,3 +148,16 @@ func TestSearchMovie(t *testing.T) {
t.Errorf("body = %v", got)
}
}
func TestReleases(t *testing.T) {
c := newTestClient(t, func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/v3/release" || r.URL.Query().Get("movieId") != "7" {
t.Errorf("unexpected request %s", r.URL)
}
w.Write([]byte(`[{"guid":"g","title":"x","approved":true}]`))
})
rels, err := c.Releases(context.Background(), 7)
if err != nil || len(rels) != 1 || rels[0].GUID != "g" {
t.Fatalf("releases = %+v, %v", rels, err)
}
}