134 lines
4.5 KiB
Go
134 lines
4.5 KiB
Go
package arr
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// StatusMessage is a warning or error attached to a queue item, e.g. why an import is blocked.
|
|
type StatusMessage struct {
|
|
Title string `json:"title"`
|
|
Messages []string `json:"messages"`
|
|
}
|
|
|
|
// QueueItem is an entry of the Radarr/Sonarr download queue.
|
|
type QueueItem struct {
|
|
ID int `json:"id"`
|
|
Title string `json:"title"` // release title
|
|
Status string `json:"status"`
|
|
TrackedDownloadStatus string `json:"trackedDownloadStatus"` // ok, warning, error
|
|
TrackedDownloadState string `json:"trackedDownloadState"` // downloading, importPending, importBlocked, ...
|
|
StatusMessages []StatusMessage `json:"statusMessages"`
|
|
ErrorMessage string `json:"errorMessage"`
|
|
Size float64 `json:"size"`
|
|
SizeLeft float64 `json:"sizeleft"`
|
|
TimeLeft string `json:"timeleft"`
|
|
DownloadClient string `json:"downloadClient"`
|
|
DownloadID string `json:"downloadId"` // the download client's id, e.g. SABnzbd's nzo_id
|
|
Indexer string `json:"indexer"`
|
|
Protocol string `json:"protocol"`
|
|
MovieID int `json:"movieId,omitempty"` // Radarr
|
|
SeriesID int `json:"seriesId,omitempty"` // Sonarr
|
|
EpisodeID int `json:"episodeId,omitempty"` // Sonarr; one item per episode
|
|
|
|
// Present depending on the include* parameters of the request.
|
|
Movie *QueueMovie `json:"movie,omitempty"`
|
|
Series *QueueSeries `json:"series,omitempty"`
|
|
Episode *QueueEpisode `json:"episode,omitempty"`
|
|
}
|
|
|
|
// QueueMovie is the movie a queue item belongs to.
|
|
type QueueMovie struct {
|
|
Title string `json:"title"`
|
|
Year int `json:"year"`
|
|
}
|
|
|
|
// QueueSeries is the series a queue item belongs to.
|
|
type QueueSeries struct {
|
|
Title string `json:"title"`
|
|
}
|
|
|
|
// QueueEpisode is the episode a queue item belongs to.
|
|
type QueueEpisode struct {
|
|
SeasonNumber int `json:"seasonNumber"`
|
|
EpisodeNumber int `json:"episodeNumber"`
|
|
Title string `json:"title"`
|
|
}
|
|
|
|
// Name returns a readable name: "Movie (2020)", "Show S01E02", or the release title.
|
|
func (q QueueItem) Name() string {
|
|
switch {
|
|
case q.Movie != nil && q.Movie.Title != "":
|
|
if q.Movie.Year > 0 {
|
|
return fmt.Sprintf("%s (%d)", q.Movie.Title, q.Movie.Year)
|
|
}
|
|
return q.Movie.Title
|
|
case q.Series != nil && q.Series.Title != "":
|
|
if q.Episode != nil {
|
|
return fmt.Sprintf("%s S%02dE%02d", q.Series.Title, q.Episode.SeasonNumber, q.Episode.EpisodeNumber)
|
|
}
|
|
return q.Series.Title
|
|
}
|
|
return q.Title
|
|
}
|
|
|
|
// Progress returns the download progress in percent (0-100).
|
|
func (q QueueItem) Progress() float64 {
|
|
if q.Size <= 0 {
|
|
return 0
|
|
}
|
|
return min(max((q.Size-q.SizeLeft)/q.Size*100, 0), 100)
|
|
}
|
|
|
|
// NeedsAttention reports whether the item has a warning or error.
|
|
func (q QueueItem) NeedsAttention() bool {
|
|
return q.TrackedDownloadStatus == "warning" || q.TrackedDownloadStatus == "error" ||
|
|
q.TrackedDownloadState == "importBlocked" || q.TrackedDownloadState == "failed" ||
|
|
q.TrackedDownloadState == "failedPending"
|
|
}
|
|
|
|
// Problem returns the item's warnings and errors as one line ("" if none).
|
|
func (q QueueItem) Problem() string {
|
|
var parts []string
|
|
if q.ErrorMessage != "" {
|
|
parts = append(parts, q.ErrorMessage)
|
|
}
|
|
for _, m := range q.StatusMessages {
|
|
parts = append(parts, m.Messages...)
|
|
if len(m.Messages) == 0 && m.Title != "" {
|
|
parts = append(parts, m.Title)
|
|
}
|
|
}
|
|
return strings.Join(parts, "; ")
|
|
}
|
|
|
|
// QueueWith lists the queue with the given extra query parameters, e.g.
|
|
// includeMovie=true. It fetches a single page of up to 200 items.
|
|
func (c *Client) QueueWith(ctx context.Context, include url.Values) ([]QueueItem, error) {
|
|
q := url.Values{"page": {"1"}, "pageSize": {"200"}}
|
|
for k, v := range include {
|
|
q[k] = v
|
|
}
|
|
var page struct {
|
|
Records []QueueItem `json:"records"`
|
|
}
|
|
if err := c.Do(ctx, http.MethodGet, "queue", q, nil, &page); err != nil {
|
|
return nil, err
|
|
}
|
|
return page.Records, nil
|
|
}
|
|
|
|
// RemoveQueueItem removes an item from the queue and the download client.
|
|
// With blocklist set, the release is blocklisted and a replacement is searched.
|
|
func (c *Client) RemoveQueueItem(ctx context.Context, id int, blocklist bool) error {
|
|
q := url.Values{
|
|
"removeFromClient": {"true"},
|
|
"blocklist": {strconv.FormatBool(blocklist)},
|
|
}
|
|
return c.Do(ctx, http.MethodDelete, "queue/"+strconv.Itoa(id), q, nil, nil)
|
|
}
|