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

core

package
v0.2.52 Latest Latest
Warning

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

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

Documentation

Overview

Package core holds the domain types and the interfaces between Ether's components. It has no dependencies on other Ether packages, so every component can import it. Changing a type here is a contract change: keep edits additive.

Index

Constants

View Source
const (
	ProtocolUsenet  = ""        // the zero value: an NZB from a Newznab indexer
	ProtocolTorrent = "torrent" // a Torznab result streamed through a debrid service
	// ProtocolUnstated is a filename preview with no protocol fact.
	// Live releases keep the empty-string Usenet default; this value
	// exists so a preview cannot report that default as evidence.
	ProtocolUnstated = "unstated"
	// ProtocolHTTP is a stream URL a Stremio addon resolved itself (often
	// through the addon owner's own debrid account), read with HTTP range
	// requests. It needs no debrid account in Ether (additive).
	ProtocolHTTP = "http"
)

Release protocols (Release.Protocol).

View Source
const (
	DeviceTV      = "tv"
	DeviceMobile  = "mobile"
	DeviceWeb     = "web"
	DeviceDesktop = "desktop"
)

Device classes for HubContext.Device.

View Source
const (
	DaypartMorning   = "morning"   // 05:00–11:59
	DaypartAfternoon = "afternoon" // 12:00–16:59
	DaypartEvening   = "evening"   // 17:00–21:59
	DaypartLate      = "late"      // 22:00–04:59
)

Dayparts for HubContext.Daypart, in the server's local time.

View Source
const (
	RowSourceEngine = "engine" // generated by the personalization engine
	RowSourceOwner  = "owner"  // an owner-designed collection
	RowSourcePlex   = "plex"   // built by the Plex front end (Continue Watching, playlists)
)

Row sources for HomeRow.Source.

View Source
const (
	ProtocolHLS  = "hls"
	ProtocolDASH = "dash"
)

Streaming protocols of HLSRequest.Protocol.

View Source
const (
	PrewarmDetail   = "detail"   // a detail page was opened
	PrewarmTrailer  = "trailer"  // a trailer or Cinema Trailer for the title is playing
	PrewarmPostPlay = "postplay" // the viewer is near the end of something and this is up next
)

Reasons for PrewarmOptions.Reason.

View Source
const (
	EphemeralSmartBase int64 = 800_000_000_000_000
	EphemeralSmartEnd  int64 = 900_000_000_000_000
)

EphemeralSmartBase and EphemeralSmartEnd bound the KindSmart payloads reserved for ephemeral generated collections. The range stops where personalize's Year in Review collections begin (payload 9*10^14 and up), and database smart-collection ids stay far below it.

View Source
const PausedSessionRetention = 30 * time.Minute

PausedSessionRetention bounds a silent pause without treating it as an abandoned stream. Explicit stop still closes immediately.

Variables

View Source
var (
	ErrNotFound          = errors.New("not found")
	ErrNotAvailable      = errors.New("not available yet") // no acceptable release exists
	ErrUnreleased        = errors.New("title has not been released")
	ErrArticleMissing    = errors.New("article missing on all providers")
	ErrArticleCorrupt    = errors.New("corrupt article or media data")
	ErrReleaseRejected   = errors.New("release cannot be streamed")
	ErrSourceUnavailable = errors.New("media source temporarily unavailable")
	ErrBudgetExhausted   = errors.New("indexer daily budget exhausted")
	ErrStreamLimit       = errors.New("stream limit reached")
	ErrUnauthorized      = errors.New("unauthorized")
)
View Source
var ErrDebridAuth = errors.New("debrid: authentication failed")

ErrDebridAuth is a debrid service rejecting the configured API key (or an account that cannot use the API). A configuration problem, never a release failure.

View Source
var ErrDownloadBudget = errors.New("download: temporary disk budget exceeded")

ErrDownloadBudget is returned when a download would exceed the temporary disk budget for remuxed files.

View Source
var ErrDownloadNeedsTranscode = errors.New("download: source codecs cannot be copied to MP4")

ErrDownloadNeedsTranscode is returned by DownloadRemuxer when the resolved streams cannot be copied into an MP4 a mobile client plays (e.g. AV1/VP9 video, TrueHD/DTS audio). Ether downloads never transcode offline, so the download decision reports this honestly as an error instead of silently re-encoding.

View Source
var ErrDownloadQueued = fmt.Errorf("sent to the debrid service to download: %w", ErrNotCached)

ErrDownloadQueued reports that an uncached torrent was handed to a debrid account to download (cached-only off): not playable yet, but it will be.

View Source
var ErrNoSources = fmt.Errorf("no indexers to search: %w", ErrNotAvailable)

ErrNoSources reports that there is nothing to search: no enabled indexer at all. A configuration problem, not proof a title has no release, so callers must not record the title as unavailable.

View Source
var ErrNoTorrentSources = fmt.Errorf("debrid has no torrent indexer to search: %w", ErrNoSources)

ErrNoTorrentSources reports that debrid is the only way to play (no Usenet provider) but no enabled indexer returns torrents (Torznab, a Stremio addon or a torrent built-in site).

View Source
var ErrNotCached = fmt.Errorf("not cached by any debrid provider: %w", ErrNotAvailable)

ErrNotCached reports that no debrid provider can serve a torrent release instantly. It is a per-release skip, never proof the release is broken, so it wraps ErrNotAvailable and is not a permanent failure.

Functions

func CheckReleaseHealth

func CheckReleaseHealth(ctx context.Context, hc HealthChecker, releaseID string, nzb []byte) (bool, error)

CheckReleaseHealth runs hc.CheckRelease when hc implements it, else hc.Check.

func ClassifyDevice

func ClassifyDevice(product, platform, device string) string

ClassifyDevice maps a Plex client's X-Plex-Product, X-Plex-Platform and X-Plex-Device headers onto a device class (DeviceTV, DeviceMobile, DeviceWeb, DeviceDesktop).

  • TV: Apple TV, Android TV / Google TV / Fire TV / Shield, Roku, smart TVs (Samsung Tizen, LG webOS, Vizio, Hisense, Sony...), game consoles (Xbox, PlayStation) and set-top boxes.
  • Desktop: Plex HTPC, Plex for Windows/Mac/Linux, Plex Media Player.
  • Web: Plex Web in any browser.
  • Mobile: Plex for iOS / Android phones and tablets.

Unknown clients fall back on their platform, then on DeviceWeb.

func DaypartAt

func DaypartAt(t time.Time) string

DaypartAt returns the daypart of t in t's own location: morning 05:00–11:59, afternoon 12:00–16:59, evening 17:00–21:59, late 22:00–04:59.

func ForegroundReserve added in v0.2.8

func ForegroundReserve(ctx context.Context) int

func InheritBackground added in v0.2.8

func InheritBackground(dst, src context.Context) context.Context

InheritBackground carries admission policy into a shared worker whose lifetime belongs to the engine, without tying that worker to one waiter's cancellation.

func IsBackgroundCheck added in v0.2.8

func IsBackgroundCheck(ctx context.Context) bool

func IsWeekend

func IsWeekend(t time.Time) bool

IsWeekend reports whether t (in its own location) falls in the weekend viewing window: Friday from 17:00 through Sunday. Friday night behaves like a weekend night (films, longer sessions), Sunday daytime does too.

func SpendBackground added in v0.2.8

func SpendBackground(ctx context.Context, resource, source string, amount int64) error

func TargetReleaseID

func TargetReleaseID(releaseID string, t ReleaseTarget) string

TargetReleaseID derives a stable release ID for one episode of a multi-file release, so health, pins, cache and bad marks apply to the episode file rather than the whole pack.

func WithBackgroundBudget added in v0.2.8

func WithBackgroundBudget(ctx context.Context, budget BackgroundBudget) context.Context

func WithForegroundReserve added in v0.2.8

func WithForegroundReserve(ctx context.Context, percent int) context.Context

Types

type ActiveUserSource added in v0.2.7

type ActiveUserSource interface {
	ActiveUser(context.Context, int64) (*User, error)
}

ActiveUserSource checks current server membership for delegated stream access.

type ActivityDay

type ActivityDay struct {
	Date          string  `json:"date"` // YYYY-MM-DD, server local time
	Plays         int     `json:"plays"`
	Hours         float64 `json:"hours"`
	UniqueViewers int     `json:"uniqueViewers"`
}

ActivityDay is one day of server-wide viewing.

type AlignRequest

type AlignRequest struct {
	Key          RatingKey     `json:"key"`
	From         Release       `json:"from"`
	To           Release       `json:"to"`
	FromDuration time.Duration `json:"fromDuration,omitempty"`
	ToDuration   time.Duration `json:"toDuration,omitempty"`
}

AlignRequest names two releases of the same title whose timelines should be matched (for example the failing and the replacement release of a mid-stream failover, or a marker source's reference cut and the release being played).

type ArticleFetcher

type ArticleFetcher interface {
	// Body returns the decoded payload of one article (one NZB segment).
	// Returns ErrArticleMissing when no provider has it.
	Body(ctx context.Context, messageID string, groups []string, prio Priority) ([]byte, error)
	// BodyPart is Body plus the article's yEnc =ybegin/=ypart header, which
	// gives the segment's exact byte range inside the posted file.
	BodyPart(ctx context.Context, messageID string, groups []string, prio Priority) ([]byte, ArticlePart, error)
	// Stat reports whether any provider has the article.
	Stat(ctx context.Context, messageID string) (bool, error)
}

ArticleFetcher fetches and yEnc-decodes articles across the configured providers, primary first, backfilling from block accounts.

type ArticlePart

type ArticlePart struct {
	Name     string `json:"name"`
	FileSize int64  `json:"fileSize"`
	Part     int    `json:"part"`
	Total    int    `json:"total"`
	Begin    int64  `json:"begin"`
	End      int64  `json:"end"`
}

ArticlePart is the yEnc header of one article. Begin and End are 1-based inclusive offsets in the posted file, as in =ypart; for single-part posts Begin is 1 and End is FileSize.

type ArtworkAccessPolicy added in v0.2.7

type ArtworkAccessPolicy interface{ Restricted(*User) bool }

ArtworkAccessPolicy exposes the optional Restricted capability.

type Auth

type Auth interface {
	// Authenticate maps an X-Plex-Token to a user with access to this server.
	Authenticate(ctx context.Context, token string) (*User, error)
	Users(ctx context.Context) ([]User, error)
	UpdateUser(ctx context.Context, u User) error
	Owner(ctx context.Context) (*User, error)
}

type AuthoritativeTitleRefresher added in v0.2.37

type AuthoritativeTitleRefresher interface {
	RefreshTitle(ctx context.Context, key RatingKey, persist bool) (*Title, error)
}

AuthoritativeTitleRefresher fetches current movie or show details and optionally persists them. It never falls back to cached data on failure and does not apply release visibility.

type AvailabilitySource added in v0.2.7

type AvailabilitySource interface {
	Available(context.Context, RatingKey) (bool, bool)
}

AvailabilitySource exposes the optional Available capability.

type BackgroundBudget added in v0.2.8

type BackgroundBudget func(ctx context.Context, resource, source string, amount int64) error

BackgroundBudget is an optional admission hook for speculative work. Ordinary playback has no hook. Implementations must be concurrency safe; denied work returns ErrBudgetExhausted and is never evidence of source absence.

type BitrateAccessPolicy added in v0.2.7

type BitrateAccessPolicy interface{ BitrateLimit(*User) int }

BitrateAccessPolicy exposes the optional BitrateLimit capability.

type BrowseQuery

type BrowseQuery struct {
	Filter Filter
	Sort   SortOrder
	Desc   bool
	Offset int
	Limit  int
}

type BufferedInputSwapper added in v0.2.8

type BufferedInputSwapper interface {
	SwapInputBuffered(context.Context, string, MediaInfo, time.Duration) error
}

BufferedInputSwapper preserves published media before starting replacement input at the contiguous buffer boundary. The caller must verify identity timeline alignment at the playhead and buffer boundary; differing trailing durations alone do not prevent reuse.

type BurstArticleFetcher added in v0.2.8

type BurstArticleFetcher interface {
	BodyPartBurst(ctx context.Context, messageID string, groups []string, prio Priority, until time.Time) ([]byte, ArticlePart, error)
}

BurstArticleFetcher optionally borrows idle live-reserved capacity for a short, explicitly bounded bulk-read burst. Live waiters retain precedence. The deadline limits new burst admission, not already transmitted bytes.

type CachePlayRecorder added in v0.2.7

type CachePlayRecorder interface{ RecordPlay(string, string, int64) }

CachePlayRecorder exposes the optional RecordPlay capability.

type CachedTitleSource added in v0.2.7

type CachedTitleSource interface {
	CachedTitles(context.Context, []RatingKey) ([]Title, error)
}

CachedTitleSource exposes the optional CachedTitles capability.

type Catalog

type Catalog interface {
	Get(ctx context.Context, key RatingKey) (*Title, error)
	// Children: seasons of a show, episodes of a season.
	Children(ctx context.Context, key RatingKey) ([]Title, error)
	// Grandchildren: all episodes of a show.
	Grandchildren(ctx context.Context, key RatingKey) ([]Title, error)
	Browse(ctx context.Context, q BrowseQuery) (Page, error)
	// Search reaches all of TMDB; matched titles join the browsable set.
	Search(ctx context.Context, query string, types []MediaType, limit int) ([]Title, error)
	// Lists from TMDB: "trending_day", "trending_week", "top_rated", "now_playing", "popular", "airing_today", "upcoming".
	List(ctx context.Context, list string, t MediaType, limit int) ([]Title, error)
	Similar(ctx context.Context, key RatingKey, limit int) ([]Title, error)
	Collection(ctx context.Context, tmdbCollection int64) (name string, items []Title, err error)
	// Facets for filter UIs: "genre", "year", "decade", "contentRating", "studio", "country".
	Facet(ctx context.Context, t MediaType, facet string) ([]FacetValue, error)
	// NextEpisode returns the episode after the given one, or ErrNotFound.
	NextEpisode(ctx context.Context, episode RatingKey) (*Title, error)
	// ByExternalID looks up a title by imdb ("tt123") or tvdb id.
	ByExternalID(ctx context.Context, source, id string) (*Title, error)
}

Catalog is Ether's library: a local mirror of a TMDB slice plus on-demand fetches for anything else.

type CatalogSchedule added in v0.2.45

type CatalogSchedule interface {
	ScheduledEpisodes(context.Context, RatingKey) ([]Title, error)
}

CatalogSchedule exposes metadata-only placeholders when the owner opts in. Only dated, unaired episodes of an already airing regular season are returned. Callers must authorize the parent season and each episode for the viewer, render no media/playback links, and use the same scope for artwork. Ordinary Catalog methods remain released-only; this interface never authorizes playback.

type ChannelPreparer added in v0.2.7

type ChannelPreparer interface {
	PrepareChannel(context.Context, *User, RatingKey, ClientCaps, string) (*PlaySession, error)
}

ChannelPreparer exposes the optional PrepareChannel capability.

type Chapter

type Chapter struct {
	ID    int64         `json:"id"`
	Title string        `json:"title,omitempty"`
	Start time.Duration `json:"start"`
	End   time.Duration `json:"end"`
}

type ClientCaps

type ClientCaps struct {
	Product         string `json:"product"`  // X-Plex-Product: "Plex Web", "Plex for iOS", "Plex for Apple TV"
	Platform        string `json:"platform"` // X-Plex-Platform
	PlatformVersion string `json:"platformVersion"`
	Device          string `json:"device"`
	DeviceName      string `json:"deviceName"`
	ClientID        string `json:"clientId"` // X-Plex-Client-Identifier
	Version         string `json:"version"`

	MaxHeight      int                 `json:"maxHeight"` // 2160, 1080, 720...
	HDR10          bool                `json:"hdr10"`
	DolbyVision    bool                `json:"dolbyVision"`
	VideoCodecs    []string            `json:"videoCodecs"`              // h264, hevc, av1, vp9
	AudioCodecs    []string            `json:"audioCodecs"`              // aac, ac3, eac3, truehd, dts, opus, flac
	Containers     []string            `json:"containers"`               // mp4, mkv, mpegts
	HLSContainers  []string            `json:"hlsContainers,omitempty"`  // explicit segmented output targets; distinct from direct-play containers
	MaxBitrateKbps int                 `json:"maxBitrateKbps"`           // 0 = unlimited
	SubtitleCodecs []string            `json:"subtitleCodecs,omitempty"` // native image/text subtitle rendering
	DVProfiles     []int               `json:"dvProfiles,omitempty"`     // explicit Dolby Vision profiles; empty uses product defaults
	VideoProfiles  map[string][]string `json:"videoProfiles,omitempty"`
	VideoLevels    map[string]int      `json:"videoLevels,omitempty"` // codec level in ffprobe units (H.264 51 = 5.1)
	Remote         bool                `json:"remote"`                // not on the server's LAN

	// TranscodeAudioCodecs is the explicit audio target for the current
	// streaming protocol. It constrains remux/transcode output only; source
	// selection and direct play continue to use AudioCodecs.
	TranscodeAudioCodecs []string `json:"transcodeAudioCodecs,omitempty"`

	// Download marks a request for an offline download (Plex Downloads /
	// Sync) rather than live playback: the resolver prefers compact,
	// mobile-native releases and the whole file is remuxed (additive).
	Download bool `json:"download,omitempty"`
}

ClientCaps is what a Plex client can play, derived from X-Plex-* headers, the client profile and its declared limits.

func (ClientCaps) SupportsAudio

func (c ClientCaps) SupportsAudio(codec string) bool

func (ClientCaps) SupportsContainer

func (c ClientCaps) SupportsContainer(ct string) bool

func (ClientCaps) SupportsVideo

func (c ClientCaps) SupportsVideo(codec string) bool

type Collection

type Collection struct {
	Key          RatingKey `json:"key"`
	Title        string    `json:"title"`
	Summary      string    `json:"summary,omitempty"`
	Kind         string    `json:"kind"` // "franchise", "trending", "smart", "user"
	Type         MediaType `json:"type"`
	Filter       *Filter   `json:"filter,omitempty"` // smart
	Sort         SortOrder `json:"sort,omitempty"`
	OwnerUserID  int64     `json:"ownerUserId,omitempty"` // 0 = server-wide
	PosterPath   string    `json:"posterPath,omitempty"`
	BackdropPath string    `json:"backdropPath,omitempty"`
	ChildCount   int       `json:"childCount"`
	Upcoming     bool      `json:"upcoming,omitempty"` // admits scoped future placeholders
	// UpdatedAt changes when the collection's members or artwork change, so
	// Plex clients refetch artwork (additive).
	UpdatedAt time.Time `json:"updatedAt,omitempty"`
}

type CollectionArtwork

type CollectionArtwork struct {
	Mode   string `json:"mode"` // auto | collage | backdrop | tmdb | custom
	URL    string `json:"url,omitempty"`
	Accent string `json:"accent,omitempty"` // "#rrggbb"
}

CollectionArtwork selects the poster Plex shows for the collection.

type CollectionArtworkSource added in v0.2.7

type CollectionArtworkSource interface {
	CollectionArtwork(context.Context, *User, RatingKey, string, int) ([]byte, string, bool, error)
}

CollectionArtworkSource exposes the optional CollectionArtwork capability.

type CollectionLayoutSource added in v0.2.7

type CollectionLayoutSource interface {
	LayoutRow(context.Context, *User, HubContext, string) (*CollectionRow, error)
}

CollectionLayoutSource exposes the optional LayoutRow capability.

type CollectionPlaceholder added in v0.2.37

type CollectionPlaceholder interface {
	AllowsPlaceholders(context.Context, *User, RatingKey, string) bool
	Placeholder(context.Context, *User, RatingKey, RatingKey, string) (*Title, error)
	FindPlaceholder(context.Context, *User, RatingKey, string) (*Title, RatingKey, error)
}

CollectionPlaceholder resolves a future title only through a visible, enabled collection in which it is currently a member for this viewer.

type CollectionPreview

type CollectionPreview struct {
	Total    int      `json:"total"`
	Items    []Title  `json:"items"`
	Warnings []string `json:"warnings"`
	TookMs   int64    `json:"tookMs"`
}

CollectionPreview is the result of evaluating an unsaved spec.

type CollectionRotate

type CollectionRotate struct {
	Size  int    `json:"size"`
	Every string `json:"every"` // "daily" | "weekly"
}

CollectionRotate shows a rotating subset of the members.

type CollectionRow

type CollectionRow struct {
	Hub          Hub    `json:"hub"`
	CollectionID string `json:"collectionId"`
	HomeRow      bool   `json:"homeRow"`
	SectionRow   bool   `json:"sectionRow"`
}

CollectionRow is an owner-designed collection offered as a Home or section row for a user in a context (targeting and schedules already applied).

type CollectionRows

type CollectionRows interface {
	HomeRowsFor(ctx context.Context, user *User, hc HubContext) ([]CollectionRow, error)
}

CollectionRows is implemented by the collection engine for the personalization engine.

type CollectionRule

type CollectionRule struct {
	Field string          `json:"field"`
	Op    string          `json:"op"`
	Value json.RawMessage `json:"value"`
	Label string          `json:"label,omitempty"`
}

CollectionRule is one condition. Value is typed by Field; Label is display text for ids.

type CollectionSchedule

type CollectionSchedule struct {
	Days     []int    `json:"days,omitempty"`
	FromHour *int     `json:"fromHour,omitempty"`
	ToHour   *int     `json:"toHour,omitempty"`
	Devices  []string `json:"devices,omitempty"`
}

CollectionSchedule limits when rows appear. Days use 0 = Sunday … 6 = Saturday; hours are local, FromHour inclusive and ToHour exclusive, wrapping past midnight when FromHour > ToHour.

type CollectionSource

type CollectionSource struct {
	Kind      string            `json:"kind"`
	List      string            `json:"list,omitempty"`
	MediaType string            `json:"mediaType,omitempty"`
	ID        int64             `json:"id,omitempty"`
	Label     string            `json:"label,omitempty"`
	Params    map[string]string `json:"params,omitempty"`
	Region    string            `json:"region,omitempty"`
	URL       string            `json:"url,omitempty"`
	Keys      []string          `json:"keys,omitempty"`
	// Role narrows tmdbPerson credits: "cast", "director", "writer",
	// "crew" or "" (every credit). Additive to the contract.
	Role string `json:"role,omitempty"`
}

CollectionSource is one place candidates come from. Kind selects which other fields apply:

catalog                                   the whole browsable catalogue
tmdbList        List, MediaType           trending_day|trending_week|popular|top_rated|now_playing|upcoming|airing_today|on_the_air
tmdbCollection  ID, Label                 a TMDB franchise
tmdbDiscover    Params                    raw /discover parameters (names allowed for ids)
tmdbKeyword | tmdbCompany | tmdbNetwork | tmdbPerson   ID, Label (Role for people)
watchProvider   ID, Region, Label         what's streaming on a service
traktList | letterboxdList | mdblist      URL
anilistList                               URL (an AniList anime list; optionally one named list)
stevenlu                                  List: movies (default) or a score-filtered variant
titles          Keys                      hand-picked rating keys

When ID is 0 the Label is resolved to a TMDB id by name.

type CollectionSpec

type CollectionSpec struct {
	ID          string             `json:"id,omitempty"` // server-assigned
	Name        string             `json:"name"`
	Description string             `json:"description,omitempty"`
	MediaType   string             `json:"mediaType"` // "movie" | "show" | "mixed"
	Sources     []CollectionSource `json:"sources"`
	Match       string             `json:"match"` // "all" | "any"
	Rules       []CollectionRule   `json:"rules"`
	Exclude     []CollectionRule   `json:"exclude"`
	// Sort: popularity | rating | released | title | added | random | personal | source.
	Sort          string            `json:"sort"`
	SortDesc      bool              `json:"sortDesc"`
	Limit         int               `json:"limit"`   // 0 = no limit
	Refresh       string            `json:"refresh"` // live | hourly | daily | weekly
	Rotate        *CollectionRotate `json:"rotate,omitempty"`
	Artwork       CollectionArtwork `json:"artwork"`
	Targets       CollectionTargets `json:"targets"`
	Enabled       bool              `json:"enabled"`
	LocalOnly     bool              `json:"localOnly,omitempty"`     // evaluate rules against the local catalogue only; no remote discovery
	ReleaseMode   string            `json:"releaseMode,omitempty"`   // released (default), upcoming, or mixed
	LookaheadDays int               `json:"lookaheadDays,omitempty"` // 1..366 when upcoming is enabled
	TemplateID    string            `json:"templateId,omitempty"`
	// Server-maintained.
	UpdatedAt   *time.Time `json:"updatedAt,omitempty"`
	LastBuiltAt *time.Time `json:"lastBuiltAt,omitempty"`
	ItemCount   int        `json:"itemCount"`

	// Additive fields beyond the contract (read-only for clients).
	BuiltIn   bool     `json:"builtIn,omitempty"`   // shipped with Ether; only targets/artwork/rotate/enabled are editable
	RatingKey string   `json:"ratingKey,omitempty"` // the Plex ratingKey of the published collection
	Warnings  []string `json:"warnings,omitempty"`  // from the last build
	// OwnerUserID is set for private collections created by a Plex user
	// (core.Collections.CreateSmart); 0 = server-wide.
	OwnerUserID int64 `json:"ownerUserId,omitempty"`
}

CollectionSpec is an owner-designed collection: candidates gathered from Sources, filtered by Rules, sorted, limited and published to Plex.

type CollectionSpecSource added in v0.2.7

type CollectionSpecSource interface {
	ListSpecs(context.Context) ([]CollectionSpec, error)
}

CollectionSpecSource exposes the optional ListSpecs capability.

type CollectionTargets

type CollectionTargets struct {
	CollectionsTab bool                `json:"collectionsTab"`
	HomeRow        bool                `json:"homeRow"`
	SectionRow     bool                `json:"sectionRow"`
	Users          UserTargets         `json:"users"`
	Schedule       *CollectionSchedule `json:"schedule,omitempty"`
}

CollectionTargets says where and for whom the collection is published.

type CollectionTemplate

type CollectionTemplate struct {
	ID          string         `json:"id"`
	Name        string         `json:"name"`
	Description string         `json:"description"`
	Category    string         `json:"category"`
	Spec        CollectionSpec `json:"spec"`
}

CollectionTemplate is a curated starting point in the Collection Studio.

type Collections

type Collections interface {
	List(ctx context.Context, user *User, t MediaType) ([]Collection, error)
	Get(ctx context.Context, user *User, key RatingKey) (*Collection, error)
	Items(ctx context.Context, user *User, key RatingKey, offset, limit int) (Page, error)
	CreateSmart(ctx context.Context, c Collection) (*Collection, error)
	DeleteSmart(ctx context.Context, key RatingKey) error
}

type ContextReaderAt added in v0.2.7

type ContextReaderAt interface {
	ReadAtContext(context.Context, []byte, int64) (int, error)
}

ContextReaderAt reads source bytes with request cancellation.

type ContextualRecommender

type ContextualRecommender interface {
	HomeHubsFor(ctx context.Context, user *User, hc HubContext, limit int) ([]Hub, error)
	SectionHubsFor(ctx context.Context, user *User, t MediaType, hc HubContext, limit int) ([]Hub, error)
}

ContextualRecommender is implemented by recommenders that adapt rows to the request context. internal/plex prefers it over Recommender when available.

type CustomFormat

type CustomFormat struct {
	ID             string                `json:"trash_id"`
	Name           string                `json:"name"`
	Scores         map[string]int        `json:"trash_scores,omitempty"`
	Specifications []FormatSpecification `json:"specifications"`
	// App names the Starr app whose enum values the specifications use:
	// Sonarr numbers sources differently from Radarr. Empty means Radarr.
	App string `json:"app,omitempty"`
}

CustomFormat retains the native TRaSH/Radarr/Sonarr JSON for owner editing. Unknown specifications are rejected at import rather than silently matching.

type DASHSession

type DASHSession interface {
	HLSSession
	// ServeManifest writes the MPD.
	ServeManifest(w http.ResponseWriter, r *http.Request)
	// ServeInit writes the initialization segment of a representation.
	ServeInit(w http.ResponseWriter, r *http.Request, rep int)
	// ServeMedia writes media segment n of a representation, waiting for
	// ffmpeg and restarting it at the right offset when a seek jumps ahead.
	ServeMedia(w http.ResponseWriter, r *http.Request, rep, n int)
}

DASHSession is a running ffmpeg remux or transcode producing MPEG-DASH: a static, full-length MPD with separate video and audio representations of fMP4 segments addressed by number ($Number$, from 0). Its HLSSession methods map onto the first representation; ServePlaylist writes the MPD.

type DamageReporter added in v0.2.7

type DamageReporter interface{ SetDamageHandler(func(int64, int64) bool) }

DamageReporter exposes the optional SetDamageHandler capability.

type DecideOptions

type DecideOptions struct {
	// DisableVideoTranscode prevents optional enhancement from promoting direct playback.
	DisableVideoTranscode bool

	DirectPlay     bool     // client allows direct play
	DirectStream   bool     // client allows remux
	MaxBitrateKbps int      // client-requested cap (maxVideoBitrate)
	MaxHeight      int      // from videoResolution
	AudioStream    int      // selected stream index, -1 default
	SubtitleStream int      // -1 none
	AudioFilters   []string // trusted owner/viewer audio processing; forces audio encoding
	BurnSubtitles  bool
}

DecideOptions are per-request choices from the Plex decision request.

type Decision

type Decision struct {
	Enhancement VideoEnhancement `json:"enhancement,omitempty"`

	Kind           DecisionKind `json:"kind"`
	VideoCopy      bool         `json:"videoCopy"`
	AudioCopy      bool         `json:"audioCopy"`
	VideoCodec     string       `json:"videoCodec"` // target codec when transcoding
	AudioCodec     string       `json:"audioCodec"`
	Container      string       `json:"container"` // "mpegts" (HLS) or original
	MaxHeight      int          `json:"maxHeight"`
	BitrateKbps    int          `json:"bitrateKbps"`
	AudioStream    int          `json:"audioStream"`
	SubtitleStream int          `json:"subtitleStream"` // -1 none
	BurnSubtitles  bool         `json:"burnSubtitles"`
	Reason         string       `json:"reason"`
	AudioFilters   []string     `json:"audioFilters,omitempty"`
	// AudioChannels and AudioSampleRate keep the already negotiated audio
	// decoder configuration across a source change. Zero uses the normal
	// encoder defaults.
	AudioChannels   int `json:"audioChannels,omitempty"`
	AudioSampleRate int `json:"audioSampleRate,omitempty"`

	// ToneMap converts HDR (HDR10, HLG, Dolby Vision) video to SDR while
	// transcoding, for clients without HDR support.
	ToneMap bool `json:"toneMap,omitempty"`

	// StripDolbyVision copies the compatible HDR10 base layer while
	// removing Dolby Vision RPU/configuration data; it never re-encodes.
	StripDolbyVision bool `json:"stripDolbyVision,omitempty"`
}

type DecisionKind

type DecisionKind string
const (
	DecisionDirectPlay DecisionKind = "directplay"
	DecisionRemux      DecisionKind = "copy" // Plex calls this "direct stream"
	DecisionTranscode  DecisionKind = "transcode"
)

type DelegationAuth added in v0.2.7

type DelegationAuth interface {
	IssueDelegation(context.Context, int64) (string, error)
}

DelegationAuth issues expiring, server-local casting credentials. Implementations must enforce current membership on use and must not delegate owner privileges.

type DirectPlayRetrier added in v0.2.7

type DirectPlayRetrier interface{ RetryDirectPlay() bool }

DirectPlayRetrier exposes the optional RetryDirectPlay capability.

type DownloadAccessPolicy added in v0.2.7

type DownloadAccessPolicy interface{ AllowDownloads(*User) bool }

DownloadAccessPolicy exposes the optional AllowDownloads capability.

type DownloadRemuxRequest

type DownloadRemuxRequest struct {
	// InputURL is the loopback URL of the playback session feeding ffmpeg.
	InputURL string
	// Info is the probed media info of the resolved source.
	Info MediaInfo
	// AudioStream is the source stream index to keep; -1 picks the
	// default (first) audio stream.
	AudioStream int
}

DownloadRemuxRequest is one whole-file remux to MP4.

type DownloadRemuxer

type DownloadRemuxer interface {
	// RemuxDownload runs ffmpeg -c copy to a temp MP4, reporting progress
	// as a fraction in [0,1] when progress is non-nil. Cancelling ctx
	// kills ffmpeg and removes the partial file.
	RemuxDownload(ctx context.Context, req DownloadRemuxRequest, progress func(fraction float64)) (path string, size int64, err error)
	// ReleaseDownload deletes a remuxed file and frees its budget.
	// Releasing an unknown path is a no-op.
	ReleaseDownload(path string)
}

DownloadRemuxer copies a resolved source into a temporary faststart MP4. Implemented by internal/transcode. The returned path is owned by the remuxer until ReleaseDownload is called; files are never kept permanently and are bounded by a disk budget.

type DurablePrefetcher added in v0.2.7

type DurablePrefetcher interface {
	DurablePrefetch(context.Context, string, []byte, int64, int64) error
}

DurablePrefetcher exposes the optional DurablePrefetch capability.

type Engine

type Engine interface {
	// Open builds the byte-offset index for the main video file of an NZB.
	Open(ctx context.Context, releaseID string, nzb []byte) (MediaSource, error)
	// Prefetch warms [offset, offset+length) at background priority.
	Prefetch(ctx context.Context, releaseID string, nzb []byte, offset, length int64) error
}

type FacetValue

type FacetValue struct {
	Key   string `json:"key"`
	Title string `json:"title"`
	Count int    `json:"count"`
}

type Filter

type Filter struct {
	// ExcludeKeys applies local visibility policy before counting and pagination.
	ExcludeKeys      []RatingKey   `json:"-"`
	Type             MediaType     `json:"type,omitempty"` // movie or show
	Genres           []string      `json:"genres,omitempty"`
	YearFrom         int           `json:"yearFrom,omitempty"`
	YearTo           int           `json:"yearTo,omitempty"`
	MinRating        float64       `json:"minRating,omitempty"`
	MaxRuntime       time.Duration `json:"maxRuntime,omitempty"`
	MinRuntime       time.Duration `json:"minRuntime,omitempty"`
	Languages        []string      `json:"languages,omitempty"`
	Countries        []string      `json:"countries,omitempty"`
	ContentRatings   []string      `json:"contentRatings,omitempty"`
	MaxContentRating string        `json:"maxContentRating,omitempty"` // per-user limit
	Collection       int64         `json:"collection,omitempty"`
	Person           int64         `json:"person,omitempty"` // cast or crew TMDB id
	Studio           string        `json:"studio,omitempty"`
	Query            string        `json:"query,omitempty"` // title substring
}

Filter narrows a browse or a smart collection. Zero values mean "any".

type FormatSpecification

type FormatSpecification struct {
	Name           string                     `json:"name"`
	Implementation string                     `json:"implementation"`
	Negate         bool                       `json:"negate"`
	Required       bool                       `json:"required"`
	Fields         map[string]json.RawMessage `json:"fields"`
}

type GoodputSession

type GoodputSession interface {
	// GoodputKbps is the rolling segment-delivery estimate; 0 unknown.
	GoodputKbps() int
}

GoodputSession is a session that measures client delivery goodput.

type HLSLadderSession

type HLSLadderSession interface {
	HLSSession
	// Rungs lists the ladder, highest first; Rungs()[0] is "base".
	Rungs() []HLSRung
	// ServeRungPlaylist writes rung's media playlist.
	ServeRungPlaylist(w http.ResponseWriter, r *http.Request, rung string)
	// ServeRungSegment writes segment n of rung (InitSegment numbering as
	// in ServeSegment: the caller passes the parsed segment number).
	ServeRungSegment(w http.ResponseWriter, r *http.Request, rung string, n int)
}

HLSLadderSession is an HLSSession that can serve a multi-rung ABR ladder. Only the primary rung's encoder runs until a client requests a segment on another rung; at most one rung encodes at a time.

type HLSRequest

type HLSRequest struct {
	SessionID string
	InputURL  string // ffmpeg input; Ether serves sources on loopback HTTP with range support
	// LiveInput permits playlist demuxers for owner-configured live sources.
	// Ordinary resolved media must leave this false, even if duration is unknown.
	LiveInput bool
	// InputHeaders are optional HTTP headers ffmpeg must send when reading
	// InputURL (e.g. for resolved trailer URLs). Transcoders that cannot
	// send headers ignore them; most resolved URLs need none (additive).
	InputHeaders map[string]string
	Info         MediaInfo
	Decision     Decision
	StartOffset  time.Duration
	// SegmentDuration defaults to 4s when zero (5s for DASH).
	SegmentDuration time.Duration
	// Protocol is ProtocolHLS (default when empty) or ProtocolDASH. A DASH
	// session also implements DASHSession; Transcoder.Session returns it as
	// an HLSSession, so type-assert to reach the DASH methods.
	Protocol string
}

type HLSRung

type HLSRung struct {
	// ID is the rung's URL component: session/{id}/{ID}/index.m3u8. The
	// primary rung is always "base" (served by the HLSSession methods).
	ID string
	// Bandwidth is the peak bitrate in bits per second (#EXT-X-STREAM-INF
	// BANDWIDTH / DASH Representation bandwidth).
	Bandwidth int
	// AverageBandwidth is the average bitrate in bits per second.
	AverageBandwidth int
	Width            int
	Height           int
	// Codecs is an RFC 6381 codecs string ("avc1...,mp4a...").
	Codecs    string
	FrameRate float64
}

HLSRung is one variant of an adaptive HLS/DASH ladder. All rungs of a session share deterministic segment numbering over one segment timeline: segment N covers the same media on every rung.

type HLSSession

type HLSSession interface {
	ID() string
	// ServePlaylist writes the media playlist (index.m3u8).
	ServePlaylist(w http.ResponseWriter, r *http.Request)
	// ServeSegment writes segment n, waiting for ffmpeg to produce it and
	// restarting ffmpeg at the right offset when a seek jumps ahead.
	ServeSegment(w http.ResponseWriter, r *http.Request, n int)
	// SwapInput restarts the session on a new input at the given position
	// (mid-stream failover). Segment numbering continues.
	SwapInput(ctx context.Context, inputURL string, info MediaInfo, at time.Duration) error
	LastAccess() time.Time
	Close() error
}

HLSSession is one running ffmpeg remux or transcode producing HLS (or DASH; see DASHSession).

type HWAccel

type HWAccel struct {
	Name     string   `json:"name"` // "videotoolbox", "nvenc", "qsv", "vaapi", "none"
	Encoders []string `json:"encoders"`
	Device   string   `json:"device,omitempty"`
	// NoGPUScale: the encoder works but the device's video processing
	// (VAAPI scale_vaapi) does not, so frames are decoded and scaled in
	// software and uploaded to the encoder.
	NoGPUScale bool `json:"noGpuScale,omitempty"`
	// NoGPUTonemap: the device's VAAPI VPP cannot run tonemap_vaapi, so HDR
	// tone-mapping falls back to the software zscale/tonemap path.
	NoGPUTonemap bool `json:"noGpuTonemap,omitempty"`
	// VAAPIQualityRC / VAAPIBitrateRC record the probed VAAPI rate-control
	// modes ("QVBR", "VBR", "CBR", "CQP") per encoder ("h264_vaapi",
	// "hevc_vaapi"); nil/empty falls back to the default QVBR/VBR modes.
	VAAPIQualityRC map[string]string `json:"vaapiQualityRc,omitempty"`
	VAAPIBitrateRC map[string]string `json:"vaapiBitrateRc,omitempty"`
}

type HealthChecker

type HealthChecker interface {
	Check(ctx context.Context, nzb []byte) (healthy bool, err error)
}

HealthChecker checks a sample of an NZB's articles exist (NNTP STAT). It also inspects archive headers: compressed or encrypted RARs and releases without a playable video file report healthy=false with a reason. Implemented by internal/stream.

type HistoryEntry

type HistoryEntry struct {
	UserID    int64     `json:"userId"`
	Key       RatingKey `json:"key"`
	ViewedAt  time.Time `json:"viewedAt"`
	Completed bool      `json:"completed"`
}

type HomeCandidateSource added in v0.2.7

type HomeCandidateSource interface {
	HomeCandidatesFor(context.Context, *User, HubContext, int) ([]Hub, error)
}

HomeCandidateSource exposes the optional HomeCandidatesFor capability.

type HomeLayout

type HomeLayout struct {
	Global    []LayoutPage   `json:"global"`
	Viewers   []ViewerLayout `json:"viewers"`
	UpdatedAt time.Time      `json:"updatedAt,omitempty"`
}

HomeLayout is the owner's global template, with complete row policies in viewer layers replacing matching global policies. Slots are one-based.

type HomeLayoutSource added in v0.2.7

type HomeLayoutSource interface {
	FinalizeHome(context.Context, *User, HubContext, string, []Hub) ([]Hub, error)
}

HomeLayoutSource exposes the optional FinalizeHome capability.

type HomePreview

type HomePreview struct {
	Rows        []HomeRow  `json:"rows"`
	GeneratedAt time.Time  `json:"generatedAt"`
	Context     HubContext `json:"context"`
	// HomeDeduped counts the titles kept off a Movies or TV Shows page
	// because the viewer's Home leads with them (DedupeHome).
	HomeDeduped int `json:"homeDeduped,omitempty"`
}

HomePreview is a viewer's assembled rows for one context.

type HomeRow

type HomeRow struct {
	HubID        string               `json:"hubId"`
	Layer        string               `json:"layer,omitempty"`
	Slot         int                  `json:"slot,omitempty"`
	AnchorSlot   int                  `json:"anchorSlot,omitempty"`
	Placement    string               `json:"placement,omitempty"`
	HiddenReason string               `json:"hiddenReason,omitempty"`
	Policy       *RowPolicy           `json:"policy,omitempty"`
	PolicyLayer  string               `json:"policyLayer,omitempty"`
	Title        string               `json:"title"`
	Kind         string               `json:"kind"` // e.g. "because-you-watched", "collection:<id>"
	Reason       string               `json:"reason"`
	Score        float64              `json:"score"` // predicted engagement used for ordering
	Pinned       bool                 `json:"pinned,omitempty"`
	Hidden       bool                 `json:"hidden,omitempty"` // hidden by a layout rule: not sent to Plex
	Source       string               `json:"source"`
	Items        []Title              `json:"items"`
	ItemReasons  map[RatingKey]string `json:"itemReasons,omitempty"`
	// Hub is the row as handed to the Plex front end.
	Hub Hub `json:"-"`
}

HomeRow is one row of a viewer's Home or section as Plex receives it, with the engine's explanation. Items are catalogue titles; the admin API turns them into TitleCards.

type Hub

type Hub struct {
	ID       string    `json:"id"` // stable identifier, e.g. "trending.week.movie"
	Title    string    `json:"title"`
	Type     MediaType `json:"type"`            // item type in the row ("mixed" allowed via "")
	Context  string    `json:"context"`         // Plex hub context, e.g. "hub.movie.recentlyreleased"
	Style    string    `json:"style,omitempty"` // "shelf", "hero", "clip"
	Promoted bool      `json:"promoted"`        // show on Home
	Items    []Title   `json:"items"`
	More     bool      `json:"more"`
	Key      RatingKey `json:"key,omitempty"` // backing collection, if any
	// ItemCount carries rendered availability when a front end retains the
	// actual metadata and asks only for row placement.
	ItemCount int `json:"itemCount,omitempty"`
}

Hub is a Home row or a section row.

func SectionView

func SectionView(h Hub, t MediaType) (Hub, bool)

SectionView restricts a Home hub to one library section's media type, so mixed rows that belong on a section too (the watchlist, Rediscover) can appear there with only that section's titles. A typed hub passes through when its type matches. Recaps (Year in Review) stay on Home. ok is false when nothing of the type remains.

type HubContext

type HubContext struct {
	Device    string    `json:"device"`
	Daypart   string    `json:"daypart"`
	Weekend   bool      `json:"weekend"`
	Now       time.Time `json:"now"`
	SectionID int       `json:"sectionId,omitempty"`
	Product   string    `json:"product,omitempty"`  // X-Plex-Product
	Platform  string    `json:"platform,omitempty"` // X-Plex-Platform
	SessionID string    `json:"sessionId,omitempty"`
	ClientID  string    `json:"clientId,omitempty"`
}

HubContext is the situation a Plex client asked for rows in: which kind of device, when, and for which section (empty = Home).

func NewHubContext

func NewHubContext(now time.Time, product, platform, device string) HubContext

NewHubContext derives the request context for a hubs call at now (in the server's local time; pass time.Now() or a time already converted with In(loc)) from the client's X-Plex-* headers.

func (HubContext) Bucket

func (hc HubContext) Bucket() string

Bucket is a stable cache key for the context's coarse shape: "section/device/daypart/weekday|weekend".

type HubImpression

type HubImpression struct {
	UserID   int64       `json:"userId"`
	HubID    string      `json:"hubId"`
	Position int         `json:"position"`
	Keys     []RatingKey `json:"keys"`
	Context  HubContext  `json:"context"`
	At       time.Time   `json:"at"`
}

HubImpression is one row as served to a user, for the personalization feedback loop.

type IDs

type IDs struct {
	TMDB int64  `json:"tmdb,omitempty"`
	IMDB string `json:"imdb,omitempty"`
	TVDB int64  `json:"tvdb,omitempty"`
}

IDs are the external identifiers of a title. Episodes carry their show's IMDb/TVDB ids in ShowIDs on Title, and their own TMDB episode id here.

type ImageURLFunc

type ImageURLFunc func(path, size string) string

ImageURL returns a full TMDB image URL for a path and size ("w500", "original").

type ImpressionSink

type ImpressionSink interface {
	RecordImpressions(user *User, hc HubContext, hubs []Hub)
}

ImpressionSink receives served rows. Implementations must not block.

type Indexer

type Indexer interface {
	Name() string
	Search(ctx context.Context, req SearchRequest) ([]Release, error)
	FetchNZB(ctx context.Context, r Release) ([]byte, error)
}

type IntentTracker

type IntentTracker interface {
	// RecordDetailOpen notes that a viewer opened a title's detail page.
	RecordDetailOpen(ctx context.Context, user *User, t *Title)
	// NotePlayback notes that playback of key started; an open for the
	// same title moments earlier was a conversion, not browsing intent.
	NotePlayback(user *User, key RatingKey)
}

IntentTracker receives live in-session browsing signals (item #32): single-item detail opens that do not turn into playback. Signals are in-memory only and decay over ~45 minutes. Implementations must not block the caller for long.

type KeyKind

type KeyKind int
const (
	KindMovie      KeyKind = 1
	KindShow       KeyKind = 2
	KindSeason     KeyKind = 3
	KindEpisode    KeyKind = 4
	KindCollection KeyKind = 5
	KindSmart      KeyKind = 6
	KindPlaylist   KeyKind = 7
	KindExtra      KeyKind = 8
	KindLocal      KeyKind = 9
)

type KeyframePoint

type KeyframePoint struct {
	Offset int64         `json:"offset"`
	Time   time.Duration `json:"time"`
}

KeyframePoint ties a byte offset in a media file to the presentation time of the sync sample (keyframe) or cue point that starts there, as read from the container index (Matroska Cues, MP4 stss with stco/co64) or a packet scan. MediaInfo.Keyframes holds them sorted by Offset and Time.

type LayoutPage

type LayoutPage struct {
	Section       string         `json:"section"`
	MaxRows       int            `json:"maxRows,omitempty"`
	DeviceMaxRows map[string]int `json:"deviceMaxRows,omitempty"`
	Rows          []RowPolicy    `json:"rows"`
	// DedupeHome keeps titles the viewer's Home leads with off this page
	// (Movies/TV Shows only). Nil means the default: on.
	DedupeHome *bool `json:"dedupeHome,omitempty"`
	// Tuning shapes how the page's rows and titles are chosen. Nil fields
	// inherit (viewer page → global page → engine default).
	Tuning *LayoutTuning `json:"tuning,omitempty"`
}

type LayoutTuning

type LayoutTuning struct {
	// Discovery: 0 sticks to proven favourites, 1 explores new territory.
	Discovery *float64 `json:"discovery,omitempty"`
	// Freshness: 0 favours timeless picks, 1 favours new and trending.
	Freshness *float64 `json:"freshness,omitempty"`
	// Variety: 0 keeps rows focused on the strongest tastes, 1 spreads
	// across genres, eras and moods.
	Variety *float64 `json:"variety,omitempty"`
}

LayoutTuning is the owner's three dials for a page, each 0..1 with 0.5 as the engine default.

type LayoutWhen

type LayoutWhen struct {
	Days      []int    `json:"days,omitempty"`
	Dayparts  []string `json:"dayparts,omitempty"`
	Months    []int    `json:"months,omitempty"`
	StartDate string   `json:"startDate,omitempty"`
	EndDate   string   `json:"endDate,omitempty"`
	FromHour  *int     `json:"fromHour,omitempty"`
	ToHour    *int     `json:"toHour,omitempty"`
	Devices   []string `json:"devices,omitempty"`
}

LayoutWhen restricts a Home layout row to days (0 = Sunday … 6 = Saturday), an hour window [FromHour, ToHour) in server local time (wraps past midnight when FromHour > ToHour) and device classes.

type LibraryExclusion added in v0.2.12

type LibraryExclusion struct {
	Name      string           `json:"name"`
	MediaType string           `json:"mediaType"` // movie, show, mixed
	Match     string           `json:"match"`     // all, any
	Rules     []CollectionRule `json:"rules"`
}

LibraryExclusion hides matching catalogue titles everywhere in Plex. Groups are ORed; each group combines its conditions with all or any. Watch history and the underlying metadata are retained.

type LookupResult

type LookupResult struct {
	ID        int64  `json:"id"`
	Name      string `json:"name"`
	Subtitle  string `json:"subtitle,omitempty"`
	ImagePath string `json:"imagePath,omitempty"` // TMDB image path
}

LookupResult is one TMDB entity (person, keyword, company, network, collection, watch provider) found by name for the collection builder.

type MediaInfo

type MediaInfo struct {
	Chapters    []Chapter     `json:"chapters,omitempty"`
	Container   string        `json:"container"` // mkv, mp4, avi, mpegts
	Duration    time.Duration `json:"duration"`
	Size        int64         `json:"size"`
	BitrateKbps int           `json:"bitrateKbps"`
	Streams     []StreamInfo  `json:"streams"`
	Estimated   bool          `json:"estimated"` // derived from release name, not probed

	// Keyframes is the container's keyframe/cue index, sorted, when it was
	// read on open or probe; empty when unknown. Not serialized: it can hold
	// thousands of points (additive).
	Keyframes []KeyframePoint `json:"-"`
}

func (MediaInfo) Video

func (m MediaInfo) Video() *StreamInfo

type MediaSource

type MediaSource interface {
	io.ReaderAt
	Size() int64
	Name() string // file name inside the release, e.g. "Movie.2024.1080p.mkv"
	ReleaseID() string
	Close() error
}

MediaSource is a seekable byte stream of one playable file (the video file inside a release, after NZB/RAR/7z unwrapping).

type MediaType

type MediaType string

MediaType matches Plex's metadata "type" attribute.

const (
	TypeMovie   MediaType = "movie"
	TypeShow    MediaType = "show"
	TypeSeason  MediaType = "season"
	TypeEpisode MediaType = "episode"
)

type MetadataReaderAt added in v0.2.7

type MetadataReaderAt interface {
	ReadMetadataAtContext(context.Context, []byte, int64) (int, error)
}

MetadataReaderAt reads container metadata without advancing playback or read-ahead.

type MicroReason

type MicroReason struct {
	Header       string    `json:"header,omitempty"`
	MatchPercent int       `json:"matchPercent,omitempty"`
	HasMatch     bool      `json:"hasMatch,omitempty"`
	Reason       string    `json:"reason,omitempty"`
	EndsAt       time.Time `json:"endsAt,omitempty"`
	HasEnd       bool      `json:"hasEnd,omitempty"`
}

MicroReason is the per-viewer decision header for a detail page (item #31): a calibrated match percentage, the strongest taste reason and the local finish time, e.g. "96% Match · Directed by Denis Villeneuve · Ends at 10:45 PM". Parts that cannot be supported honestly (low taste confidence, unknown runtime) are left empty.

func (MicroReason) Empty

func (m MicroReason) Empty() bool

Empty reports whether there is nothing worth showing.

type MicroReasoner

type MicroReasoner interface {
	MicroReason(ctx context.Context, user *User, t *Title, now time.Time) MicroReason
}

MicroReasoner builds a MicroReason for one viewer and title. The title is only read, never mutated, so a shared catalogue record can never leak one viewer's header to another.

type OriginSource added in v0.2.44

type OriginSource interface {
	Origin() string
}

OriginSource is implemented by a MediaSource that can name the service its bytes come from, such as "Real-Debrid" (additive). Sources without it are Usenet.

type Page

type Page struct {
	Items []Title
	Total int
}

type Person

type Person struct {
	TMDB      int64  `json:"tmdb,omitempty"`
	Name      string `json:"name"`
	Role      string `json:"role,omitempty"` // character for cast, job for crew
	ThumbPath string `json:"thumbPath,omitempty"`
}

type PersonalScorer

type PersonalScorer interface {
	ScoreTitles(ctx context.Context, user *User, titles []Title) (map[RatingKey]float64, error)
}

PersonalScorer scores titles for a user (higher = better fit). The collection engine uses it for the "personal" sort.

type PlaySession

type PlaySession struct {
	PlaybackGroup string `json:"playbackGroup,omitempty"`
	ID            string
	UserID        int64
	ClientID      string
	Key           RatingKey
	Resolution    *Resolution
	Source        MediaSource // swaps on failover; read through Playback.Source
	Info          MediaInfo
	StartedAt     time.Time

	// Status for the admin dashboard and /status/sessions, filled on the
	// snapshots Playback returns.
	TitleName      string       // "Movie (2024)" or "Show - S01E02 - Episode"
	Username       string       // user display name
	Client         ClientCaps   // the client's product, device and limits
	DecisionReason string       `json:"decisionReason,omitempty"`
	Decision       DecisionKind // directplay once bytes go straight to the client; "" until known
	BytesServed    int64        // source bytes delivered (direct play and ffmpeg input)
	SpeedBps       int64        // recent delivery rate, bytes per second
	LastActive     time.Time
	// ViewOffset is the client's last reported playhead in milliseconds.
	// Nil means no timeline has arrived; source bytes do not imply progress.
	ViewOffset *int64 `json:"viewOffset,omitempty"`
	Failovers  int    // mid-stream release swaps so far
	// Origin names where the current release streams from: "Usenet" or the
	// debrid service, such as "Real-Debrid" (additive).
	Origin string `json:"origin,omitempty"`
	// DownFailovers counts speed-driven swaps to a lower-bitrate release
	// (a subset of Failovers) (additive).
	DownFailovers int `json:"downFailovers,omitempty"`
	// RequiresRemux is set when a direct-play failover could not splice or
	// find an exact-size replacement: the next decision/start for this
	// session must be a zero-encode remux (additive).
	RequiresRemux bool `json:"requiresRemux,omitempty"`
	// Node is the cluster node serving a session reported by another node
	// (empty for this node's own sessions and on a standalone server).
	Node string `json:"node,omitempty"`
}

PlaySession binds a user, a title and a client to a resolved, open source.

type Playback

type Playback interface {
	// Estimate is the virtual media info shown before a title is resolved.
	Estimate(ctx context.Context, t *Title, caps ClientCaps) MediaInfo
	// Prepare resolves and opens a source for the user and client, enforcing
	// stream limits. Reuses an existing session for the same user/client/key.
	Prepare(ctx context.Context, user *User, key RatingKey, caps ClientCaps) (*PlaySession, error)
	Session(id string) (*PlaySession, bool)
	// SessionFor finds the live session of a client for a title.
	SessionFor(userID int64, clientID string, key RatingKey) (*PlaySession, bool)
	// ServeRange serves the session's current source with HTTP range
	// support, failing over to the next release when articles are missing.
	ServeRange(w http.ResponseWriter, r *http.Request, sessionID string)
	// InputURL is a loopback URL ffmpeg can read the session's source from.
	InputURL(sessionID string) string
	// Failover swaps the session to the next candidate release, keeping the
	// timestamp. Returns the new media info.
	Failover(ctx context.Context, sessionID string, at time.Duration) (*PlaySession, error)
	Stop(sessionID string)
	Sessions() []*PlaySession
}

type PlaybackAccessPolicy added in v0.2.7

type PlaybackAccessPolicy interface {
	CanPlay(*User, bool, time.Time) bool
}

PlaybackAccessPolicy exposes the optional CanPlay capability.

type PlaybackPosition added in v0.2.7

type PlaybackPosition interface{ RequestedPosition() time.Duration }

PlaybackPosition exposes the optional RequestedPosition capability.

type PlaybackPreparer added in v0.2.7

type PlaybackPreparer interface {
	PreResolve(context.Context, *User, RatingKey, ClientCaps) bool
}

PlaybackPreparer warms a title without reserving playback or changing history.

type PlaybackTimeline added in v0.2.8

type PlaybackTimeline interface {
	PlaybackProgress(userID int64, clientID string, key RatingKey, position time.Duration, state string)
}

PlaybackTimeline carries actual client position without changing history.

type Playlist

type Playlist struct {
	Key       RatingKey   `json:"key"`
	UserID    int64       `json:"userId"`
	Title     string      `json:"title"`
	Smart     bool        `json:"smart"`
	Items     []RatingKey `json:"items"`
	UpdatedAt time.Time   `json:"updatedAt"`
}

Playlists are user-created lists.

type Playlists

type Playlists interface {
	List(ctx context.Context, userID int64) ([]Playlist, error)
	Get(ctx context.Context, userID int64, key RatingKey) (*Playlist, error)
	Create(ctx context.Context, userID int64, title string, items []RatingKey) (*Playlist, error)
	Add(ctx context.Context, userID int64, key RatingKey, items []RatingKey) error
	Remove(ctx context.Context, userID int64, key RatingKey, itemIndex int) error
	Move(ctx context.Context, userID int64, key RatingKey, from, to int) error
	Delete(ctx context.Context, userID int64, key RatingKey) error
}

type PostPlayPick

type PostPlayPick struct {
	Title      Title   `json:"title"`
	Score      float64 `json:"score"`
	TasteFit   float64 `json:"tasteFit"`
	Similarity float64 `json:"similarity"`
	RuntimeFit float64 `json:"runtimeFit"`
	Reason     string  `json:"reason,omitempty"`
}

PostPlayPick is one ranked post-play recommendation with its explained score components (item #30): Score = taste factor × similarity factor × runtime factor (× a continuation bonus for the franchise's next entry), where TasteFit is the raw taste fit (-1..1), Similarity the feature overlap with the just-watched title (0..1) and RuntimeFit how well the runtime fits the viewer's remaining time tonight (0..1).

type PostPlayer

type PostPlayer interface {
	PostPlay(ctx context.Context, user *User, justWatched RatingKey, hc HubContext, limit int) ([]PostPlayPick, error)
}

PostPlayer ranks a personalized post-play "hook queue" after a title. Implementations cache the ranking per user and just-watched title for the session, so the pick pre-buffered during playback is the pick the post-play endpoint returns first.

type PrefetchReaderAt added in v0.2.7

type PrefetchReaderAt interface {
	ReadAtPrefetchContext(context.Context, []byte, int64) (int, error)
}

PrefetchReaderAt reads speculative bytes at background priority.

type PrewarmOptions

type PrewarmOptions struct {
	// Reason labels the trigger for logs and budgets (Prewarm* constants).
	Reason string `json:"reason"`
	// PrefetchBytes is how much of the opening of the file to pre-buffer at
	// background priority once the release is resolved, opened and probed.
	// Zero uses the implementation's default for the reason.
	PrefetchBytes int64 `json:"prefetchBytes,omitempty"`
	// DuringPlayback allows the warm-up while the viewer already has an
	// active session (a trailer, or the end of a film, is playing).
	DuringPlayback bool `json:"duringPlayback,omitempty"`
}

PrewarmOptions tune a speculative warm-up of a title before the viewer presses Play.

type Prewarmer

type Prewarmer interface {
	Prewarm(ctx context.Context, user *User, key RatingKey, caps ClientCaps, opts PrewarmOptions) bool
}

Prewarmer is implemented by playback managers that can resolve, open, probe and pre-buffer a title ahead of Play, so the later Prepare for the same user, client and title is near-instant. A warm-up never reserves a stream or writes resume/history records. It reports whether a warm-up was scheduled (false when skipped by budgets or already warm).

type Priority

type Priority int
const (
	PriorityLive      Priority = 0 // a viewer is waiting
	PriorityReadahead Priority = 1
	PriorityPrefetch  Priority = 2 // background; yields to live streams
)

type ProcessCPUTime added in v0.2.7

type ProcessCPUTime interface {
	CPUTime() (time.Duration, time.Duration)
}

ProcessCPUTime exposes the optional CPUTime capability.

type ProcessIdentity added in v0.2.7

type ProcessIdentity interface{ PID() int }

ProcessIdentity exposes the optional PID capability.

type ProcessPeakMemory added in v0.2.7

type ProcessPeakMemory interface{ PeakRSSBytes() (uint64, bool) }

ProcessPeakMemory exposes the optional PeakRSSBytes capability.

type QualityProfile

type QualityProfile struct {
	ReleaseRules         []ReleaseRule  `json:"releaseRules,omitempty"`
	AllowedQualities     []string       `json:"allowedQualities,omitempty"` // imported source-resolution gates
	CustomFormatScores   map[string]int `json:"customFormatScores,omitempty"`
	MinCustomFormatScore *int           `json:"minCustomFormatScore,omitempty"`
	Name                 string         `json:"name"`
	MaxHeight            int            `json:"maxHeight"` // cap, e.g. 1080
	// ConfiguredMaxHeight is MaxHeight as the owner configured it,
	// before download reshaping or a client's caps clamped the
	// effective MaxHeight. -1 means the owner set no ceiling ("Any");
	// 0 means unset (a profile built outside SelectProfile), in which
	// case MaxHeight itself is the configured value. Runtime-only,
	// never persisted with the profile.
	ConfiguredMaxHeight int      `json:"-"`
	MinHeight           int      `json:"minHeight"` // floor, e.g. 720
	AllowHDR            bool     `json:"allowHdr"`  // only applied when the client also supports it
	AllowDolbyVision    bool     `json:"allowDolbyVision"`
	PreferredSources    []Source `json:"preferredSources"` // best first
	PreferredCodecs     []string `json:"preferredCodecs"`  // best first; codecs the client can't play are dropped anyway
	PreferredAudio      []string `json:"preferredAudio"`
	MinSizeMBPerMin     float64  `json:"minSizeMbPerMin"` // size sanity, per minute of runtime
	MaxSizeMBPerMin     float64  `json:"maxSizeMbPerMin"`
	PreferredLanguages  []string `json:"preferredLanguages"`
	AllowCam            bool     `json:"allowCam"` // also gated by the owner's global cam setting
	// SelectionKey is the canonical hash of this profile's configured
	// definition, computed by resolver.SelectProfile from the pristine
	// configured profile before download reshaping or client-cap
	// clamping. Release rules are part of that definition. The catalogue
	// original language is not: it is per-title evaluation context, not a
	// profile fact. Runtime-only, never persisted with the profile.
	SelectionKey string `json:"-"`
}

QualityProfile is an owner-editable rule set ranking releases for a class of client.

func (QualityProfile) ComputeSelectionKey added in v0.2.48

func (p QualityProfile) ComputeSelectionKey() string

ComputeSelectionKey returns the canonical key of the configured definition in p: sha256 of its JSON (runtime-only fields tagged json:"-" are excluded) truncated to 16 hex chars. Any selection-field edit (heights, HDR/DV, codecs, sources, audio, languages, sizes, qualities, scores, release rules) changes it; client caps never enter it.

type RankerFeature

type RankerFeature struct {
	Name   string  `json:"name"`
	Label  string  `json:"label"`
	Weight float64 `json:"weight"`
}

RankerFeature is one learned weight, largest magnitude first.

type RankerInsight

type RankerInsight struct {
	TrainedAt *time.Time `json:"trainedAt,omitempty"`
	// Examples/Positives are the impressions and plays the model last
	// trained on.
	Examples  int `json:"examples"`
	Positives int `json:"positives"`
	// AUC of the learned model and of the hand-tuned baseline on held-out
	// impressions (0.5 = chance). Zero when there is not enough data.
	AUC         float64 `json:"auc"`
	BaselineAUC float64 `json:"baselineAuc"`
	// Active is true when the learned model is being used for ranking.
	Active   bool            `json:"active"`
	Features []RankerFeature `json:"features"`
}

RankerInsight describes the learned ranking model for the owner UI.

type RatingKey

type RatingKey int64

RatingKey is the Plex ratingKey of an Ether item. It is derived from TMDB identifiers so a title keeps the same key before and after anything about it is resolved or cached.

Layout (decimal, kept below 2^53 so JavaScript clients parse it safely):

kind * 10^15 + payload

KindMovie       payload = tmdb movie id
KindShow        payload = tmdb tv id
KindSeason      payload = show*10^4 + season           (season < 10^4)
KindEpisode     payload = show*10^7 + season*10^4 + ep  (show < 10^8, season < 1000, ep < 10^4)
KindCollection  payload = tmdb collection id (franchise)
KindSmart       payload = local smart-collection / generated hub id;
                payloads in [EphemeralSmartBase, EphemeralSmartEnd)
                are reserved for ephemeral (in-memory, per-viewer)
                generated collections, and payloads from
                EphemeralSmartEnd up for personalize's Year in Review
                collections; database ids stay far below both
KindPlaylist    payload = local playlist id
KindExtra       payload = compact extra id from the extras registry
KindLocal       payload = local file id (spike / test library)

func CollectionKey

func CollectionKey(id int64) RatingKey

func EphemeralSmartKey

func EphemeralSmartKey(seq int64) RatingKey

EphemeralSmartKey builds an ephemeral collection key from a sequence number (1 .. 10^14-1).

func EpisodeKey

func EpisodeKey(show int64, season, episode int) RatingKey

func ExtraKey

func ExtraKey(id int64) RatingKey

func LocalKey

func LocalKey(id int64) RatingKey

func MovieKey

func MovieKey(tmdb int64) RatingKey

func ParseRatingKey

func ParseRatingKey(s string) (RatingKey, error)

func PlaylistKey

func PlaylistKey(id int64) RatingKey

func SeasonKey

func SeasonKey(show int64, season int) RatingKey

func ShowKey

func ShowKey(tmdb int64) RatingKey

func SmartKey

func SmartKey(id int64) RatingKey

func (RatingKey) Episode

func (k RatingKey) Episode() int

Episode returns the episode number for episode keys.

func (RatingKey) ExtraID

func (k RatingKey) ExtraID() int64

ExtraID returns the extras-registry id of an extra key.

func (RatingKey) IsEphemeral

func (k RatingKey) IsEphemeral() bool

IsEphemeral reports whether k is an ephemeral generated collection key.

func (RatingKey) Kind

func (k RatingKey) Kind() KeyKind

func (RatingKey) Parent

func (k RatingKey) Parent() RatingKey

Parent returns the season key of an episode and the show key of a season.

func (RatingKey) Season

func (k RatingKey) Season() int

Season returns the season number for season and episode keys.

func (RatingKey) String

func (k RatingKey) String() string

func (RatingKey) TMDB

func (k RatingKey) TMDB() int64

TMDB returns the TMDB id for movie, show, and collection keys, and the show id for season and episode keys.

func (RatingKey) Valid

func (k RatingKey) Valid() bool

type Recommender

type Recommender interface {
	// HomeHubs are the rows on the Home screen across both sections.
	HomeHubs(ctx context.Context, user *User, limit int) ([]Hub, error)
	// SectionHubs are the rows at the top of a library section.
	SectionHubs(ctx context.Context, user *User, t MediaType, limit int) ([]Hub, error)
	// Related are rows on a detail page ("More like this", franchise).
	Related(ctx context.Context, user *User, key RatingKey, limit int) ([]Hub, error)
}

Recommender produces per-user Home and section rows.

type RecoveryBuffer added in v0.2.8

type RecoveryBuffer struct {
	Position, ReadyUntil time.Duration
	SeekAt               time.Time // a discontinuous output request invalidates older telemetry
	// DeliveredUntil is contiguous complete HTTP delivery, not a promise that
	// the client retained or decoded it. Zero means unavailable.
	DeliveredUntil time.Duration
}

RecoveryBuffer reports contiguous, published media, not the encoder's input offset. It deliberately does not claim that bytes reached the client.

type RecoveryBufferSource added in v0.2.8

type RecoveryBufferSource interface{ RecoveryBuffer() RecoveryBuffer }

type Release

type Release struct {
	ID       string      `json:"id"`      // stable: sha1(indexer + guid)
	GUID     string      `json:"guid"`    // indexer guid
	Indexer  string      `json:"indexer"` // indexer name
	Name     string      `json:"name"`    // release name
	NZBURL   string      `json:"nzbUrl"`
	Size     int64       `json:"size"`
	PubDate  time.Time   `json:"pubDate"`
	Grabs    int         `json:"grabs,omitempty"`
	Password bool        `json:"password,omitempty"` // indexer flagged passworded
	Category int         `json:"category,omitempty"`
	Info     ReleaseInfo `json:"info"`
	Score    float64     `json:"score"`
	// TitleIDs are movie/series identifiers reported by the indexer. They
	// must never be inferred from the search request's identifiers.
	TitleIDs IDs `json:"titleIds,omitempty"`

	// Torrent releases (additive). Protocol is "" for Usenet and
	// ProtocolTorrent for Torznab results; torrents are streamed through
	// an owner-configured debrid service (internal/debrid).
	Protocol  string `json:"protocol,omitempty"`
	InfoHash  string `json:"infoHash,omitempty"` // lower-case hex BitTorrent v1 info hash
	MagnetURL string `json:"magnetUrl,omitempty"`
	Seeders   int    `json:"seeders,omitempty"`
	Peers     int    `json:"peers,omitempty"`
	// Target narrows a multi-file release (a season pack) to the wanted
	// episode; set by the resolver on candidates before fetching.
	Target *ReleaseTarget `json:"target,omitempty"`
	// DebridCached and DebridUncached name the debrid services (config
	// Debrid* kinds) the source reported as holding, or not holding, the
	// torrent, such as a Stremio addon's cached marker (additive). Hints
	// only: a provider's own answer always wins.
	DebridCached   []string `json:"debridCached,omitempty"`
	DebridUncached []string `json:"debridUncached,omitempty"`
	// HTTP stream releases (ProtocolHTTP, additive). StreamURL is the URL
	// the addon answered with; it and StreamHeaders (request headers the
	// addon requires) can carry the addon's credentials, so they never
	// leave the server. StreamOrigin is the owner-configured addon origin
	// (scheme://host:port), the only private address the URL may reach.
	StreamURL     string            `json:"streamUrl,omitempty"`
	StreamHeaders map[string]string `json:"streamHeaders,omitempty"`
	StreamOrigin  string            `json:"streamOrigin,omitempty"`
}

Release is an indexer search result for a title.

type ReleaseBlockChecker added in v0.2.34

type ReleaseBlockChecker interface {
	ReleaseBlocked(context.Context, string) (bool, error)
}

ReleaseBlockChecker lets stored source consumers honor durable owner blocks. Errors must prevent reuse, rather than silently bypassing the blocklist.

type ReleaseCondition added in v0.2.49

type ReleaseCondition struct {
	Field    string  `json:"field"`
	Operator string  `json:"operator"` // equals, contains, regex, range
	Value    string  `json:"value,omitempty"`
	Min      float64 `json:"min,omitempty"`
	Max      float64 `json:"max,omitempty"`
}

ReleaseCondition inspects declared release metadata, never presumed language. Language and subtitle facts describe the indexer's/name's declarations, not verified tracks. A missing fact is unknown, not a successful negative match. audio_matches_original takes no value: evaluation compares declared audio with the catalogue original language supplied beside the release.

type ReleaseFeed added in v0.2.8

type ReleaseFeed struct {
	Categories []int  `json:"categories,omitempty"` // local filter when a feed ignores category queries
	ID         string `json:"id"`
	Name       string `json:"name"`
	Generation string `json:"-"`
	Protocol   string `json:"protocol"`
	Supported  bool   `json:"supported"`
	Reason     string `json:"reason,omitempty"`
}

ReleaseFeed describes an indexer's recent-release discovery capability.

type ReleaseHealthChecker

type ReleaseHealthChecker interface {
	HealthChecker
	CheckRelease(ctx context.Context, releaseID string, nzb []byte) (healthy bool, err error)
}

ReleaseHealthChecker is a HealthChecker that is also told which release the NZB belongs to, so what the check reads (the PAR2 index, archive headers) lands in the segment cache under the ID a following Open uses. Optional: callers fall back to Check when it is not implemented (additive).

type ReleaseInfo

type ReleaseInfo struct {
	Title       string   `json:"title"`
	Edition     string   `json:"edition,omitempty"`
	Year        int      `json:"year,omitempty"`
	Seasons     []int    `json:"seasons,omitempty"`
	Episodes    []int    `json:"episodes,omitempty"`
	FullSeason  bool     `json:"fullSeason,omitempty"`
	Height      int      `json:"height,omitempty"` // 480, 720, 1080, 2160
	Source      Source   `json:"source,omitempty"`
	VideoCodec  string   `json:"videoCodec,omitempty"` // h264, hevc, av1
	HDR10       bool     `json:"hdr10,omitempty"`
	DolbyVision bool     `json:"dolbyVision,omitempty"`
	AudioCodec  string   `json:"audioCodec,omitempty"` // aac, ac3, eac3, truehd, dts, dtshd
	Atmos       bool     `json:"atmos,omitempty"`
	Channels    string   `json:"channels,omitempty"` // "5.1"
	Group       string   `json:"group,omitempty"`
	Languages   []string `json:"languages,omitempty"`
	// SubtitleLanguages are subtitle languages a release declares without
	// claiming the audio (VOSTFR, VOSE, SUBFRENCH) (additive).
	SubtitleLanguages []string `json:"subtitleLanguages,omitempty"`
	// AbsoluteEpisodes are episode numbers counted from the series start,
	// as anime releases number them ("Show - 1071") (additive).
	AbsoluteEpisodes []int  `json:"absoluteEpisodes,omitempty"`
	Proper           bool   `json:"proper,omitempty"`
	Repack           bool   `json:"repack,omitempty"`
	Container        string `json:"container,omitempty"`
}

ReleaseInfo is what can be parsed from a release name.

type ReleaseRule added in v0.2.49

type ReleaseRule struct {
	ID         string             `json:"id"`
	Name       string             `json:"name"`
	Action     string             `json:"action"`          // require, prefer, avoid (exclude)
	Match      string             `json:"match,omitempty"` // all (default) or any
	Conditions []ReleaseCondition `json:"conditions"`
	Unless     []ReleaseCondition `json:"unless,omitempty"`   // any matching exception skips the rule
	Fallback   []ReleaseCondition `json:"fallback,omitempty"` // require only, any matching condition
	Disabled   bool               `json:"disabled,omitempty"`
}

ReleaseRule is a protocol-neutral owner preference. Ordered soft preferences break ties lexicographically before ordinary quality scores. All hard rules must pass. Empty rules preserve existing profile behavior.

type ReleaseSizeSource added in v0.2.7

type ReleaseSizeSource interface{ ReleaseSizes() map[string]int64 }

ReleaseSizeSource exposes the optional ReleaseSizes capability.

type ReleaseTarget

type ReleaseTarget struct {
	Season       int    `json:"season,omitempty"`
	Episode      int    `json:"episode,omitempty"`
	ShowYear     int    `json:"showYear,omitempty"`
	EpisodeTitle string `json:"episodeTitle,omitempty"`
	// ExpectedIDs identify the requested series; ReportedIDs are the
	// release's indexer-reported series IDs, retained for file validation.
	ExpectedIDs IDs `json:"expectedIds,omitempty"`
	ReportedIDs IDs `json:"reportedIds,omitempty"`
}

ReleaseTarget is the episode wanted from a multi-file release such as a season pack.

type ReleaseVariant

type ReleaseVariant struct {
	// ID is stable for the title and release; front ends encode it (or its
	// position) in Media/Part ids.
	ID string `json:"id"`
	// Label is the viewer-facing name, e.g. "4K Dolby Vision Remux" or
	// "1080p WEB-DL (High Compatibility)".
	Label string `json:"label"`
	// Edition is the cut, e.g. "Director's Cut", "Extended", "IMAX"; empty
	// for the theatrical/unspecified cut.
	Edition string `json:"edition,omitempty"`
	// Auto marks the resolver's own pick for this client ("Best for Device").
	Auto    bool    `json:"auto,omitempty"`
	Release Release `json:"release"`
	// Info is the expected media info, estimated from the release name until
	// the release has been probed.
	Info MediaInfo `json:"info"`
}

ReleaseVariant is one playable version of a title, offered in the Plex "Play Version" picker: a distinct candidate release, by quality tier or edition (cut).

type RemuxRequirer

type RemuxRequirer interface {
	RequiresRemux(sessionID string) bool
}

RemuxRequirer is implemented by playback managers that can report whether a session must be (re)started as a zero-encode remux (HLS/DASH) rather than direct play — set when a mid-stream failover of a non-spliceable direct-play session found no exact-size replacement release, so the client needs fresh container headers at the mapped timestamp.

type RepairEvent added in v0.2.8

type RepairEvent struct {
	Key            int
	Attempt        uint64 // distinguishes concurrent readers repairing one article
	Offset         int64
	Approximate    bool
	Phase          string // missing, planning, repairing, repaired, failed, obsolete
	Estimate       time.Duration
	RemainingBytes int64
}

RepairEvent reports verified damage and bounded recovery work. Offset is in the selected media file, never an NNTP article or archive-volume coordinate. Estimate is conservative, not a guarantee; zero means not yet known.

type RepairReporter added in v0.2.8

type RepairReporter interface {
	SetRepairHandler(func(RepairEvent) RepairWindow)
}

type RepairWindow added in v0.2.8

type RepairWindow struct {
	Deadline       time.Time
	Stop           bool
	ReadAheadBytes int64
}

RepairWindow is playback's current useful repair lifetime. A zero deadline leaves the source's ordinary bound in force. Stop cancels obsolete work.

type RescueIndexer added in v0.2.7

type RescueIndexer interface {
	SearchRescue(context.Context, SearchRequest) ([]Release, error)
}

RescueIndexer exposes the optional SearchRescue capability.

type Resolution

type Resolution struct {
	Title      RatingKey `json:"title"`
	Release    Release   `json:"release"`
	NZB        []byte    `json:"-"`
	Alternates []Release `json:"alternates"` // next candidates, best first
	ResolvedAt time.Time `json:"resolvedAt"`
}

Resolution is the resolver's answer for a title and client: the winning release, already health-checked, plus fallbacks for mid-stream failover.

type ResolveRequest

type ResolveRequest struct {
	Title  *Title
	Client ClientCaps
	User   *User
	// Exclude releases already tried (failover).
	Exclude []string
	// Prefer is a release ID to try first (the viewer picked a version in
	// Plex's "Play Version" picker). It is health-checked like any other
	// candidate; when it is unavailable, normal ranking applies (additive).
	Prefer string
}

type Resolver

type Resolver interface {
	// Resolve finds a healthy release. Returns ErrNotAvailable when nothing
	// acceptable exists.
	Resolve(ctx context.Context, req ResolveRequest) (*Resolution, error)
	// MarkBad records a release as broken so nobody gets it again.
	MarkBad(ctx context.Context, releaseID string, reason string) error
	// Available reports a cached availability answer without searching:
	// (known, available).
	Available(ctx context.Context, key RatingKey) (known bool, available bool)
}

type RowDescriptor

type RowDescriptor struct {
	HubID       string `json:"hubId"`
	Title       string `json:"title"`
	Kind        string `json:"kind"`
	Description string `json:"description"`
	// Sections lists the pages this row can appear on ("home", "movies",
	// "shows"); empty means every page.
	Sections []string `json:"sections,omitempty"`
	// Group is the designer's category: "picks", "explore", "shows",
	// "household", "collections" or "plex".
	Group string `json:"group,omitempty"`
}

RowDescriptor describes a kind of row the owner can place in the Home layout.

type RowInsight

type RowInsight struct {
	HubID         string  `json:"hubId"`
	Title         string  `json:"title"`
	Impressions7d int     `json:"impressions7d"`
	Plays7d       int     `json:"plays7d"`
	PlayRate      float64 `json:"playRate"`
}

RowInsight is the 7-day engagement of one row kind.

type RowPolicy

type RowPolicy struct {
	Row            string      `json:"row"`
	Status         string      `json:"status"`
	Slot           int         `json:"slot,omitempty"`
	MinSlot        int         `json:"minSlot,omitempty"`
	MaxSlot        int         `json:"maxSlot,omitempty"`
	Priority       float64     `json:"priority,omitempty"`
	MaxPerPage     int         `json:"maxPerPage,omitempty"`
	MaxDaysPerWeek int         `json:"maxDaysPerWeek,omitempty"`
	When           *LayoutWhen `json:"when,omitempty"`
}

type SearchRequest

type SearchRequest struct {
	Type          MediaType // movie or episode
	IDs           IDs       // movie ids, or the show's ids for episodes
	Title         string
	OriginalTitle string // optional original-language or canonical title (additive)
	Year          int
	Season        int
	Episode       int
	EpisodeTitle  string // optional title used to distinguish same-numbered episodes
	// AbsoluteEpisode is the episode's number counted from the series
	// start (0 = unknown), and Anime marks a series whose releases are
	// commonly numbered that way (additive).
	AbsoluteEpisode int
	Anime           bool
}

type SectionCandidateSource added in v0.2.7

type SectionCandidateSource interface {
	SectionCandidatesFor(context.Context, *User, MediaType, HubContext, int) ([]Hub, error)
}

SectionCandidateSource exposes the optional SectionCandidatesFor capability.

type SegmentCache

type SegmentCache interface {
	Get(releaseID string, segment int) ([]byte, bool)
	Put(releaseID string, segment int, data []byte) error
	// Pin protects every segment of a release from eviction.
	Pin(releaseID string, reason string) error
	Unpin(releaseID string, reason string) error
	Usage() (used, budget int64)
}

SegmentCache stores decoded segments on disk under an LRU budget.

type SegmentPresence added in v0.2.7

type SegmentPresence interface{ Has(string, int) bool }

SegmentPresence exposes the optional Has capability.

type SessionActivity added in v0.2.9

type SessionActivity interface{ PlaybackState(state string) }

SessionActivity is an optional output-session extension. Only authenticated, ownership-checked player timelines may renew it; playlist polls may not.

type ShowTrajectory added in v0.2.7

type ShowTrajectory struct {
	ShowKey       RatingKey `json:"showKey"`
	EarlyRating   float64   `json:"earlyRating,omitempty"`
	FinaleRating  float64   `json:"finaleRating,omitempty"`
	PeakRating    float64   `json:"peakRating,omitempty"`
	RatedSeasons  int       `json:"ratedSeasons,omitempty"`
	SticksLanding bool      `json:"sticksLanding,omitempty"`
}

ShowTrajectory summarizes a series' season/episode rating arc (additive).

type SortOrder

type SortOrder string
const (
	SortPopularity SortOrder = "popularity"
	SortTitle      SortOrder = "title"
	SortReleased   SortOrder = "released"
	SortRating     SortOrder = "rating"
	SortAdded      SortOrder = "added" // when it joined the browsable set
)

type Source

type Source string

Source is the release source parsed from a release name.

const (
	SourceUnknown  Source = ""
	SourceCam      Source = "cam" // CAM, TS, TC, HDCAM, telesync
	SourceScreener Source = "screener"
	SourceDVD      Source = "dvd"
	SourceHDTV     Source = "hdtv"
	SourceWEBRip   Source = "webrip"
	SourceWEBDL    Source = "webdl"
	SourceBluRay   Source = "bluray"
	SourceRemux    Source = "remux"
)

type SourceDeliveryObserver added in v0.2.9

type SourceDeliveryObserver interface {
	ObserveSourceDelivery(Release, float64)
}

SourceDeliveryObserver receives uncached source throughput evidence. It is neither client bandwidth nor a claim about an entire provider network.

type State

type State interface {
	Get(ctx context.Context, userID int64, key RatingKey) (WatchStatus, error)
	GetMany(ctx context.Context, userID int64, keys []RatingKey) (map[RatingKey]WatchStatus, error)
	// Progress records a timeline update. Crossing 90% marks it watched.
	Progress(ctx context.Context, userID int64, key RatingKey, offset, duration time.Duration, state string) error
	Scrobble(ctx context.Context, userID int64, key RatingKey) error   // mark watched
	Unscrobble(ctx context.Context, userID int64, key RatingKey) error // mark unwatched
	Rate(ctx context.Context, userID int64, key RatingKey, rating float64) error
	// OnDeck: in-progress items and next episodes, most recent first.
	InProgress(ctx context.Context, userID int64, limit int) ([]WatchStatus, error)
	History(ctx context.Context, userID int64, since time.Time, limit int) ([]HistoryEntry, error)
	// InProgressAny lists keys in progress for any user (cache protection, prefetch).
	InProgressAny(ctx context.Context) ([]RatingKey, error)
}

type StoredTitleLookup added in v0.2.37

type StoredTitleLookup interface {
	StoredTitle(context.Context, RatingKey) (*Title, error)
}

StoredTitleLookup reads local metadata without remote discovery. Callers must apply their own authorization before exposing the returned title.

type StreamInfo

type StreamInfo struct {
	// Probe hints for enhancement backends that require progressive, steady-rate input.
	Interlaced        bool `json:"interlaced,omitempty"`
	VariableFrameRate bool `json:"variableFrameRate,omitempty"`

	Index         int     `json:"index"`
	Type          string  `json:"type"` // video, audio, subtitle
	Codec         string  `json:"codec"`
	Profile       string  `json:"profile,omitempty"`
	Level         int     `json:"level,omitempty"`
	Language      string  `json:"language,omitempty"`
	Title         string  `json:"title,omitempty"`
	Default       bool    `json:"default,omitempty"`
	Forced        bool    `json:"forced,omitempty"`
	Width         int     `json:"width,omitempty"`
	Height        int     `json:"height,omitempty"`
	FrameRate     float64 `json:"frameRate,omitempty"`
	BitDepth      int     `json:"bitDepth,omitempty"`
	ColorTransfer string  `json:"colorTransfer,omitempty"` // smpte2084 = HDR10, arib-std-b67 = HLG
	// Signalled picture format, in ffprobe's names; empty when the stream
	// and container leave it unspecified (decoders then assume square
	// pixels and limited range).
	SampleAspectRatio string `json:"sampleAspectRatio,omitempty"` // "N:D"
	ColorRange        string `json:"colorRange,omitempty"`        // tv, pc
	ColorPrimaries    string `json:"colorPrimaries,omitempty"`    // bt709, bt2020, smpte170m...
	ColorSpace        string `json:"colorSpace,omitempty"`        // matrix: bt709, bt2020nc, smpte170m...
	DolbyVision       bool   `json:"dolbyVision,omitempty"`
	Channels          int    `json:"channels,omitempty"`
	ChannelLayout     string `json:"channelLayout,omitempty"`
	SampleRate        int    `json:"sampleRate,omitempty"`
	BitrateKbps       int    `json:"bitrateKbps,omitempty"`
	External          bool   `json:"external,omitempty"` // sidecar subtitle (OpenSubtitles)
	ExternalURL       string `json:"externalUrl,omitempty"`

	// DolbyVisionProfile is the DV profile (5, 7, 8...) from the DOVI
	// configuration record; 0 when unknown or not Dolby Vision.
	DolbyVisionProfile         int    `json:"dolbyVisionProfile,omitempty"`
	DolbyVisionLevel           int    `json:"dolbyVisionLevel,omitempty"`
	DolbyVisionCompatibilityID int    `json:"dolbyVisionCompatibilityId,omitempty"`
	CodecString                string `json:"codecString,omitempty"` // RFC 6381, derived from the codec configuration

	// Configuration-record facts are optional: codec tags alone do not
	// establish whether the enhancement/base layers or RPU are present.
	DolbyVisionVersion    string `json:"dolbyVisionVersion,omitempty"`
	DolbyVisionBLPresent  *bool  `json:"dolbyVisionBLPresent,omitempty"`
	DolbyVisionELPresent  *bool  `json:"dolbyVisionELPresent,omitempty"`
	DolbyVisionRPUPresent *bool  `json:"dolbyVisionRPUPresent,omitempty"`
}

type StreamingIndexer added in v0.2.7

type StreamingIndexer interface {
	SearchWith(context.Context, SearchRequest, func([]Release)) ([]Release, error)
}

StreamingIndexer exposes the optional SearchWith capability.

type StreamingRescueIndexer added in v0.2.7

type StreamingRescueIndexer interface {
	SearchRescueWith(context.Context, SearchRequest, func([]Release)) ([]Release, error)
}

StreamingRescueIndexer exposes the optional SearchRescueWith capability.

type SubtitleExtractor added in v0.2.49

type SubtitleExtractor interface {
	ExtractSubtitle(ctx context.Context, inputURL string, streamIndex int, codec string, w io.Writer) error
}

SubtitleExtractor is optionally implemented by transcoders that can write an embedded text subtitle stream as WebVTT, so a track picked mid-playback can be served from the transcode session without a restart. Image subtitles (PGS/VobSub) return an error.

type TasteDecade

type TasteDecade struct {
	Decade int     `json:"decade"`
	Weight float64 `json:"weight"`
}

TasteDecade is an affinity for a release decade (1990 = the 90s).

type TasteHabits

type TasteHabits struct {
	WeeknightMinutes float64 `json:"weeknightMinutes"` // typical minutes watched on an active weeknight
	WeekendMinutes   float64 `json:"weekendMinutes"`   // typical minutes watched on an active weekend day
	PreferredDevice  string  `json:"preferredDevice"`  // DeviceTV, DeviceMobile, ... or ""
	BingeScore       float64 `json:"bingeScore"`       // 0..1: share of episodes watched in same-day runs of 3+
	RewatchScore     float64 `json:"rewatchScore"`     // 0..1: share of finished titles watched again
	PeakHours        []int   `json:"peakHours"`        // most common start hours (local), busiest first
}

TasteHabits are a viewer's learned viewing habits.

type TasteLanguage

type TasteLanguage struct {
	Code   string  `json:"code"`
	Weight float64 `json:"weight"`
}

TasteLanguage is an affinity for an original language (ISO 639-1).

type TasteOverride

type TasteOverride struct {
	Kind   string  `json:"kind"`
	Value  string  `json:"value"`
	Weight float64 `json:"weight"`
}

TasteOverride is an owner correction applied on top of the learned model: Kind "genre" (Value = genre name), "person" (Value = TMDB person id) or "keyword" (Value = keyword); Weight -1..1 replaces the learned weight.

type TastePerson

type TastePerson struct {
	ID     int64   `json:"id"`
	Name   string  `json:"name"`
	Role   string  `json:"role"` // "director", "writer", "actor"
	Weight float64 `json:"weight"`
	Image  string  `json:"image,omitempty"` // browser-loadable URL
}

TastePerson is an affinity for a cast or crew member.

type TasteProfile

type TasteProfile struct {
	UserID     int64           `json:"userId"`
	UpdatedAt  time.Time       `json:"updatedAt"`
	Confidence float64         `json:"confidence"` // 0..1, grows with history
	Genres     []TasteWeight   `json:"genres"`
	People     []TastePerson   `json:"people"`
	Keywords   []TasteWeight   `json:"keywords"`
	Decades    []TasteDecade   `json:"decades"`
	Languages  []TasteLanguage `json:"languages"`
	Habits     TasteHabits     `json:"habits"`
	Overrides  []TasteOverride `json:"overrides"`
}

TasteProfile is the explainable view of one viewer's taste model.

type TasteWeight

type TasteWeight struct {
	Name   string  `json:"name"`
	Weight float64 `json:"weight"`
}

TasteWeight is one named affinity, weight in -1..1.

type TimelineAligner

type TimelineAligner interface {
	AlignTime(ctx context.Context, req AlignRequest, at time.Duration) (mapped time.Duration, ok bool)
}

TimelineAligner maps a moment in one release to the same moment in another release of the title using dialogue (subtitle) timing rather than video, so nothing has to be downloaded. The mapping may be piecewise (different cuts or intros shift later timestamps). ok is false when there is no confident alignment; callers then fall back to duration-based alignment.

type Title

type Title struct {
	Key  RatingKey `json:"key"`
	Type MediaType `json:"type"`
	IDs  IDs       `json:"ids"`

	// Seasons and episodes.
	ShowKey           RatingKey `json:"showKey,omitempty"`
	ShowIDs           IDs       `json:"showIds,omitempty"`
	ShowTitle         string    `json:"showTitle,omitempty"`
	ShowOriginalTitle string    `json:"showOriginalTitle,omitempty"` // canonical original series name, distinct from episode title
	ShowYear          int       `json:"showYear,omitempty"`          // series premiere year, distinct from episode air year
	SeasonTitle       string    `json:"seasonTitle,omitempty"`
	Season            int       `json:"season,omitempty"`
	Episode           int       `json:"episode,omitempty"`
	// AbsoluteEpisode counts an episode from the series start across its
	// regular seasons; 0 when an earlier season's size is unknown (additive).
	AbsoluteEpisode int `json:"absoluteEpisode,omitempty"`

	Title                       string        `json:"title"`
	OriginalTitle               string        `json:"originalTitle,omitempty"`
	SortTitle                   string        `json:"sortTitle,omitempty"`
	Tagline                     string        `json:"tagline,omitempty"`
	Summary                     string        `json:"summary,omitempty"`
	Year                        int           `json:"year,omitempty"`
	Released                    time.Time     `json:"released,omitempty"` // first air / release date
	HomeReleaseChecked          bool          `json:"homeReleaseChecked,omitempty"`
	HomeReleased                time.Time     `json:"homeReleased,omitempty"`
	HomeReleaseFallbackAdmitted bool          `json:"homeReleaseFallbackAdmitted,omitempty"`
	Runtime                     time.Duration `json:"runtime,omitempty"`
	Status                      string        `json:"status,omitempty"` // TMDB status: Released, In Production, Ended...

	Genres        []string `json:"genres,omitempty"`
	Keywords      []string `json:"keywords,omitempty"` // TMDB keywords (lower-case), for content-based recommendations
	Studios       []string `json:"studios,omitempty"`
	Countries     []string `json:"countries,omitempty"`
	Language      string   `json:"language,omitempty"` // ISO 639-1 original language
	ContentRating string   `json:"contentRating,omitempty"`
	Adult         bool     `json:"adult,omitempty"`

	Rating     float64 `json:"rating,omitempty"` // TMDB vote average 0-10
	VoteCount  int     `json:"voteCount,omitempty"`
	Popularity float64 `json:"popularity,omitempty"`

	PosterPath   string `json:"posterPath,omitempty"`
	BackdropPath string `json:"backdropPath,omitempty"`
	StillPath    string `json:"stillPath,omitempty"` // episodes
	LogoPath     string `json:"logoPath,omitempty"`

	Cast      []Person `json:"cast,omitempty"`
	Directors []Person `json:"directors,omitempty"`
	Writers   []Person `json:"writers,omitempty"`
	Crew      []Person `json:"crew,omitempty"` // creators, cinematographers, composers (additive)

	CollectionID   int64  `json:"collectionId,omitempty"` // TMDB franchise
	CollectionName string `json:"collectionName,omitempty"`

	ChildCount int `json:"childCount,omitempty"` // seasons of a show, episodes of a season
	LeafCount  int `json:"leafCount,omitempty"`  // episodes of a show

	UpdatedAt time.Time `json:"updatedAt,omitempty"`
}

Title is a catalogue item: movie, show, season or episode. Image paths are TMDB paths ("/abc.jpg"); the front end turns them into URLs.

func (*Title) IsReleased

func (t *Title) IsReleased(now time.Time) bool

Released reports whether the title has a release/air date in the past.

type Transcoder

type Transcoder interface {
	Probe(ctx context.Context, inputURL string) (*MediaInfo, error)
	Decide(info MediaInfo, caps ClientCaps, opts DecideOptions) Decision
	// StartHLS starts an HLS session, or a DASH session (a DASHSession)
	// when req.Protocol is ProtocolDASH.
	StartHLS(ctx context.Context, req HLSRequest) (HLSSession, error)
	Session(id string) (HLSSession, bool)
	Stop(id string)
	HWAccel() HWAccel
}

type TrickplayCanceler added in v0.2.7

type TrickplayCanceler interface{ CancelTrickplay(string) }

TrickplayCanceler exposes the optional CancelTrickplay capability.

type TrickplayProvider

type TrickplayProvider interface {
	// EnsureTrickplay starts (or continues) progressive thumbnail
	// generation for the source in the background. It returns
	// immediately; duplicate calls for a release already generating are
	// no-ops.
	EnsureTrickplay(ctx context.Context, src TrickplaySource)
	// TrickplayAvailable reports whether thumbnails for the release
	// exist or are being generated, so a front end can advertise them
	// (Plex's indexes="sd").
	TrickplayAvailable(releaseID string) bool
	// TrickplayBIF returns a snapshot of the release's sparse BIF index
	// built from the frames generated so far.
	TrickplayBIF(releaseID string) ([]byte, bool)
	// TrickplayFrame returns the JPEG of the latest generated frame at
	// or before at (a fallback still when no real frame precedes it).
	TrickplayFrame(releaseID string, at time.Duration) ([]byte, bool)
	// SetTrickplayFallback records a low-fidelity still (TMDB episode
	// still or backdrop, already JPEG at roughly BIF width) used to fill
	// not-yet-generated intervals. Real frames replace it as they arrive.
	SetTrickplayFallback(releaseID string, jpeg []byte)
}

TrickplayProvider supplies on-the-fly preview thumbnails (Plex "BIF" indexes) for releases as they stream. It is an optional capability: callers reach it through a type assertion or an optional dependency and must tolerate its absence. Implementations only extract from sources handed to EnsureTrickplay (an active playback or warmup session); they never resolve or download a title just for thumbnails.

type TrickplaySource

type TrickplaySource struct {
	ReleaseID string
	InputURL  string
	Info      MediaInfo
	// AvailableUntil is the playback position already read by the client.
	// Generators must only fill intervals at or before this point and may
	// advance it on later calls as playback progresses.
	AvailableUntil time.Duration
}

TrickplaySource identifies one playable source thumbnails can be extracted from: the release being streamed and the loopback URL its bytes are readable through (Playback.InputURL). Info carries the duration and, when known, the container keyframe index used to align extractions to keyframes.

type User

type User struct {
	// Delegated marks short-lived server credentials; these never grant owner access.
	Delegated bool   `json:"-"`
	ID        int64  `json:"id"` // local id
	PlexID    int64  `json:"plexId"`
	UUID      string `json:"uuid,omitempty"`
	Username  string `json:"username"`
	Title     string `json:"title"` // display name
	Email     string `json:"email,omitempty"`
	Thumb     string `json:"thumb,omitempty"`
	IsOwner   bool   `json:"isOwner"`

	StreamLimit       int      `json:"streamLimit"`       // 0 = server default
	MaxContentRating  string   `json:"maxContentRating"`  // "" = unrestricted, e.g. "PG-13", "TV-14"
	SubtitleLanguages []string `json:"subtitleLanguages"` // ISO 639-1, preference order
	AudioLanguages    []string `json:"audioLanguages"`
	AllowedSections   []int    `json:"allowedSections"` // empty = all
}

User is a Plex account with access to this server: the owner or a friend the owner shared with on plex.tv.

type UserPolicySource added in v0.2.7

type UserPolicySource interface{ UserPolicy(*User) *User }

UserPolicySource exposes the optional UserPolicy capability.

type UserTargets

type UserTargets struct {
	All bool
	IDs []int64
}

UserTargets is either every user ("all" in JSON) or a list of local user ids.

func AllUsers

func AllUsers() UserTargets

AllUsers targets everyone.

func (UserTargets) Includes

func (u UserTargets) Includes(user *User) bool

Includes reports whether a user (nil = anonymous) is targeted.

func (UserTargets) MarshalJSON

func (u UserTargets) MarshalJSON() ([]byte, error)

func (*UserTargets) UnmarshalJSON

func (u *UserTargets) UnmarshalJSON(b []byte) error

type VariantPlayback

type VariantPlayback interface {
	PrepareVariant(ctx context.Context, user *User, key RatingKey, caps ClientCaps, releaseID string) (*PlaySession, error)
}

VariantPlayback is implemented by playback managers that can prepare a session on a specific release (the viewer picked a version). The release is health-checked like any other; when it is unavailable the manager falls back to the normal choice for the client.

type VariantResolver

type VariantResolver interface {
	Variants(ctx context.Context, req ResolveRequest, max int) ([]ReleaseVariant, error)
}

VariantResolver lists distinct candidate releases of a title for a client, best first, without health-checking every one of them. Implemented by internal/resolver.

type VibeQuery

type VibeQuery struct {
	// Query is the viewer's original text.
	Query string `json:"query,omitempty"`
	// Title is the human collection title, e.g.
	// "90s Thrillers Under 100 Minutes".
	Title string `json:"title,omitempty"`
	// Filter is the catalogue filter the members are browsed with.
	Filter Filter    `json:"filter"`
	Sort   SortOrder `json:"sort,omitempty"`
	Desc   bool      `json:"desc,omitempty"`
	// SimilarSeed is the unresolved seed title text of a
	// "shows like the office" query; SimilarKey is it resolved.
	SimilarSeed string    `json:"similarSeed,omitempty"`
	SimilarKey  RatingKey `json:"similarKey,omitempty"`
	// PersonName is an unresolved person name ("christopher nolan
	// films"); once resolved it lands in Filter.Person.
	PersonName string `json:"personName,omitempty"`
}

VibeQuery is a natural-language ("vibe") search turned into a structured catalogue filter, e.g. "90s thrillers under 100 mins". It backs the ephemeral collections synthesized in search results (additive).

type VibeRefiner

type VibeRefiner interface {
	RefineVibe(ctx context.Context, user *User, query string) (VibeQuery, bool, error)
}

VibeRefiner maps an otherwise unparseable vibe query to a VibeQuery (implemented by the optional LLM service). It must be quick, respect ctx, and report ok=false when it cannot help.

type VibeSearcher

type VibeSearcher interface {
	VibeCollection(ctx context.Context, user *User, query string) (c *Collection, items []Title, ok bool, err error)
	// PromoteEphemeral saves an ephemeral collection as a persisted
	// personal smart collection for its owner. ok is false when key is
	// not a live ephemeral collection owned by user.
	PromoteEphemeral(ctx context.Context, user *User, key RatingKey) (c *Collection, ok bool, err error)
}

VibeSearcher is optionally implemented by the collections engine: it turns a vibe query into a browseable ephemeral collection for the viewer, alongside the collection's top items. ok is false when the query is not a vibe query (plain title searches stay untouched).

type VideoEnhancement added in v0.2.4

type VideoEnhancement struct {
	Backend string `json:"backend,omitempty"`
	State   string `json:"state,omitempty"` // off, unavailable, ineligible, selected, starting, running, fallback, stopped
	Reason  string `json:"reason,omitempty"`
	Width   int    `json:"width,omitempty"`
	Height  int    `json:"height,omitempty"`
	Quality int    `json:"quality,omitempty"`
}

VideoEnhancement is an observable plan/run status, never a promise based solely on installed hardware. Only running means enhanced segments have actually been produced in the current run. Fallback always carries a reason.

type VideoEnhancementSupport added in v0.2.4

type VideoEnhancementSupport struct {
	// SetupState is off, checking, installing, ready, or unavailable.
	SetupState string   `json:"setupState,omitempty"`
	Backend    string   `json:"backend"`
	Available  bool     `json:"available"`
	Reason     string   `json:"reason,omitempty"`
	Version    string   `json:"version,omitempty"`
	Protocols  []string `json:"protocols,omitempty"`
	Active     int      `json:"active"`
}

VideoEnhancementSupport is detection, not successful execution. Its protocols are the transports verified for this backend version.

type ViewerActivity

type ViewerActivity struct {
	Title     Title     `json:"title"`
	At        time.Time `json:"at"`
	Completed bool      `json:"completed"`
	Progress  float64   `json:"progress"` // 0..1
}

ViewerActivity is one recent viewing.

type ViewerInvalidator added in v0.2.7

type ViewerInvalidator interface{ Invalidate(userID int64) }

ViewerInvalidator exposes the optional Invalidate capability.

type ViewerLayout

type ViewerLayout struct {
	UserID int64        `json:"userId"`
	Pages  []LayoutPage `json:"pages"`
}

type ViewerStats

type ViewerStats struct {
	UserID          int64     `json:"userId"`
	LastActive      time.Time `json:"lastActive,omitempty"`
	Plays30d        int       `json:"plays30d"`
	HoursWatched30d float64   `json:"hoursWatched30d"`
}

ViewerStats is a viewer's recent activity summary.

type WarmProbeWaiter added in v0.2.52

type WarmProbeWaiter interface {
	WaitWarmProbe(ctx context.Context, user *User, caps ClientCaps, key RatingKey, budget time.Duration) (MediaInfo, bool)
}

WarmProbeWaiter is optionally implemented by playback managers that can wait up to a bounded budget for an already-scheduled warm probe (PreResolve) to finish, so the first detail-page open can render the file's real audio and subtitle streams instead of an estimate. It never cancels the background warm-up when the budget expires.

type WarmProber added in v0.2.49

type WarmProber interface {
	WarmProbeInfo(user *User, caps ClientCaps, key RatingKey) (MediaInfo, bool)
}

WarmProber is optionally implemented by playback managers that complete a speculative probe (Prewarm/PreResolve) before Play. WarmProbeInfo returns the probed media info a warm-up already produced for this user/client/key, so metadata and play-queue renders can list the file's real audio and subtitle tracks instead of the single invented audio track of the estimate. ok=false when no warm probe has completed (or it has expired). It never starts a probe or blocks on one in flight.

It complements ResumedTracks, which only covers a viewer's later opens: this covers the first open, once the detail-page warm-up has landed.

type WatchStatus

type WatchStatus struct {
	Key          RatingKey     `json:"key"`
	ViewOffset   time.Duration `json:"viewOffset"`
	ViewCount    int           `json:"viewCount"`
	LastViewedAt time.Time     `json:"lastViewedAt,omitempty"`
	UserRating   float64       `json:"userRating,omitempty"` // 0-10
	Duration     time.Duration `json:"duration,omitempty"`
	UpdatedAt    time.Time     `json:"updatedAt"`
}

Jump to

Keyboard shortcuts

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