Documentation
¶
Overview ¶
Package resolver implements core.Resolver: it turns a catalogue title plus client capabilities into a health-checked release.
Index ¶
- Constants
- Variables
- func FetchFormatJSON(ctx context.Context, cl *http.Client, rawURL, cachePath string) ([]byte, error)
- func ImportCustomFormats(data []byte) ([]core.CustomFormat, error)
- func SelectProfile(q config.Quality, caps core.ClientCaps, user *core.User) core.QualityProfile
- type BlockEntry
- type BlockFilter
- type BlockRequest
- type CandidateTrace
- type ConsoleCatalog
- type ConsoleDeps
- type ConsoleRelease
- type ConsoleSearch
- type FormatMatch
- type FormatScorer
- func (s *FormatScorer) Score(r core.Release) (int, []FormatMatch, error)
- func (s *FormatScorer) ScoreForTitle(r core.Release, overrides map[string]int, original string) (int, []FormatMatch, error)
- func (s *FormatScorer) ScoreWithScores(r core.Release, overrides map[string]int) (int, []FormatMatch, error)
- type HealthResult
- type HistoryFilter
- type HistoryRecord
- type ImportFormat
- type Options
- type Pin
- type PinStatus
- type QualityImport
- type QualityImportPlan
- type SearchOptions
- type Service
- func (s *Service) Available(ctx context.Context, key core.RatingKey) (bool, bool)
- func (s *Service) Block(ctx context.Context, req BlockRequest, reason string) (BlockEntry, error)
- func (s *Service) Blocklist(ctx context.Context, f BlockFilter) ([]BlockEntry, int, error)
- func (s *Service) CheckRelease(ctx context.Context, releaseID string, titleKey core.RatingKey) (HealthResult, error)
- func (s *Service) Close() error
- func (s *Service) ConsoleHandler(deps ConsoleDeps) http.Handler
- func (s *Service) ConsoleProfiles() (def string, names []string)
- func (s *Service) DiscoveryCandidate(t *core.Title, r core.Release) string
- func (s *Service) FeedCandidate(ctx context.Context, t *core.Title, r core.Release) (core.Release, string, error)
- func (s *Service) Forget(ctx context.Context, t *core.Title) error
- func (s *Service) History(ctx context.Context, f HistoryFilter) ([]HistoryRecord, int, error)
- func (s *Service) HistoryEntry(ctx context.Context, id int64) (HistoryRecord, error)
- func (s *Service) KnownUnavailable(ctx context.Context, keys []core.RatingKey) map[core.RatingKey]bool
- func (s *Service) KnownUnavailableSince(ctx context.Context, keys []core.RatingKey) map[core.RatingKey]time.Time
- func (s *Service) LookupRelease(id string) (core.Release, bool)
- func (s *Service) MarkBad(ctx context.Context, releaseID string, reason string) error
- func (s *Service) ObserveSourceDelivery(r core.Release, bytesPerSecond float64)
- func (s *Service) Pin(ctx context.Context, key core.RatingKey, titleName string, rel core.Release, ...) (Pin, error)
- func (s *Service) PinFor(key core.RatingKey) (Pin, bool)
- func (s *Service) Pins() []Pin
- func (s *Service) ProfileFor(caps core.ClientCaps, user *core.User) core.QualityProfile
- func (s *Service) Prune(ctx context.Context) (int64, error)
- func (s *Service) Resolve(ctx context.Context, req core.ResolveRequest) (*core.Resolution, error)
- func (s *Service) SearchReleases(ctx context.Context, t *core.Title, opts SearchOptions) (*ConsoleSearch, error)
- func (s *Service) SetBus(bus clusterbus.Bus)
- func (s *Service) SetSourceSignals(sig SourceSignals)
- func (s *Service) SourceStartups() []SourceStartup
- func (s *Service) Traces() []Trace
- func (s *Service) Unblock(ctx context.Context, releaseID string) (BlockEntry, error)
- func (s *Service) Unpin(ctx context.Context, key core.RatingKey) (bool, error)
- func (s *Service) UpdateConfig(cfg config.Config)
- func (s *Service) Variants(ctx context.Context, req core.ResolveRequest, max int) ([]core.ReleaseVariant, error)
- func (s *Service) VerifyFeedCandidate(ctx context.Context, t *core.Title, r core.Release) error
- type SourceSignals
- type SourceStartup
- type Trace
- type TrashGuides
- func (g *TrashGuides) CustomFormats(ctx context.Context, app string, want map[string]string) (map[string]core.CustomFormat, error)
- func (g *TrashGuides) Profile(ctx context.Context, app, id string) ([]byte, error)
- func (g *TrashGuides) Profiles(ctx context.Context, app string) ([]TrashProfile, error)
- type TrashOptions
- type TrashProfile
Constants ¶
const ( BlockAuto = "auto" // health checks and playback failover (MarkBad) BlockOwner = "owner" // the owner blocked it )
Blocklist sources.
const ( // ProfileNone lists results without profile gates: identity, client // and blocklist rules still apply, quality policy does not. ProfileNone = "none" // ProfileDownload is the owner's default profile reshaped for an // offline download, as SelectProfile does for caps.Download. ProfileDownload = "download" )
Special console profile names.
const ( AppRadarr = "radarr" AppSonarr = "sonarr" )
The Starr apps whose TRaSH Guides data Ether imports.
const ( FormatPresent = "present" // already in the owner's custom formats FormatAdd = "add" // added by the import FormatUnsupported = "unsupported" // uses a rule Ether can't evaluate FormatMissing = "missing" // not found in TRaSH Guides )
Import format statuses.
const ( PreferAuto = "auto" PreferUsenet = "usenet" PreferDebrid = "debrid" )
Variables ¶
var ( ErrUnknownRelease = errors.New("release is not among recent search results; search again") ErrUnknownProfile = errors.New("no such quality profile") ErrPinBlocked = errors.New("release is blocklisted; unblock it before pinning") ErrNotPinnable = errors.New("only movies and episodes can be pinned") )
Console errors the owner API maps to client errors.
var ( // ErrProfileExists rejects an import whose name another profile uses. ErrProfileExists = errors.New("a profile with that name already exists") // ErrImportRejected wraps configuration validation failures on import. ErrImportRejected = errors.New("the import would make the configuration invalid") )
var ( // ErrTrashNotFound reports a profile that TRaSH Guides doesn't list. ErrTrashNotFound = errors.New("TRaSH Guides has no such profile") ErrTrashUnavailable = errors.New("TRaSH Guides is unavailable") )
Functions ¶
func FetchFormatJSON ¶
func FetchFormatJSON(ctx context.Context, cl *http.Client, rawURL, cachePath string) ([]byte, error)
FetchFormatJSON conditionally refreshes an owner-selected JSON resource. The last valid download is cached atomically, and remains usable offline.
func ImportCustomFormats ¶
func ImportCustomFormats(data []byte) ([]core.CustomFormat, error)
ImportCustomFormats accepts a native single definition or an array, validating before returning. Callers persist the result in config.Resolver.CustomFormats.
func SelectProfile ¶
func SelectProfile(q config.Quality, caps core.ClientCaps, user *core.User) core.QualityProfile
SelectProfile picks the quality profile for a client and clamps it to what the client can direct-play. Playback uses it for pre-resolution estimates; the resolver uses it for ranking.
Selection order: remote clients get q.Remote (when set); otherwise the first rule whose non-empty matchers all match; otherwise q.Default; otherwise the first profile. Clamping: MaxHeight is capped at caps.MaxHeight, HDR/DV are only allowed when the client supports them, and preferred codecs the client cannot decode are dropped.
Types ¶
type BlockEntry ¶
type BlockEntry struct {
ReleaseID string `json:"releaseId"`
Name string `json:"name,omitempty"`
Indexer string `json:"indexer,omitempty"`
TitleKey core.RatingKey `json:"titleKey,string"`
TitleName string `json:"titleName,omitempty"`
Reason string `json:"reason"`
Source string `json:"source"`
Manual bool `json:"manual"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
BlockEntry is one blocklisted release. Manual (owner) entries are never expired by retention pruning.
type BlockFilter ¶
type BlockFilter struct {
TitleKey core.RatingKey
Source string // BlockAuto or BlockOwner
Query string // substring of the release or title name
Limit int // default 100, at most 500
Offset int
}
BlockFilter narrows Blocklist. Zero values match everything.
type BlockRequest ¶
type BlockRequest struct {
ReleaseID string
Name string
Indexer string
TitleKey core.RatingKey
TitleName string
}
BlockRequest describes a release the owner blocks. Name, Indexer and TitleName are filled from recent search results when empty.
type CandidateTrace ¶
type CandidateTrace struct {
ReleaseID string `json:"releaseId"`
Name string `json:"name"`
Indexer string `json:"indexer"`
Score float64 `json:"score"`
CustomFormats []FormatMatch `json:"customFormats,omitempty"`
CompatibilityReason string `json:"compatibilityReason,omitempty"`
// Route explains a move by source routing (routing.go), e.g. "faster
// source: cached on debrid".
Route string `json:"route,omitempty"`
Rejected bool `json:"rejected"`
Reason string `json:"reason,omitempty"`
Size int64 `json:"size,omitempty"`
Pinned bool `json:"pinned,omitempty"` // the owner's pin for the title
Attempted bool `json:"attempted,omitempty"` // fetch or health validation actually ran
HealthChecked bool `json:"healthChecked"`
Healthy bool `json:"healthy"`
FetchDuration time.Duration `json:"fetchDuration"`
HealthDuration time.Duration `json:"healthDuration"`
}
CandidateTrace explains one candidate's fate in a resolution.
type ConsoleCatalog ¶
type ConsoleCatalog interface {
Get(ctx context.Context, key core.RatingKey) (*core.Title, error)
Children(ctx context.Context, key core.RatingKey) ([]core.Title, error)
}
ConsoleCatalog is the catalogue surface the release console needs (satisfied by core.Catalog).
type ConsoleDeps ¶
type ConsoleDeps struct {
Catalog ConsoleCatalog
// Invalidate drops anything prepared for a title (playback's
// pre-resolved warm entries) so the next play honours a change.
Invalidate func(core.RatingKey)
Logger *slog.Logger
// Guides reads TRaSH Guides for quality profile imports; nil uses
// GitHub with default options.
Guides *TrashGuides
// SaveConfig saves an import through the owner's validated config
// path: it applies mutate to the current settings (again if they changed
// meanwhile), validates and saves them. nil disables imports.
SaveConfig func(mutate func(*config.Config) error) error
}
ConsoleDeps wires the release console's owner API.
type ConsoleRelease ¶
type ConsoleRelease struct {
Release core.Release `json:"release"`
Score float64 `json:"score"`
FormatScore int `json:"formatScore"`
CustomFormats []FormatMatch `json:"customFormats,omitempty"`
Compatibility string `json:"compatibility,omitempty"`
// Rejected and Reason are Resolve's verdict for this profile.
Rejected bool `json:"rejected"`
Reason string `json:"reason,omitempty"`
// Rank is the 1-based position among accepted releases in the order
// Resolve tries them (the pin first); 0 when rejected.
Rank int `json:"rank,omitempty"`
Winner bool `json:"winner,omitempty"`
Blocked bool `json:"blocked,omitempty"`
BlockReason string `json:"blockReason,omitempty"`
Pinned bool `json:"pinned,omitempty"`
Health *HealthResult `json:"health,omitempty"`
// Route explains a move by source routing (routing.go).
Route string `json:"route,omitempty"`
}
ConsoleRelease is one search result as Resolve would judge it.
type ConsoleSearch ¶
type ConsoleSearch struct {
TitleKey core.RatingKey `json:"titleKey,string"`
TitleName string `json:"titleName"`
Profile string `json:"profile"`
Cached bool `json:"cached"`
SearchedAt time.Time `json:"searchedAt"`
Duration time.Duration `json:"duration"`
Winner string `json:"winner,omitempty"` // release Resolve would try first
Routing string `json:"routing,omitempty"` // source-routing decision, if any
Pin *Pin `json:"pin,omitempty"`
PinNote string `json:"pinNote,omitempty"`
Releases []ConsoleRelease `json:"releases"`
}
ConsoleSearch is an interactive search answer.
type FormatMatch ¶
type FormatScorer ¶
type FormatScorer struct {
// contains filtered or unexported fields
}
func NewFormatScorer ¶
func NewFormatScorer(defs []core.CustomFormat, scores map[string]int) (*FormatScorer, error)
NewFormatScorer validates every rule before publishing an immutable scorer. .NET lookarounds used by TRaSH are supported; regex execution is time bounded.
func (*FormatScorer) Score ¶
func (s *FormatScorer) Score(r core.Release) (int, []FormatMatch, error)
Score mirrors Starr grouping: every specification type must match; within a type all required rules and at least one rule must match.
func (*FormatScorer) ScoreForTitle ¶ added in v0.2.10
func (s *FormatScorer) ScoreForTitle(r core.Release, overrides map[string]int, original string) (int, []FormatMatch, error)
ScoreForTitle scores r for a title whose original language is original (a release-parser language name, "" when unknown), which "Original" language specifications compare against.
func (*FormatScorer) ScoreWithScores ¶
func (s *FormatScorer) ScoreWithScores(r core.Release, overrides map[string]int) (int, []FormatMatch, error)
type HealthResult ¶
type HealthResult struct {
ReleaseID string `json:"releaseId"`
CheckedAt time.Time `json:"checkedAt"`
Healthy bool `json:"healthy"`
Reason string `json:"reason,omitempty"`
Duration time.Duration `json:"duration,omitempty"`
// Source is "check" (an owner check), "verified" (passed a check
// during resolution, still trusted) or "blocked" (on the blocklist).
Source string `json:"source"`
// Blocked reports the check put the release on the blocklist.
Blocked bool `json:"blocked,omitempty"`
}
HealthResult is the last known health of a release.
type HistoryFilter ¶
type HistoryFilter struct {
TitleKey core.RatingKey
Limit int // default 50, at most 500
Offset int
}
HistoryFilter narrows History. Zero values match everything.
type HistoryRecord ¶
type HistoryRecord struct {
ID int64 `json:"id"`
At time.Time `json:"at"`
TitleKey core.RatingKey `json:"titleKey,string"`
TitleName string `json:"titleName"`
Profile string `json:"profile,omitempty"`
ReleaseID string `json:"releaseId,omitempty"`
ReleaseName string `json:"releaseName,omitempty"`
Indexer string `json:"indexer,omitempty"`
Size int64 `json:"size,omitempty"`
Error string `json:"error,omitempty"`
Failover bool `json:"failover,omitempty"`
Pinned bool `json:"pinned,omitempty"` // the owner's pin was used
Candidates int `json:"candidates"`
// Trace is the full decision (candidates capped at 50); only
// HistoryEntry fills it.
Trace *Trace `json:"trace,omitempty"`
}
HistoryRecord is one persisted resolution outcome.
type ImportFormat ¶ added in v0.2.22
type ImportFormat struct {
ID string `json:"id"`
Name string `json:"name"`
Score int `json:"score"`
Status string `json:"status"`
Reason string `json:"reason,omitempty"`
}
ImportFormat is one custom format an imported profile references.
type Options ¶
type Options struct {
SearchObserved func(context.Context, *core.Title, []core.Release)
Discovered func(context.Context, *core.Title) []core.Release
Observed func(context.Context, *core.Title, core.Release)
Invalidated func(context.Context, string)
Logger *slog.Logger
Now func() time.Time
MaxTraces int
RetentionDays int // posts older than this are penalised; 0 = no extra penalty
// HealthTTL is how long a passed health check is trusted before the
// release is sampled again (default 6h; negative disables the cache).
HealthTTL time.Duration
// HistoryMax bounds the persisted resolution history (default 5000
// rows); HistoryMaxAge drops older rows (default 60 days).
HistoryMax int
HistoryMaxAge time.Duration
}
Options tunes a Service. Zero values get sensible defaults.
type Pin ¶
type Pin struct {
TitleKey core.RatingKey `json:"titleKey,string"`
TitleName string `json:"titleName"`
Release core.Release `json:"release"`
Note string `json:"note,omitempty"`
PinnedAt time.Time `json:"pinnedAt"`
LastStatus PinStatus `json:"lastStatus,omitempty"`
LastReason string `json:"lastReason,omitempty"`
LastCheckedAt time.Time `json:"lastCheckedAt"`
}
Pin is an owner's release pin for a title.
type PinStatus ¶
type PinStatus string
PinStatus is the outcome of the last time a pin was tried or checked.
type QualityImport ¶
type QualityImport struct {
Profile core.QualityProfile `json:"profile"`
FormatIDs map[string]string `json:"formatIds"`
// ScoreSet names the trash_scores entry the profile's formats are scored
// with ("sqp-1-2160p"); empty means each format's default score.
ScoreSet string `json:"scoreSet,omitempty"`
// App is the Starr app the quality names identify, or "" when they
// read the same in Radarr and Sonarr.
App string `json:"app,omitempty"`
// Skipped lists enabled qualities Ether has no equivalent for.
Skipped []string `json:"skipped,omitempty"`
// Warnings explain profile settings that do not carry over.
Warnings []string `json:"warnings,omitempty"`
}
QualityImport is a TRaSH quality profile translated into Ether's terms. It keeps the profile's format references (formatItems) so the referenced custom formats can be resolved before the profile is saved.
func ImportQualityProfile ¶
func ImportQualityProfile(data []byte) (QualityImport, error)
ImportQualityProfile translates native TRaSH quality groups into exact source/resolution gates. Download upgrade/cutoff fields have no streaming equivalent; source ordering and minimum format score do. An enabled quality Ether cannot match (Raw-HD, SDTV) is skipped and reported, unless nothing playable would remain.
type QualityImportPlan ¶ added in v0.2.22
type QualityImportPlan struct {
App string `json:"app"`
Profile core.QualityProfile `json:"profile"`
ScoreSet string `json:"scoreSet,omitempty"`
Formats []ImportFormat `json:"formats"`
Skipped []string `json:"skippedQualities"`
Warnings []string `json:"warnings"`
// contains filtered or unexported fields
}
QualityImportPlan previews an import: the profile as it will be saved, the custom formats it references and what carries over.
func PlanQualityImport ¶ added in v0.2.22
func PlanQualityImport(imp QualityImport, app string, found map[string]core.CustomFormat, cfg config.Config) QualityImportPlan
PlanQualityImport resolves imp's custom formats against the owner's current ones (cfg) and the TRaSH definitions found (by trash ID), and names the profile so it doesn't clash. Definitions the scorer cannot compile are reported as unsupported and left out, so an import can never make the scoring configuration invalid.
func (QualityImportPlan) Added ¶ added in v0.2.22
func (p QualityImportPlan) Added() int
Added counts the custom formats the import adds.
func (QualityImportPlan) Apply ¶ added in v0.2.22
func (p QualityImportPlan) Apply(c *config.Config, name string) error
Apply adds the planned profile to c under name (the planned name when empty) and merges the formats it needs into c's custom formats, keeping every existing definition. The new profile scores only its own formats, as in Radarr and Sonarr, and every other profile scores the added formats 0: importing a profile never changes how the others rank releases. Apply only mutates c, so it can be retried against fresher settings.
type SearchOptions ¶
type SearchOptions struct {
// Profile names the quality profile to rank with: "" for the owner's
// default, ProfileNone, ProfileDownload, or a configured profile.
Profile string
// Fresh bypasses the search cache (the fresh answer is cached).
Fresh bool
}
SearchOptions tune an interactive search.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service implements core.Resolver.
func New ¶
func New(indexer core.Indexer, health core.HealthChecker, db *sql.DB, cfg config.Config, opts Options) *Service
New builds a resolver Service. indexer is the aggregate indexer (typically *newznab.Manager); health may be nil (health checks are then skipped); db may be nil (state then lives in memory).
func (*Service) Block ¶
func (s *Service) Block(ctx context.Context, req BlockRequest, reason string) (BlockEntry, error)
Block puts a release on the blocklist on the owner's behalf (manual). Resolve never picks it again until it is unblocked.
func (*Service) Blocklist ¶
func (s *Service) Blocklist(ctx context.Context, f BlockFilter) ([]BlockEntry, int, error)
Blocklist lists blocklisted releases, most recently marked first, with the total matching count.
func (*Service) CheckRelease ¶
func (s *Service) CheckRelease(ctx context.Context, releaseID string, titleKey core.RatingKey) (HealthResult, error)
CheckRelease health-checks one release on demand (a fresh check, never the verified cache): FetchNZB plus the health checker, exactly as resolution validates a candidate. A pass refreshes the verified cache; a definitive failure (missing or corrupt articles, a rejected release) is blocklisted automatically like a failed optimistic check. Transient failures are reported but change nothing. titleKey, when known, names the title for the blocklist.
func (*Service) Close ¶
Close cancels and joins optimistic checks. Owners should close before the DB.
func (*Service) ConsoleHandler ¶
func (s *Service) ConsoleHandler(deps ConsoleDeps) http.Handler
ConsoleHandler serves the release console under the owner API (mounted at /ether/api/releases behind owner authentication):
GET /profiles profiles interactive search can rank with
GET /search?key=&profile=&fresh=1
GET /titles/{key}/children seasons of a show, episodes of a season
POST /health {key, releaseId}
POST /forget {key}
GET /pins
POST /pins {key, releaseId, note}
DELETE /pins/{key}
GET /blocklist?key=&source=&q=&limit=&offset=
POST /blocklist {releaseId, key, reason, name, indexer}
DELETE /blocklist/{releaseId}
GET /history?key=&limit=&offset=
GET /history/{id}
GET /trash/{app}/profiles TRaSH Guides quality profiles (radarr, sonarr)
POST /quality-import/preview {app, profile | json}
POST /quality-import {app, profile | json, name}
func (*Service) ConsoleProfiles ¶
ConsoleProfiles lists the profiles interactive search can rank with: the configured profiles, plus ProfileNone and ProfileDownload.
func (*Service) DiscoveryCandidate ¶ added in v0.2.9
DiscoveryCandidate is local, name-based negative evidence only. It does not certify quality or fetch a provider. Feed and playback share this policy.
func (*Service) FeedCandidate ¶ added in v0.2.8
func (s *Service) FeedCandidate(ctx context.Context, t *core.Title, r core.Release) (core.Release, string, error)
FeedCandidate applies the existing quality, identity and block policies without searching. Broad capabilities mean the default owner's profile determines eligibility; this is source evidence, never a claim about every device.
func (*Service) Forget ¶
Forget clears what the resolver remembers about a title's search: its availability answer (and cool-off) and its cached search results, so the next play or search asks the indexers afresh.
func (*Service) History ¶
func (s *Service) History(ctx context.Context, f HistoryFilter) ([]HistoryRecord, int, error)
History lists persisted resolution outcomes, newest first, without their traces, plus the total matching count.
func (*Service) HistoryEntry ¶
HistoryEntry returns one history record with its full trace, or core.ErrNotFound.
func (*Service) KnownUnavailable ¶ added in v0.2.9
func (s *Service) KnownUnavailable(ctx context.Context, keys []core.RatingKey) map[core.RatingKey]bool
KnownUnavailable returns only unexpired resolver negatives, in one local batch. It never searches, probes, or turns missing evidence into a negative.
func (*Service) KnownUnavailableSince ¶ added in v0.2.9
func (*Service) LookupRelease ¶
LookupRelease finds a release by id among recent search results and owner pins.
func (*Service) ObserveSourceDelivery ¶ added in v0.2.9
func (*Service) Pin ¶
func (s *Service) Pin(ctx context.Context, key core.RatingKey, titleName string, rel core.Release, note string) (Pin, error)
Pin pins rel to a movie or episode, replacing any earlier pin. The full release is stored, so the pin works after the release drops out of cached search results.
func (*Service) ProfileFor ¶
func (s *Service) ProfileFor(caps core.ClientCaps, user *core.User) core.QualityProfile
ProfileFor exposes SelectProfile against the live config.
func (*Service) Prune ¶
Prune drops expired entries from the in-memory caches and deletes resolver rows that can no longer be served: expired availability, health passes older than the health TTL and automatic bad marks not re-marked within badRetention (owner blocks never expire). Indexer reputation is a handful of counters and is kept. It is meant to be called periodically (the app runs it daily).
func (*Service) Resolve ¶
func (s *Service) Resolve(ctx context.Context, req core.ResolveRequest) (*core.Resolution, error)
Resolve implements core.Resolver. Concurrent calls for the same title+profile share one search via a singleflight.
func (*Service) SearchReleases ¶
func (s *Service) SearchReleases(ctx context.Context, t *core.Title, opts SearchOptions) (*ConsoleSearch, error)
SearchReleases runs the indexer search for a title (through the search cache unless opts.Fresh) and returns every result with the score, custom formats and rejection reason Resolve would give it, plus its blocklist, pin and last known health. It never health-checks.
func (*Service) SetBus ¶
func (s *Service) SetBus(bus clusterbus.Bus)
SetBus connects the cluster bus (nil disconnects). A standalone server never calls it.
func (*Service) SetSourceSignals ¶
func (s *Service) SetSourceSignals(sig SourceSignals)
SetSourceSignals wires the debrid cache knowledge; nil disables it.
func (*Service) SourceStartups ¶
func (s *Service) SourceStartups() []SourceStartup
SourceStartups returns the measured startup per source.
func (*Service) Unblock ¶
Unblock removes a release from the blocklist (whatever its source) and forgets its verified health, so the next resolution checks it afresh; the title's availability cool-off is cleared too. It returns the removed entry, or core.ErrNotFound.
func (*Service) UpdateConfig ¶
UpdateConfig swaps the resolver/quality config snapshot. When the owner changes where releases come from (indexers, Usenet or debrid accounts, debrid's cached-only switch), cached searches and "not available" answers describe the old sources and are forgotten.
func (*Service) Variants ¶
func (s *Service) Variants(ctx context.Context, req core.ResolveRequest, max int) ([]core.ReleaseVariant, error)
Variants implements core.VariantResolver. It reads only the cached search results — rendering a detail page never spends indexer budget. When nothing is cached it returns (nil, nil) and the caller renders the single Auto media; the detail page's PreResolve populates the cache, so a later load shows versions.
func (*Service) VerifyFeedCandidate ¶ added in v0.2.8
VerifyFeedCandidate validates exactly the discovered release. It never invokes Search or rescue searches and never writes a title-wide negative result.
type SourceSignals ¶
type SourceSignals interface {
// KnownCached reports whether a debrid provider is known to hold the
// torrent (from bulk cache checks or earlier plays), without a request.
KnownCached(infoHash string) bool
}
SourceSignals is what routing needs from the debrid service.
type SourceStartup ¶
type SourceStartup struct {
Protocol string `json:"protocol"` // "usenet" or "torrent"
Average time.Duration `json:"average"` // exponentially weighted
Last time.Duration `json:"last"`
Samples int64 `json:"samples"`
}
SourceStartup is a source's measured startup time.
type Trace ¶
type Trace struct {
TitleKey core.RatingKey `json:"titleKey"`
TitleName string `json:"titleName"`
Profile string `json:"profile"`
At time.Time `json:"at"`
Candidates []CandidateTrace `json:"candidates"`
Winner string `json:"winner,omitempty"` // release id
Err string `json:"err,omitempty"`
SearchDuration time.Duration `json:"searchDuration"`
SourcePreparationDuration time.Duration `json:"sourcePreparationDuration"`
TotalDuration time.Duration `json:"totalDuration"`
// Failover marks a resolution that excluded already-tried releases.
Failover bool `json:"failover,omitempty"`
// Pinned is the owner-pinned release id considered for the title;
// PinStatus says what became of it ("used", "unhealthy: …", …).
Pinned string `json:"pinned,omitempty"`
PinStatus string `json:"pinStatus,omitempty"`
// Omitted counts candidates dropped from a persisted trace.
Omitted int `json:"omitted,omitempty"`
// Routing summarises the source-routing decision (routing.go).
Routing string `json:"routing,omitempty"`
}
Trace is one recorded resolution attempt, for the admin UI.
type TrashGuides ¶ added in v0.2.22
type TrashGuides struct {
// contains filtered or unexported fields
}
TrashGuides reads TRaSH Guides profiles and custom formats, caching what it fetched in memory for TTL. When a refresh fails, the last good copy is served instead.
func NewTrashGuides ¶ added in v0.2.22
func NewTrashGuides(o TrashOptions) *TrashGuides
NewTrashGuides returns a client for TRaSH Guides' published JSON.
func (*TrashGuides) CustomFormats ¶ added in v0.2.22
func (g *TrashGuides) CustomFormats(ctx context.Context, app string, want map[string]string) (map[string]core.CustomFormat, error)
CustomFormats finds the definitions of the trash IDs in want (format name -> trash ID) among app's custom formats. File names are slugs that don't reliably follow from a format's name, so cached files are searched first, then files named like a wanted format, then the rest, fetching at most trashParallel at a time and stopping once every ID is found. The error reports why some IDs could not be looked up; the result holds what was found.
func (*TrashGuides) Profile ¶ added in v0.2.22
Profile returns the JSON of app's TRaSH profile id (a listed file name without .json).
func (*TrashGuides) Profiles ¶ added in v0.2.22
func (g *TrashGuides) Profiles(ctx context.Context, app string) ([]TrashProfile, error)
Profiles lists app's TRaSH quality profiles, grouped as the guide groups them.
type TrashOptions ¶ added in v0.2.22
type TrashOptions struct {
APIBase string // default https://api.github.com
RawBase string // default https://raw.githubusercontent.com
Client *http.Client
UserAgent string
TTL time.Duration // cache lifetime, default 1h
Now func() time.Time
Parallel int // file fetches in flight, default trashParallel
}
TrashOptions configures a TrashGuides client. Zero values use GitHub.
type TrashProfile ¶ added in v0.2.22
type TrashProfile struct {
ID string `json:"id"` // file name without .json
TrashID string `json:"trashId,omitempty"`
Name string `json:"name"`
Description string `json:"description,omitempty"` // plain text
Group int `json:"group,omitempty"`
URL string `json:"url,omitempty"` // the guide page, always https://trash-guides.info
ScoreSet string `json:"scoreSet,omitempty"`
Formats int `json:"formats"`
}
TrashProfile summarises one TRaSH quality profile for picking.
Source Files
¶
- bandwidth.go
- blocklist.go
- candidate_prepare.go
- capability_assertions.go
- cluster.go
- console.go
- console_http.go
- console_import.go
- customformats.go
- delivery.go
- feed.go
- healthcache.go
- history.go
- pins.go
- prepare_sources.go
- profile.go
- prune.go
- qualityimport.go
- racing.go
- ranked_hedge.go
- ranked_window.go
- resolve_stages.go
- resolver.go
- routing.go
- search_prepare.go
- source_suitability.go
- store.go
- trash.go
- variants.go