Files
arr-tui/internal/arr/release.go
T
nilsandClaude Sonnet 5.5 f928427993 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>
2026-10-20 18:09:19 +02:00

67 lines
2.2 KiB
Go

package arr
import (
"context"
"fmt"
"net/http"
"net/url"
"strings"
)
// Release is an indexer search result from an interactive search.
type Release struct {
GUID string `json:"guid"`
Title string `json:"title"`
Indexer string `json:"indexer"`
IndexerID int `json:"indexerId"`
Protocol string `json:"protocol"` // usenet or torrent
Size int64 `json:"size"`
AgeMinutes float64 `json:"ageMinutes"`
QualityWeight int `json:"qualityWeight"`
CustomFormatScore int `json:"customFormatScore"`
ReleaseGroup string `json:"releaseGroup"`
Approved bool `json:"approved"`
Rejected bool `json:"rejected"`
TemporarilyRejected bool `json:"temporarilyRejected"`
Rejections []string `json:"rejections"`
FullSeason bool `json:"fullSeason"`
Quality struct {
Quality struct {
Name string `json:"name"`
} `json:"quality"`
} `json:"quality"`
Languages []struct {
Name string `json:"name"`
} `json:"languages"`
}
// QualityName returns the release's quality, e.g. "Bluray-1080p".
func (r Release) QualityName() string { return r.Quality.Quality.Name }
// LanguageNames returns the release's languages joined by commas.
func (r Release) LanguageNames() string {
names := make([]string, len(r.Languages))
for i, l := range r.Languages {
names[i] = l.Name
}
return strings.Join(names, ", ")
}
// Releases runs an interactive search with the given query, e.g. movieId=12.
// Searching queries every indexer and can take a while; use a generous timeout.
func (c *Client) Releases(ctx context.Context, query url.Values) ([]Release, error) {
var releases []Release
err := c.Do(ctx, http.MethodGet, "release", query, nil, &releases)
return releases, err
}
// Grab sends a release found by a previous search to the download client.
// Releases that were rejected can be grabbed as well.
func (c *Client) Grab(ctx context.Context, r Release) error {
if r.GUID == "" {
return fmt.Errorf("release has no guid")
}
body := map[string]any{"guid": r.GUID, "indexerId": r.IndexerID}
return c.Do(ctx, http.MethodPost, "release", nil, body, nil)
}