Files
kindexr/internal/torznab/xml.go
T
enki 2e491e20c1 feat: Phase 1 — Nostr reader + Torznab API
Reader (internal/nostr):
- Connects to all configured relays via WebSocket
- Subscribes to kind 2003 (NIP-35) events since (now - backfill_days)
- Parses x/title/file/tracker/i/t tags into DB rows
- Reconnects with exponential backoff (5s → 5min) on disconnect

Torznab API (internal/torznab):
- GET /api?t=caps  — XML capabilities doc (exact shape Sonarr expects)
- GET /api?t=search — FTS5 full-text search over title+description
- All other t= types (tvsearch, movie, etc.) route to the same search handler
- API key auth via ?apikey= query param; 401 XML error on missing/bad key
- Magnet links built from info_hash + event tracker tags

DB (internal/db/queries.go):
- InsertTorrent with upsert + related rows (trackers, tags, files)
- Search with FTS5 + optional category filter (parent cat expands to range)
- GetAPIKey, CreateAPIKey, UpsertRelay, UpdateRelaySync/LastEvent

CLI (cmd/kindexr-cli):
- apikey create --label <name> [--visibility all|wot|curated]

Health endpoint now reports live relays_connected count.
2026-05-16 19:19:49 -07:00

115 lines
2.8 KiB
Go

package torznab
import "encoding/xml"
// Caps XML types.
type Caps struct {
XMLName xml.Name `xml:"caps"`
Server CapsServer `xml:"server"`
Limits CapsLimits `xml:"limits"`
Searching CapsSearching `xml:"searching"`
Categories CapsCategories `xml:"categories"`
}
type CapsServer struct {
Version string `xml:"version,attr"`
Title string `xml:"title,attr"`
Strapline string `xml:"strapline,attr"`
Email string `xml:"email,attr"`
URL string `xml:"url,attr"`
Image string `xml:"image,attr"`
}
type CapsLimits struct {
Max int `xml:"max,attr"`
Default int `xml:"default,attr"`
}
type CapsSearching struct {
Search CapsSearch `xml:"search"`
TVSearch CapsSearch `xml:"tv-search"`
MovieSearch CapsSearch `xml:"movie-search"`
MusicSearch CapsSearch `xml:"music-search"`
AudioSearch CapsSearch `xml:"audio-search"`
BookSearch CapsSearch `xml:"book-search"`
}
type CapsSearch struct {
Available string `xml:"available,attr"`
SupportedParams string `xml:"supportedParams,attr"`
}
type CapsCategories struct {
Categories []CapsCategory `xml:"category"`
}
type CapsCategory struct {
ID int `xml:"id,attr"`
Name string `xml:"name,attr"`
SubCats []CapsSubcat `xml:"subcat"`
}
type CapsSubcat struct {
ID int `xml:"id,attr"`
Name string `xml:"name,attr"`
}
// RSS/Torznab search result types.
type RSS struct {
XMLName xml.Name `xml:"rss"`
Version string `xml:"version,attr"`
Atom string `xml:"xmlns:atom,attr"`
Torznab string `xml:"xmlns:torznab,attr"`
Channel RSSChannel `xml:"channel"`
}
type RSSChannel struct {
AtomLink RSSAtomLink `xml:"atom:link"`
Title string `xml:"title"`
Description string `xml:"description"`
Link string `xml:"link"`
Language string `xml:"language"`
Category string `xml:"category"`
Items []RSSItem `xml:"item"`
}
type RSSAtomLink struct {
Rel string `xml:"rel,attr"`
Type string `xml:"type,attr"`
}
type RSSItem struct {
Title string `xml:"title"`
GUID RSSGUID `xml:"guid"`
Link string `xml:"link"`
PubDate string `xml:"pubDate"`
Size int64 `xml:"size"`
Enclosure RSSEnclosure `xml:"enclosure"`
Attrs []TorznabAttr `xml:"torznab:attr"`
}
type RSSGUID struct {
IsPermaLink string `xml:"isPermaLink,attr"`
Value string `xml:",chardata"`
}
type RSSEnclosure struct {
URL string `xml:"url,attr"`
Length int64 `xml:"length,attr"`
Type string `xml:"type,attr"`
}
type TorznabAttr struct {
Name string `xml:"name,attr"`
Value string `xml:"value,attr"`
}
// ErrorResponse is the Torznab XML error envelope.
type ErrorResponse struct {
XMLName xml.Name `xml:"error"`
Code int `xml:"code,attr"`
Descr string `xml:"description,attr"`
}