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

core

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: GPL-3.0 Imports: 17 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). The same
	// name is Plex for Mac / Desktop's protocol=http conversion (a chunked
	// Matroska stream on /video/:/transcode/universal/start).
	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
	PrewarmBrowse   = "browse"   // a home/section row is on screen; open the debrid link only
)

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 (
	RankRulePrefix  = "rule:"
	RankLanguage    = "language"
	RankConnection  = "connection"
	RankResolution  = "resolution"
	RankReleaseType = "releaseType"
	RankHDR         = "hdr"
	RankVerified    = "verified"
	RankVideoCodec  = "videoCodec"
	RankAudioFormat = "audioFormat"
	RankSize        = "size"
)

Ranking ladder step keys for QualityProfile.Ranking.

View Source
const EncoderInputScopeHeader = "X-Ether-Encoder-Input"

EncoderInputScopeHeader is reserved for a manager-issued input lease. It is never provider metadata, client authorization, a source lease or a URL token.

View Source
const GrantCredentialPrefix = "eg1_"

GrantCredentialPrefix reserves Ether-issued credentials from external providers.

View Source
const MaxVariants = 7

MaxVariants caps the Play Version picker: Auto, up to three same-quality alternatives (the owner's setting), and up to three tiers and cuts. The resolver and the Plex front end both read it so the two caps move together.

View Source
const PausedSessionRetention = 30 * time.Minute

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

View Source
const ProbeFactsVersion = 2

ProbeFactsVersion is the generation of facts a probe is expected to carry, including H.264/HEVC reference frames and descriptive audio handler titles. Persisted probes from an older generation are cache misses.

View Source
const RowSourcePrecomposed = "precomposed"

RowSourcePrecomposed identifies utility rows already assembled by a host service. Their ordering and items are not generated by the recommendation engine. RowSourcePlex remains supported for existing data and clients.

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")
	// ErrPolicyUnconfirmed means the durable policy store could not be read.
	// Callers may keep an unrestricted cached decision. Restricted viewers fail closed.
	ErrPolicyUnconfirmed = errors.New("policy store unavailable")
)

DefaultRankingSteps is the built-in ranking ladder order after any Prefer rules.

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 or converted into an MP4 the requesting client's download profile plays.

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 ErrEncoderInputPending = errors.New("encoder input producers are pending")
View Source
var ErrEncoderInputUnverified = errors.New("encoder input producer retirement is unverified")
View Source
var ErrEncoderPending = errors.New("encoder retirement is pending")
View Source
var ErrForegroundOnly = errors.New("background check cannot decide this source")

ErrForegroundOnly reports a speculative check that could not decide a source without work only a foreground attempt may do (adding a torrent to a debrid account, say). Like ErrBudgetExhausted it is never evidence of source absence: a foreground caller tries the source itself.

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.

View Source
var ErrSourceChanged = errors.New("playback: source revision changed")

ErrSourceChanged signals an absent or different source revision.

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 hub form factor (DeviceTV, DeviceMobile, DeviceWeb, DeviceDesktop). Playback codecs, HDR and subtitle support are not decided here; those come from the client capability registry.

  • 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 DefaultRanking added in v0.3.0

func DefaultRanking(rules []ReleaseRule) []string

DefaultRanking returns today's default ladder order: each Prefer rule in rules (in slice order), followed by DefaultRankingSteps.

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 IsGrantCredential added in v0.3.0

func IsGrantCredential(s string) bool

IsGrantCredential reports whether a credential uses Ether's reserved prefix.

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 PlexSaferRating added in v0.3.0

func PlexSaferRating(ctx context.Context) bool

PlexSaferRating reports that catalogue queries on ctx should hide titles whose cached Plex rating exceeds the viewer's limit.

func SourceRevision added in v0.3.0

func SourceRevision(s *PlaySession) string

SourceRevision identifies a release/file without exposing its name or upstream identifiers. Local revisions detect filesystem changes; they do not claim content immutability. Unsupported or unknown sources are empty.

func SpendBackground added in v0.2.8

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

func StreamKey added in v0.3.0

func StreamKey(ctx context.Context) any

StreamKey returns the key set by WithStreamKey, or nil.

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 ValidateRanking added in v0.3.0

func ValidateRanking(ranking []string, rules []ReleaseRule) error

ValidateRanking checks an explicit ranking ladder against a profile's rules. An empty ladder is valid and means DefaultRanking(rules).

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

func WithPlexSaferRating added in v0.3.0

func WithPlexSaferRating(ctx context.Context) context.Context

WithPlexSaferRating marks a request so catalogue SQL also applies cached Plex content-rating levels. Other frontends leave the context unset.

func WithReadRetirement added in v0.3.0

func WithReadRetirement(ctx context.Context, tracker *ReadRetirement) context.Context

WithReadRetirement attaches request-scoped retirement tracking.

func WithStreamKey added in v0.3.0

func WithStreamKey(ctx context.Context, key any) context.Context

WithStreamKey tags ctx with the playback stream its reads serve, so the Usenet fetcher can tell a stalled read from ordinary variance between concurrent viewers. key must be comparable (a pointer, typically); nil leaves ctx unchanged.

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 BatchWatchState added in v0.3.0

type BatchWatchState interface {
	ScrobbleMany(context.Context, int64, []RatingKey) error
	UnscrobbleMany(context.Context, int64, []RatingKey) error
}

BatchWatchState updates a collection of watched states atomically.

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
	SecondarySorts []SortSpec
	Offset         int
	Limit          int
	// NoTotal skips counting the filter's matches, which reads every title
	// the filter admits. Page.Total is then offset plus the items returned.
	NoTotal bool
}

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 ClientCapabilityOverride added in v0.3.0

type ClientCapabilityOverride struct {
	ID       string `json:"id,omitempty"`
	Product  string `json:"product,omitempty"`
	Platform string `json:"platform,omitempty"`
	Device   string `json:"device,omitempty"`
	// Version is the minimum client version (dotted numbers) this override requires.
	Version             string   `json:"version,omitempty"`
	VideoCodecs         []string `json:"videoCodecs,omitempty"`
	AudioCodecs         []string `json:"audioCodecs,omitempty"`
	Containers          []string `json:"containers,omitempty"`
	SubtitleCodecs      []string `json:"subtitleCodecs,omitempty"`
	DolbyVisionProfiles []int    `json:"dolbyVisionProfiles,omitempty"`
	MaxHeight           int      `json:"maxHeight,omitempty"`
	MaxBitrateKbps      int      `json:"maxBitrateKbps,omitempty"`
	HDR10               *bool    `json:"hdr10,omitempty"`
	DolbyVision         *bool    `json:"dolbyVision,omitempty"`
	Notes               string   `json:"notes,omitempty"`
}

ClientCapabilityOverride is an owner edit of one registry client. Nil slices and zero numbers leave the registry value in place. A client that declares the same fact at runtime still wins.

type ClientCapabilityProfiles added in v0.3.0

type ClientCapabilityProfiles struct {
	DirectPlay                                                                []ClientDirectPlayProfile
	Transcoding                                                               []ClientTranscodeProfile
	Subtitles                                                                 []ClientSubtitleProfile
	Containers                                                                []ClientContainerProfile
	Codecs                                                                    []ClientCodecProfile
	MaxStaticBitrate, MusicStreamingTranscodingBitrate, MaxStaticMusicBitrate int
}

ClientCapabilityProfiles are declared together by a single client. Nil profile collections mean omitted; an explicit empty collection remains empty. A nil pointer on ClientCaps preserves legacy protocol behavior.

type ClientCaps

type ClientCaps struct {
	Profiles        *ClientCapabilityProfiles `json:"profiles,omitempty"`
	Protocol        Protocol                  `json:"protocol,omitempty"`
	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...
	ExplicitMaxHeight    bool                `json:"explicitMaxHeight,omitempty"` // true when MaxHeight came from a request header/query limitation rather than a product default
	HDR10                bool                `json:"hdr10"`
	ExplicitNoHDR        bool                `json:"explicitNoHdr,omitempty"` // true when HDR was explicitly denied by profile-extra limitations
	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
	DefaultRemoteBitrate bool                `json:"defaultRemoteBitrate,omitempty"` // true when MaxBitrateKbps came from the server's default remote cap rather than a client limitation
	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 ClientCodecProfile added in v0.3.0

type ClientCodecProfile struct {
	MediaType                         string
	Codecs, Containers, SubContainers []string
	Conditions, ApplyConditions       []ClientProfileCondition
}

type ClientContainerProfile added in v0.3.0

type ClientContainerProfile struct {
	MediaType                 string
	Containers, SubContainers []string
	Conditions                []ClientProfileCondition
}

type ClientDirectPlayProfile added in v0.3.0

type ClientDirectPlayProfile struct {
	MediaType   string   `json:"mediaType"`
	Containers  []string `json:"containers"`
	VideoCodecs []string `json:"videoCodecs"`
	AudioCodecs []string `json:"audioCodecs"`
}

ClientDirectPlayProfile keeps container and codec alternatives paired. Empty alternatives represent an unconstrained field, not another profile.

type ClientProfileCondition added in v0.3.0

type ClientProfileCondition struct {
	Operator string `json:"operator"`
	Property string `json:"property"`
	Value    string `json:"value"`
	Required bool   `json:"required"`
}

ClientProfileCondition preserves a client-declared condition for the broker. Required conditions unknown to an evaluator must not be silently accepted.

type ClientSubtitleProfile added in v0.3.0

type ClientSubtitleProfile struct{ Format, Method, DIDLMode, Language, Container string }

type ClientTranscodeProfile added in v0.3.0

type ClientTranscodeProfile struct {
	MediaType, Delivery, Context                                           string
	Containers, VideoCodecs, AudioCodecs                                   []string
	EstimateContentLength, MPEGTSM2TS, CopyTimestamps, SubtitlesInManifest bool
	SeekInfo, MaxAudioChannels                                             string
	MinSegments, SegmentLength                                             int
	BreakOnNonKeyFrames, AudioVBR                                          *bool
	Conditions                                                             []ClientProfileCondition
}

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 CreditFilter added in v0.3.0

type CreditFilter struct {
	People  []int64 `json:"people,omitempty"`
	Kind    string  `json:"kind,omitempty"`
	Exclude bool    `json:"exclude,omitempty"`
}

CreditFilter narrows by credited person TMDB IDs and optional credit kind ("cast", "director", "writer", "crew", or "" for any).

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 DateFilter added in v0.3.0

type DateFilter struct {
	Op      string `json:"op,omitempty"`
	Unix    int64  `json:"unix,omitempty"`
	SpanDay bool   `json:"spanDay,omitempty"`
}

DateFilter narrows a release timestamp with a comparison operator ("=", "!=", "<<", "<=", ">>", ">="). SpanDay is true when the value was parsed from a calendar day (YYYY-MM-DD), so "=" and "!=" cover the 24-hour UTC day.

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
	// Protocol is the conversion protocol the client asked for (hls, dash,
	// http). Empty keeps the HLS target. Additive.
	Protocol string
}

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"`
	// H264Level is a verified output level_idc for a constrained H.264 encode.
	// Zero retains the encoder's existing automatic selection.
	H264Level int `json:"h264Level,omitempty"`
	// H264FrameRate bounds the selected encoder output rate for that level.
	H264FrameRate  float64  `json:"h264FrameRate,omitempty"`
	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 DecisionCandidate added in v0.3.0

type DecisionCandidate struct {
	ReleaseID     string
	Name          string
	Height        int
	Size          int64
	Score         float64
	Rejected      bool
	Reason        string
	Healthy       bool
	HealthChecked bool
}

DecisionCandidate is one ranked or rejected release in a DecisionTrace.

type DecisionKind

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

type DecisionTrace added in v0.3.0

type DecisionTrace struct {
	Profile            string
	ProfileMaxHeight   int
	ClientMaxHeight    int
	EffectiveMaxHeight int
	AllowHDR           bool
	AllowDolbyVision   bool
	Chooser            string
	ChosenVersion      string
	Candidates         []DecisionCandidate
}

DecisionTrace is the privacy-safe shape of the latest resolution for a title: ceilings, who chose, and a bounded candidate list. It has no username, address, or stream URL. Release names are the file names the resolver ranked.

type DecisionTraceSource added in v0.3.0

type DecisionTraceSource interface {
	LatestDecision(key RatingKey) (DecisionTrace, bool)
}

DecisionTraceSource is the latest resolution for a title. Plex reads it for the support capture and stays outside the resolver package.

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 Device added in v0.3.0

type Device struct {
	ID         string
	Client     string
	Product    string
	Version    string
	Platform   string
	DeviceName string
	Language   string
}

Device identifies the client receiving a surface-specific credential.

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
	// ViewerID bounds converting downloads to one encode per viewer.
	// Zero skips the per-viewer encode lock (copy remuxes do not take it).
	ViewerID int64
	// Caps is the requesting client's download profile. Empty uses
	// H.264/HEVC + AAC/AC3 in MP4, the mobile download target.
	Caps ClientCaps
	// Options are the per-request transcode choices (bitrate, height, burn).
	Options DecideOptions
}

DownloadRemuxRequest is one whole-file remux to MP4.

type DownloadRemuxer

type DownloadRemuxer interface {
	// RemuxDownload copies or converts a resolved source 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 EncoderInputLease added in v0.3.0

type EncoderInputLease interface {
	// ClaimInput succeeds once, before seal. Failed claims never call onDrained.
	// Successful claims call it exactly once, possibly before this returns,
	// after sealed producers and registry ownership are released. The trusted
	// callback must only do bounded accounting/requeue; no I/O, waits or source
	// operations. It runs outside every manager/session/scope lock.
	ClaimInput(onDrained func(error)) error
	HeadersForInput(inputURL string) (map[string]string, error)
	SealInput()
	InputDone() <-chan struct{}
	InputErr() error
}

EncoderInputLease is issued by PlaybackEncoderInputs for one exact source lifetime. Host composition passes the opaque object; wire inputs cannot mint one. Each encoder must obtain its own lease, including shared-source owners.

type EncoderRetirement added in v0.3.0

type EncoderRetirement interface {
	EncoderDone() <-chan struct{}
	EncoderErr() error
}

EncoderRetirement covers one exact encoder session, including all its runs and preflights. EncoderDone closes only at a terminal result. EncoderErr is ErrEncoderPending before then and is immutable afterwards; only nil proves full retirement. SourceRetirement alone never supplies this proof.

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 ExactVariantPlayback added in v0.3.0

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

ExactVariantPlayback never substitutes a different release, including during later failover. The requested release may instead become unavailable.

type FacetValue

type FacetValue struct {
	ID    string `json:"id,omitempty"` // Stable namespace-aware neutral identifier.
	Key   string `json:"key"`
	Title string `json:"title"`
	Count int    `json:"count"`
	// Labels are the equivalent values Key stands for, where a facet groups
	// them (content-rating levels list every board's label).
	Labels []string `json:"labels,omitempty"`
}

type Filter

type Filter struct {
	KnownAddedDate bool `json:"-"` // only released/aired titles, before pagination
	// ExcludeKeys applies local visibility policy before counting and pagination.
	ExcludeKeys []RatingKey `json:"-"`
	// IncludeKeys, when set, restricts the page to these keys before
	// counting and pagination. An empty list matches nothing.
	IncludeKeys []RatingKey `json:"-"`
	// ExcludeGenres and ExcludeKeywords remove viewer-blocked facts before
	// counting and pagination. Matching is case-insensitive.
	ExcludeGenres         []string        `json:"-"`
	ExcludeKeywords       []string        `json:"-"`
	AddedAfter            int64           `json:"-"`              // inclusive Unix timestamp bound on added/release time
	AddedBefore           int64           `json:"-"`              // inclusive Unix timestamp bound on added/release time
	UpdatedAfter          int64           `json:"-"`              // inclusive Unix timestamp bound on metadata update time
	UpdatedBefore         int64           `json:"-"`              // inclusive Unix timestamp bound on metadata update time
	Type                  MediaType       `json:"type,omitempty"` // movie or show
	Genres                []string        `json:"genres,omitempty"`
	Keywords              []string        `json:"keywords,omitempty"` // every keyword must match (case-insensitive)
	YearFrom              int             `json:"yearFrom,omitempty"`
	YearTo                int             `json:"yearTo,omitempty"`
	Years                 []int           `json:"years,omitempty"`
	ExcludeYears          []int           `json:"excludeYears,omitempty"`
	MinRating             float64         `json:"minRating,omitempty"`
	MaxRating             float64         `json:"maxRating,omitempty"`
	ExcludeRatings        []float64       `json:"excludeRatings,omitempty"`
	MaxRuntime            time.Duration   `json:"maxRuntime,omitempty"`
	MinRuntime            time.Duration   `json:"minRuntime,omitempty"`
	ExcludeRuntimes       []time.Duration `json:"excludeRuntimes,omitempty"`
	Languages             []string        `json:"languages,omitempty"`
	Countries             []string        `json:"countries,omitempty"`
	ExcludeCountries      []string        `json:"excludeCountries,omitempty"`
	ContentRatings        []string        `json:"contentRatings,omitempty"`
	ExcludeContentRatings []string        `json:"excludeContentRatings,omitempty"`
	MaxContentRating      string          `json:"maxContentRating,omitempty"` // per-user limit
	// SaferPlexRating applies cached Plex content-rating levels in SQL
	// together with MaxContentRating so paging and counts stay correct.
	SaferPlexRating bool `json:"-"`

	Collection         int64          `json:"collection,omitempty"`
	Collections        []int64        `json:"collections,omitempty"`
	ExcludeCollections []int64        `json:"excludeCollections,omitempty"`
	Person             int64          `json:"person,omitempty"` // cast or crew TMDB id
	Credits            []CreditFilter `json:"credits,omitempty"`
	Studio             string         `json:"studio,omitempty"`
	StudioFilters      []StringFilter `json:"studioFilters,omitempty"`
	FirstCharacter     string         `json:"firstCharacter,omitempty"`
	Query              string         `json:"query,omitempty"` // title substring
	TitleFilters       []StringFilter `json:"titleFilters,omitempty"`
	ReleasedFilters    []DateFilter   `json:"releasedFilters,omitempty"`
	// MinContentRating is an additive share-policy predicate (plex.tv share
	// filters). It does not replace MaxContentRating or ContentRatings.
	MinContentRating string `json:"-"`
	// IncludeCollectionNames / ExcludeCollectionNames match
	// Title.CollectionName (plex.tv share label filters). An empty
	// collection name is unmappable and is not excluded.
	IncludeCollectionNames []string `json:"-"`
	ExcludeCollectionNames []string `json:"-"`
}

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 HLSIndependentSegments added in v0.3.0

type HLSIndependentSegments interface {
	IndependentSegments() bool
}

HLSIndependentSegments is an optional, session-wide guarantee that every segment in every advertised rendition is independently decodable. A session must not implement it as true unless it can prove that guarantee for copied media, all encoder paths, and every rung it actually advertises.

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 {
	// RequireIndependentProof emits HLS independence tags only when the
	// session can guarantee every advertised segment is independently decodable.
	// Zero preserves the legacy playlist contract for existing callers.
	RequireIndependentProof bool
	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
	// InputLease is trusted host composition only, never decoded from wire
	// requests. A caller retains an unclaimed lease if admission fails early.
	InputLease  EncoderInputLease `json:"-"`
	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), ProtocolDASH, or
	// ProtocolHTTP (Plex for Mac / Desktop's chunked Matroska stream;
	// the constant lives with the other "http" protocol name in debrid.go).
	// A DASH session also implements DASHSession; an HTTP session also
	// implements HTTPSession. Transcoder.Session returns either as an
	// HLSSession, so type-assert to reach the protocol 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 HTTPSession added in v0.3.0

type HTTPSession interface {
	HLSSession
	ServeHTTP(w http.ResponseWriter, r *http.Request)
}

HTTPSession is a running ffmpeg remux or transcode writing one streamable Matroska to the client (Plex for Mac protocol=http). ServeHTTP writes the chunked body; a later start with offset is a new StartHLS of the same session id.

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 HomeSituation added in v0.3.0

type HomeSituation struct {
	Device, Daypart                        string
	Weekend                                bool
	Now                                    time.Time
	LibraryID                              int
	Product, Platform, SessionID, ClientID string
}

HomeSituation describes the viewer's device and local time for row ranking.

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 HubItemSource added in v0.2.56

type HubItemSource interface {
	HubItemsFor(context.Context, *User, HubContext, string, int) (Hub, bool)
}

HubItemSource exposes the optional HubItemsFor capability for resolving a specific hub by identifier even after a model or window transition.

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 Identity added in v0.3.0

type Identity interface {
	Resolve(ctx context.Context, surface, credential string) (*Viewer, error)
}

Identity resolves a credential only for the supplied surface. Surface IDs are protocol-defined strings so additional front ends can register them.

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 is optional. Clients label a rule from its conditions.
	Name      string           `json:"name"`
	Action    string           `json:"action,omitempty"` // show, hide; empty means hide
	MediaType string           `json:"mediaType"`        // movie, show, mixed
	Match     string           `json:"match"`            // all, any
	Rules     []CollectionRule `json:"rules"`
}

LibraryExclusion is one ordered visibility rule. Slice order is evaluation order: the first match wins. Action is "show" or "hide"; an empty Action is "hide", so a saved exclusion group stays a hide rule. Conditions inside a rule combine with Match "all" or "any". Watch history and metadata are kept.

type LikesState added in v0.3.0

type LikesState interface {
	SetLikes(context.Context, int64, RatingKey, *bool) error
}

LikesState atomically applies a viewer's tri-state like to the shared rating. Nil clears the rating; true and false preserve existing compatible ratings.

type LocalFileSource added in v0.3.0

type LocalFileSource interface{ LocalFile() *os.File }

LocalFileSource exposes the opened file for change detection. The path and inode must still identify the same regular file when a revision is checked.

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 MarkedPlaylists added in v0.3.0

type MarkedPlaylists interface {
	MarkedPlaylist(context.Context, int64, string) (*Playlist, error)
	EnsureMarkedPlaylist(context.Context, int64, string, string) (*Playlist, error)
}

MarkedPlaylists provides stable, per-viewer playlist identities independent of names.

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 CloneMediaInfo added in v0.3.0

func CloneMediaInfo(info MediaInfo) MediaInfo

CloneMediaInfo detaches every mutable fact while preserving nil and empty values. The caller must own the input or hold its writer's lock during this copy; cloning does not synchronize concurrent mutation of the input itself.

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 NeutralEntryRef added in v0.3.0

type NeutralEntryRef struct{ Kind, ID string }

NeutralEntryRef identifies one delivered entry in a stable namespace.

type NeutralHomeLayoutSource added in v0.3.0

type NeutralHomeLayoutSource interface {
	FinalizeNeutralHome(context.Context, *User, HomeSituation, string, []NeutralHomeRow) ([]NeutralHomeRow, error)
}

NeutralHomeLayoutSource arranges rows without a protocol-specific context.

type NeutralHomeRow added in v0.3.0

type NeutralHomeRow struct {
	ID, Title, Kind string
	Purpose         RowPurpose
	Type            MediaType
	Items           []Title
	ItemCount       int
}

NeutralHomeRow carries a stable producer identity and semantic row type. Kind is the owner-layout identity (for example a collection rule), not a wire context.

type NeutralImpression added in v0.3.0

type NeutralImpression struct {
	Situation          HomeSituation
	RowID, Kind, Title string
	Purpose            RowPurpose
	Rank               int
	Items              []RatingKey
	Collection         RatingKey
	Item               RatingKey // Parent item for detail rows.
	Placement, ViewID  string
	Entries            []NeutralEntryRef
}

NeutralImpression is a server-issued snapshot of a delivered row.

type NeutralImpressionSink added in v0.3.0

type NeutralImpressionSink interface {
	RecordNeutralImpression(*User, NeutralImpression)
}

NeutralImpressionSink records a validated server-issued row snapshot.

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 OwnedEncoderSession added in v0.3.0

type OwnedEncoderSession interface {
	HLSSession
	EncoderRetirement() EncoderRetirement
	RetireEncoder() error
}

OwnedEncoderSession retires this exact object, never a replacement with the same public ID. RetireEncoder admits bounded asynchronous detachment; callers must inspect EncoderRetirement for completion, including input producers.

type OwnedSourcePlayback added in v0.3.0

type OwnedSourcePlayback interface {
	PrepareSurfaceSource(context.Context, *User, RatingKey, ClientCaps, string, bool) (*PlaySession, SourceRetirement, error)
	SourceRetirement(string) (SourceRetirement, bool)
	// RetireSource detaches the exact captured source lifetime and schedules
	// cleanup. Admission failure leaves the session attached. It is idempotent
	// for the same lifetime and cannot detach a replacement.
	RetireSource(string, SourceRetirement) error
}

OwnedSourcePlayback exposes source responsibility even when preparation fails or its caller cancels while a shared backend Open is still running.

type OwnedTranscoder added in v0.3.0

type OwnedTranscoder interface {
	StartHLSOwned(context.Context, HLSRequest) (HLSSession, EncoderRetirement, error)
}

OwnedTranscoder returns responsibility even when a canceled start is still completing. A late start cannot publish after that responsibility is sealed.

type Page

type Page struct {
	Items []Title
	Total int
}

type Person

type Person struct {
	TMDB      int64  `json:"tmdb,omitempty"`
	IMDB      string `json:"imdb,omitempty"`
	ID        string `json:"id,omitempty"` // catalogue person id when there is no TMDB id
	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"`
	// PreparedOnly marks a source reserved for planning but not opened by a
	// frontend. Cluster snapshots carry it for owner Activity.
	PreparedOnly bool `json:"preparedOnly,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 PlaybackEncoderInputs added in v0.3.0

type PlaybackEncoderInputs interface {
	ReserveEncoderInput(context.Context, string, SourceRetirement) (EncoderInputLease, error)
}

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 PlaybackRevisionPin added in v0.3.0

type PlaybackRevisionPin interface {
	PinSourceRevision(ctx context.Context, id, revision string) error
	UnpinSourceRevision(id string)
}

PlaybackRevisionPin keeps an opened generation from silently failing over while a client consumes metadata bound to its source revision.

type PlaybackSourceHistory added in v0.3.0

type PlaybackSourceHistory struct {
	Revision, ReleaseDigest, VideoCodec string
	Height                              int
	HDR                                 *bool
}

PlaybackSourceHistory contains persisted source facts without raw release identifiers. A digest can only be matched against authorized candidates.

type PlaybackSourceObservation added in v0.3.0

type PlaybackSourceObservation struct {
	Revision, ReleaseID string
	VideoCodec          string
	Height              int
	HDR                 *bool
}

PlaybackSourceObservation contains scalar facts from one opened source generation. Empty Revision means its identity cannot be established safely; nil HDR and empty codec/zero height remain unknown.

type PlaybackSourceSnapshot added in v0.3.0

type PlaybackSourceSnapshot interface {
	CurrentSource(context.Context, string) (PlaybackSourceObservation, error)
}

PlaybackSourceSnapshot copies immutable facts validated at source open/probe for the current generation. It performs no filesystem I/O and is not fresh authorization or a claim that a local path has remained unchanged.

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 PolicyHomeCandidateSource added in v0.3.0

type PolicyHomeCandidateSource interface {
	HomeCandidatesWithPolicy(context.Context, *User, HubContext, int, func(context.Context, *User, *Title) bool) ([]Hub, error)
	SectionCandidatesWithPolicy(context.Context, *User, MediaType, HubContext, int, func(context.Context, *User, *Title) bool) ([]Hub, error)
}

PolicyHomeCandidateSource filters a viewer's candidate pool before ranking and row limits. The caller supplies a fail-closed visibility predicate.

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 Protocol added in v0.3.0

type Protocol string

Protocol identifies a client-facing surface independently of client product.

type QualityProfile

type QualityProfile struct {
	ReleaseRules         []ReleaseRule  `json:"releaseRules,omitempty"`
	Ranking              []string       `json:"ranking,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 (movies, and fallback for TV)
	MaxSizeMBPerMin        float64  `json:"maxSizeMbPerMin"`
	EpisodeMinSizeMBPerMin float64  `json:"episodeMinSizeMbPerMin,omitempty"` // optional TV episode size bounds (0 uses MinSizeMBPerMin/MaxSizeMBPerMin)
	EpisodeMaxSizeMBPerMin float64  `json:"episodeMaxSizeMbPerMin,omitempty"`
	PreferLargest          bool     `json:"preferLargest,omitempty"` // prefer larger files among candidates matching the profile
	PreferredLanguages     []string `json:"preferredLanguages"`
	AllowCam               bool     `json:"allowCam"` // the only CAM switch; cinema recordings play only for profiles that allow them
	// 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, ranking) changes it; client caps never enter it.

func (QualityProfile) EffectiveRanking added in v0.3.0

func (p QualityProfile) EffectiveRanking() []string

EffectiveRanking returns the complete ranking ladder for p: DefaultRanking when Ranking is empty, or p.Ranking with any unmentioned Prefer rules prepended and any omitted built-in steps placed in their default relative positions.

func (QualityProfile) SizeBoundsFor added in v0.2.58

func (p QualityProfile) SizeBoundsFor(t *Title) (minMBPerMin, maxMBPerMin float64)

SizeBoundsFor returns the configured (min, max) MB/min bounds for a title.

func (QualityProfile) SizeBoundsForKey added in v0.2.58

func (p QualityProfile) SizeBoundsForKey(key RatingKey) (minMBPerMin, maxMBPerMin float64)

SizeBoundsForKey returns the configured (min, max) MB/min bounds for a rating key. TV episodes use EpisodeMinSizeMBPerMin / EpisodeMaxSizeMBPerMin when positive, falling back to MinSizeMBPerMin / MaxSizeMBPerMin.

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 ReadRetirement added in v0.3.0

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

ReadRetirement tracks producer references belonging to one media request. Seal returns a channel closed only after all registered producers release their references. It does not wait or cancel those producers and does not prove that the underlying MediaSource.Close succeeded.

func ReadRetirementFrom added in v0.3.0

func ReadRetirementFrom(ctx context.Context) *ReadRetirement

ReadRetirementFrom returns the tracker attached to ctx, if any.

func (*ReadRetirement) Begin added in v0.3.0

func (t *ReadRetirement) Begin() (func(), bool)

Begin registers one producer before it starts. Once sealed, registrations fail closed; a successful registration must call its returned function once.

func (*ReadRetirement) Seal added in v0.3.0

func (t *ReadRetirement) Seal() <-chan struct{}

Seal rejects later producers and returns the completion signal.

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"`
	// DailyLimit is the indexer's own cap. Zero means unlimited.
	DailyLimit int `json:"dailyLimit,omitempty"`
	// PageMax is the page size reported by caps. Zero means unknown.
	PageMax int `json:"pageMax,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
	// PlayedRelease is the release this viewer last played of the title
	// (the one a resume opens); Variants names Auto after it (additive).
	PlayedRelease string
	// 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
	// ExactHEVCTranscode permits the explicitly preferred HEVC source to reach
	// playback admission for an H.264 encode. The broker must still validate
	// the probed source and the client's complete output profile.
	ExactHEVCTranscode bool
}

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 RowPurpose added in v0.3.0

type RowPurpose string

RowPurpose describes the semantic reason for a row, independent of its wire representation.

type SearchCacheRefiller added in v0.2.56

type SearchCacheRefiller interface {
	RefillSearchCache(*Title) <-chan struct{}
}

SearchCacheRefiller requests best-effort, non-blocking candidate recovery for an admitted detail warm-up reusing a remembered source. It searches only; it must not resolve or replace a playback source. The resolver owns the work independently of request/warm-up cancellation and joins it on Close. A nil signal means skipped; otherwise the signal closes when the existing or newly admitted refill finishes (not necessarily successfully).

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
	// AlternativeTitles are other names of the movie or series (TMDB
	// alternative titles) a release name may use; matching only, never
	// part of a query or cache key (additive).
	AlternativeTitles []string `json:"-"`
}

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"
	SortYear       SortOrder = "year"
	SortRating     SortOrder = "rating"
	SortDuration   SortOrder = "duration"
	SortAdded      SortOrder = "added"   // movie/episode release or air date; series and seasons use the latest aired episode, then the premiere
	SortUpdated    SortOrder = "updated" // actual catalogue metadata update time
)

type SortSpec added in v0.3.0

type SortSpec struct {
	Sort SortOrder `json:"sort,omitempty"`
	Desc bool      `json:"desc,omitempty"`
}

SortSpec pairs a catalogue sort column with its direction.

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 SourceRetirement added in v0.3.0

type SourceRetirement interface {
	Done() <-chan struct{}
	Err() error
}

SourceRetirement reports backend responsibility for one exact manager source lifetime, including provisional and prior generations. Done is terminal; Err is nil only after Done, successful Close of every acquired source and settlement of its durable payload cleanup. Pending cleanup keeps Done open; a terminal backend Close error never becomes success. This does not prove encoder retirement or a durable ownership transfer.

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"`
	CodecLongName string `json:"codecLongName,omitempty"`
	Default       bool   `json:"default,omitempty"`
	Forced        bool   `json:"forced,omitempty"`
	// Nil means the source did not report this ffprobe disposition.
	DefaultDisposition *bool   `json:"defaultDisposition,omitempty"`
	ForcedDisposition  *bool   `json:"forcedDisposition,omitempty"`
	HearingImpaired    *bool   `json:"hearingImpaired,omitempty"`
	Original           *bool   `json:"original,omitempty"`
	VisualImpaired     *bool   `json:"visualImpaired,omitempty"`
	Commentary         *bool   `json:"commentary,omitempty"`
	Dub                *bool   `json:"dub,omitempty"`
	Width              int     `json:"width,omitempty"`
	Height             int     `json:"height,omitempty"`
	FrameRate          float64 `json:"frameRate,omitempty"`
	// Optional probe facts are nil/empty when the source did not report them.
	AverageFrameRate   *float64 `json:"averageFrameRate,omitempty"`
	ReferenceFrameRate *float64 `json:"referenceFrameRate,omitempty"`
	DisplayAspectRatio string   `json:"displayAspectRatio,omitempty"`
	PixelFormat        string   `json:"pixelFormat,omitempty"`
	CodecTag           string   `json:"codecTag,omitempty"`
	TimeBase           string   `json:"timeBase,omitempty"`
	NALLengthSize      *int     `json:"nalLengthSize,omitempty"`
	ReferenceFrames    *int     `json:"referenceFrames,omitempty"`
	BitsPerRawSample   *int     `json:"bitsPerRawSample,omitempty"`
	ProbedLevel        *int     `json:"probedLevel,omitempty"`
	FieldOrder         string   `json:"fieldOrder,omitempty"`
	ProbedInterlaced   *bool    `json:"probedInterlaced,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"`
	// HDR10Plus is nil unless the probe provides affirmative or explicit negative evidence.
	HDR10Plus     *bool  `json:"hdr10Plus,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 presence is known from a completed MP4/Matroska container
	// probe; nil means unprobed, unsupported or ambiguous. Codec tags alone do
	// not establish whether enhancement/base layers or RPU are present.
	DolbyVisionConfigurationPresent     *bool  `json:"dolbyVisionConfigurationPresent,omitempty"`
	DolbyVisionBLCompatibilityIDPresent *bool  `json:"dolbyVisionBLCompatibilityIdPresent,omitempty"`
	ProbedDolbyVisionProfile            *int   `json:"probedDolbyVisionProfile,omitempty"`
	ProbedDolbyVisionLevel              *int   `json:"probedDolbyVisionLevel,omitempty"`
	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 StringFilter added in v0.3.0

type StringFilter struct {
	Op    string `json:"op,omitempty"`
	Value string `json:"value,omitempty"`
}

StringFilter narrows a string column with a Plex string operator ("=", "!=", "==", "!==", "<=", ">=").

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 SurfaceScopedPlayback added in v0.3.0

type SurfaceScopedPlayback interface {
	PrepareSurface(ctx context.Context, user *User, key RatingKey, caps ClientCaps, releaseID string, exact bool) (*PlaySession, error)
	SessionForSurface(surface string, userID int64, clientID string, key RatingKey, releaseID string, exact bool) (*PlaySession, bool)
	MarkSurfaceOpened(id string) bool
	MarkSurfacePrepared(id string) bool
}

SurfaceScopedPlayback separates client identifiers by their authenticated protocol surface, even when different clients supply identical IDs.

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"`
	// PrimaryReleased is TMDB's primary release_date for a movie. The title
	// year comes from it. Released stays the date added (theatrical, when
	// one exists). Empty when the year came from a premiere or the release
	// itself. Not serialized.
	PrimaryReleased time.Time `json:"-"`
	// YearSource records which date produced Year: primary, premiere,
	// released, none, or missing. Empty means a row stored before that
	// date was kept. Not serialized.
	YearSource                  string        `json:"-"`
	Released                    time.Time     `json:"released,omitempty"`   // first air / release date
	AddedAt                     time.Time     `json:"addedAt,omitzero"`     // series/season latest aired episode, or a derived air date; not a catalogue insertion time
	LastAirDate                 time.Time     `json:"lastAirDate,omitzero"` // last aired episode date when known
	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
	// AlternativeTitles are other names the title goes by (TMDB alternative
	// titles: romanized originals, regional and working titles). Release
	// matching only; never serialized (additive). Episodes and seasons carry
	// their series' in ShowAlternativeTitles.
	AlternativeTitles     []string `json:"-"`
	ShowAlternativeTitles []string `json:"-"`
	RemoteTrailerName     string   `json:"remoteTrailerName,omitempty"`
	RemoteTrailerURL      string   `json:"remoteTrailerURL,omitempty"`
	Studios               []string `json:"studios,omitempty"`
	Countries             []string `json:"countries,omitempty"`
	Language              string   `json:"language,omitempty"` // ISO 639-1 original language
	// ContentRating is the certification in contentrating's stored form:
	// bare US labels ("PG", "TV-14"), region-qualified otherwise ("de/6").
	ContentRating string `json:"contentRating,omitempty"`
	// ContentRatingSource records where ContentRating came from: "tmdb:US:6"
	// (TMDB, country, movie release type), "tmdb:CA" (TV), "omdb", "mdblist".
	ContentRatingSource string `json:"contentRatingSource,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) DateAdded added in v0.3.0

func (title *Title) DateAdded(now time.Time) time.Time

DateAdded is the owner date-added rule. A movie uses its release date and an episode uses its air date. A series uses the air date of its most recently aired episode, then its first-air date. A season uses the latest aired episode in that season, then the season premiere. Those series and season dates are carried on AddedAt after catalogue hydration; a virtual season with no premiere of its own uses the same field. Catalogue insertion, first observation, metadata refresh, file modification and release-publication times are never the date added. Future dates are unknown.

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 TransactionalMarkedPlaylists added in v0.3.0

type TransactionalMarkedPlaylists interface {
	EnsureMarkedPlaylistTx(context.Context, *sql.Tx, int64, string, string) (RatingKey, error)
	DeleteMarkedPlaylistTx(context.Context, *sql.Tx, int64, string) error
	MarkedPlaylistKeyTx(context.Context, *sql.Tx, int64, string) (RatingKey, error)
}

TransactionalMarkedPlaylists joins marked-playlist creation to an existing shared database transaction.

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, a DASH session (a DASHSession)
	// when req.Protocol is ProtocolDASH, or an HTTP Matroska session
	// (an HTTPSession) when req.Protocol is ProtocolHTTP.
	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 TrickplayLease added in v0.3.0

type TrickplayLease interface {
	Ensure(context.Context, TrickplaySource)
	Release(cancelIfIdle bool)
}

TrickplayLease holds one release. Ensure after Release is inert. Release is idempotent; cancelIfIdle applies the caller's idle policy only when no other holder remains. Only the runtime owner can Close the provider.

type TrickplayLeaser added in v0.3.0

type TrickplayLeaser interface {
	AcquireTrickplay(context.Context, TrickplaySource) (TrickplayLease, error)
}

TrickplayLeaser is supplied by the runtime-owned shared provider. Acquire registers a holder and starts generation atomically with idle cancellation. Calls return promptly; consumers retain one lease until their source ends.

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 {
	// Timestamps are internal identity metadata and do not change existing wire JSON.
	CreatedAt time.Time `json:"-"`
	UpdatedAt time.Time `json:"-"`
	// 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 Viewer added in v0.3.0

type Viewer = User

Viewer is the household member that owns per-viewer state. Its ID is the existing local auth_users ID, independent of any identity provider.

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
	InWatchlist     bool          `json:"inWatchlist,omitempty"`
	IsFavorite      bool          `json:"isFavorite,omitempty"`      // Derived from the viewer’s favorites set.
	IsNotInterested bool          `json:"isNotInterested,omitempty"` // Derived from the viewer’s recommendation exclusions.
	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