golift.io/starr@v1.0.0/shared.go (about)

     1  package starr
     2  
     3  import (
     4  	"crypto/tls"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net/http"
     8  	"net/url"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	"golift.io/starr/debuglog"
    14  )
    15  
    16  /* This file contains shared structs or constants for all the *arr apps. */
    17  
    18  // App can be used to satisfy a context value key.
    19  // It is not used in this library; provided for convenience.
    20  type App string
    21  
    22  // These constants are just here for convenience.
    23  const (
    24  	Emby     App = "Emby"
    25  	Lidarr   App = "Lidarr"
    26  	Plex     App = "Plex"
    27  	Prowlarr App = "Prowlarr"
    28  	Radarr   App = "Radarr"
    29  	Readarr  App = "Readarr"
    30  	Sonarr   App = "Sonarr"
    31  	Whisparr App = "Whisparr"
    32  )
    33  
    34  // String turns an App name into a string.
    35  func (a App) String() string {
    36  	return string(a)
    37  }
    38  
    39  // Lower turns an App name into a lowercase string.
    40  func (a App) Lower() string {
    41  	return strings.ToLower(string(a))
    42  }
    43  
    44  // Client returns the default client, and is used if one is not passed in.
    45  func Client(timeout time.Duration, verifySSL bool) *http.Client {
    46  	return &http.Client{
    47  		Timeout: timeout,
    48  		CheckRedirect: func(r *http.Request, via []*http.Request) error {
    49  			return http.ErrUseLastResponse
    50  		},
    51  		Transport: &http.Transport{
    52  			TLSClientConfig: &tls.Config{InsecureSkipVerify: !verifySSL}, //nolint:gosec
    53  		},
    54  	}
    55  }
    56  
    57  // ClientWithDebug returns an http client with a debug logger enabled.
    58  func ClientWithDebug(timeout time.Duration, verifySSL bool, logConfig debuglog.Config) *http.Client {
    59  	client := Client(timeout, verifySSL)
    60  	client.Transport = debuglog.NewLoggingRoundTripper(logConfig, client.Transport)
    61  
    62  	return client
    63  }
    64  
    65  // CalendarTimeFilterFormat is the Go time format the calendar expects the filter to be in.
    66  const CalendarTimeFilterFormat = "2006-01-02T03:04:05.000Z"
    67  
    68  // StatusMessage represents the status of the item. All apps use this.
    69  type StatusMessage struct {
    70  	Title    string   `json:"title"`
    71  	Messages []string `json:"messages"`
    72  }
    73  
    74  // BaseQuality is a base quality profile.
    75  type BaseQuality struct {
    76  	ID         int64  `json:"id"`
    77  	Name       string `json:"name"`
    78  	Source     string `json:"source,omitempty"`
    79  	Resolution int    `json:"resolution,omitempty"`
    80  	Modifier   string `json:"modifier,omitempty"`
    81  }
    82  
    83  // Quality is a download quality profile attached to a movie, book, track or series.
    84  // It may contain 1 or more profiles.
    85  // Sonarr nor Readarr use Name or ID in this struct.
    86  type Quality struct {
    87  	Name     string           `json:"name,omitempty"`
    88  	ID       int              `json:"id,omitempty"`
    89  	Quality  *BaseQuality     `json:"quality,omitempty"`
    90  	Items    []*Quality       `json:"items,omitempty"`
    91  	Allowed  bool             `json:"allowed"`
    92  	Revision *QualityRevision `json:"revision,omitempty"` // Not sure which app had this....
    93  }
    94  
    95  // QualityRevision is probably used in Sonarr.
    96  type QualityRevision struct {
    97  	Version  int64 `json:"version"`
    98  	Real     int64 `json:"real"`
    99  	IsRepack bool  `json:"isRepack,omitempty"`
   100  }
   101  
   102  // Ratings belong to a few types.
   103  type Ratings struct {
   104  	Votes      int64   `json:"votes"`
   105  	Value      float64 `json:"value"`
   106  	Popularity float64 `json:"popularity,omitempty"`
   107  	Type       string  `json:"type,omitempty"`
   108  }
   109  
   110  // OpenRatings is a ratings type that has a source and type.
   111  type OpenRatings map[string]Ratings
   112  
   113  // IsLoaded is a generic struct used in a few places.
   114  type IsLoaded struct {
   115  	IsLoaded bool `json:"isLoaded"`
   116  }
   117  
   118  // Link is used in a few places.
   119  type Link struct {
   120  	URL  string `json:"url"`
   121  	Name string `json:"name"`
   122  }
   123  
   124  // Tag may be applied to nearly anything.
   125  type Tag struct {
   126  	ID    int    `json:"id,omitempty"`
   127  	Label string `json:"label"`
   128  }
   129  
   130  // Image is used in a few places.
   131  type Image struct {
   132  	CoverType string `json:"coverType"`
   133  	URL       string `json:"url,omitempty"`
   134  	RemoteURL string `json:"remoteUrl,omitempty"`
   135  	Extension string `json:"extension,omitempty"`
   136  }
   137  
   138  // Path is for unmanaged folder paths.
   139  type Path struct {
   140  	Name string `json:"name"`
   141  	Path string `json:"path"`
   142  }
   143  
   144  // RemotePathMapping is the remotePathMapping endpoint.
   145  type RemotePathMapping struct {
   146  	ID         int64  `json:"id,omitempty"`
   147  	Host       string `json:"host"`
   148  	RemotePath string `json:"remotePath"`
   149  	LocalPath  string `json:"localPath"`
   150  }
   151  
   152  // Value is generic ID/Name struct applied to a few places.
   153  type Value struct {
   154  	ID   int64  `json:"id"`
   155  	Name string `json:"name"`
   156  }
   157  
   158  // FieldOutput is generic Name/Value struct applied to a few places.
   159  type FieldOutput struct {
   160  	Advanced                    bool            `json:"advanced,omitempty"`
   161  	Order                       int64           `json:"order,omitempty"`
   162  	HelpLink                    string          `json:"helpLink,omitempty"`
   163  	HelpText                    string          `json:"helpText,omitempty"`
   164  	Hidden                      string          `json:"hidden,omitempty"`
   165  	Label                       string          `json:"label,omitempty"`
   166  	Name                        string          `json:"name"`
   167  	SelectOptionsProviderAction string          `json:"selectOptionsProviderAction,omitempty"`
   168  	Type                        string          `json:"type,omitempty"`
   169  	Privacy                     string          `json:"privacy"`
   170  	Value                       interface{}     `json:"value,omitempty"`
   171  	SelectOptions               []*SelectOption `json:"selectOptions,omitempty"`
   172  }
   173  
   174  // FieldInput is generic Name/Value struct applied to a few places.
   175  type FieldInput struct {
   176  	Name  string      `json:"name"`
   177  	Value interface{} `json:"value,omitempty"`
   178  }
   179  
   180  // SelectOption is part of Field.
   181  type SelectOption struct {
   182  	DividerAfter bool   `json:"dividerAfter,omitempty"`
   183  	Order        int64  `json:"order"`
   184  	Value        int64  `json:"value"`
   185  	Hint         string `json:"hint"`
   186  	Name         string `json:"name"`
   187  }
   188  
   189  // KeyValue is yet another reusable generic type.
   190  type KeyValue struct {
   191  	Key   string `json:"key"`
   192  	Value int    `json:"value"`
   193  }
   194  
   195  // BackupFile comes from the system/backup paths in all apps.
   196  type BackupFile struct {
   197  	Name string    `json:"name"`
   198  	Path string    `json:"path"`
   199  	Type string    `json:"type"`
   200  	Time time.Time `json:"time"`
   201  	ID   int64     `json:"id"`
   202  	Size int64     `json:"size"`
   203  }
   204  
   205  // QueueDeleteOpts are the extra inputs when deleting an item from the Activity Queue.
   206  // Set these appropriately for your expectations. All inputs are the same in all apps.
   207  // Providing this input to the QueueDelete methods is optional; nil sets the defaults shown.
   208  type QueueDeleteOpts struct {
   209  	// Default True, use starr.False() to change it.
   210  	RemoveFromClient *bool
   211  	// Default False
   212  	BlockList bool
   213  	// Default False
   214  	SkipRedownload bool
   215  }
   216  
   217  // Values turns delete options into http get query parameters.
   218  func (o *QueueDeleteOpts) Values() url.Values {
   219  	params := make(url.Values)
   220  	params.Set("removeFromClient", "true")
   221  
   222  	if o == nil {
   223  		return params
   224  	}
   225  
   226  	params.Set("blocklist", fmt.Sprint(o.BlockList))
   227  	params.Set("skipRedownload", fmt.Sprint(o.SkipRedownload))
   228  
   229  	if o.RemoveFromClient != nil {
   230  		params.Set("removeFromClient", fmt.Sprint(*o.RemoveFromClient))
   231  	}
   232  
   233  	return params
   234  }
   235  
   236  // PlayTime is used in at least Sonarr, maybe other places.
   237  // Holds a string duration converted from hh:mm:ss.
   238  type PlayTime struct { //nolint:musttag
   239  	Original string
   240  	time.Duration
   241  }
   242  
   243  // FormatItem is part of a quality profile.
   244  type FormatItem struct {
   245  	Format int64  `json:"format"`
   246  	Name   string `json:"name"`
   247  	Score  int64  `json:"score"`
   248  }
   249  
   250  // UnmarshalJSON parses a run time duration in format hh:mm:ss.
   251  func (d *PlayTime) UnmarshalJSON(b []byte) error {
   252  	d.Original = strings.Trim(string(b), `"'`)
   253  
   254  	switch parts := strings.Split(d.Original, ":"); len(parts) {
   255  	case 3: //nolint:gomnd // hh:mm:ss
   256  		h, _ := strconv.Atoi(parts[0])
   257  		m, _ := strconv.Atoi(parts[1])
   258  		s, _ := strconv.Atoi(parts[2])
   259  		d.Duration = (time.Hour * time.Duration(h)) + (time.Minute * time.Duration(m)) + (time.Second * time.Duration(s))
   260  	case 2: //nolint:gomnd // mm:ss
   261  		m, _ := strconv.Atoi(parts[0])
   262  		s, _ := strconv.Atoi(parts[1])
   263  		d.Duration = (time.Minute * time.Duration(m)) + (time.Second * time.Duration(s))
   264  	case 1: // ss
   265  		s, _ := strconv.Atoi(parts[0])
   266  		d.Duration += (time.Second * time.Duration(s))
   267  	}
   268  
   269  	return nil
   270  }
   271  
   272  func (d *PlayTime) MarshalJSON() ([]byte, error) {
   273  	return []byte(`"` + d.Original + `"`), nil
   274  }
   275  
   276  var _ json.Unmarshaler = (*PlayTime)(nil)
   277  
   278  // ApplyTags is an enum used as an input for Bulk editors, and perhaps other places.
   279  type ApplyTags string
   280  
   281  // ApplyTags enum constants. Use these as inputs for "ApplyTags" member values.
   282  // Schema doc'd here: https://radarr.video/docs/api/#/MovieEditor/put_api_v3_movie_editor
   283  const (
   284  	TagsAdd     ApplyTags = "add"
   285  	TagsRemove  ApplyTags = "remove"
   286  	TagsReplace ApplyTags = "replace"
   287  )
   288  
   289  // Ptr returns a pointer to an apply tags value. Useful for a BulkEdit struct.
   290  func (a ApplyTags) Ptr() *ApplyTags {
   291  	return &a
   292  }
   293  
   294  // TimeSpan is part of AudioTags and possibly used other places.
   295  type TimeSpan struct {
   296  	Ticks             int64 `json:"ticks"`
   297  	Days              int64 `json:"days"`
   298  	Hours             int64 `json:"hours"`
   299  	Milliseconds      int64 `json:"milliseconds"`
   300  	Minutes           int64 `json:"minutes"`
   301  	Seconds           int64 `json:"seconds"`
   302  	TotalDays         int64 `json:"totalDays"`
   303  	TotalHours        int64 `json:"totalHours"`
   304  	TotalMilliseconds int64 `json:"totalMilliseconds"`
   305  	TotalMinutes      int64 `json:"totalMinutes"`
   306  	TotalSeconds      int64 `json:"totalSeconds"`
   307  }