Documentation
¶
Index ¶
- Constants
- Variables
- func GetIndexerDomainFromInfo(indexerInfo map[int]EnabledIndexerInfo, indexerID int) string
- func GetIndexerNameFromInfo(indexerInfo map[int]EnabledIndexerInfo, indexerID int) string
- func WithSearchPriority(ctx context.Context, priority RateLimitPriority) context.Context
- type ActivityStatus
- type CategoryInfo
- type Client
- func (c *Client) Download(ctx context.Context, downloadURL string) ([]byte, error)
- func (c *Client) FetchCaps(ctx context.Context, indexerID string) (*torznabCaps, error)
- func (c *Client) GetCapabilitiesDirect() (*gojackett.Indexers, error)
- func (c *Client) Search(ctx context.Context, indexer string, params map[string]string) ([]Result, error)
- func (c *Client) SearchAll(ctx context.Context, params map[string]string) ([]Result, error)
- func (c *Client) SearchDirect(ctx context.Context, params map[string]string) ([]Result, error)
- type DiscoveryResult
- type DownloadError
- type DownloadRateLimitError
- type EnabledIndexerInfo
- type HistoryRecorder
- type IndexerCooldownStatus
- type IndexerInfo
- type IndexerOutcome
- type IndexerOutcomeStore
- type IndexerStore
- type IndexersResponse
- type JackettIndexer
- type JobCallbacks
- type MagnetDownloadError
- type RateLimitOptions
- type RateLimitPriority
- type RateLimitWaitError
- type RateLimiter
- func (r *RateLimiter) ClearCooldown(indexerID int)
- func (r *RateLimiter) GetCooldownIndexers() map[int]time.Time
- func (r *RateLimiter) IsInCooldown(indexerID int) (bool, time.Time)
- func (r *RateLimiter) LoadCooldowns(cooldowns map[int]time.Time)
- func (r *RateLimiter) NextWait(indexer *models.TorznabIndexer, opts *RateLimitOptions) time.Duration
- func (r *RateLimiter) RecordFailure(indexerID int) time.Duration
- func (r *RateLimiter) RecordRequest(indexerID int, ts time.Time)
- func (r *RateLimiter) RecordSuccess(indexerID int)
- func (r *RateLimiter) SetCooldown(indexerID int, until time.Time)
- func (r *RateLimiter) WaitForMinInterval(ctx context.Context, indexer *models.TorznabIndexer, opts *RateLimitOptions) error
- type Result
- type SchedulerJobStatus
- type SchedulerStatus
- type SchedulerTaskStatus
- type SearchCacheConfig
- type SearchCacheMetadata
- type SearchHistoryBuffer
- func (b *SearchHistoryBuffer) Count() int
- func (b *SearchHistoryBuffer) GetByIndexer(indexerID int, limit int) []SearchHistoryEntry
- func (b *SearchHistoryBuffer) GetRecent(limit int) []SearchHistoryEntry
- func (b *SearchHistoryBuffer) Push(entry SearchHistoryEntry) uint64
- func (b *SearchHistoryBuffer) Stats() SearchHistoryStats
- type SearchHistoryEntry
- type SearchHistoryEntryWithOutcome
- type SearchHistoryResponse
- type SearchHistoryResponseWithOutcome
- type SearchHistoryStats
- type SearchResponse
- type SearchResult
- type Service
- func (s *Service) DownloadTorrent(ctx context.Context, req TorrentDownloadRequest) ([]byte, error)
- func (s *Service) FilterIndexersForCapabilities(ctx context.Context, requested []int, requiredCaps []string, categories []int) ([]int, error)
- func (s *Service) FlushSearchCache(ctx context.Context) (int64, error)
- func (s *Service) GetActivityStatus(ctx context.Context) (*ActivityStatus, error)
- func (s *Service) GetEnabledIndexersInfo(ctx context.Context) (map[int]EnabledIndexerInfo, error)
- func (s *Service) GetEnabledTrackerDomains(ctx context.Context) ([]string, error)
- func (s *Service) GetIndexerDomain(ctx context.Context, indexerName string) (string, error)
- func (s *Service) GetIndexerName(ctx context.Context, id int) string
- func (s *Service) GetIndexers(ctx context.Context) (*IndexersResponse, error)
- func (s *Service) GetOptimalCategoriesForIndexers(ctx context.Context, requestedCategories []int, indexerIDs []int) []int
- func (s *Service) GetRecentSearches(ctx context.Context, scope string, limit int) ([]*models.TorznabRecentSearch, error)
- func (s *Service) GetSearchCacheStats(ctx context.Context) (*models.TorznabSearchCacheStats, error)
- func (s *Service) GetSearchHistory(_ context.Context, limit int) (*SearchHistoryResponseWithOutcome, error)
- func (s *Service) GetSearchHistoryStats(_ context.Context) (*SearchHistoryStats, error)
- func (s *Service) GetTrackerDomainDetails(ctx context.Context) ([]TrackerDomainInfo, error)
- func (s *Service) GetTrackerDomains(ctx context.Context) ([]string, error)
- func (s *Service) InvalidateSearchCache(ctx context.Context, indexerIDs []int) (int64, error)
- func (s *Service) MapCategoriesToIndexerCapabilities(ctx context.Context, indexer *models.TorznabIndexer, requestedCategories []int) []int
- func (s *Service) Recent(ctx context.Context, limit int, indexerIDs []int, ...) error
- func (s *Service) ReportIndexerOutcome(jobID uint64, indexerID int, outcome string, addedCount int, message string)
- func (s *Service) Search(ctx context.Context, req *TorznabSearchRequest) error
- func (s *Service) SearchGeneric(ctx context.Context, req *TorznabSearchRequest) error
- func (s *Service) SearchWithScope(ctx context.Context, req *TorznabSearchRequest, scope string) error
- func (s *Service) SyncIndexerCaps(ctx context.Context, indexerID int) (*models.TorznabIndexer, error)
- func (s *Service) UpdateSearchCacheSettings(ctx context.Context, ttlMinutes int) (*models.TorznabSearchCacheSettings, error)
- type ServiceOption
- type SubmitRequest
- type TorrentDownloadRequest
- type TorznabSearchRequest
- type TrackerDomainInfo
Constants ¶
const ( // Movies CategoryMovies = 2000 CategoryMoviesSD = 2030 CategoryMoviesHD = 2040 CategoryMovies4K = 2045 CategoryMovies3D = 2050 // TV CategoryTV = 5000 CategoryTVSD = 5030 CategoryTVHD = 5040 CategoryTV4K = 5045 CategoryTVSport = 5060 CategoryTVAnime = 5070 CategoryTVDocumentary = 5080 // XXX CategoryXXX = 6000 CategoryXXXDVD = 6010 CategoryXXXWMV = 6020 CategoryXXXXviD = 6030 CategoryXXXx264 = 6040 CategoryXXXPack = 6050 CategoryXXXImageSet = 6060 CategoryXXXOther = 6070 // Audio CategoryAudio = 3000 // PC CategoryPC = 4000 // Books CategoryBooks = 7000 CategoryBooksEbook = 7020 CategoryBooksComics = 7030 CacheModeDefault = "" CacheModeBypass = "bypass" )
Torznab category constants
const ( DefaultSearchCacheTTL = defaultSearchCacheTTL MinSearchCacheTTL = minSearchCacheTTL MinSearchCacheTTLMinutes = int(minSearchCacheTTL / time.Minute) DefaultSearchCacheTTLMinutes = int(defaultSearchCacheTTL / time.Minute) )
Exported constants for cache settings.
Variables ¶
var ErrMissingIndexerIdentifier = errors.New("torznab indexer identifier is required for caps sync")
ErrMissingIndexerIdentifier signals that the Torznab backend requires an indexer ID to fetch caps.
Functions ¶
func GetIndexerDomainFromInfo ¶
func GetIndexerDomainFromInfo(indexerInfo map[int]EnabledIndexerInfo, indexerID int) string
GetIndexerDomainFromInfo returns the indexer domain for a given ID using cached indexer info
func GetIndexerNameFromInfo ¶
func GetIndexerNameFromInfo(indexerInfo map[int]EnabledIndexerInfo, indexerID int) string
GetIndexerNameFromInfo returns the indexer name for a given ID using cached indexer info
func WithSearchPriority ¶
func WithSearchPriority(ctx context.Context, priority RateLimitPriority) context.Context
WithSearchPriority annotates a context with a desired search priority for scheduling.
Types ¶
type ActivityStatus ¶
type ActivityStatus struct {
Scheduler *SchedulerStatus `json:"scheduler,omitempty"`
CooldownIndexers []IndexerCooldownStatus `json:"cooldownIndexers"`
}
ActivityStatus represents the current activity state of the indexer service
type CategoryInfo ¶
CategoryInfo represents a Torznab category
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client wraps the Torznab backend client implementation
func NewClient ¶
func NewClient(baseURL, apiKey string, basicUsername, basicPassword *string, backend models.TorznabBackend, timeoutSeconds int) *Client
NewClient creates a new Torznab client for the desired backend
func (*Client) FetchCaps ¶
FetchCaps retrieves the Torznab caps document for the configured backend/indexer.
func (*Client) GetCapabilitiesDirect ¶
GetCapabilitiesDirect gets capabilities from a direct Torznab endpoint
func (*Client) Search ¶
func (c *Client) Search(ctx context.Context, indexer string, params map[string]string) ([]Result, error)
Search performs a search on a specific indexer or "all"
type DiscoveryResult ¶ added in v1.9.0
type DiscoveryResult struct {
Indexers []JackettIndexer `json:"indexers"`
Warnings []string `json:"warnings,omitempty"`
}
DiscoveryResult contains discovered indexers and any warnings from partial failures
func DiscoverJackettIndexers ¶
func DiscoverJackettIndexers(ctx context.Context, baseURL, apiKey string, basicUsername, basicPassword *string) (DiscoveryResult, error)
DiscoverJackettIndexers discovers all configured indexers from a Jackett instance. The context is used to cancel in-flight capability fetches if the request is cancelled. Returns a DiscoveryResult containing indexers and any warnings about partial failures.
type DownloadError ¶ added in v1.9.0
DownloadError represents an HTTP error during torrent download. It preserves the status code for rate-limit detection and retry logic.
func (*DownloadError) Error ¶ added in v1.9.0
func (e *DownloadError) Error() string
func (*DownloadError) Is ¶ added in v1.9.0
func (e *DownloadError) Is(target error) bool
func (*DownloadError) IsRateLimited ¶ added in v1.9.0
func (e *DownloadError) IsRateLimited() bool
IsRateLimited returns true if this error indicates rate limiting (HTTP 429).
type DownloadRateLimitError ¶ added in v1.9.0
type DownloadRateLimitError struct {
IndexerID int
IndexerName string
ResumeAt time.Time
// Queued indicates whether the request was queued for automatic retry.
// TODO: Set to true when download retry queue is implemented.
Queued bool
}
DownloadRateLimitError indicates that a download was blocked due to rate limiting. It includes retry information to help callers decide whether to queue for later.
func (*DownloadRateLimitError) Error ¶ added in v1.9.0
func (e *DownloadRateLimitError) Error() string
func (*DownloadRateLimitError) Is ¶ added in v1.9.0
func (e *DownloadRateLimitError) Is(target error) bool
type EnabledIndexerInfo ¶
EnabledIndexerInfo holds both name and domain information for an enabled indexer
type HistoryRecorder ¶
type HistoryRecorder interface {
Record(entry SearchHistoryEntry)
}
HistoryRecorder is the interface for recording search history.
func NewHistoryRecorder ¶
func NewHistoryRecorder(buffer *SearchHistoryBuffer) HistoryRecorder
NewHistoryRecorder creates a new history recorder with the given buffer.
type IndexerCooldownStatus ¶
type IndexerCooldownStatus struct {
IndexerID int `json:"indexerId"`
IndexerName string `json:"indexerName"`
CooldownEnd time.Time `json:"cooldownEnd"`
Reason string `json:"reason,omitempty"`
}
IndexerCooldownStatus represents an indexer in cooldown
type IndexerInfo ¶
type IndexerInfo struct {
// ID of the indexer
ID string `json:"id"`
// Name of the indexer
Name string `json:"name"`
// Description
Description string `json:"description,omitempty"`
// Type (public, semi-private, private)
Type string `json:"type"`
// Configured (whether the indexer is configured)
Configured bool `json:"configured"`
// Supported categories
Categories []CategoryInfo `json:"categories,omitempty"`
}
IndexerInfo represents information about a Jackett indexer
type IndexerOutcome ¶
type IndexerOutcome struct {
Outcome string `json:"outcome"` // "added", "failed", "no_match", ""
AddedCount int `json:"addedCount,omitempty"` // Number of torrents added from this indexer
Message string `json:"message,omitempty"`
RecordedAt time.Time `json:"recordedAt"`
}
IndexerOutcome represents the cross-seed outcome for a specific indexer's search results.
type IndexerOutcomeStore ¶
type IndexerOutcomeStore struct {
// contains filtered or unexported fields
}
IndexerOutcomeStore tracks cross-seed outcomes per (JobID, IndexerID). Uses a bounded map with a ring buffer for FIFO eviction.
func NewIndexerOutcomeStore ¶
func NewIndexerOutcomeStore(maxSize int) *IndexerOutcomeStore
NewIndexerOutcomeStore creates a new outcome store with the given capacity.
func (*IndexerOutcomeStore) Count ¶
func (s *IndexerOutcomeStore) Count() int
Count returns the current number of outcomes stored.
func (*IndexerOutcomeStore) Get ¶
func (s *IndexerOutcomeStore) Get(jobID uint64, indexerID int) (IndexerOutcome, bool)
Get retrieves the outcome for a specific (jobID, indexerID) pair.
type IndexerStore ¶
type IndexerStore interface {
Get(ctx context.Context, id int) (*models.TorznabIndexer, error)
List(ctx context.Context) ([]*models.TorznabIndexer, error)
ListEnabled(ctx context.Context) ([]*models.TorznabIndexer, error)
GetDecryptedAPIKey(indexer *models.TorznabIndexer) (string, error)
GetDecryptedBasicPassword(indexer *models.TorznabIndexer) (string, error)
GetCapabilities(ctx context.Context, indexerID int) ([]string, error)
SetCapabilities(ctx context.Context, indexerID int, capabilities []string) error
SetCategories(ctx context.Context, indexerID int, categories []models.TorznabIndexerCategory) error
SetLimits(ctx context.Context, indexerID, limitDefault, limitMax int) error
RecordLatency(ctx context.Context, indexerID int, operationType string, latencyMs int, success bool) error
RecordError(ctx context.Context, indexerID int, errorMessage, errorCode string) error
ListRateLimitCooldowns(ctx context.Context) ([]models.TorznabIndexerCooldown, error)
UpsertRateLimitCooldown(ctx context.Context, indexerID int, resumeAt time.Time, cooldown time.Duration, reason string) error
DeleteRateLimitCooldown(ctx context.Context, indexerID int) error
}
IndexerStore defines the interface for indexer storage operations
type IndexersResponse ¶
type IndexersResponse struct {
Indexers []IndexerInfo `json:"indexers"`
}
IndexersResponse represents the list of available indexers
type JackettIndexer ¶
type JackettIndexer struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
Configured bool `json:"configured"`
Backend models.TorznabBackend `json:"backend"`
Caps []string `json:"caps,omitempty"`
Categories []models.TorznabIndexerCategory `json:"categories,omitempty"`
}
JackettIndexer represents an indexer from Jackett's indexer list
type JobCallbacks ¶
type JobCallbacks struct {
// OnComplete fires for each indexer when it finishes (success or error).
// Called in a goroutine - safe to do blocking work.
OnComplete func(jobID uint64, indexer *models.TorznabIndexer, results []Result, coverage []int, err error)
// OnJobDone fires once after ALL indexers complete (optional, can be nil).
// Called in a goroutine - safe to do blocking work.
OnJobDone func(jobID uint64)
}
JobCallbacks defines the callbacks for async job completion.
type MagnetDownloadError ¶ added in v1.13.0
type MagnetDownloadError struct {
MagnetURL string
}
MagnetDownloadError indicates that the download endpoint redirected to a magnet URL. Callers should add the magnet directly to the torrent client.
func (*MagnetDownloadError) Error ¶ added in v1.13.0
func (e *MagnetDownloadError) Error() string
type RateLimitOptions ¶
type RateLimitOptions struct {
Priority RateLimitPriority
MinInterval time.Duration
MaxWait time.Duration
}
type RateLimitPriority ¶
type RateLimitPriority string
const ( RateLimitPriorityInteractive RateLimitPriority = "interactive" RateLimitPriorityRSS RateLimitPriority = "rss" RateLimitPriorityCompletion RateLimitPriority = "completion" RateLimitPriorityBackground RateLimitPriority = "background" )
func SearchPriority ¶ added in v1.13.0
func SearchPriority(ctx context.Context) (RateLimitPriority, bool)
SearchPriority returns the desired scheduler priority embedded in ctx via WithSearchPriority.
type RateLimitWaitError ¶
type RateLimitWaitError struct {
IndexerID int
IndexerName string
Wait time.Duration
MaxWait time.Duration
Priority RateLimitPriority
}
func (*RateLimitWaitError) Error ¶
func (e *RateLimitWaitError) Error() string
func (*RateLimitWaitError) Is ¶
func (e *RateLimitWaitError) Is(target error) bool
type RateLimiter ¶
type RateLimiter struct {
// contains filtered or unexported fields
}
RateLimiter tracks per-indexer request timing and computes wait times. It does not block; the scheduler queries it to make dispatch decisions.
func NewRateLimiter ¶
func NewRateLimiter(minInterval time.Duration) *RateLimiter
func (*RateLimiter) ClearCooldown ¶
func (r *RateLimiter) ClearCooldown(indexerID int)
func (*RateLimiter) GetCooldownIndexers ¶
func (r *RateLimiter) GetCooldownIndexers() map[int]time.Time
GetCooldownIndexers returns a list of indexer IDs that are currently in cooldown
func (*RateLimiter) IsInCooldown ¶
func (r *RateLimiter) IsInCooldown(indexerID int) (bool, time.Time)
IsInCooldown checks if an indexer is currently in cooldown without blocking
func (*RateLimiter) LoadCooldowns ¶
func (r *RateLimiter) LoadCooldowns(cooldowns map[int]time.Time)
LoadCooldowns seeds the rate limiter with pre-existing cooldown windows.
func (*RateLimiter) NextWait ¶
func (r *RateLimiter) NextWait(indexer *models.TorznabIndexer, opts *RateLimitOptions) time.Duration
NextWait returns the amount of time the caller would need to wait before a request could be made against the provided indexer using the supplied options. This is a non-blocking helper used by the job scheduler to decide if a request can run immediately.
func (*RateLimiter) RecordFailure ¶
func (r *RateLimiter) RecordFailure(indexerID int) time.Duration
RecordFailure increments the escalation level and sets a cooldown based on the new level. This implements Prowlarr-style escalating backoff for rate limit failures.
func (*RateLimiter) RecordRequest ¶
func (r *RateLimiter) RecordRequest(indexerID int, ts time.Time)
func (*RateLimiter) RecordSuccess ¶
func (r *RateLimiter) RecordSuccess(indexerID int)
RecordSuccess resets the escalation level to 0 on successful request.
func (*RateLimiter) SetCooldown ¶
func (r *RateLimiter) SetCooldown(indexerID int, until time.Time)
func (*RateLimiter) WaitForMinInterval ¶ added in v1.13.0
func (r *RateLimiter) WaitForMinInterval(ctx context.Context, indexer *models.TorznabIndexer, opts *RateLimitOptions) error
WaitForMinInterval blocks until a new request slot is available for the given indexer according to the configured min interval and priority multiplier, then reserves the slot by recording the request time.
Note: this intentionally does NOT wait for cooldown windows. Callers that care about cooldowns should check IsInCooldown separately (downloads typically return immediately when in cooldown).
type Result ¶
type Result struct {
Tracker string
IndexerID int
Title string
Link string
Details string
GUID string
PublishDate time.Time
Category string
Size int64
Seeders int
Peers int
DownloadVolumeFactor float64
UploadVolumeFactor float64
Imdb string
// Attributes stores every Torznab attribute with lowercase keys from RSS item attr entries (see convertRssToResults normalization).
Attributes map[string]string
}
Result represents a single search result (simplified format)
type SchedulerJobStatus ¶
type SchedulerJobStatus struct {
JobID uint64 `json:"jobId"`
TotalTasks int `json:"totalTasks"`
CompletedTasks int `json:"completedTasks"`
}
SchedulerJobStatus represents a job's completion status
type SchedulerStatus ¶
type SchedulerStatus struct {
QueuedTasks []SchedulerTaskStatus `json:"queuedTasks"`
InFlightTasks []SchedulerTaskStatus `json:"inFlightTasks"`
ActiveJobs []SchedulerJobStatus `json:"activeJobs"`
QueueLength int `json:"queueLength"`
WorkerCount int `json:"workerCount"`
WorkersInUse int `json:"workersInUse"`
}
SchedulerStatus represents the current state of the scheduler
type SchedulerTaskStatus ¶
type SchedulerTaskStatus struct {
JobID uint64 `json:"jobId"`
TaskID uint64 `json:"taskId"`
IndexerID int `json:"indexerId"`
IndexerName string `json:"indexerName"`
Priority string `json:"priority"`
CreatedAt time.Time `json:"createdAt"`
IsRSS bool `json:"isRss"`
}
SchedulerTaskStatus represents a single task's status
type SearchCacheConfig ¶
SearchCacheConfig defines caching behaviour for Torznab search queries.
type SearchCacheMetadata ¶
type SearchCacheMetadata struct {
Hit bool `json:"hit"`
Scope string `json:"scope"`
Source string `json:"source"`
CachedAt time.Time `json:"cachedAt"`
ExpiresAt time.Time `json:"expiresAt"`
LastUsed *time.Time `json:"lastUsed,omitempty"`
}
SearchCacheMetadata describes how the response was sourced.
type SearchHistoryBuffer ¶
type SearchHistoryBuffer struct {
// contains filtered or unexported fields
}
SearchHistoryBuffer is a thread-safe ring buffer for live search history.
func NewSearchHistoryBuffer ¶
func NewSearchHistoryBuffer(capacity int) *SearchHistoryBuffer
NewSearchHistoryBuffer creates a new ring buffer with the given capacity.
func (*SearchHistoryBuffer) Count ¶
func (b *SearchHistoryBuffer) Count() int
Count returns the current number of entries in the buffer.
func (*SearchHistoryBuffer) GetByIndexer ¶
func (b *SearchHistoryBuffer) GetByIndexer(indexerID int, limit int) []SearchHistoryEntry
GetByIndexer returns recent entries for a specific indexer.
func (*SearchHistoryBuffer) GetRecent ¶
func (b *SearchHistoryBuffer) GetRecent(limit int) []SearchHistoryEntry
GetRecent returns the most recent entries, up to the specified limit. Entries are returned in reverse chronological order (newest first).
func (*SearchHistoryBuffer) Push ¶
func (b *SearchHistoryBuffer) Push(entry SearchHistoryEntry) uint64
Push adds an entry to the buffer and returns its assigned ID.
func (*SearchHistoryBuffer) Stats ¶
func (b *SearchHistoryBuffer) Stats() SearchHistoryStats
type SearchHistoryEntry ¶
type SearchHistoryEntry struct {
ID uint64 `json:"id"`
JobID uint64 `json:"jobId"`
TaskID uint64 `json:"taskId"`
// Indexer details
IndexerID int `json:"indexerId"`
IndexerName string `json:"indexerName"`
// Search parameters
Query string `json:"query,omitempty"`
ReleaseName string `json:"releaseName,omitempty"` // Original full release name
Params map[string]string `json:"params,omitempty"` // Raw params sent to indexer
Categories []int `json:"categories,omitempty"`
ContentType string `json:"contentType,omitempty"`
Priority string `json:"priority"`
SearchMode string `json:"searchMode,omitempty"`
// Results
Status string `json:"status"` // success, error, skipped, rate_limited
ResultCount int `json:"resultCount"`
// Timing
StartedAt time.Time `json:"startedAt"`
CompletedAt time.Time `json:"completedAt"`
DurationMs int `json:"durationMs"`
// Error details (if applicable)
ErrorMessage string `json:"errorMessage,omitempty"`
}
SearchHistoryEntry captures details of a completed search task.
type SearchHistoryEntryWithOutcome ¶
type SearchHistoryEntryWithOutcome struct {
SearchHistoryEntry
Outcome string `json:"outcome,omitempty"` // "added", "failed", "no_match", ""
AddedCount int `json:"addedCount,omitempty"` // Torrents added from this indexer
}
SearchHistoryEntryWithOutcome extends SearchHistoryEntry with outcome data for API responses.
type SearchHistoryResponse ¶
type SearchHistoryResponse struct {
Entries []SearchHistoryEntry `json:"entries"`
Total int `json:"total"`
Source string `json:"source"` // "memory" or "database"
}
SearchHistoryResponse is the API response for search history queries.
type SearchHistoryResponseWithOutcome ¶
type SearchHistoryResponseWithOutcome struct {
Entries []SearchHistoryEntryWithOutcome `json:"entries"`
Total int `json:"total"`
Source string `json:"source"` // "memory" or "database"
}
SearchHistoryResponseWithOutcome is the API response that includes outcome data.
type SearchHistoryStats ¶
type SearchHistoryStats struct {
Count int `json:"count"`
Capacity int `json:"capacity"`
ByStatus map[string]int `json:"byStatus"`
ByPriority map[string]int `json:"byPriority"`
AvgDuration float64 `json:"avgDurationMs"`
}
Stats returns basic statistics about the buffer.
type SearchResponse ¶
type SearchResponse struct {
Results []SearchResult `json:"results"`
Total int `json:"total"`
Cache *SearchCacheMetadata `json:"cache,omitempty"`
Partial bool `json:"partial,omitempty"`
// JobID identifies this search for outcome tracking (cross-seed)
JobID uint64 `json:"jobId,omitempty"`
}
type SearchResult ¶
type SearchResult struct {
// Indexer name
Indexer string `json:"indexer"`
// Indexer identifier
IndexerID int `json:"indexer_id"`
// Title of the release
Title string `json:"title"`
// Download URL for the torrent
DownloadURL string `json:"download_url"`
// Info URL (details page)
InfoURL string `json:"info_url,omitempty"`
// Size in bytes
Size int64 `json:"size"`
// Seeders count
Seeders int `json:"seeders"`
// Leechers count
Leechers int `json:"leechers"`
// Category ID
CategoryID int `json:"category_id"`
// Category name
CategoryName string `json:"category_name"`
// Published date
PublishDate time.Time `json:"publish_date"`
// Download volume factor (0.0 = free, 1.0 = normal)
DownloadVolumeFactor float64 `json:"download_volume_factor"`
// Upload volume factor
UploadVolumeFactor float64 `json:"upload_volume_factor"`
// GUID (unique identifier)
GUID string `json:"guid"`
// InfoHashV1 if available
InfoHashV1 string `json:"infohash_v1,omitempty"`
// InfoHashV2 if available
InfoHashV2 string `json:"infohash_v2,omitempty"`
// IMDb ID if available
IMDbID string `json:"imdb_id,omitempty"`
// TVDb ID if available
TVDbID string `json:"tvdb_id,omitempty"`
// Source parsed from release name (e.g., "WEB-DL", "BluRay", "HDTV")
Source string `json:"source,omitempty"`
// Collection/streaming service parsed from release name (e.g., "AMZN", "NF", "HULU", "MAX")
Collection string `json:"collection,omitempty"`
// Release group parsed from release name
Group string `json:"group,omitempty"`
}
SearchResult represents a single search result from Jackett
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service provides Jackett integration for Torznab searching
func NewService ¶
func NewService(indexerStore IndexerStore, opts ...ServiceOption) *Service
NewService creates a new Jackett service
func (*Service) DownloadTorrent ¶
DownloadTorrent fetches the raw torrent bytes for a specific indexer result. It respects rate limits, retries on transient failures, and records 429 responses in the shared rate limiter to prevent hammering indexers.
func (*Service) FilterIndexersForCapabilities ¶
func (s *Service) FilterIndexersForCapabilities(ctx context.Context, requested []int, requiredCaps []string, categories []int) ([]int, error)
FilterIndexersForCapabilities restricts requested indexers to those matching required caps/categories.
func (*Service) FlushSearchCache ¶
FlushSearchCache removes all cached search responses.
func (*Service) GetActivityStatus ¶
func (s *Service) GetActivityStatus(ctx context.Context) (*ActivityStatus, error)
GetActivityStatus returns the current activity status including scheduler state and cooldowns
func (*Service) GetEnabledIndexersInfo ¶
GetEnabledIndexersInfo retrieves both names and domains for all enabled indexers in a single operation
func (*Service) GetEnabledTrackerDomains ¶
GetEnabledTrackerDomains extracts domain names from enabled indexers only
func (*Service) GetIndexerDomain ¶
GetIndexerDomain gets the tracker domain for a specific indexer by name
func (*Service) GetIndexerName ¶
GetIndexerName resolves a Torznab indexer ID to its configured name.
func (*Service) GetIndexers ¶
func (s *Service) GetIndexers(ctx context.Context) (*IndexersResponse, error)
GetIndexers retrieves all configured Torznab indexers
func (*Service) GetOptimalCategoriesForIndexers ¶
func (s *Service) GetOptimalCategoriesForIndexers(ctx context.Context, requestedCategories []int, indexerIDs []int) []int
GetOptimalCategoriesForIndexers returns categories optimized for the given indexers based on their capabilities
func (*Service) GetRecentSearches ¶
func (s *Service) GetRecentSearches(ctx context.Context, scope string, limit int) ([]*models.TorznabRecentSearch, error)
GetRecentSearches returns the most recently cached search queries for UI hints.
func (*Service) GetSearchCacheStats ¶
GetSearchCacheStats returns summary stats for the cache table.
func (*Service) GetSearchHistory ¶
func (s *Service) GetSearchHistory(_ context.Context, limit int) (*SearchHistoryResponseWithOutcome, error)
GetSearchHistory returns recent search history entries from the in-memory buffer, merged with any recorded cross-seed outcomes.
func (*Service) GetSearchHistoryStats ¶
func (s *Service) GetSearchHistoryStats(_ context.Context) (*SearchHistoryStats, error)
GetSearchHistoryStats returns statistics about search history.
func (*Service) GetTrackerDomainDetails ¶
func (s *Service) GetTrackerDomainDetails(ctx context.Context) ([]TrackerDomainInfo, error)
GetTrackerDomainDetails returns detailed information about tracker domains from all indexers
func (*Service) GetTrackerDomains ¶
GetTrackerDomains extracts domain names from all configured indexers
func (*Service) InvalidateSearchCache ¶
InvalidateSearchCache clears cached searches referencing the provided indexers.
func (*Service) MapCategoriesToIndexerCapabilities ¶
func (s *Service) MapCategoriesToIndexerCapabilities(ctx context.Context, indexer *models.TorznabIndexer, requestedCategories []int) []int
MapCategoriesToIndexerCapabilities maps requested categories to categories supported by the specific indexer
func (*Service) Recent ¶
func (s *Service) Recent(ctx context.Context, limit int, indexerIDs []int, callback func(*SearchResponse, error)) error
Recent fetches the latest releases across selected indexers without a search query.
func (*Service) ReportIndexerOutcome ¶
func (s *Service) ReportIndexerOutcome(jobID uint64, indexerID int, outcome string, addedCount int, message string)
ReportIndexerOutcome records a cross-seed outcome for a specific indexer's search results. Called by the cross-seed service after processing search results.
func (*Service) Search ¶
func (s *Service) Search(ctx context.Context, req *TorznabSearchRequest) error
Search searches enabled Torznab indexers with intelligent category detection
func (*Service) SearchGeneric ¶
func (s *Service) SearchGeneric(ctx context.Context, req *TorznabSearchRequest) error
SearchGeneric performs a general Torznab search across specified or all enabled indexers
func (*Service) SearchWithScope ¶ added in v1.13.0
func (s *Service) SearchWithScope(ctx context.Context, req *TorznabSearchRequest, scope string) error
SearchWithScope performs a Torznab search with a custom cache scope. This is used by dir-scan and other features that need cache separation.
func (*Service) SyncIndexerCaps ¶
func (s *Service) SyncIndexerCaps(ctx context.Context, indexerID int) (*models.TorznabIndexer, error)
SyncIndexerCaps fetches and persists Torznab capabilities and categories for an indexer.
func (*Service) UpdateSearchCacheSettings ¶
func (s *Service) UpdateSearchCacheSettings(ctx context.Context, ttlMinutes int) (*models.TorznabSearchCacheSettings, error)
UpdateSearchCacheSettings updates the TTL configuration at runtime.
type ServiceOption ¶
type ServiceOption func(*Service)
ServiceOption configures optional behaviour on the Jackett service.
func WithIndexerOutcomes ¶
func WithIndexerOutcomes(capacity int) ServiceOption
WithIndexerOutcomes enables cross-seed outcome tracking per (jobID, indexerID). Pass 0 to use the default capacity (1000 entries).
func WithSearchCache ¶
func WithSearchCache(cache searchCacheStore, cfg SearchCacheConfig) ServiceOption
WithSearchCache wires the search cache store and configuration.
func WithSearchHistory ¶
func WithSearchHistory(capacity int) ServiceOption
WithSearchHistory enables in-memory search history tracking with the given capacity. Pass 0 to use the default capacity (500 entries).
func WithTorrentCache ¶
func WithTorrentCache(cache *models.TorznabTorrentCacheStore) ServiceOption
WithTorrentCache wires a torrent payload cache into the service.
type SubmitRequest ¶
type SubmitRequest struct {
Indexers []*models.TorznabIndexer
Params url.Values
Meta *searchContext
Callbacks JobCallbacks
ExecFn func(context.Context, []*models.TorznabIndexer, url.Values, *searchContext) ([]Result, []int, error)
}
SubmitRequest contains all parameters for submitting a job to the scheduler.
type TorrentDownloadRequest ¶
type TorrentDownloadRequest struct {
IndexerID int
DownloadURL string
GUID string
Title string
Size int64
// Pace applies per-indexer min-interval pacing before contacting the backend.
// This is useful for background/automated workflows (e.g. dirscan) to avoid bursts of .torrent downloads.
Pace bool
}
TorrentDownloadRequest captures the metadata required to download (and cache) a torrent payload.
type TorznabSearchRequest ¶
type TorznabSearchRequest struct {
// Query is the search term
Query string `json:"query"`
// ReleaseName is the original full release name (for debugging/logging)
ReleaseName string `json:"release_name,omitempty"`
// Categories to search
Categories []int `json:"categories,omitempty"`
// IMDbID for movies/shows (optional)
IMDbID string `json:"imdb_id,omitempty"`
// TVDbID for TV shows (optional)
TVDbID string `json:"tvdb_id,omitempty"`
// TMDbID for movies (optional, from ARR lookup)
TMDbID int `json:"tmdb_id,omitempty"`
// TVMazeID for TV shows (optional, from ARR lookup)
TVMazeID int `json:"tvmaze_id,omitempty"`
// Year for movies/shows/music (optional)
Year int `json:"year,omitempty"`
// Season for TV shows (optional)
Season *int `json:"season,omitempty"`
// Episode for TV shows (optional)
Episode *int `json:"episode,omitempty"`
// Artist for music searches (optional)
Artist string `json:"artist,omitempty"`
// Album for music searches (optional)
Album string `json:"album,omitempty"`
// Limit the number of results
Limit int `json:"limit,omitempty"`
// Offset for pagination
Offset int `json:"offset,omitempty"`
// IndexerIDs to search (empty = all enabled indexers)
IndexerIDs []int `json:"indexer_ids,omitempty"`
// CacheMode controls cache behaviour (""=default, "bypass" = skip cache)
CacheMode string `json:"cache_mode,omitempty"`
// OmitQueryForIDs when true, omits the q parameter if IDs are present (for cross-seed ID-driven searches)
OmitQueryForIDs bool `json:"-"`
// SkipHistory prevents recording this search in the history buffer
SkipHistory bool `json:"-"`
// OnComplete is called when a search job for an indexer completes
OnComplete func(jobID uint64, indexerID int, err error) `json:"-"`
// OnAllComplete is called when all search jobs complete with the final results
OnAllComplete func(*SearchResponse, error) `json:"-"`
}
TorznabSearchRequest represents a general Torznab search request
type TrackerDomainInfo ¶
type TrackerDomainInfo struct {
Domain string `json:"domain"`
IndexerID int `json:"indexer_id"`
Name string `json:"name"`
BaseURL string `json:"base_url"`
JackettID string `json:"jackett_id,omitempty"`
Backend string `json:"backend"`
Enabled bool `json:"enabled"`
}
TrackerDomainInfo represents detailed information about a tracker domain