func readarrQualityProfiles(r *http.Request) (int, interface{}) {
	profiles, err := getReadarr(r).GetQualityProfiles()
	if err != nil {
		return http.StatusInternalServerError, fmt.Errorf("getting profiles: %w", err)
	}
	p := make(map[int64]string)
	for i := range profiles {
		p[profiles[i].ID] = profiles[i].Name
	}
	return http.StatusOK, p
}
Get the quality profiles from readarr.
package apps
import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"
	"github.com/Notifiarr/notifiarr/pkg/mnd"
	"github.com/gorilla/mux"
	"golift.io/cnfg"
	"golift.io/starr"
	"golift.io/starr/readarr"
)
func (a *Apps) readarrHandlers() {
	a.HandleAPIpath(starr.Readarr, "/add", readarrAddBook, "POST")
	a.HandleAPIpath(starr.Readarr, "/author/{authorid:[0-9]+}", readarrGetAuthor, "GET")
	a.HandleAPIpath(starr.Readarr, "/check/{grid:[0-9]+}", readarrCheckBook, "GET")
	a.HandleAPIpath(starr.Readarr, "/get/{bookid:[0-9]+}", readarrGetBook, "GET")
	a.HandleAPIpath(starr.Readarr, "/metadataProfiles", readarrMetaProfiles, "GET")
	a.HandleAPIpath(starr.Readarr, "/qualityProfiles", readarrQualityProfiles, "GET")
	a.HandleAPIpath(starr.Readarr, "/qualityProfile", readarrGetQualityProfile, "GET")
	a.HandleAPIpath(starr.Readarr, "/qualityProfile", readarrAddQualityProfile, "POST")
	a.HandleAPIpath(starr.Readarr, "/qualityProfile/{profileID:[0-9]+}", readarrUpdateQualityProfile, "PUT")
	a.HandleAPIpath(starr.Readarr, "/rootFolder", readarrRootFolders, "GET")
	a.HandleAPIpath(starr.Readarr, "/search/{query}", readarrSearchBook, "GET")
	a.HandleAPIpath(starr.Readarr, "/update", readarrUpdateBook, "PUT")
	a.HandleAPIpath(starr.Readarr, "/tag", readarrGetTags, "GET")
	a.HandleAPIpath(starr.Readarr, "/tag/{tid:[0-9]+}/{label}", readarrUpdateTag, "PUT")
	a.HandleAPIpath(starr.Readarr, "/tag/{label}", readarrSetTag, "PUT")
	a.HandleAPIpath(starr.Readarr, "/updateauthor", readarrUpdateAuthor, "PUT")
	a.HandleAPIpath(starr.Readarr, "/command/search/{bookid:[0-9]+}", readarrTriggerSearchBook, "GET")
}
type ReadarrConfig struct {
	Name      string        `toml:"name" xml:"name"`
	Interval  cnfg.Duration `toml:"interval" xml:"interval"`
	StuckItem bool          `toml:"stuck_items" xml:"stuck_items"`
	CheckQ    *uint         `toml:"check_q" xml:"check_q"`
	*starr.Config
	*readarr.Readarr
	Errorf func(string, ...interface{}) `toml:"-" xml:"-"`
}
func (r *ReadarrConfig) setup(timeout time.Duration) {
	r.Readarr = readarr.New(r.Config)
	if r.Timeout.Duration == 0 {
		r.Timeout.Duration = timeout
	}
	if r.StuckItem && r.CheckQ == nil {
		i := uint(0)
		r.CheckQ = &i
	} else if r.CheckQ != nil {
		r.StuckItem = true
	}
	if u, err := r.GetURL(); err != nil {
		r.Errorf("Checking Readarr Path: %v", err)
	} else if u = strings.TrimRight(u, "/"); u != r.URL {
		r.Errorf("Readarr URL fixed: %s -> %s (continuing)", r.URL, u)
		r.URL = u
	}
}
func readarrAddBook(r *http.Request) (int, interface{}) {
	payload := &readarr.AddBookInput{}
	switch err := json.NewDecoder(r.Body).Decode(payload); {
	case err != nil:
		return http.StatusBadRequest, fmt.Errorf("decoding payload: %w", err)
	case len(payload.Editions) != 1:
		return http.StatusUnprocessableEntity,
			fmt.Errorf("invalid editions count; only 1 allowed: %d, %w", len(payload.Editions), ErrNoGRID)
	case payload.Editions[0].ForeignEditionID == "":
		return http.StatusUnprocessableEntity, fmt.Errorf("0: %w", ErrNoGRID)
	}
	app := getReadarr(r)
	m, err := app.GetBook(payload.Editions[0].ForeignEditionID)
	if err != nil {
		return http.StatusServiceUnavailable, fmt.Errorf("checking book: %w", err)
	} else if len(m) > 0 {
		return http.StatusConflict, readarrData(m[0])
	}
	book, err := app.AddBook(payload)
	if err != nil {
		return http.StatusInternalServerError, fmt.Errorf("adding book: %w", err)
	}
	return http.StatusCreated, book
}
func readarrGetAuthor(r *http.Request) (int, interface{}) {
	authorID, _ := strconv.ParseInt(mux.Vars(r)["authorid"], mnd.Base10, mnd.Bits64)
	author, err := getReadarr(r).GetAuthorByID(authorID)
	if err != nil {
		return http.StatusServiceUnavailable, fmt.Errorf("getting author: %w", err)
	}
	return http.StatusOK, author
}
func readarrData(book *readarr.Book) map[string]interface{} {
	hasFile := false
	if book.Statistics != nil {
		hasFile = book.Statistics.SizeOnDisk > 0
	}
	return map[string]interface{}{
		"id":        book.ID,
		"hasFile":   hasFile,
		"monitored": book.Monitored,
	}
}
func readarrCheckBook(r *http.Request) (int, interface{}) {
	grid := mux.Vars(r)["grid"]
	m, err := getReadarr(r).GetBook(grid)
	if err != nil {
		return http.StatusServiceUnavailable, fmt.Errorf("checking book: %w", err)
	} else if len(m) > 0 {
		return http.StatusConflict, readarrData(m[0])
	}
	return http.StatusOK, http.StatusText(http.StatusNotFound)
}
func readarrGetBook(r *http.Request) (int, interface{}) {
	bookID, _ := strconv.ParseInt(mux.Vars(r)["bookid"], mnd.Base10, mnd.Bits64)
	book, err := getReadarr(r).GetBookByID(bookID)
	if err != nil {
		return http.StatusServiceUnavailable, fmt.Errorf("checking book: %w", err)
	}
	return http.StatusOK, book
}
func readarrTriggerSearchBook(r *http.Request) (int, interface{}) {
	bookID, _ := strconv.ParseInt(mux.Vars(r)["bookid"], mnd.Base10, mnd.Bits64)
	output, err := getReadarr(r).SendCommand(&readarr.CommandRequest{
		Name:    "BookSearch",
		BookIDs: []int64{bookID},
	})
	if err != nil {
		return http.StatusServiceUnavailable, fmt.Errorf("checking book: %w", err)
	}
	return http.StatusOK, output.Status
}
func readarrMetaProfiles(r *http.Request) (int, interface{}) {
	profiles, err := getReadarr(r).GetMetadataProfiles()
	if err != nil {
		return http.StatusInternalServerError, fmt.Errorf("getting profiles: %w", err)
	}
	p := make(map[int64]string)
	for i := range profiles {
		p[profiles[i].ID] = profiles[i].Name
	}
	return http.StatusOK, p
}