golift.io/starr@v1.0.0/sonarr/series.go (about) 1 package sonarr 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "fmt" 8 "net/url" 9 "path" 10 "time" 11 12 "golift.io/starr" 13 ) 14 15 // Define Base Path for Series calls. 16 const bpSeries = APIver + "/series" 17 18 // AddSeriesInput is the input for /api/v3/series endpoint. 19 type AddSeriesInput struct { 20 Monitored bool `json:"monitored"` 21 SeasonFolder bool `json:"seasonFolder,omitempty"` 22 UseSceneNumbering bool `json:"useSceneNumbering,omitempty"` 23 ID int64 `json:"id,omitempty"` 24 LanguageProfileID int64 `json:"languageProfileId,omitempty"` 25 QualityProfileID int64 `json:"qualityProfileId,omitempty"` 26 TvdbID int64 `json:"tvdbId,omitempty"` 27 ImdbID string `json:"imdbId,omitempty"` 28 TvMazeID int64 `json:"tvMazeId,omitempty"` 29 TvRageID int64 `json:"tvRageId,omitempty"` 30 Path string `json:"path,omitempty"` 31 SeriesType string `json:"seriesType,omitempty"` 32 Title string `json:"title,omitempty"` 33 TitleSlug string `json:"titleSlug,omitempty"` 34 RootFolderPath string `json:"rootFolderPath,omitempty"` 35 Tags []int `json:"tags,omitempty"` 36 Seasons []*Season `json:"seasons,omitempty"` 37 Images []*starr.Image `json:"images,omitempty"` 38 // to be used only on POST, not for PUT 39 AddOptions *AddSeriesOptions `json:"addOptions,omitempty"` 40 } 41 42 // Series is the output of /api/v3/series endpoint. 43 type Series struct { 44 Ended bool `json:"ended,omitempty"` 45 Monitored bool `json:"monitored"` 46 SeasonFolder bool `json:"seasonFolder,omitempty"` 47 UseSceneNumbering bool `json:"useSceneNumbering,omitempty"` 48 Runtime int `json:"runtime,omitempty"` 49 Year int `json:"year,omitempty"` 50 ID int64 `json:"id,omitempty"` 51 LanguageProfileID int64 `json:"languageProfileId,omitempty"` 52 QualityProfileID int64 `json:"qualityProfileId,omitempty"` 53 TvdbID int64 `json:"tvdbId,omitempty"` 54 TvMazeID int64 `json:"tvMazeId,omitempty"` 55 TvRageID int64 `json:"tvRageId,omitempty"` 56 AirTime string `json:"airTime,omitempty"` 57 Certification string `json:"certification,omitempty"` 58 CleanTitle string `json:"cleanTitle,omitempty"` 59 ImdbID string `json:"imdbId,omitempty"` 60 Network string `json:"network,omitempty"` 61 Overview string `json:"overview,omitempty"` 62 Path string `json:"path,omitempty"` 63 SeriesType string `json:"seriesType,omitempty"` 64 SortTitle string `json:"sortTitle,omitempty"` 65 Status string `json:"status,omitempty"` 66 Title string `json:"title,omitempty"` 67 TitleSlug string `json:"titleSlug,omitempty"` 68 RootFolderPath string `json:"rootFolderPath,omitempty"` 69 Added time.Time `json:"added,omitempty"` 70 FirstAired time.Time `json:"firstAired,omitempty"` 71 NextAiring time.Time `json:"nextAiring,omitempty"` 72 PreviousAiring time.Time `json:"previousAiring,omitempty"` 73 Ratings *starr.Ratings `json:"ratings,omitempty"` 74 Statistics *Statistics `json:"statistics,omitempty"` 75 Tags []int `json:"tags,omitempty"` 76 Genres []string `json:"genres,omitempty"` 77 AlternateTitles []*AlternateTitle `json:"alternateTitles,omitempty"` 78 Seasons []*Season `json:"seasons,omitempty"` 79 Images []*starr.Image `json:"images,omitempty"` 80 } 81 82 // AddSeriesOptions is part of AddSeriesInput. 83 type AddSeriesOptions struct { 84 SearchForMissingEpisodes bool `json:"searchForMissingEpisodes"` 85 SearchForCutoffUnmetEpisodes bool `json:"searchForCutoffUnmetEpisodes,omitempty"` 86 IgnoreEpisodesWithFiles bool `json:"ignoreEpisodesWithFiles,omitempty"` 87 IgnoreEpisodesWithoutFiles bool `json:"ignoreEpisodesWithoutFiles,omitempty"` 88 } 89 90 // AlternateTitle is part of a AddSeriesInput. 91 type AlternateTitle struct { 92 SeasonNumber int `json:"seasonNumber"` 93 Title string `json:"title"` 94 } 95 96 // Season is part of AddSeriesInput and Queue and used in a few places. 97 type Season struct { 98 Monitored bool `json:"monitored"` 99 SeasonNumber int `json:"seasonNumber"` 100 Statistics *Statistics `json:"statistics,omitempty"` 101 } 102 103 // Statistics is part of AddSeriesInput and Queue. 104 type Statistics struct { 105 SeasonCount int `json:"seasonCount"` 106 EpisodeFileCount int `json:"episodeFileCount"` 107 EpisodeCount int `json:"episodeCount"` 108 TotalEpisodeCount int `json:"totalEpisodeCount"` 109 SizeOnDisk int64 `json:"sizeOnDisk"` 110 PercentOfEpisodes float64 `json:"percentOfEpisodes"` 111 PreviousAiring time.Time `json:"previousAiring"` 112 } 113 114 // GetAllSeries returns all configured series. 115 // This may not deal well with pagination atm, let us know? 116 func (s *Sonarr) GetAllSeries() ([]*Series, error) { 117 return s.GetAllSeriesContext(context.Background()) 118 } 119 120 // GetAllSeriesContext returns all configured series. 121 // This may not deal well with pagination atm, let us know? 122 func (s *Sonarr) GetAllSeriesContext(ctx context.Context) ([]*Series, error) { 123 return s.GetSeriesContext(ctx, 0) 124 } 125 126 // GetSeries locates and returns a series by tvdbID. If tvdbID is 0, returns all series. 127 func (s *Sonarr) GetSeries(tvdbID int64) ([]*Series, error) { 128 return s.GetSeriesContext(context.Background(), tvdbID) 129 } 130 131 // GetSeriesContext locates and returns a series by tvdbID. If tvdbID is 0, returns all series. 132 func (s *Sonarr) GetSeriesContext(ctx context.Context, tvdbID int64) ([]*Series, error) { 133 var output []*Series 134 135 req := starr.Request{URI: bpSeries, Query: make(url.Values)} 136 if tvdbID != 0 { 137 req.Query.Add("tvdbId", fmt.Sprint(tvdbID)) 138 } 139 140 if err := s.GetInto(ctx, req, &output); err != nil { 141 return nil, fmt.Errorf("api.Get(%s): %w", &req, err) 142 } 143 144 return output, nil 145 } 146 147 // UpdateSeries updates a series in place. 148 func (s *Sonarr) UpdateSeries(series *AddSeriesInput, moveFiles bool) (*Series, error) { 149 return s.UpdateSeriesContext(context.Background(), series, moveFiles) 150 } 151 152 // UpdateSeriesContext updates a series in place. 153 func (s *Sonarr) UpdateSeriesContext(ctx context.Context, series *AddSeriesInput, moveFiles bool) (*Series, error) { 154 var body bytes.Buffer 155 if err := json.NewEncoder(&body).Encode(series); err != nil { 156 return nil, fmt.Errorf("json.Marshal(%s): %w", bpSeries, err) 157 } 158 159 var output Series 160 161 req := starr.Request{ 162 URI: path.Join(bpSeries, fmt.Sprint(series.ID)), 163 Query: make(url.Values), 164 Body: &body, 165 } 166 req.Query.Add("moveFiles", fmt.Sprint(moveFiles)) 167 168 if err := s.PutInto(ctx, req, &output); err != nil { 169 return nil, fmt.Errorf("api.Put(%s): %w", &req, err) 170 } 171 172 return &output, nil 173 } 174 175 // AddSeries adds a new series to Sonarr. 176 func (s *Sonarr) AddSeries(series *AddSeriesInput) (*Series, error) { 177 return s.AddSeriesContext(context.Background(), series) 178 } 179 180 // AddSeriesContext adds a new series to Sonarr. 181 func (s *Sonarr) AddSeriesContext(ctx context.Context, series *AddSeriesInput) (*Series, error) { 182 var body bytes.Buffer 183 if err := json.NewEncoder(&body).Encode(series); err != nil { 184 return nil, fmt.Errorf("json.Marshal(%s): %w", bpSeries, err) 185 } 186 187 var output Series 188 189 req := starr.Request{URI: bpSeries, Query: make(url.Values), Body: &body} 190 if err := s.PostInto(ctx, req, &output); err != nil { 191 return nil, fmt.Errorf("api.Post(%s): %w", &req, err) 192 } 193 194 return &output, nil 195 } 196 197 // GetSeriesByID locates and returns a series by DB [series] ID. 198 func (s *Sonarr) GetSeriesByID(seriesID int64) (*Series, error) { 199 return s.GetSeriesByIDContext(context.Background(), seriesID) 200 } 201 202 // GetSeriesByIDContext locates and returns a series by DB [series] ID. 203 func (s *Sonarr) GetSeriesByIDContext(ctx context.Context, seriesID int64) (*Series, error) { 204 var output Series 205 206 req := starr.Request{URI: path.Join(bpSeries, fmt.Sprint(seriesID))} 207 if err := s.GetInto(ctx, req, &output); err != nil { 208 return nil, fmt.Errorf("api.Get(%s): %w", &req, err) 209 } 210 211 return &output, nil 212 } 213 214 // GetSeriesLookup searches for a series [in Servarr] using a search term or a tvdbid. 215 // Provide a search term or a tvdbid. If you provide both, tvdbID is used. 216 func (s *Sonarr) GetSeriesLookup(term string, tvdbID int64) ([]*Series, error) { 217 return s.GetSeriesLookupContext(context.Background(), term, tvdbID) 218 } 219 220 // GetSeriesLookupContext searches for a series [in Servarr] using a search term or a tvdbid. 221 // Provide a search term or a tvdbid. If you provide both, tvdbID is used. 222 func (s *Sonarr) GetSeriesLookupContext(ctx context.Context, term string, tvdbID int64) ([]*Series, error) { 223 var output []*Series 224 225 req := starr.Request{URI: path.Join(bpSeries, "lookup"), Query: make(url.Values)} 226 if tvdbID > 0 { 227 req.Query.Add("term", "tvdbid:"+fmt.Sprint(tvdbID)) 228 } else { 229 req.Query.Add("term", term) 230 } 231 232 if err := s.GetInto(ctx, req, &output); err != nil { 233 return nil, fmt.Errorf("api.Get(%s): %w", &req, err) 234 } 235 236 return output, nil 237 } 238 239 // Lookup will search for series matching the specified search term. 240 // Searches for new shows on TheTVDB.com utilizing sonarr.tv's caching and augmentation proxy. 241 func (s *Sonarr) Lookup(term string) ([]*Series, error) { 242 return s.LookupContext(context.Background(), term) 243 } 244 245 // Lookup will search for series matching the specified search term. 246 // Searches for new shows on TheTVDB.com utilizing sonarr.tv's caching and augmentation proxy. 247 func (s *Sonarr) LookupContext(ctx context.Context, term string) ([]*Series, error) { 248 return s.GetSeriesLookupContext(ctx, term, 0) 249 } 250 251 // DeleteSeries removes a single Series. 252 // deleteFiles flag defines the deleteFiles query parameter. 253 // importExclude defines the addImportListExclusion query parameter. 254 func (s *Sonarr) DeleteSeries(seriesID int, deleteFiles bool, importExclude bool) error { 255 return s.DeleteSeriesContext(context.Background(), seriesID, deleteFiles, importExclude) 256 } 257 258 // DeleteSeries removes a single Series. 259 // deleteFiles flag defines the deleteFiles query parameter. 260 // importExclude defines the addImportListExclusion query parameter. 261 func (s *Sonarr) DeleteSeriesContext(ctx context.Context, seriesID int, deleteFiles bool, importExclude bool) error { 262 req := starr.Request{URI: path.Join(bpSeries, fmt.Sprint(seriesID)), Query: make(url.Values)} 263 req.Query.Add("deleteFiles", fmt.Sprint(deleteFiles)) 264 req.Query.Add("addImportListExclusion", fmt.Sprint(importExclude)) 265 266 if err := s.DeleteAny(ctx, req); err != nil { 267 return fmt.Errorf("api.Delete(%s): %w", &req, err) 268 } 269 270 return nil 271 } 272 273 // DeleteSeriesDefault defines the behaviour to set deleteFiles to true and addImportListExclusion to false. 274 func (s *Sonarr) DeleteSeriesDefault(seriesID int) error { 275 return s.DeleteSeriesContext(context.Background(), seriesID, true, false) 276 }