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  }