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 descending, then seeder count descending
- Returns the top entry
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 |