140 lines
3.7 KiB
Go
140 lines
3.7 KiB
Go
package indexer
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
"homelab.lan/music-agregator/internal/config"
|
|
)
|
|
|
|
type JacketIndexer struct {
|
|
cfg config.Config
|
|
client *http.Client
|
|
}
|
|
|
|
func NewIndexer(cfg config.Config) Indexer {
|
|
return &JacketIndexer{
|
|
cfg: cfg,
|
|
client: &http.Client{
|
|
Timeout: 60 * time.Second,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (indexer *JacketIndexer) BuildSearchURL(query string, limit int32, tracker string) string {
|
|
searchTracker := "all"
|
|
if len(tracker) != 0 {
|
|
searchTracker = tracker
|
|
}
|
|
|
|
uri := fmt.Sprintf("%v/api/v2.0/indexers/%v/results/torznab?apikey=%v&cat=3010,3040&q=%v&t=search",
|
|
indexer.cfg.Indexer.Url, searchTracker, indexer.cfg.Indexer.ApiKey, url.QueryEscape(query))
|
|
if limit > 0 {
|
|
uri += fmt.Sprintf("&limit=%d", limit)
|
|
}
|
|
|
|
return uri
|
|
}
|
|
|
|
func (indexer *JacketIndexer) Search(query string, limit int32, tracker string) (SearchResult, error) {
|
|
uri := indexer.BuildSearchURL(query, limit, tracker)
|
|
return indexer.FetchURL(uri)
|
|
}
|
|
|
|
type JackettError struct {
|
|
Code string `xml:"code,attr"`
|
|
Description string `xml:"description,attr"`
|
|
}
|
|
|
|
func (e *JackettError) Error() string {
|
|
return fmt.Sprintf("jackett error %s: %s", e.Code, e.Description)
|
|
}
|
|
|
|
func (indexer *JacketIndexer) FetchURL(uri string) (SearchResult, error) {
|
|
log.Trace().Str("uri", uri).Msg("jackett request")
|
|
|
|
req, err := http.NewRequest("GET", uri, nil)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("error creating request")
|
|
return SearchResult{}, err
|
|
}
|
|
|
|
start := time.Now()
|
|
resp, err := indexer.client.Do(req)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("error making search request")
|
|
return SearchResult{}, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("error reading search response body")
|
|
return SearchResult{}, err
|
|
}
|
|
|
|
log.Trace().
|
|
Int("status", resp.StatusCode).
|
|
Int("body_bytes", len(body)).
|
|
Dur("duration", time.Since(start)).
|
|
Msg("jackett response")
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
var jackettErr JackettError
|
|
if xmlErr := xml.Unmarshal(body, &jackettErr); xmlErr == nil && jackettErr.Code != "" {
|
|
log.Error().Str("code", jackettErr.Code).Str("description", jackettErr.Description).Msg("jackett returned error")
|
|
return SearchResult{}, &jackettErr
|
|
}
|
|
return SearchResult{}, fmt.Errorf("jackett returned HTTP %d", resp.StatusCode)
|
|
}
|
|
|
|
var searchResult SearchResult
|
|
if err := xml.Unmarshal(body, &searchResult); err != nil {
|
|
log.Error().Err(err).Msg("error parsing search XML")
|
|
return SearchResult{}, err
|
|
}
|
|
|
|
log.Trace().Int("items", len(searchResult.Items)).Msg("jackett XML parsed")
|
|
|
|
return searchResult, nil
|
|
}
|
|
|
|
func (indexer *JacketIndexer) Capabilities(indexerName string) (IndexerCapabilities, error) {
|
|
url := indexer.cfg.Indexer.Url
|
|
uri := fmt.Sprintf("%v/api/v2.0/indexers/%v/results/torznab/api?apikey=%v&t=caps", url, indexerName, indexer.cfg.Indexer.ApiKey)
|
|
|
|
req, err := http.NewRequest("GET", uri, nil)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Error creating request")
|
|
return IndexerCapabilities{}, err
|
|
}
|
|
|
|
resp, err := indexer.client.Do(req)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Error making capabilities request")
|
|
return IndexerCapabilities{}, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Error reading response body")
|
|
return IndexerCapabilities{}, err
|
|
}
|
|
|
|
var capabilities IndexerCapabilities
|
|
if err := xml.Unmarshal(body, &capabilities); err != nil {
|
|
log.Error().Err(err).Msg("Error parsing capabilities XML")
|
|
return IndexerCapabilities{}, err
|
|
}
|
|
|
|
log.Debug().Str("server", capabilities.Server.Title).Msg("Parsed capabilities")
|
|
|
|
return capabilities, nil
|
|
}
|