Files
arr-tui/internal/arr/release.go
T
2026-10-20 20:30:12 +02:00

97 lines
3.1 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"`
Resolution int `json:"resolution"`
} `json:"quality"`
} `json:"quality"`
Languages []struct {
Name string `json:"name"`
} `json:"languages"`
CustomFormats []struct {
Name string `json:"name"`
} `json:"customFormats"`
}
// 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)
}
// Attrs derives the filterable properties from what the release announces:
// its title, quality, custom formats and languages. Codec and HDR only show up
// when the uploader put them into the title.
func (r Release) Attrs() FileAttrs {
names := []string{r.Title, r.Quality.Quality.Name}
for _, cf := range r.CustomFormats {
names = append(names, cf.Name)
}
text := strings.Join(names, " ")
a := FileAttrs{Resolution: r.Quality.Quality.Resolution, Codec: codecOf(text)}
if a.Resolution == 0 {
if m := reRes.FindStringSubmatch(text); m != nil {
a.Resolution = atoi(m[1])
}
}
a.DV = reDV.MatchString(text)
a.HDR = reHDR.MatchString(text)
for _, l := range r.Languages {
if l.Name != "" {
a.Languages = append(a.Languages, l.Name)
}
}
return a
}