Indexer service pipeline
The indexer service watches the monitor table and searches torrent indexers for pending episodes and seasons. When it finds a match it inserts a download queue entry for the downloader service to pick up.
Startup
- Database connection initialised (same PostgreSQL as core).
- Provider packages self-register via
init()(kbdex and Prowlarr are blank-imported incmd/indexer/main.go). PollAndQueuestarts in a background goroutine.- Health/test HTTP server starts on port
8082.
Poll loop
Runs every prowlarrInterval seconds (default 10 s).
Timer fires
│
▼
Query DB for one pending monitor
WHERE monitored = true
AND available = false
AND (status = 'pending'
OR status = 'searching' for > 10 min)
ORDER BY is_season DESC ← season packs searched first
│
▼
Mark monitor status = 'searching'
│
▼
Build SearchRequest {title, alternate_titles, season, episode, library_id, ...}
│
▼
GetEnabled(db) → active providers (each enabled via its kbdexEnabled / prowlarrEnabled toggle; results from all are merged)
│
▼
Provider.Search(ctx, db, req) [see provider detail below]
│
▼
buildMatchLog → filter by title similarity + season + episode
│
▼
pickBest → quality rank + seeders sort, blacklist check
│
├── match found → insert download_queue row, set monitor status = 'queued'
└── no match → set monitor status = 'pending' (retry next cycle)
Provider: kbdex
kbdex is an AniDB-aware search service. It handles title resolution and episode filtering server-side.
SearchRequest
│
▼
lookupAnidbID: SELECT source_id FROM media WHERE id = library_id
│
▼
GET {kbdexUrl}/search?anidb_id=N[&season=S][&episode=E]
│ Results cached on disk keyed by "kbdex:{id}:s{S}:e{E}"
│ Cache TTL = prowlarrCacheAge
▼
[]TorrentResult (already filtered by kbdex server-side via anitopy)
Provider: Prowlarr
Prowlarr is a generic indexer aggregator. kbarr queries it with multiple title variants and merges results.
SearchRequest
│
▼
Build query variants from title + alternate titles
├── Season searches: "Title S01", "Title Season 1", "Title Batch", ...
└── Episode searches: "Title S01E05", "Title - 05", ...
│
▼
For each variant: GET {prowlarrUrl}/api/v1/search?query=...&apikey=...
│ Each variant result set cached on disk (prowlarrCacheAge TTL)
▼
Merge + deduplicate by DownloadURL
│
▼
[]TorrentResult
Client-side filtering (shared by both providers)
After the provider returns results, buildMatchLog and pickBest apply a second pass:
buildMatchLog — runs each torrent filename through anitogo (Go torrent filename parser), then checks:
- Title similarity ≥
matchThreshold(default 80%) using Sørensen–Dice bigrams - Parsed season matches the monitor's season
- For season packs: rejects individual episode releases
- For episodes: parsed episode matches exactly
pickBest — from passing results:
- Removes blacklisted torrents
- Drops results above the
preferredQualitycap (e.g. drops 4K if preference is 1080p) - Sorts by quality rank, subtitle language, release-group preference, and seeder count (order depends on
releaseGroupPriorityMode) - Returns the top entry
Release group preference ranking
Two settings control release-group-based ordering:
| Setting | Type | Default | Description |
|---|---|---|---|
releaseGroupPreferenceOrder | comma-separated string | All 11 canonical groups | Ordered list of preferred release groups. First = most preferred. Anime Time is accepted as an alias for ASW. |
releaseGroupPriorityMode | enum | tie-break | How strongly the group preference is applied. |
Tie-break mode (default): Compares the configured group order only when quality rank and subtitle language are equal, acting as a tiebreaker before seeders. Releases from preferred groups are favoured but do not outrank a higher-quality release or one with a matching subtitle.
Strong mode: Compares the configured group order immediately after the quality-cap gate, before quality rank itself. A preferred-group release can outrank a higher-quality release from a non-preferred group. The quality cap still applies (e.g. 4K excluded if preference is 1080p).
Fallback: When no candidate matches any configured group or the preference list is empty, the existing quality → subtitle → seeders ordering applies unchanged. Unknown or missing release groups sit after all preferred groups.
Key packages
| Package | Role |
|---|---|
internal/indexer/provider/ | Provider interface, registry, shared cache utilities |
internal/indexer/provider/kbdex/ | kbdex provider implementation |
internal/indexer/provider/prowlarr/ | Prowlarr provider implementation |
internal/indexer/service/ | Poll loop, match scoring, download queue insertion |
internal/parser/ | anitogo wrapper with memoisation and Japanese episode fallbacks |