golift.io/starr@v1.0.0/readarr/book.go (about) 1 package readarr 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 const bpBook = APIver + "/book" 16 17 // Book is the /api/v1/book endpoint among others, and gets used across this package. 18 type Book struct { 19 Added time.Time `json:"added"` 20 AnyEditionOk bool `json:"anyEditionOk"` 21 AuthorID int64 `json:"authorId"` 22 AuthorTitle string `json:"authorTitle"` 23 Disambiguation string `json:"disambiguation,omitempty"` 24 Editions []*Edition `json:"editions"` 25 ForeignBookID string `json:"foreignBookId"` 26 Genres []string `json:"genres"` 27 ID int64 `json:"id"` 28 Images []*starr.Image `json:"images"` 29 Links []*starr.Link `json:"links"` 30 Monitored bool `json:"monitored"` 31 Grabbed bool `json:"grabbed"` 32 Overview string `json:"overview"` 33 PageCount int `json:"pageCount"` 34 Ratings *starr.Ratings `json:"ratings"` 35 ReleaseDate time.Time `json:"releaseDate"` 36 RemoteCover string `json:"remoteCover,omitempty"` 37 SeriesTitle string `json:"seriesTitle"` 38 Statistics *Statistics `json:"statistics,omitempty"` 39 Title string `json:"title"` 40 TitleSlug string `json:"titleSlug"` 41 Author *Author `json:"author"` 42 } 43 44 // Edition is more Book meta data. 45 type Edition struct { 46 ID int64 `json:"id"` 47 BookID int64 `json:"bookId"` 48 ForeignEditionID string `json:"foreignEditionId"` 49 TitleSlug string `json:"titleSlug"` 50 Isbn13 string `json:"isbn13"` 51 Asin string `json:"asin"` 52 Title string `json:"title"` 53 Overview string `json:"overview"` 54 Format string `json:"format"` 55 Publisher string `json:"publisher"` 56 PageCount int `json:"pageCount"` 57 ReleaseDate time.Time `json:"releaseDate"` 58 Images []*starr.Image `json:"images"` 59 Links []*starr.Link `json:"links"` 60 Ratings *starr.Ratings `json:"ratings"` 61 Monitored bool `json:"monitored"` 62 ManualAdd bool `json:"manualAdd"` 63 IsEbook bool `json:"isEbook"` 64 } 65 66 // AddBookInput is the input to add a book. 67 type AddBookInput struct { 68 Monitored bool `json:"monitored"` 69 Tags []int `json:"tags"` 70 AddOptions *AddBookOptions `json:"addOptions"` // Contains Search. 71 Author *AddBookAuthor `json:"author"` // Contains Author ID 72 Editions []*AddBookEdition `json:"editions"` // contains GRID Edition ID 73 ForeignBookID string `json:"foreignBookId"` // GRID Book ID. 74 } 75 76 // AddBookOptions is part of AddBookInput. 77 type AddBookOptions struct { 78 AddType string `json:"addType,omitempty"` 79 SearchForNewBook bool `json:"searchForNewBook"` 80 } 81 82 // AddBookAuthor is part of AddBookInput. 83 type AddBookAuthor struct { 84 Monitored bool `json:"monitored"` // true? 85 QualityProfileID int64 `json:"qualityProfileId"` // required 86 MetadataProfileID int64 `json:"metadataProfileId"` // required 87 ForeignAuthorID string `json:"foreignAuthorId"` // required 88 RootFolderPath string `json:"rootFolderPath"` // required 89 Tags []int `json:"tags"` 90 AddOptions *AddAuthorOptions `json:"addOptions"` 91 } 92 93 // AddAuthorOptions is part of AddBookAuthor. 94 type AddAuthorOptions struct { 95 SearchForMissingBooks bool `json:"searchForMissingBooks"` 96 Monitored bool `json:"monitored"` 97 Monitor string `json:"monitor"` 98 BooksToMonitor []int64 `json:"booksToMonitor"` 99 } 100 101 // AddBookEdition is part of AddBookInput. 102 type AddBookEdition struct { 103 Title string `json:"title"` // Edition Title 104 TitleSlug string `json:"titleSlug"` // Slugs are dumb 105 Images []*starr.Image `json:"images"` // this is dumb too 106 ForeignEditionID string `json:"foreignEditionId"` // GRID ID 107 Monitored bool `json:"monitored"` // true 108 ManualAdd bool `json:"manualAdd"` // true 109 } 110 111 // GetBook returns books. All books are returned if gridID is empty. 112 func (r *Readarr) GetBook(gridID string) ([]*Book, error) { 113 return r.GetBookContext(context.Background(), gridID) 114 } 115 116 // GetBookContext returns books. All books are returned if gridID is empty. 117 func (r *Readarr) GetBookContext(ctx context.Context, gridID string) ([]*Book, error) { 118 req := starr.Request{URI: bpBook, Query: make(url.Values)} 119 if gridID != "" { 120 req.Query.Add("titleSlug", gridID) // this may change, but works for now. 121 } 122 123 var output []*Book 124 125 if err := r.GetInto(ctx, req, &output); err != nil { 126 return nil, fmt.Errorf("api.Get(%s): %w", &req, err) 127 } 128 129 return output, nil 130 } 131 132 // GetBookByID returns a book. 133 func (r *Readarr) GetBookByID(bookID int64) (*Book, error) { 134 return r.GetBookByIDContext(context.Background(), bookID) 135 } 136 137 // GetBookByIDContext returns a book. 138 func (r *Readarr) GetBookByIDContext(ctx context.Context, bookID int64) (*Book, error) { 139 var output Book 140 141 req := starr.Request{URI: path.Join(bpBook, fmt.Sprint(bookID))} 142 if err := r.GetInto(ctx, req, &output); err != nil { 143 return nil, fmt.Errorf("api.Get(%s): %w", &req, err) 144 } 145 146 return &output, nil 147 } 148 149 // UpdateBook updates a book in place. 150 func (r *Readarr) UpdateBook(bookID int64, book *Book, moveFiles bool) error { 151 return r.UpdateBookContext(context.Background(), bookID, book, moveFiles) 152 } 153 154 // UpdateBookContext updates a book in place. 155 func (r *Readarr) UpdateBookContext(ctx context.Context, bookID int64, book *Book, moveFiles bool) error { 156 var body bytes.Buffer 157 if err := json.NewEncoder(&body).Encode(book); err != nil { 158 return fmt.Errorf("json.Marshal(%s): %w", bpBook, err) 159 } 160 161 req := starr.Request{ 162 URI: path.Join(bpBook, fmt.Sprint(bookID)), 163 Query: make(url.Values), 164 Body: &body, 165 } 166 req.Query.Add("moveFiles", fmt.Sprint(moveFiles)) 167 168 var output interface{} // do not know what this looks like. 169 170 if err := r.PutInto(ctx, req, &output); err != nil { 171 return fmt.Errorf("api.Put(%s): %w", &req, err) 172 } 173 174 return nil 175 } 176 177 // AddBook adds a new book to the library. 178 func (r *Readarr) AddBook(book *AddBookInput) (*Book, error) { 179 return r.AddBookContext(context.Background(), book) 180 } 181 182 // AddBookContext adds a new book to the library. 183 func (r *Readarr) AddBookContext(ctx context.Context, book *AddBookInput) (*Book, error) { 184 var body bytes.Buffer 185 if err := json.NewEncoder(&body).Encode(book); err != nil { 186 return nil, fmt.Errorf("json.Marshal(%s): %w", bpBook, err) 187 } 188 189 req := starr.Request{ 190 URI: bpBook, 191 Query: make(url.Values), 192 Body: &body, 193 } 194 195 var output Book 196 if err := r.PostInto(ctx, req, &output); err != nil { 197 return nil, fmt.Errorf("api.Post(%s): %w", &req, err) 198 } 199 200 return &output, nil 201 } 202 203 // Lookup will search for books matching the specified search term. 204 func (r *Readarr) Lookup(term string) ([]*Book, error) { 205 return r.LookupContext(context.Background(), term) 206 } 207 208 // LookupContext will search for books matching the specified search term. 209 func (r *Readarr) LookupContext(ctx context.Context, term string) ([]*Book, error) { 210 var output []*Book 211 212 if term == "" { 213 return output, nil 214 } 215 216 req := starr.Request{URI: path.Join(bpBook, "lookup"), Query: make(url.Values)} 217 req.Query.Set("term", term) 218 219 if err := r.GetInto(ctx, req, &output); err != nil { 220 return nil, fmt.Errorf("api.Get(%s): %w", &req, err) 221 } 222 223 return output, nil 224 } 225 226 // DeleteBook removes a Book from the database. 227 // Setting deleteFiles true will delete all content for the Book. 228 func (r *Readarr) DeleteBook(bookID int64, deleteFiles, addImportExclusion bool) error { 229 return r.DeleteBookContext(context.Background(), bookID, deleteFiles, addImportExclusion) 230 } 231 232 // DeleteBookContext removes a Book from the database. 233 // Setting deleteFiles true will delete all content for the Book. 234 func (r *Readarr) DeleteBookContext(ctx context.Context, bookID int64, deleteFiles, addImportExclusion bool) error { 235 req := starr.Request{URI: path.Join(bpBook, fmt.Sprint(bookID)), Query: make(url.Values)} 236 req.Query.Set("deleteFiles", fmt.Sprint(deleteFiles)) 237 req.Query.Set("addImportListExclusion", fmt.Sprint(addImportExclusion)) 238 239 if err := r.DeleteAny(ctx, req); err != nil { 240 return fmt.Errorf("api.Delete(%s): %w", &req, err) 241 } 242 243 return nil 244 }