Help us shape the future of Go! Take the 2026 Go Developer Survey

resolver

package
v0.2.44 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 29, 2026 License: GPL-3.0 Imports: 29 Imported by: 0

Documentation

Overview

Package resolver implements core.Resolver: it turns a catalogue title plus client capabilities into a health-checked release.

Index

Constants

View Source
const (
	BlockAuto  = "auto"  // health checks and playback failover (MarkBad)
	BlockOwner = "owner" // the owner blocked it
)

Blocklist sources.

View Source
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.

View Source
const (
	AppRadarr = "radarr"
	AppSonarr = "sonarr"
)

The Starr apps whose TRaSH Guides data Ether imports.

View Source
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.

View Source
const (
	PreferAuto   = "auto"
	PreferUsenet = "usenet"
	PreferDebrid = "debrid"
)

Variables

View Source
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.

View Source
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")
)
View Source
var (
	// ErrTrashNotFound reports a profile that TRaSH Guides doesn't list.
	ErrTrashNotFound = errors.New("TRaSH Guides has no such profile")
	// ErrTrashUnavailable wraps failures to reach TRaSH Guides.
	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 DiscoveryTrace added in v0.2.39

type DiscoveryTrace struct {
	Path   string `json:"path"`
	Count  int    `json:"count"`
	Rescue bool   `json:"rescue,omitempty"`
}

DiscoveryTrace records only the origin and number of returned releases.

type FormatMatch

type FormatMatch struct {
	ID    string `json:"id"`
	Name  string `json:"name"`
	Score int    `json:"score"`
}

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.

const (
	PinHealthy     PinStatus = "healthy"     // passed a health check and played
	PinUnhealthy   PinStatus = "unhealthy"   // failed a health check or the NZB is gone
	PinRejected    PinStatus = "rejected"    // a hard gate excluded it for a client
	PinUnavailable PinStatus = "unavailable" // a transient failure (provider outage)
)

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) Available

func (s *Service) Available(ctx context.Context, key core.RatingKey) (bool, bool)

Available implements core.Resolver.

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

func (s *Service) Close() error

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

func (s *Service) ConsoleProfiles() (def string, names []string)

ConsoleProfiles lists the profiles interactive search can rank with: the configured profiles, plus ProfileNone and ProfileDownload.

func (*Service) DiscoveryCandidate added in v0.2.9

func (s *Service) DiscoveryCandidate(t *core.Title, r core.Release) string

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

func (s *Service) Forget(ctx context.Context, t *core.Title) error

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

func (s *Service) HistoryEntry(ctx context.Context, id int64) (HistoryRecord, error)

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 (s *Service) KnownUnavailableSince(ctx context.Context, keys []core.RatingKey) map[core.RatingKey]time.Time

func (*Service) LocalReleaseBlocked added in v0.2.34

func (s *Service) LocalReleaseBlocked(id string) bool

LocalReleaseBlocked reads the in-memory snapshot without database access. Selection boundaries must use ReleaseBlocked for authoritative cluster checks.

func (*Service) LookupRelease

func (s *Service) LookupRelease(id string) (core.Release, bool)

LookupRelease finds a release by id among recent search results and owner pins.

func (*Service) MarkBad

func (s *Service) MarkBad(ctx context.Context, releaseID string, reason string) error

MarkBad implements core.Resolver.

func (*Service) ObserveSourceDelivery added in v0.2.9

func (s *Service) ObserveSourceDelivery(r core.Release, bytesPerSecond float64)

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) PinFor

func (s *Service) PinFor(key core.RatingKey) (Pin, bool)

PinFor returns a title's pin.

func (*Service) Pins

func (s *Service) Pins() []Pin

Pins lists every pin, newest first.

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

func (s *Service) Prune(ctx context.Context) (int64, error)

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) ReleaseBlocked added in v0.2.34

func (s *Service) ReleaseBlocked(ctx context.Context, id string) (bool, error)

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) Traces

func (s *Service) Traces() []Trace

Traces returns the most recent resolution traces, newest first.

func (*Service) Unblock

func (s *Service) Unblock(ctx context.Context, releaseID string) (BlockEntry, error)

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) Unpin

func (s *Service) Unpin(ctx context.Context, key core.RatingKey) (bool, error)

Unpin removes a title's pin; it reports whether there was one.

func (*Service) UpdateConfig

func (s *Service) UpdateConfig(cfg config.Config)

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

func (s *Service) VerifyFeedCandidate(ctx context.Context, t *core.Title, r core.Release) error

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 {
	Discovery                 []DiscoveryTrace `json:"discovery,omitempty"`
	Limits                    *TraceLimits     `json:"limits,omitempty"`
	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 TraceLimits added in v0.2.39

type TraceLimits struct {
	ProfileMinHeight     int `json:"profileMinHeight"`
	ProfileMaxHeight     int `json:"profileMaxHeight"`
	ClientMaxHeight      int `json:"clientMaxHeight"`
	ClientMaxBitrateKbps int `json:"clientMaxBitrateKbps"`
}

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 Parallel 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

func (g *TrashGuides) Profile(ctx context.Context, app, id string) ([]byte, error)

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL