Documentation
¶
Index ¶
- func CanonicalInfoHash(rawHash, magnet string) string
- type Client
- func (c *Client) GetIndexers() ([]Indexer, error)
- func (c *Client) ListIndexers() ([]Indexer, error)
- func (c *Client) Search(query, category string, indexers []string) ([]Result, error)
- func (c *Client) SearchOnIndexer(ctx context.Context, indexerID, query, category string) ([]Result, error)
- func (c *Client) StreamSearch(ctx context.Context, query, category string, indexers []string, ...) error
- func (c *Client) TestConnection() error
- type Indexer
- type IndexerHit
- type Result
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CanonicalInfoHash ¶
CanonicalInfoHash returns a lowercase 40-char hex btih for a Jackett result, or "" if none can be derived. It prefers the dedicated InfoHash field and falls back to the magnet's xt=urn:btih: param when the field is absent — many indexers ship only the magnet.
Both the hex (40-char) and base32 (32-char) btih encodings are coerced to hex so the SAME torrent always yields the SAME key. Without this, indexers that disagree on encoding/case (or omit the field) scatter one torrent across several group buckets, rendering as duplicate cards that all Play the same magnet — the "two cards, same torrent" bug. Exported so the SSE dedup layer can re-canonicalize cached rows that predate this normalization.
Types ¶
type Client ¶
func (*Client) GetIndexers ¶
func (*Client) ListIndexers ¶
ListIndexers returns the configured indexers via Jackett's torznab `t=indexers` endpoint. This works with API key (no admin cookie needed), unlike /api/v2.0/indexers.
func (*Client) SearchOnIndexer ¶
func (c *Client) SearchOnIndexer(ctx context.Context, indexerID, query, category string) ([]Result, error)
SearchOnIndexer queries one specific indexer (by id). Used for parallel fan-out.
func (*Client) StreamSearch ¶
func (c *Client) StreamSearch( ctx context.Context, query, category string, indexers []string, perIndexerTimeout time.Duration, onHit func(IndexerHit), ) error
StreamSearch fires concurrent queries to every configured indexer (or a filtered subset). Invokes `onHit` from a single goroutine as each indexer finishes. Blocks until all done or ctx cancels.
`indexers` filters which to query; empty or ["all"] means every configured indexer. `perIndexerTimeout` caps each individual query (default 30s); slow indexers don't block the rest.
func (*Client) TestConnection ¶
TestConnection probes Jackett to confirm URL + apikey are valid.
We deliberately hit the torznab caps endpoint (`/api/v2.0/indexers/all/results/torznab/api?t=caps`) instead of `/api/v2.0/indexers`. Recent Jackett versions only allow the latter after an admin login (the apikey alone gets redirected to /UI/Login), but the torznab path always honors apikey — so it's the right "is Jackett reachable + authorized" probe. A 200 means everything works; 302 means we hit the login redirect (key invalid or Jackett misconfigured); other codes are real errors.
type IndexerHit ¶
type IndexerHit struct {
IndexerID string
IndexerName string
Results []Result
Err error
Duration time.Duration
}
IndexerHit is what the streaming search callback receives per indexer. One IndexerHit means one indexer is finished (or failed) — emit its results.
type Result ¶
type Result struct {
Title string `json:"title"`
Tracker string `json:"tracker"`
TrackerID string `json:"trackerId"`
CategoryID int `json:"categoryId"`
Category string `json:"category"`
Size int64 `json:"size"`
Seeders int `json:"seeders"`
Leechers int `json:"leechers"`
Age string `json:"age"`
MagnetURI string `json:"magnetUri"`
Link string `json:"link"`
InfoHash string `json:"infoHash"`
PublishDate string `json:"publishDate"`
}