2e491e20c1
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.
115 lines
2.8 KiB
Go
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"`
|
|
}
|