indexer

package
v0.0.0-...-d771ed5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 29, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package indexer manages indexer configurations and orchestrates release searches.

⚠ Before changing anything in this file — especially `seedWeight`, `applyMinSeedersFilter`, or the `Search()` sort/filter closure — run:

go test ./internal/core/indexer/...

The tests in service_test.go lock down the dead-torrent regression fix: the 847-seeders-on-a-5-year-old-release bug would have been caught by `TestSearchSort_ReproducesIncident` if the suite had existed at the time. See pilot/CLAUDE.md "Regression guard: dead-torrent release search" for the full list of guarded files and the rationale.

Index

Constants

This section is empty.

Variables

View Source
var ErrNotFound = errors.New("indexer not found")

ErrNotFound is returned when an indexer config does not exist.

Functions

This section is empty.

Types

type Config

type Config struct {
	ID         string
	Name       string
	Kind       string // "torznab", "newznab"
	Enabled    bool
	Priority   int
	MinSeeders int // releases below this threshold get tagged with a filter reason
	Settings   json.RawMessage
	CreatedAt  time.Time
	UpdatedAt  time.Time
}

Config is the domain representation of a stored indexer configuration.

type CreateRequest

type CreateRequest struct {
	Name       string
	Kind       string
	Enabled    bool
	Priority   int
	MinSeeders int // 0 means "use the sensible default (5)"
	Settings   json.RawMessage
}

CreateRequest carries the fields needed to create an indexer config.

type GrabRequest

type GrabRequest struct {
	SeriesID     string
	EpisodeID    string // optional; empty for season-pack grabs
	SeasonNumber int    // optional; 0 when not known
	Release      plugin.Release
	IndexerID    string
	// Source is "interactive" (user-initiated via UI) or "auto_search"
	// (triggered by scheduled episode search). Controls whether the stall
	// watcher triggers automatic re-search on failure.
	Source string
}

GrabRequest carries the fields needed to record a grab history entry.

type PackType

type PackType string

PackType classifies a release by what it covers: a full season, multiple episodes, a single episode, or unknown. Populated from parser.EpisodeInfo.

const (
	PackTypeUnknown PackType = ""
	PackTypeSeason  PackType = "season"
	PackTypeMulti   PackType = "multi_episode"
	PackTypeEpisode PackType = "episode"
)

type SearchResult

type SearchResult struct {
	plugin.Release
	// IndexerID is the DB UUID of the indexer that returned this release.
	IndexerID    string
	IndexerName  string
	QualityScore int
	// PackType classifies the release as a season pack, multi-episode pack,
	// single episode, or unknown. Parsed from the release title once at
	// ingestion so the sort comparator and API response can share it.
	PackType PackType
	// EpisodeCount is how many discrete episodes the release covers. For
	// season packs it is sentinel-large (see effectiveEpisodeCount) so the
	// ranking comparator prefers full packs over partial packs within the
	// same quality tier.
	EpisodeCount int
	// FilterReasons lists all the reasons this release was filtered out
	// of the "active" set. If non-empty, the UI should render the row
	// grayed with the reasons shown and an "override" button. Empty means
	// the release is in the active/top list. We return filtered rows
	// intentionally so users can see what was dropped and why — hiding
	// them creates "content loss traps" when false-positive blocklists
	// or overly-aggressive min_seeders thresholds silently eat content.
	FilterReasons []string
}

SearchResult pairs a plugin release with its source indexer identity.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service manages indexer configuration and search orchestration.

func NewService

func NewService(q db.Querier, reg *registry.Registry, bus *events.Bus, rl *ratelimit.Registry, logger *slog.Logger) *Service

NewService creates a new Service. A nil logger falls back to slog.Default() so existing callers continue to work without migration.

func (*Service) Create

func (s *Service) Create(ctx context.Context, req CreateRequest) (Config, error)

Create persists a new indexer configuration.

func (*Service) CreateGrab

func (s *Service) CreateGrab(ctx context.Context, req GrabRequest) (db.GrabHistory, error)

CreateGrab records a grab history entry linking the release to the series and optionally to a specific episode. Pass empty strings for fields that are not applicable (e.g. season-pack grabs have no episodeID).

func (*Service) Delete

func (s *Service) Delete(ctx context.Context, id string) error

Delete removes an indexer config. Returns ErrNotFound if absent.

func (*Service) Get

func (s *Service) Get(ctx context.Context, id string) (Config, error)

Get returns an indexer config by ID. Returns ErrNotFound if absent.

func (*Service) GetGrabByInfoHash

func (s *Service) GetGrabByInfoHash(ctx context.Context, infoHash string) (db.GrabHistory, error)

GetGrabByInfoHash returns the most-recent grab for the given info_hash. Returns sql.ErrNoRows when none matches — callers map that to 404 at the API edge. Used by the /grabs/by-hash/{hash}/research endpoint to find the original grab so it can blocklist the dead release and retry.

func (*Service) GetRecent

func (s *Service) GetRecent(ctx context.Context) ([]SearchResult, error)

GetRecent fetches the most recent releases from all enabled indexers via their RSS feeds. Results from all indexers are merged and returned together. Errors from individual indexers are collected; a combined error is returned alongside any results that were gathered.

func (*Service) GrabHistory

func (s *Service) GrabHistory(ctx context.Context, seriesID string) ([]db.GrabHistory, error)

GrabHistory returns the grab history for a series, newest first.

func (*Service) List

func (s *Service) List(ctx context.Context) ([]Config, error)

List returns all indexer configs, ordered by priority then name.

func (*Service) Search

func (s *Service) Search(ctx context.Context, q plugin.SearchQuery) ([]SearchResult, error)

Search queries all enabled indexers with the given search query and returns results sorted by quality score, then an age-aware log10 seed weight, then release age. Errors from individual indexers are collected; a combined error is returned alongside any results that were gathered.

func (*Service) Test

func (s *Service) Test(ctx context.Context, id string) error

Test instantiates the indexer plugin and verifies connectivity.

func (*Service) Update

func (s *Service) Update(ctx context.Context, id string, req UpdateRequest) (Config, error)

Update replaces the mutable fields of an indexer config. Returns ErrNotFound if the indexer does not exist.

func (*Service) UpdateGrabDownloadClient

func (s *Service) UpdateGrabDownloadClient(ctx context.Context, arg db.UpdateGrabDownloadClientParams) error

UpdateGrabDownloadClient records the download client and client-assigned item ID on an existing grab history entry. Called after a release is successfully submitted to a download client.

func (*Service) UpdateGrabInfoHash

func (s *Service) UpdateGrabInfoHash(ctx context.Context, grabID, infoHash string) error

UpdateGrabInfoHash records the BitTorrent info_hash on a grab row. The stall watcher uses this to correlate Haul's stall events back to the grab that initiated them.

func (*Service) UpdateGrabStatus

func (s *Service) UpdateGrabStatus(ctx context.Context, grabID, status string) error

UpdateGrabStatus sets the download_status on a grab row. Used when the grab handler can't make forward progress (e.g. download client rejects the release because the indexer response had no download URL) — without this, the row stays at "queued" forever and shows up in the search guardrail as "already grabbed", suppressing the retry the user wants.

type UpdateRequest

type UpdateRequest = CreateRequest

UpdateRequest carries the fields needed to update an indexer config.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL