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

availability

package
v0.2.20 Latest Latest
Warning

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

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

README

Availability monitoring

Ether observes recent releases and reuses successful resolver health checks. It never removes catalogue/search results because evidence is missing. Movie dates and episode air dates still use the catalogue's existing release eligibility; quality, CAM acceptance, identities, blocklists and episode targeting use the resolver's existing policies rather than a second rules engine.

Owner controls

Sources → Availability provides monitoring, selective validation, participating feeds, debrid participation, preference for checked recommendations and optional newly available shelves. Monitoring and validation start disabled. Presentation preferences start enabled and can benefit from ordinary playback observations.

Default daily background allowances (UTC, shared across nodes):

  • 100 feed API requests, and 100 validation API requests.
  • 20 candidate checks and 20 descriptor grabs (NZB or torrent).
  • 512 MiB of conservative data reservations. NNTP BODY reservations use the provider's maximum article size, so actual transferred bytes can be much lower.
  • Preserve 80% of each indexer's configured daily cap for foreground work.

An allowance of zero disables that work. Foreground requests still have their normal account limits. Every feed page, provider API request and source read goes through admission before network work. Ordinary playback does not use the background allowance. Shared worker contexts retain the admission hook.

Discovery and evidence

Newznab/Torznab recent-release pages use an empty t=search request, following the incremental model documented by Radarr. One page describes up to 100 releases. Polling defaults to 30 minutes. The first sync starts at the head; later syncs look for overlap with the previous head, with at most three pages. Failed requests do not advance the cursor. Missing overlap records a visible coverage gap rather than triggering unlimited catch-up. Search remains the fallback for older and missed releases. Built-in sites without a recent-feed contract remain available for ordinary search.

Matching scans local title identities and resolves episodes locally; it does not call TMDB for every feed item. Conflicting identifiers and ambiguous title matches are rejected. A season pack only contributes known, already-aired episodes, and its health check is targeted to the chosen episode using the existing resolver.

The queue prioritizes popular titles, checks one candidate per title per pass, and defers extra candidates when the title already has fresh successful evidence. It never searches the entire catalogue in the background. Playback attempts can reuse discovered descriptors before normal search, with a five-second shortlist budget. Viewer/device policy is always reapplied on that path.

Debrid cache lookups are batched and capability-dependent. Background validation can inspect existing account items and validate a bounded HTTPS range; it never starts an uncached download or adds torrents. Unsupported cache APIs leave availability unknown. A debrid account by itself is not a global discovery feed; recent torrent discovery requires a configured Torznab source. Usenet, debrid, both, and no playback provider are supported configurations.

Successful evidence is source-, title-, policy- and configuration-specific. It expires after six hours. Configuration changes invalidate old positives. A source failure never creates a title-wide negative. Timeout, exhausted allowance and unsupported checks remain unknown. Normal resolver health-cache hits do not renew evidence timestamps. Sampled health is useful evidence, not a guarantee of all bytes or all clients.

Only fresh checked movies move ahead within popular/trending/recommendation shelves; no items are removed. Explicit chronology and Continue Watching retain their order. Newly available shelves mean first successfully checked by this server, not necessarily a film's worldwide premiere.

Operations

The existing cluster leader runs one scheduler; cursors, observations and atomic usage counters are durable in the shared database. Hot settings are respected by admission. Checks pause while local playback is active. Each cycle is bounded, cancellation propagates, transient outcomes back off, and descriptors expire after 30 days with a hard retention cap of 30,000 observations. Signed release locators are sealed using the config store; owner status never exposes them.

Tests use fake feeds/providers and local HTTP fixtures. They cover admission, account reserve, midnight reset, restart/overlap/gaps, identity conflicts, source/policy invalidation, stale evidence, disabled-provider behavior, diverse candidate selection, resolver reuse, and budget propagation across shared workers.

Documentation

Overview

Package availability discovers releases incrementally and spends a bounded background allowance validating useful candidates. Recent releases require evidence for discovery; older catalogue entries need no background search.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EvidenceWindow added in v0.2.9

func EvidenceWindow(t core.Title, now time.Time) bool

EvidenceWindow is a proof window, not a delay: an eligible observed release unlocks a title immediately. Older catalogue entries do not need a scan.

Types

type Catalog

type Catalog interface {
	ReleaseMatches(context.Context, []core.Release) (map[string][]core.Title, error)
	CachedTitle(context.Context, core.RatingKey) (*core.Title, error)
	CachedTitles(context.Context, []core.RatingKey) ([]core.Title, error)
}

type Deps

type Deps struct {
	DB         *sql.DB
	Config     func() config.Config
	Feeds      Feeds
	Catalog    Catalog
	Validator  Validator
	Seal       func(string) (string, error)
	Open       func(string) (string, error)
	CacheCheck func(context.Context, []string) (map[string]bool, bool)
	Active     func() bool
	Now        func() time.Time
	Logger     *slog.Logger
}

type FeedStatus

type FeedStatus struct {
	core.ReleaseFeed
	Selected    bool   `json:"selected"`
	LastAttempt int64  `json:"lastAttempt"`
	LastSync    int64  `json:"lastSync"`
	Gap         bool   `json:"gap"`
	Message     string `json:"message"`
	Matched     int    `json:"matched"`
}

type Feeds

type Feeds interface {
	FeedSources() []core.ReleaseFeed
	Recent(context.Context, string, int, int) ([]core.Release, error)
}

type Observation

type Observation struct {
	Key           core.RatingKey `json:"key"`
	Title         string         `json:"title"`
	Release       string         `json:"release"`
	Protocol      string         `json:"protocol"`
	State         string         `json:"state"`
	Reason        string         `json:"reason"`
	FirstSeen     int64          `json:"firstSeen"`
	FirstVerified int64          `json:"firstVerified"`
	VerifiedAt    int64          `json:"verifiedAt"`
	NextCheck     int64          `json:"nextCheck"`
}

type Service

type Service struct {
	// contains filtered or unexported fields
}

func New

func New(d Deps) (*Service, error)

func (*Service) Candidates

func (s *Service) Candidates(ctx context.Context, t *core.Title) []core.Release

Candidates serves remembered descriptors locally; playback still applies its exact viewer/device policy and validates the selected source.

func (*Service) Cycle

func (s *Service) Cycle(ctx context.Context) error

func (*Service) FilterDiscovery added in v0.2.9

func (s *Service) FilterDiscovery(ctx context.Context, items []core.Title) []core.Title

A short local memo keeps repeated Plex artwork/list requests inexpensive. New observations and invalidations clear it immediately. Other cluster nodes see changes within fifteen seconds, without a provider request.

func (*Service) Handler

func (s *Service) Handler() http.Handler

Handler is mounted only inside the authenticated owner API.

func (*Service) HiddenMovies added in v0.2.9

func (s *Service) HiddenMovies(ctx context.Context) []core.RatingKey

HiddenMovies supplies SQL pagination with exclusions from the recent local mirror only. No per-title provider lookup or historical catalogue walk occurs.

func (*Service) HiddenShows added in v0.2.9

func (s *Service) HiddenShows(ctx context.Context) []core.RatingKey

HiddenShows excludes first-day series before SQL pagination, just as movies.

func (*Service) Hubs

func (s *Service) Hubs(ctx context.Context, limit int) []core.Hub

func (*Service) Invalidate

func (s *Service) Invalidate(ctx context.Context, id string)

Invalidate retracts positive evidence immediately when playback or the owner invalidates a release. The title and its other sources remain discoverable.

func (*Service) Observe

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

Observe reuses an actual successful resolver health check. Optimistic starts and prepared sessions without health evidence do not call this method.

func (*Service) Prioritize

func (s *Service) Prioritize(ctx context.Context, items []core.Title) []core.Title

func (*Service) RecordSearch added in v0.2.9

func (s *Service) RecordSearch(ctx context.Context, t *core.Title, rs []core.Release)

RecordSearch retains descriptors from an actual requested search. It does not initiate searches, grab media or claim health. Policy is reevaluated on reads.

func (*Service) Run

func (s *Service) Run(ctx context.Context)

func (*Service) Status

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

type Status

type Status struct {
	Mode    string           `json:"mode"`
	Message string           `json:"message"`
	Feeds   []FeedStatus     `json:"feeds"`
	Usage   map[string]int64 `json:"usage"`
	Counts  map[string]int   `json:"counts"`
	Recent  []Observation    `json:"recent"`
	Day     string           `json:"day"`
}

type Validator

type Validator interface {
	FeedCandidate(context.Context, *core.Title, core.Release) (core.Release, string, error)
	VerifyFeedCandidate(context.Context, *core.Title, core.Release) error
}

Jump to

Keyboard shortcuts

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