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
- Variables
- func CheckReleaseHealth(ctx context.Context, hc HealthChecker, releaseID string, nzb []byte) (bool, error)
- func ClassifyDevice(product, platform, device string) string
- func DaypartAt(t time.Time) string
- func ForegroundReserve(ctx context.Context) int
- func InheritBackground(dst, src context.Context) context.Context
- func IsBackgroundCheck(ctx context.Context) bool
- func IsWeekend(t time.Time) bool
- func SpendBackground(ctx context.Context, resource, source string, amount int64) error
- func TargetReleaseID(releaseID string, t ReleaseTarget) string
- func WithBackgroundBudget(ctx context.Context, budget BackgroundBudget) context.Context
- func WithForegroundReserve(ctx context.Context, percent int) context.Context
- type ActiveUserSource
- type ActivityDay
- type AlignRequest
- type ArticleFetcher
- type ArticlePart
- type ArtworkAccessPolicy
- type Auth
- type AvailabilitySource
- type BackgroundBudget
- type BitrateAccessPolicy
- type BrowseQuery
- type BufferedInputSwapper
- type BurstArticleFetcher
- type CachePlayRecorder
- type CachedTitleSource
- type Catalog
- type ChannelPreparer
- type Chapter
- type ClientCaps
- type Collection
- type CollectionArtwork
- type CollectionArtworkSource
- type CollectionLayoutSource
- type CollectionPreview
- type CollectionRotate
- type CollectionRow
- type CollectionRows
- type CollectionRule
- type CollectionSchedule
- type CollectionSource
- type CollectionSpec
- type CollectionSpecSource
- type CollectionTargets
- type CollectionTemplate
- type Collections
- type ContextReaderAt
- type ContextualRecommender
- type CustomFormat
- type DASHSession
- type DamageReporter
- type DecideOptions
- type Decision
- type DecisionKind
- type DelegationAuth
- type DirectPlayRetrier
- type DownloadAccessPolicy
- type DownloadRemuxRequest
- type DownloadRemuxer
- type DurablePrefetcher
- type Engine
- type FacetValue
- type Filter
- type FormatSpecification
- type GoodputSession
- type HLSLadderSession
- type HLSRequest
- type HLSRung
- type HLSSession
- type HWAccel
- type HealthChecker
- type HistoryEntry
- type HomeCandidateSource
- type HomeLayout
- type HomeLayoutSource
- type HomePreview
- type HomeRow
- type Hub
- type HubContext
- type HubImpression
- type IDs
- type ImageURLFunc
- type ImpressionSink
- type Indexer
- type IntentTracker
- type KeyKind
- type KeyframePoint
- type LayoutPage
- type LayoutTuning
- type LayoutWhen
- type LibraryExclusion
- type LookupResult
- type MediaInfo
- type MediaSource
- type MediaType
- type MetadataReaderAt
- type MicroReason
- type MicroReasoner
- type Page
- type Person
- type PersonalScorer
- type PlaySession
- type Playback
- type PlaybackAccessPolicy
- type PlaybackPosition
- type PlaybackPreparer
- type PlaybackTimeline
- type Playlist
- type Playlists
- type PostPlayPick
- type PostPlayer
- type PrefetchReaderAt
- type PrewarmOptions
- type Prewarmer
- type Priority
- type ProcessCPUTime
- type ProcessIdentity
- type ProcessPeakMemory
- type QualityProfile
- type RankerFeature
- type RankerInsight
- type RatingKey
- func CollectionKey(id int64) RatingKey
- func EphemeralSmartKey(seq int64) RatingKey
- func EpisodeKey(show int64, season, episode int) RatingKey
- func ExtraKey(id int64) RatingKey
- func LocalKey(id int64) RatingKey
- func MovieKey(tmdb int64) RatingKey
- func ParseRatingKey(s string) (RatingKey, error)
- func PlaylistKey(id int64) RatingKey
- func SeasonKey(show int64, season int) RatingKey
- func ShowKey(tmdb int64) RatingKey
- func SmartKey(id int64) RatingKey
- func (k RatingKey) Episode() int
- func (k RatingKey) ExtraID() int64
- func (k RatingKey) IsEphemeral() bool
- func (k RatingKey) Kind() KeyKind
- func (k RatingKey) Parent() RatingKey
- func (k RatingKey) Season() int
- func (k RatingKey) String() string
- func (k RatingKey) TMDB() int64
- func (k RatingKey) Valid() bool
- type Recommender
- type RecoveryBuffer
- type RecoveryBufferSource
- type Release
- type ReleaseFeed
- type ReleaseHealthChecker
- type ReleaseInfo
- type ReleaseSizeSource
- type ReleaseTarget
- type ReleaseVariant
- type RemuxRequirer
- type RepairEvent
- type RepairReporter
- type RepairWindow
- type RescueIndexer
- type Resolution
- type ResolveRequest
- type Resolver
- type RowDescriptor
- type RowInsight
- type RowPolicy
- type SearchRequest
- type SectionCandidateSource
- type SegmentCache
- type SegmentPresence
- type SessionActivity
- type ShowTrajectory
- type SortOrder
- type Source
- type SourceDeliveryObserver
- type State
- type StreamInfo
- type StreamingIndexer
- type StreamingRescueIndexer
- type TasteDecade
- type TasteHabits
- type TasteLanguage
- type TasteOverride
- type TastePerson
- type TasteProfile
- type TasteWeight
- type TimelineAligner
- type Title
- type Transcoder
- type TrickplayCanceler
- type TrickplayProvider
- type TrickplaySource
- type User
- type UserPolicySource
- type UserTargets
- type VariantPlayback
- type VariantResolver
- type VibeQuery
- type VibeRefiner
- type VibeSearcher
- type VideoEnhancement
- type VideoEnhancementSupport
- type ViewerActivity
- type ViewerInvalidator
- type ViewerLayout
- type ViewerStats
- type WatchStatus
Constants ¶
const ( ProtocolUsenet = "" // the zero value: an NZB from a Newznab indexer ProtocolTorrent = "torrent" // a Torznab result streamed through a debrid service )
Release protocols (Release.Protocol).
const ( DeviceTV = "tv" DeviceMobile = "mobile" DeviceWeb = "web" DeviceDesktop = "desktop" )
Device classes for HubContext.Device.
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.
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.
const ( ProtocolHLS = "hls" ProtocolDASH = "dash" )
Streaming protocols of HLSRequest.Protocol.
const ( PrewarmDetail = "detail" // a detail page was opened PrewarmTrailer = "trailer" // a trailer or Cinema Trailer for the title is playing PrewarmPostPlay = "postplay" // the viewer is near the end of something and this is up next )
Reasons for PrewarmOptions.Reason.
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.
const PausedSessionRetention = 30 * time.Minute
PausedSessionRetention bounds a silent pause without treating it as an abandoned stream. Explicit stop still closes immediately.
Variables ¶
var ( ErrNotFound = errors.New("not found") ErrNotAvailable = errors.New("not available yet") // no acceptable release exists ErrArticleMissing = errors.New("article missing on all providers") ErrArticleCorrupt = errors.New("corrupt article or media data") ErrReleaseRejected = errors.New("release cannot be streamed") ErrBudgetExhausted = errors.New("indexer daily budget exhausted") ErrStreamLimit = errors.New("stream limit reached") )
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.
var ErrDownloadBudget = errors.New("download: temporary disk budget exceeded")
ErrDownloadBudget is returned when a download would exceed the temporary disk budget for remuxed files.
var ErrDownloadNeedsTranscode = errors.New("download: source codecs cannot be copied to MP4")
ErrDownloadNeedsTranscode is returned by DownloadRemuxer when the resolved streams cannot be copied into an MP4 a mobile client plays (e.g. AV1/VP9 video, TrueHD/DTS audio). Ether downloads never transcode offline, so the download decision reports this honestly as an error instead of silently re-encoding.
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.
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).
var ErrNotCached = fmt.Errorf("not cached by any debrid provider: %w", ErrNotAvailable)
ErrNotCached reports that no debrid provider can serve a torrent release instantly. It is a per-release skip, never proof the release is broken, so it wraps ErrNotAvailable and is not a permanent failure.
Functions ¶
func CheckReleaseHealth ¶
func CheckReleaseHealth(ctx context.Context, hc HealthChecker, releaseID string, nzb []byte) (bool, error)
CheckReleaseHealth runs hc.CheckRelease when hc implements it, else hc.Check.
func ClassifyDevice ¶
ClassifyDevice maps a Plex client's X-Plex-Product, X-Plex-Platform and X-Plex-Device headers onto a device class (DeviceTV, DeviceMobile, DeviceWeb, DeviceDesktop).
- TV: Apple TV, Android TV / Google TV / Fire TV / Shield, Roku, smart TVs (Samsung Tizen, LG webOS, Vizio, Hisense, Sony...), game consoles (Xbox, PlayStation) and set-top boxes.
- Desktop: Plex HTPC, Plex for Windows/Mac/Linux, Plex Media Player.
- Web: Plex Web in any browser.
- Mobile: Plex for iOS / Android phones and tablets.
Unknown clients fall back on their platform, then on DeviceWeb.
func DaypartAt ¶
DaypartAt returns the daypart of t in t's own location: morning 05:00–11:59, afternoon 12:00–16:59, evening 17:00–21:59, late 22:00–04:59.
func ForegroundReserve ¶ added in v0.2.8
func InheritBackground ¶ added in v0.2.8
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 IsWeekend ¶
IsWeekend reports whether t (in its own location) falls in the weekend viewing window: Friday from 17:00 through Sunday. Friday night behaves like a weekend night (films, longer sessions), Sunday daytime does too.
func SpendBackground ¶ added in v0.2.8
func TargetReleaseID ¶
func TargetReleaseID(releaseID string, t ReleaseTarget) string
TargetReleaseID derives a stable release ID for one episode of a multi-file release, so health, pins, cache and bad marks apply to the episode file rather than the whole pack.
func WithBackgroundBudget ¶ added in v0.2.8
func WithBackgroundBudget(ctx context.Context, budget BackgroundBudget) context.Context
Types ¶
type ActiveUserSource ¶ added in v0.2.7
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
ArtworkAccessPolicy exposes the optional Restricted capability.
type AvailabilitySource ¶ added in v0.2.7
AvailabilitySource exposes the optional Available capability.
type BackgroundBudget ¶ added in v0.2.8
BackgroundBudget is an optional admission hook for speculative work. Ordinary playback has no hook. Implementations must be concurrency safe; denied work returns ErrBudgetExhausted and is never evidence of source absence.
type BitrateAccessPolicy ¶ added in v0.2.7
BitrateAccessPolicy exposes the optional BitrateLimit capability.
type BrowseQuery ¶
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
CachePlayRecorder exposes the optional RecordPlay capability.
type CachedTitleSource ¶ added in v0.2.7
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 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 ClientCaps ¶
type ClientCaps struct {
Product string `json:"product"` // X-Plex-Product: "Plex Web", "Plex for iOS", "Plex for Apple TV"
Platform string `json:"platform"` // X-Plex-Platform
PlatformVersion string `json:"platformVersion"`
Device string `json:"device"`
DeviceName string `json:"deviceName"`
ClientID string `json:"clientId"` // X-Plex-Client-Identifier
Version string `json:"version"`
MaxHeight int `json:"maxHeight"` // 2160, 1080, 720...
HDR10 bool `json:"hdr10"`
DolbyVision bool `json:"dolbyVision"`
VideoCodecs []string `json:"videoCodecs"` // h264, hevc, av1, vp9
AudioCodecs []string `json:"audioCodecs"` // aac, ac3, eac3, truehd, dts, opus, flac
Containers []string `json:"containers"` // mp4, mkv, mpegts
HLSContainers []string `json:"hlsContainers,omitempty"` // explicit segmented output targets; distinct from direct-play containers
MaxBitrateKbps int `json:"maxBitrateKbps"` // 0 = unlimited
SubtitleCodecs []string `json:"subtitleCodecs,omitempty"` // native image/text subtitle rendering
DVProfiles []int `json:"dvProfiles,omitempty"` // explicit Dolby Vision profiles; empty uses product defaults
VideoProfiles map[string][]string `json:"videoProfiles,omitempty"`
VideoLevels map[string]int `json:"videoLevels,omitempty"` // codec level in ffprobe units (H.264 51 = 5.1)
Remote bool `json:"remote"` // not on the server's LAN
// Download marks a request for an offline download (Plex Downloads /
// Sync) rather than live playback: the resolver prefers compact,
// mobile-native releases and the whole file is remuxed (additive).
Download bool `json:"download,omitempty"`
}
ClientCaps is what a Plex client can play, derived from X-Plex-* headers, the client profile and its declared limits.
func (ClientCaps) SupportsAudio ¶
func (c ClientCaps) SupportsAudio(codec string) bool
func (ClientCaps) SupportsContainer ¶
func (c ClientCaps) SupportsContainer(ct string) bool
func (ClientCaps) SupportsVideo ¶
func (c ClientCaps) SupportsVideo(codec string) bool
type Collection ¶
type Collection struct {
Key RatingKey `json:"key"`
Title string `json:"title"`
Summary string `json:"summary,omitempty"`
Kind string `json:"kind"` // "franchise", "trending", "smart", "user"
Type MediaType `json:"type"`
Filter *Filter `json:"filter,omitempty"` // smart
Sort SortOrder `json:"sort,omitempty"`
OwnerUserID int64 `json:"ownerUserId,omitempty"` // 0 = server-wide
PosterPath string `json:"posterPath,omitempty"`
BackdropPath string `json:"backdropPath,omitempty"`
ChildCount int `json:"childCount"`
// 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 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 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
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
ContextReaderAt reads source bytes with request cancellation.
type ContextualRecommender ¶
type ContextualRecommender interface {
HomeHubsFor(ctx context.Context, user *User, hc HubContext, limit int) ([]Hub, error)
SectionHubsFor(ctx context.Context, user *User, t MediaType, hc HubContext, limit int) ([]Hub, error)
}
ContextualRecommender is implemented by recommenders that adapt rows to the request context. internal/plex prefers it over Recommender when available.
type CustomFormat ¶
type CustomFormat struct {
ID string `json:"trash_id"`
Name string `json:"name"`
Scores map[string]int `json:"trash_scores,omitempty"`
Specifications []FormatSpecification `json:"specifications"`
}
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
DamageReporter exposes the optional SetDamageHandler capability.
type DecideOptions ¶
type DecideOptions struct {
// DisableVideoTranscode prevents optional enhancement from promoting direct playback.
DisableVideoTranscode bool
DirectPlay bool // client allows direct play
DirectStream bool // client allows remux
MaxBitrateKbps int // client-requested cap (maxVideoBitrate)
MaxHeight int // from videoResolution
AudioStream int // selected stream index, -1 default
SubtitleStream int // -1 none
AudioFilters []string // trusted owner/viewer audio processing; forces audio encoding
BurnSubtitles bool
}
DecideOptions are per-request choices from the Plex decision request.
type Decision ¶
type Decision struct {
Enhancement VideoEnhancement `json:"enhancement,omitempty"`
Kind DecisionKind `json:"kind"`
VideoCopy bool `json:"videoCopy"`
AudioCopy bool `json:"audioCopy"`
VideoCodec string `json:"videoCodec"` // target codec when transcoding
AudioCodec string `json:"audioCodec"`
Container string `json:"container"` // "mpegts" (HLS) or original
MaxHeight int `json:"maxHeight"`
BitrateKbps int `json:"bitrateKbps"`
AudioStream int `json:"audioStream"`
SubtitleStream int `json:"subtitleStream"` // -1 none
BurnSubtitles bool `json:"burnSubtitles"`
Reason string `json:"reason"`
AudioFilters []string `json:"audioFilters,omitempty"`
// AudioChannels and AudioSampleRate keep the already negotiated audio
// decoder configuration across a source change. Zero uses the normal
// encoder defaults.
AudioChannels int `json:"audioChannels,omitempty"`
AudioSampleRate int `json:"audioSampleRate,omitempty"`
// ToneMap converts HDR (HDR10, HLG, Dolby Vision) video to SDR while
// transcoding, for clients without HDR support.
ToneMap bool `json:"toneMap,omitempty"`
// StripDolbyVision copies the compatible HDR10 base layer while
// removing Dolby Vision RPU/configuration data; it never re-encodes.
StripDolbyVision bool `json:"stripDolbyVision,omitempty"`
}
type DecisionKind ¶
type DecisionKind string
const ( DecisionDirectPlay DecisionKind = "directplay" DecisionRemux DecisionKind = "copy" // Plex calls this "direct stream" DecisionTranscode DecisionKind = "transcode" )
type DelegationAuth ¶ added in v0.2.7
DelegationAuth issues expiring, server-local casting credentials. Implementations must enforce current membership on use and must not delegate owner privileges.
type DirectPlayRetrier ¶ added in v0.2.7
type DirectPlayRetrier interface{ RetryDirectPlay() bool }
DirectPlayRetrier exposes the optional RetryDirectPlay capability.
type DownloadAccessPolicy ¶ added in v0.2.7
DownloadAccessPolicy exposes the optional AllowDownloads capability.
type DownloadRemuxRequest ¶
type DownloadRemuxRequest struct {
// InputURL is the loopback URL of the playback session feeding ffmpeg.
InputURL string
// Info is the probed media info of the resolved source.
Info MediaInfo
// AudioStream is the source stream index to keep; -1 picks the
// default (first) audio stream.
AudioStream int
}
DownloadRemuxRequest is one whole-file remux to MP4.
type DownloadRemuxer ¶
type DownloadRemuxer interface {
// RemuxDownload runs ffmpeg -c copy to a temp MP4, reporting progress
// as a fraction in [0,1] when progress is non-nil. Cancelling ctx
// kills ffmpeg and removes the partial file.
RemuxDownload(ctx context.Context, req DownloadRemuxRequest, progress func(fraction float64)) (path string, size int64, err error)
// ReleaseDownload deletes a remuxed file and frees its budget.
// Releasing an unknown path is a no-op.
ReleaseDownload(path string)
}
DownloadRemuxer copies a resolved source into a temporary faststart MP4. Implemented by internal/transcode. The returned path is owned by the remuxer until ReleaseDownload is called; files are never kept permanently and are bounded by a disk budget.
type DurablePrefetcher ¶ added in v0.2.7
type DurablePrefetcher interface {
DurablePrefetch(context.Context, string, []byte, int64, int64) error
}
DurablePrefetcher exposes the optional DurablePrefetch capability.
type Engine ¶
type Engine interface {
// Open builds the byte-offset index for the main video file of an NZB.
Open(ctx context.Context, releaseID string, nzb []byte) (MediaSource, error)
// Prefetch warms [offset, offset+length) at background priority.
Prefetch(ctx context.Context, releaseID string, nzb []byte, offset, length int64) error
}
type FacetValue ¶
type Filter ¶
type Filter struct {
// ExcludeKeys applies local visibility policy before counting and pagination.
ExcludeKeys []RatingKey `json:"-"`
Type MediaType `json:"type,omitempty"` // movie or show
Genres []string `json:"genres,omitempty"`
YearFrom int `json:"yearFrom,omitempty"`
YearTo int `json:"yearTo,omitempty"`
MinRating float64 `json:"minRating,omitempty"`
MaxRuntime time.Duration `json:"maxRuntime,omitempty"`
MinRuntime time.Duration `json:"minRuntime,omitempty"`
Languages []string `json:"languages,omitempty"`
Countries []string `json:"countries,omitempty"`
ContentRatings []string `json:"contentRatings,omitempty"`
MaxContentRating string `json:"maxContentRating,omitempty"` // per-user limit
Collection int64 `json:"collection,omitempty"`
Person int64 `json:"person,omitempty"` // cast or crew TMDB id
Studio string `json:"studio,omitempty"`
Query string `json:"query,omitempty"` // title substring
}
Filter narrows a browse or a smart collection. Zero values mean "any".
type FormatSpecification ¶
type GoodputSession ¶
type GoodputSession interface {
// GoodputKbps is the rolling segment-delivery estimate; 0 unknown.
GoodputKbps() int
}
GoodputSession is a session that measures client delivery goodput.
type HLSLadderSession ¶
type HLSLadderSession interface {
HLSSession
// Rungs lists the ladder, highest first; Rungs()[0] is "base".
Rungs() []HLSRung
// ServeRungPlaylist writes rung's media playlist.
ServeRungPlaylist(w http.ResponseWriter, r *http.Request, rung string)
// ServeRungSegment writes segment n of rung (InitSegment numbering as
// in ServeSegment: the caller passes the parsed segment number).
ServeRungSegment(w http.ResponseWriter, r *http.Request, rung string, n int)
}
HLSLadderSession is an HLSSession that can serve a multi-rung ABR ladder. Only the primary rung's encoder runs until a client requests a segment on another rung; at most one rung encodes at a time.
type HLSRequest ¶
type HLSRequest struct {
SessionID string
InputURL string // ffmpeg input; Ether serves sources on loopback HTTP with range support
// LiveInput permits playlist demuxers for owner-configured live sources.
// Ordinary resolved media must leave this false, even if duration is unknown.
LiveInput bool
// InputHeaders are optional HTTP headers ffmpeg must send when reading
// InputURL (e.g. for resolved trailer URLs). Transcoders that cannot
// send headers ignore them; most resolved URLs need none (additive).
InputHeaders map[string]string
Info MediaInfo
Decision Decision
StartOffset time.Duration
// SegmentDuration defaults to 4s when zero (5s for DASH).
SegmentDuration time.Duration
// Protocol is ProtocolHLS (default when empty) or ProtocolDASH. A DASH
// session also implements DASHSession; Transcoder.Session returns it as
// an HLSSession, so type-assert to reach the DASH methods.
Protocol string
}
type HLSRung ¶
type HLSRung struct {
// ID is the rung's URL component: session/{id}/{ID}/index.m3u8. The
// primary rung is always "base" (served by the HLSSession methods).
ID string
// Bandwidth is the peak bitrate in bits per second (#EXT-X-STREAM-INF
// BANDWIDTH / DASH Representation bandwidth).
Bandwidth int
// AverageBandwidth is the average bitrate in bits per second.
AverageBandwidth int
Width int
Height int
// Codecs is an RFC 6381 codecs string ("avc1...,mp4a...").
Codecs string
FrameRate float64
}
HLSRung is one variant of an adaptive HLS/DASH ladder. All rungs of a session share deterministic segment numbering over one segment timeline: segment N covers the same media on every rung.
type HLSSession ¶
type HLSSession interface {
ID() string
// ServePlaylist writes the media playlist (index.m3u8).
ServePlaylist(w http.ResponseWriter, r *http.Request)
// ServeSegment writes segment n, waiting for ffmpeg to produce it and
// restarting ffmpeg at the right offset when a seek jumps ahead.
ServeSegment(w http.ResponseWriter, r *http.Request, n int)
// SwapInput restarts the session on a new input at the given position
// (mid-stream failover). Segment numbering continues.
SwapInput(ctx context.Context, inputURL string, info MediaInfo, at time.Duration) error
LastAccess() time.Time
Close() error
}
HLSSession is one running ffmpeg remux or transcode producing HLS (or DASH; see DASHSession).
type HealthChecker ¶
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 HomeCandidateSource ¶ added in v0.2.7
type HomeCandidateSource interface {
HomeCandidatesFor(context.Context, *User, HubContext, int) ([]Hub, error)
}
HomeCandidateSource exposes the optional HomeCandidatesFor capability.
type HomeLayout ¶
type HomeLayout struct {
Global []LayoutPage `json:"global"`
Viewers []ViewerLayout `json:"viewers"`
UpdatedAt time.Time `json:"updatedAt,omitempty"`
}
HomeLayout is the owner's global template, with complete row policies in viewer layers replacing matching global policies. Slots are one-based.
type HomeLayoutSource ¶ added in v0.2.7
type HomeLayoutSource interface {
FinalizeHome(context.Context, *User, HubContext, string, []Hub) ([]Hub, error)
}
HomeLayoutSource exposes the optional FinalizeHome capability.
type HomePreview ¶
type HomePreview struct {
Rows []HomeRow `json:"rows"`
GeneratedAt time.Time `json:"generatedAt"`
Context HubContext `json:"context"`
// HomeDeduped counts the titles kept off a Movies or TV Shows page
// because the viewer's Home leads with them (DedupeHome).
HomeDeduped int `json:"homeDeduped,omitempty"`
}
HomePreview is a viewer's assembled rows for one context.
type HomeRow ¶
type HomeRow struct {
HubID string `json:"hubId"`
Layer string `json:"layer,omitempty"`
Slot int `json:"slot,omitempty"`
AnchorSlot int `json:"anchorSlot,omitempty"`
Placement string `json:"placement,omitempty"`
HiddenReason string `json:"hiddenReason,omitempty"`
Policy *RowPolicy `json:"policy,omitempty"`
PolicyLayer string `json:"policyLayer,omitempty"`
Title string `json:"title"`
Kind string `json:"kind"` // e.g. "because-you-watched", "collection:<id>"
Reason string `json:"reason"`
Score float64 `json:"score"` // predicted engagement used for ordering
Pinned bool `json:"pinned,omitempty"`
Hidden bool `json:"hidden,omitempty"` // hidden by a layout rule: not sent to Plex
Source string `json:"source"`
Items []Title `json:"items"`
ItemReasons map[RatingKey]string `json:"itemReasons,omitempty"`
// Hub is the row as handed to the Plex front end.
Hub Hub `json:"-"`
}
HomeRow is one row of a viewer's Home or section as Plex receives it, with the engine's explanation. Items are catalogue titles; the admin API turns them into TitleCards.
type Hub ¶
type Hub struct {
ID string `json:"id"` // stable identifier, e.g. "trending.week.movie"
Title string `json:"title"`
Type MediaType `json:"type"` // item type in the row ("mixed" allowed via "")
Context string `json:"context"` // Plex hub context, e.g. "hub.movie.recentlyreleased"
Style string `json:"style,omitempty"` // "shelf", "hero", "clip"
Promoted bool `json:"promoted"` // show on Home
Items []Title `json:"items"`
More bool `json:"more"`
Key RatingKey `json:"key,omitempty"` // backing collection, if any
// ItemCount carries rendered availability when a front end retains the
// actual metadata and asks only for row placement.
ItemCount int `json:"itemCount,omitempty"`
}
Hub is a Home row or a section row.
func SectionView ¶
SectionView restricts a Home hub to one library section's media type, so mixed rows that belong on a section too (the watchlist, Rediscover) can appear there with only that section's titles. A typed hub passes through when its type matches. Recaps (Year in Review) stay on Home. ok is false when nothing of the type remains.
type HubContext ¶
type HubContext struct {
Device string `json:"device"`
Daypart string `json:"daypart"`
Weekend bool `json:"weekend"`
Now time.Time `json:"now"`
SectionID int `json:"sectionId,omitempty"`
Product string `json:"product,omitempty"` // X-Plex-Product
Platform string `json:"platform,omitempty"` // X-Plex-Platform
SessionID string `json:"sessionId,omitempty"`
ClientID string `json:"clientId,omitempty"`
}
HubContext is the situation a Plex client asked for rows in: which kind of device, when, and for which section (empty = Home).
func NewHubContext ¶
func NewHubContext(now time.Time, product, platform, device string) HubContext
NewHubContext derives the request context for a hubs call at now (in the server's local time; pass time.Now() or a time already converted with In(loc)) from the client's X-Plex-* headers.
func (HubContext) Bucket ¶
func (hc HubContext) Bucket() string
Bucket is a stable cache key for the context's coarse shape: "section/device/daypart/weekday|weekend".
type HubImpression ¶
type HubImpression struct {
UserID int64 `json:"userId"`
HubID string `json:"hubId"`
Position int `json:"position"`
Keys []RatingKey `json:"keys"`
Context HubContext `json:"context"`
At time.Time `json:"at"`
}
HubImpression is one row as served to a user, for the personalization feedback loop.
type IDs ¶
type IDs struct {
TMDB int64 `json:"tmdb,omitempty"`
IMDB string `json:"imdb,omitempty"`
TVDB int64 `json:"tvdb,omitempty"`
}
IDs are the external identifiers of a title. Episodes carry their show's IMDb/TVDB ids in ShowIDs on Title, and their own TMDB episode id here.
type ImageURLFunc ¶
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 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 KeyframePoint ¶
KeyframePoint ties a byte offset in a media file to the presentation time of the sync sample (keyframe) or cue point that starts there, as read from the container index (Matroska Cues, MP4 stss with stco/co64) or a packet scan. MediaInfo.Keyframes holds them sorted by Offset and Time.
type LayoutPage ¶
type LayoutPage struct {
Section string `json:"section"`
MaxRows int `json:"maxRows,omitempty"`
DeviceMaxRows map[string]int `json:"deviceMaxRows,omitempty"`
Rows []RowPolicy `json:"rows"`
// DedupeHome keeps titles the viewer's Home leads with off this page
// (Movies/TV Shows only). Nil means the default: on.
DedupeHome *bool `json:"dedupeHome,omitempty"`
// Tuning shapes how the page's rows and titles are chosen. Nil fields
// inherit (viewer page → global page → engine default).
Tuning *LayoutTuning `json:"tuning,omitempty"`
}
type LayoutTuning ¶
type LayoutTuning struct {
// Discovery: 0 sticks to proven favourites, 1 explores new territory.
Discovery *float64 `json:"discovery,omitempty"`
// Freshness: 0 favours timeless picks, 1 favours new and trending.
Freshness *float64 `json:"freshness,omitempty"`
// Variety: 0 keeps rows focused on the strongest tastes, 1 spreads
// across genres, eras and moods.
Variety *float64 `json:"variety,omitempty"`
}
LayoutTuning is the owner's three dials for a page, each 0..1 with 0.5 as the engine default.
type LayoutWhen ¶
type LayoutWhen struct {
Days []int `json:"days,omitempty"`
Dayparts []string `json:"dayparts,omitempty"`
Months []int `json:"months,omitempty"`
StartDate string `json:"startDate,omitempty"`
EndDate string `json:"endDate,omitempty"`
FromHour *int `json:"fromHour,omitempty"`
ToHour *int `json:"toHour,omitempty"`
Devices []string `json:"devices,omitempty"`
}
LayoutWhen restricts a Home layout row to days (0 = Sunday … 6 = Saturday), an hour window [FromHour, ToHour) in server local time (wraps past midnight when FromHour > ToHour) and device classes.
type LibraryExclusion ¶ added in v0.2.12
type LibraryExclusion struct {
Name string `json:"name"`
MediaType string `json:"mediaType"` // movie, show, mixed
Match string `json:"match"` // all, any
Rules []CollectionRule `json:"rules"`
}
LibraryExclusion hides matching catalogue titles everywhere in Plex. Groups are ORed; each group combines its conditions with all or any. Watch history and the underlying metadata are retained.
type LookupResult ¶
type LookupResult struct {
ID int64 `json:"id"`
Name string `json:"name"`
Subtitle string `json:"subtitle,omitempty"`
ImagePath string `json:"imagePath,omitempty"` // TMDB image path
}
LookupResult is one TMDB entity (person, keyword, company, network, collection, watch provider) found by name for the collection builder.
type MediaInfo ¶
type MediaInfo struct {
Chapters []Chapter `json:"chapters,omitempty"`
Container string `json:"container"` // mkv, mp4, avi, mpegts
Duration time.Duration `json:"duration"`
Size int64 `json:"size"`
BitrateKbps int `json:"bitrateKbps"`
Streams []StreamInfo `json:"streams"`
Estimated bool `json:"estimated"` // derived from release name, not probed
// Keyframes is the container's keyframe/cue index, sorted, when it was
// read on open or probe; empty when unknown. Not serialized: it can hold
// thousands of points (additive).
Keyframes []KeyframePoint `json:"-"`
}
func (MediaInfo) Video ¶
func (m MediaInfo) Video() *StreamInfo
type MediaSource ¶
type MediaSource interface {
io.ReaderAt
Size() int64
Name() string // file name inside the release, e.g. "Movie.2024.1080p.mkv"
ReleaseID() string
Close() error
}
MediaSource is a seekable byte stream of one playable file (the video file inside a release, after NZB/RAR/7z unwrapping).
type 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 PersonalScorer ¶
type PersonalScorer interface {
ScoreTitles(ctx context.Context, user *User, titles []Title) (map[RatingKey]float64, error)
}
PersonalScorer scores titles for a user (higher = better fit). The collection engine uses it for the "personal" sort.
type PlaySession ¶
type PlaySession struct {
PlaybackGroup string `json:"playbackGroup,omitempty"`
ID string
UserID int64
ClientID string
Key RatingKey
Resolution *Resolution
Source MediaSource // swaps on failover; read through Playback.Source
Info MediaInfo
StartedAt time.Time
// Status for the admin dashboard and /status/sessions, filled on the
// snapshots Playback returns.
TitleName string // "Movie (2024)" or "Show - S01E02 - Episode"
Username string // user display name
Client ClientCaps // the client's product, device and limits
DecisionReason string `json:"decisionReason,omitempty"`
Decision DecisionKind // directplay once bytes go straight to the client; "" until known
BytesServed int64 // source bytes delivered (direct play and ffmpeg input)
SpeedBps int64 // recent delivery rate, bytes per second
LastActive time.Time
// ViewOffset is the client's last reported playhead in milliseconds.
// Nil means no timeline has arrived; source bytes do not imply progress.
ViewOffset *int64 `json:"viewOffset,omitempty"`
Failovers int // mid-stream release swaps so far
// 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
PlaybackAccessPolicy exposes the optional CanPlay capability.
type PlaybackPosition ¶ added in v0.2.7
PlaybackPosition exposes the optional RequestedPosition capability.
type PlaybackPreparer ¶ added in v0.2.7
PlaybackPreparer warms a title without reserving playback or changing history.
type PlaybackTimeline ¶ added in v0.2.8
type PlaybackTimeline interface {
PlaybackProgress(userID int64, clientID string, key RatingKey, position time.Duration, state string)
}
PlaybackTimeline carries actual client position without changing history.
type Playlist ¶
type Playlist struct {
Key RatingKey `json:"key"`
UserID int64 `json:"userId"`
Title string `json:"title"`
Smart bool `json:"smart"`
Items []RatingKey `json:"items"`
UpdatedAt time.Time `json:"updatedAt"`
}
Playlists are user-created lists.
type Playlists ¶
type Playlists interface {
List(ctx context.Context, userID int64) ([]Playlist, error)
Get(ctx context.Context, userID int64, key RatingKey) (*Playlist, error)
Create(ctx context.Context, userID int64, title string, items []RatingKey) (*Playlist, error)
Add(ctx context.Context, userID int64, key RatingKey, items []RatingKey) error
Remove(ctx context.Context, userID int64, key RatingKey, itemIndex int) error
Move(ctx context.Context, userID int64, key RatingKey, from, to int) error
Delete(ctx context.Context, userID int64, key RatingKey) error
}
type PostPlayPick ¶
type PostPlayPick struct {
Title Title `json:"title"`
Score float64 `json:"score"`
TasteFit float64 `json:"tasteFit"`
Similarity float64 `json:"similarity"`
RuntimeFit float64 `json:"runtimeFit"`
Reason string `json:"reason,omitempty"`
}
PostPlayPick is one ranked post-play recommendation with its explained score components (item #30): Score = taste factor × similarity factor × runtime factor (× a continuation bonus for the franchise's next entry), where TasteFit is the raw taste fit (-1..1), Similarity the feature overlap with the just-watched title (0..1) and RuntimeFit how well the runtime fits the viewer's remaining time tonight (0..1).
type PostPlayer ¶
type PostPlayer interface {
PostPlay(ctx context.Context, user *User, justWatched RatingKey, hc HubContext, limit int) ([]PostPlayPick, error)
}
PostPlayer ranks a personalized post-play "hook queue" after a title. Implementations cache the ranking per user and just-watched title for the session, so the pick pre-buffered during playback is the pick the post-play endpoint returns first.
type PrefetchReaderAt ¶ added in v0.2.7
type PrefetchReaderAt interface {
ReadAtPrefetchContext(context.Context, []byte, int64) (int, error)
}
PrefetchReaderAt reads speculative bytes at background priority.
type PrewarmOptions ¶
type PrewarmOptions struct {
// Reason labels the trigger for logs and budgets (Prewarm* constants).
Reason string `json:"reason"`
// PrefetchBytes is how much of the opening of the file to pre-buffer at
// background priority once the release is resolved, opened and probed.
// Zero uses the implementation's default for the reason.
PrefetchBytes int64 `json:"prefetchBytes,omitempty"`
// DuringPlayback allows the warm-up while the viewer already has an
// active session (a trailer, or the end of a film, is playing).
DuringPlayback bool `json:"duringPlayback,omitempty"`
}
PrewarmOptions tune a speculative warm-up of a title before the viewer presses Play.
type Prewarmer ¶
type Prewarmer interface {
Prewarm(ctx context.Context, user *User, key RatingKey, caps ClientCaps, opts PrewarmOptions) bool
}
Prewarmer is implemented by playback managers that can resolve, open, probe and pre-buffer a title ahead of Play, so the later Prepare for the same user, client and title is near-instant. A warm-up never reserves a stream or writes resume/history records. It reports whether a warm-up was scheduled (false when skipped by budgets or already warm).
type ProcessCPUTime ¶ added in v0.2.7
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
ProcessPeakMemory exposes the optional PeakRSSBytes capability.
type QualityProfile ¶
type QualityProfile struct {
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
MinHeight int `json:"minHeight"` // floor, e.g. 720
AllowHDR bool `json:"allowHdr"` // only applied when the client also supports it
AllowDolbyVision bool `json:"allowDolbyVision"`
PreferredSources []Source `json:"preferredSources"` // best first
PreferredCodecs []string `json:"preferredCodecs"` // best first; codecs the client can't play are dropped anyway
PreferredAudio []string `json:"preferredAudio"`
MinSizeMBPerMin float64 `json:"minSizeMbPerMin"` // size sanity, per minute of runtime
MaxSizeMBPerMin float64 `json:"maxSizeMbPerMin"`
PreferredLanguages []string `json:"preferredLanguages"`
AllowCam bool `json:"allowCam"` // also gated by the owner's global cam setting
}
QualityProfile is an owner-editable rule set ranking releases for a class of client.
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 EphemeralSmartKey ¶
EphemeralSmartKey builds an ephemeral collection key from a sequence number (1 .. 10^14-1).
func EpisodeKey ¶
func ParseRatingKey ¶
func PlaylistKey ¶
func (RatingKey) IsEphemeral ¶
IsEphemeral reports whether k is an ephemeral generated collection key.
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"`
}
Release is an indexer search result for a title.
type ReleaseFeed ¶ added in v0.2.8
type ReleaseFeed struct {
Categories []int `json:"categories,omitempty"` // local filter when a feed ignores category queries
ID string `json:"id"`
Name string `json:"name"`
Generation string `json:"-"`
Protocol string `json:"protocol"`
Supported bool `json:"supported"`
Reason string `json:"reason,omitempty"`
}
ReleaseFeed describes an indexer's recent-release discovery capability.
type ReleaseHealthChecker ¶
type ReleaseHealthChecker interface {
HealthChecker
CheckRelease(ctx context.Context, releaseID string, nzb []byte) (healthy bool, err error)
}
ReleaseHealthChecker is a HealthChecker that is also told which release the NZB belongs to, so what the check reads (the PAR2 index, archive headers) lands in the segment cache under the ID a following Open uses. Optional: callers fall back to Check when it is not implemented (additive).
type ReleaseInfo ¶
type ReleaseInfo struct {
Title string `json:"title"`
Edition string `json:"edition,omitempty"`
Year int `json:"year,omitempty"`
Seasons []int `json:"seasons,omitempty"`
Episodes []int `json:"episodes,omitempty"`
FullSeason bool `json:"fullSeason,omitempty"`
Height int `json:"height,omitempty"` // 480, 720, 1080, 2160
Source Source `json:"source,omitempty"`
VideoCodec string `json:"videoCodec,omitempty"` // h264, hevc, av1
HDR10 bool `json:"hdr10,omitempty"`
DolbyVision bool `json:"dolbyVision,omitempty"`
AudioCodec string `json:"audioCodec,omitempty"` // aac, ac3, eac3, truehd, dts, dtshd
Atmos bool `json:"atmos,omitempty"`
Channels string `json:"channels,omitempty"` // "5.1"
Group string `json:"group,omitempty"`
Languages []string `json:"languages,omitempty"`
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 ReleaseSizeSource ¶ added in v0.2.7
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 ¶
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
RepairWindow is playback's current useful repair lifetime. A zero deadline leaves the source's ordinary bound in force. Stop cancels obsolete work.
type RescueIndexer ¶ added in v0.2.7
type RescueIndexer interface {
SearchRescue(context.Context, SearchRequest) ([]Release, error)
}
RescueIndexer exposes the optional SearchRescue capability.
type Resolution ¶
type Resolution struct {
Title RatingKey `json:"title"`
Release Release `json:"release"`
NZB []byte `json:"-"`
Alternates []Release `json:"alternates"` // next candidates, best first
ResolvedAt time.Time `json:"resolvedAt"`
}
Resolution is the resolver's answer for a title and client: the winning release, already health-checked, plus fallbacks for mid-stream failover.
type ResolveRequest ¶
type ResolveRequest struct {
Title *Title
Client ClientCaps
User *User
// Exclude releases already tried (failover).
Exclude []string
// Prefer is a release ID to try first (the viewer picked a version in
// Plex's "Play Version" picker). It is health-checked like any other
// candidate; when it is unavailable, normal ranking applies (additive).
Prefer string
}
type Resolver ¶
type Resolver interface {
// Resolve finds a healthy release. Returns ErrNotAvailable when nothing
// acceptable exists.
Resolve(ctx context.Context, req ResolveRequest) (*Resolution, error)
// MarkBad records a release as broken so nobody gets it again.
MarkBad(ctx context.Context, releaseID string, reason string) error
// Available reports a cached availability answer without searching:
// (known, available).
Available(ctx context.Context, key RatingKey) (known bool, available bool)
}
type RowDescriptor ¶
type RowDescriptor struct {
HubID string `json:"hubId"`
Title string `json:"title"`
Kind string `json:"kind"`
Description string `json:"description"`
// Sections lists the pages this row can appear on ("home", "movies",
// "shows"); empty means every page.
Sections []string `json:"sections,omitempty"`
// Group is the designer's category: "picks", "explore", "shows",
// "household", "collections" or "plex".
Group string `json:"group,omitempty"`
}
RowDescriptor describes a kind of row the owner can place in the Home layout.
type RowInsight ¶
type RowInsight struct {
HubID string `json:"hubId"`
Title string `json:"title"`
Impressions7d int `json:"impressions7d"`
Plays7d int `json:"plays7d"`
PlayRate float64 `json:"playRate"`
}
RowInsight is the 7-day engagement of one row kind.
type RowPolicy ¶
type RowPolicy struct {
Row string `json:"row"`
Status string `json:"status"`
Slot int `json:"slot,omitempty"`
MinSlot int `json:"minSlot,omitempty"`
MaxSlot int `json:"maxSlot,omitempty"`
Priority float64 `json:"priority,omitempty"`
MaxPerPage int `json:"maxPerPage,omitempty"`
MaxDaysPerWeek int `json:"maxDaysPerWeek,omitempty"`
When *LayoutWhen `json:"when,omitempty"`
}
type SearchRequest ¶
type SearchRequest struct {
Type MediaType // movie or episode
IDs IDs // movie ids, or the show's ids for episodes
Title string
OriginalTitle string // optional original-language or canonical title (additive)
Year int
Season int
Episode int
EpisodeTitle string // optional title used to distinguish same-numbered episodes
}
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
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 SourceDeliveryObserver ¶ added in v0.2.9
SourceDeliveryObserver receives uncached source throughput evidence. It is neither client bandwidth nor a claim about an entire provider network.
type State ¶
type State interface {
Get(ctx context.Context, userID int64, key RatingKey) (WatchStatus, error)
GetMany(ctx context.Context, userID int64, keys []RatingKey) (map[RatingKey]WatchStatus, error)
// Progress records a timeline update. Crossing 90% marks it watched.
Progress(ctx context.Context, userID int64, key RatingKey, offset, duration time.Duration, state string) error
Scrobble(ctx context.Context, userID int64, key RatingKey) error // mark watched
Unscrobble(ctx context.Context, userID int64, key RatingKey) error // mark unwatched
Rate(ctx context.Context, userID int64, key RatingKey, rating float64) error
// OnDeck: in-progress items and next episodes, most recent first.
InProgress(ctx context.Context, userID int64, limit int) ([]WatchStatus, error)
History(ctx context.Context, userID int64, since time.Time, limit int) ([]HistoryEntry, error)
// InProgressAny lists keys in progress for any user (cache protection, prefetch).
InProgressAny(ctx context.Context) ([]RatingKey, error)
}
type StreamInfo ¶
type StreamInfo struct {
// Probe hints for enhancement backends that require progressive, steady-rate input.
Interlaced bool `json:"interlaced,omitempty"`
VariableFrameRate bool `json:"variableFrameRate,omitempty"`
Index int `json:"index"`
Type string `json:"type"` // video, audio, subtitle
Codec string `json:"codec"`
Profile string `json:"profile,omitempty"`
Level int `json:"level,omitempty"`
Language string `json:"language,omitempty"`
Title string `json:"title,omitempty"`
Default bool `json:"default,omitempty"`
Forced bool `json:"forced,omitempty"`
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
FrameRate float64 `json:"frameRate,omitempty"`
BitDepth int `json:"bitDepth,omitempty"`
ColorTransfer string `json:"colorTransfer,omitempty"` // smpte2084 = HDR10, arib-std-b67 = HLG
// Signalled picture format, in ffprobe's names; empty when the stream
// and container leave it unspecified (decoders then assume square
// pixels and limited range).
SampleAspectRatio string `json:"sampleAspectRatio,omitempty"` // "N:D"
ColorRange string `json:"colorRange,omitempty"` // tv, pc
ColorPrimaries string `json:"colorPrimaries,omitempty"` // bt709, bt2020, smpte170m...
ColorSpace string `json:"colorSpace,omitempty"` // matrix: bt709, bt2020nc, smpte170m...
DolbyVision bool `json:"dolbyVision,omitempty"`
Channels int `json:"channels,omitempty"`
ChannelLayout string `json:"channelLayout,omitempty"`
SampleRate int `json:"sampleRate,omitempty"`
BitrateKbps int `json:"bitrateKbps,omitempty"`
External bool `json:"external,omitempty"` // sidecar subtitle (OpenSubtitles)
ExternalURL string `json:"externalUrl,omitempty"`
// DolbyVisionProfile is the DV profile (5, 7, 8...) from the DOVI
// configuration record; 0 when unknown or not Dolby Vision.
DolbyVisionProfile int `json:"dolbyVisionProfile,omitempty"`
DolbyVisionLevel int `json:"dolbyVisionLevel,omitempty"`
DolbyVisionCompatibilityID int `json:"dolbyVisionCompatibilityId,omitempty"`
CodecString string `json:"codecString,omitempty"` // RFC 6381, derived from the codec configuration
// Configuration-record facts are optional: codec tags alone do not
// establish whether the enhancement/base layers or RPU are present.
DolbyVisionVersion string `json:"dolbyVisionVersion,omitempty"`
DolbyVisionBLPresent *bool `json:"dolbyVisionBLPresent,omitempty"`
DolbyVisionELPresent *bool `json:"dolbyVisionELPresent,omitempty"`
DolbyVisionRPUPresent *bool `json:"dolbyVisionRPUPresent,omitempty"`
}
type StreamingIndexer ¶ added in v0.2.7
type StreamingIndexer interface {
SearchWith(context.Context, SearchRequest, func([]Release)) ([]Release, error)
}
StreamingIndexer exposes the optional SearchWith capability.
type StreamingRescueIndexer ¶ added in v0.2.7
type StreamingRescueIndexer interface {
SearchRescueWith(context.Context, SearchRequest, func([]Release)) ([]Release, error)
}
StreamingRescueIndexer exposes the optional SearchRescueWith capability.
type TasteDecade ¶
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 ¶
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 ¶
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"`
Title string `json:"title"`
OriginalTitle string `json:"originalTitle,omitempty"`
SortTitle string `json:"sortTitle,omitempty"`
Tagline string `json:"tagline,omitempty"`
Summary string `json:"summary,omitempty"`
Year int `json:"year,omitempty"`
Released time.Time `json:"released,omitempty"` // first air / release date
Runtime time.Duration `json:"runtime,omitempty"`
Status string `json:"status,omitempty"` // TMDB status: Released, In Production, Ended...
Genres []string `json:"genres,omitempty"`
Keywords []string `json:"keywords,omitempty"` // TMDB keywords (lower-case), for content-based recommendations
Studios []string `json:"studios,omitempty"`
Countries []string `json:"countries,omitempty"`
Language string `json:"language,omitempty"` // ISO 639-1 original language
ContentRating string `json:"contentRating,omitempty"`
Adult bool `json:"adult,omitempty"`
Rating float64 `json:"rating,omitempty"` // TMDB vote average 0-10
VoteCount int `json:"voteCount,omitempty"`
Popularity float64 `json:"popularity,omitempty"`
PosterPath string `json:"posterPath,omitempty"`
BackdropPath string `json:"backdropPath,omitempty"`
StillPath string `json:"stillPath,omitempty"` // episodes
LogoPath string `json:"logoPath,omitempty"`
Cast []Person `json:"cast,omitempty"`
Directors []Person `json:"directors,omitempty"`
Writers []Person `json:"writers,omitempty"`
Crew []Person `json:"crew,omitempty"` // creators, cinematographers, composers (additive)
CollectionID int64 `json:"collectionId,omitempty"` // TMDB franchise
CollectionName string `json:"collectionName,omitempty"`
ChildCount int `json:"childCount,omitempty"` // seasons of a show, episodes of a season
LeafCount int `json:"leafCount,omitempty"` // episodes of a show
UpdatedAt time.Time `json:"updatedAt,omitempty"`
}
Title is a catalogue item: movie, show, season or episode. Image paths are TMDB paths ("/abc.jpg"); the front end turns them into URLs.
type Transcoder ¶
type Transcoder interface {
Probe(ctx context.Context, inputURL string) (*MediaInfo, error)
Decide(info MediaInfo, caps ClientCaps, opts DecideOptions) Decision
// StartHLS starts an HLS session, or a DASH session (a DASHSession)
// when req.Protocol is ProtocolDASH.
StartHLS(ctx context.Context, req HLSRequest) (HLSSession, error)
Session(id string) (HLSSession, bool)
Stop(id string)
HWAccel() HWAccel
}
type TrickplayCanceler ¶ added in v0.2.7
type TrickplayCanceler interface{ CancelTrickplay(string) }
TrickplayCanceler exposes the optional CancelTrickplay capability.
type TrickplayProvider ¶
type TrickplayProvider interface {
// EnsureTrickplay starts (or continues) progressive thumbnail
// generation for the source in the background. It returns
// immediately; duplicate calls for a release already generating are
// no-ops.
EnsureTrickplay(ctx context.Context, src TrickplaySource)
// TrickplayAvailable reports whether thumbnails for the release
// exist or are being generated, so a front end can advertise them
// (Plex's indexes="sd").
TrickplayAvailable(releaseID string) bool
// TrickplayBIF returns a snapshot of the release's sparse BIF index
// built from the frames generated so far.
TrickplayBIF(releaseID string) ([]byte, bool)
// TrickplayFrame returns the JPEG of the latest generated frame at
// or before at (a fallback still when no real frame precedes it).
TrickplayFrame(releaseID string, at time.Duration) ([]byte, bool)
// SetTrickplayFallback records a low-fidelity still (TMDB episode
// still or backdrop, already JPEG at roughly BIF width) used to fill
// not-yet-generated intervals. Real frames replace it as they arrive.
SetTrickplayFallback(releaseID string, jpeg []byte)
}
TrickplayProvider supplies on-the-fly preview thumbnails (Plex "BIF" indexes) for releases as they stream. It is an optional capability: callers reach it through a type assertion or an optional dependency and must tolerate its absence. Implementations only extract from sources handed to EnsureTrickplay (an active playback or warmup session); they never resolve or download a title just for thumbnails.
type TrickplaySource ¶
type TrickplaySource struct {
ReleaseID string
InputURL string
Info MediaInfo
// AvailableUntil is the playback position already read by the client.
// Generators must only fill intervals at or before this point and may
// advance it on later calls as playback progresses.
AvailableUntil time.Duration
}
TrickplaySource identifies one playable source thumbnails can be extracted from: the release being streamed and the loopback URL its bytes are readable through (Playback.InputURL). Info carries the duration and, when known, the container keyframe index used to align extractions to keyframes.
type User ¶
type User struct {
// Delegated marks short-lived server credentials; these never grant owner access.
Delegated bool `json:"-"`
ID int64 `json:"id"` // local id
PlexID int64 `json:"plexId"`
UUID string `json:"uuid,omitempty"`
Username string `json:"username"`
Title string `json:"title"` // display name
Email string `json:"email,omitempty"`
Thumb string `json:"thumb,omitempty"`
IsOwner bool `json:"isOwner"`
StreamLimit int `json:"streamLimit"` // 0 = server default
MaxContentRating string `json:"maxContentRating"` // "" = unrestricted, e.g. "PG-13", "TV-14"
SubtitleLanguages []string `json:"subtitleLanguages"` // ISO 639-1, preference order
AudioLanguages []string `json:"audioLanguages"`
AllowedSections []int `json:"allowedSections"` // empty = all
}
User is a Plex account with access to this server: the owner or a friend the owner shared with on plex.tv.
type UserPolicySource ¶ added in v0.2.7
UserPolicySource exposes the optional UserPolicy capability.
type UserTargets ¶
UserTargets is either every user ("all" in JSON) or a list of local user ids.
func (UserTargets) Includes ¶
func (u UserTargets) Includes(user *User) bool
Includes reports whether a user (nil = anonymous) is targeted.
func (UserTargets) MarshalJSON ¶
func (u UserTargets) MarshalJSON() ([]byte, error)
func (*UserTargets) UnmarshalJSON ¶
func (u *UserTargets) UnmarshalJSON(b []byte) error
type VariantPlayback ¶
type VariantPlayback interface {
PrepareVariant(ctx context.Context, user *User, key RatingKey, caps ClientCaps, releaseID string) (*PlaySession, error)
}
VariantPlayback is implemented by playback managers that can prepare a session on a specific release (the viewer picked a version). The release is health-checked like any other; when it is unavailable the manager falls back to the normal choice for the client.
type VariantResolver ¶
type VariantResolver interface {
Variants(ctx context.Context, req ResolveRequest, max int) ([]ReleaseVariant, error)
}
VariantResolver lists distinct candidate releases of a title for a client, best first, without health-checking every one of them. Implemented by internal/resolver.
type VibeQuery ¶
type VibeQuery struct {
// Query is the viewer's original text.
Query string `json:"query,omitempty"`
// Title is the human collection title, e.g.
// "90s Thrillers Under 100 Minutes".
Title string `json:"title,omitempty"`
// Filter is the catalogue filter the members are browsed with.
Filter Filter `json:"filter"`
Sort SortOrder `json:"sort,omitempty"`
Desc bool `json:"desc,omitempty"`
// SimilarSeed is the unresolved seed title text of a
// "shows like the office" query; SimilarKey is it resolved.
SimilarSeed string `json:"similarSeed,omitempty"`
SimilarKey RatingKey `json:"similarKey,omitempty"`
// PersonName is an unresolved person name ("christopher nolan
// films"); once resolved it lands in Filter.Person.
PersonName string `json:"personName,omitempty"`
}
VibeQuery is a natural-language ("vibe") search turned into a structured catalogue filter, e.g. "90s thrillers under 100 mins". It backs the ephemeral collections synthesized in search results (additive).
type VibeRefiner ¶
type VibeRefiner interface {
RefineVibe(ctx context.Context, user *User, query string) (VibeQuery, bool, error)
}
VibeRefiner maps an otherwise unparseable vibe query to a VibeQuery (implemented by the optional LLM service). It must be quick, respect ctx, and report ok=false when it cannot help.
type VibeSearcher ¶
type VibeSearcher interface {
VibeCollection(ctx context.Context, user *User, query string) (c *Collection, items []Title, ok bool, err error)
// PromoteEphemeral saves an ephemeral collection as a persisted
// personal smart collection for its owner. ok is false when key is
// not a live ephemeral collection owned by user.
PromoteEphemeral(ctx context.Context, user *User, key RatingKey) (c *Collection, ok bool, err error)
}
VibeSearcher is optionally implemented by the collections engine: it turns a vibe query into a browseable ephemeral collection for the viewer, alongside the collection's top items. ok is false when the query is not a vibe query (plain title searches stay untouched).
type VideoEnhancement ¶ added in v0.2.4
type VideoEnhancement struct {
Backend string `json:"backend,omitempty"`
State string `json:"state,omitempty"` // off, unavailable, ineligible, selected, starting, running, fallback, stopped
Reason string `json:"reason,omitempty"`
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
Quality int `json:"quality,omitempty"`
}
VideoEnhancement is an observable plan/run status, never a promise based solely on installed hardware. Only running means enhanced segments have actually been produced in the current run. Fallback always carries a reason.
type VideoEnhancementSupport ¶ added in v0.2.4
type VideoEnhancementSupport struct {
// SetupState is off, checking, installing, ready, or unavailable.
SetupState string `json:"setupState,omitempty"`
Backend string `json:"backend"`
Available bool `json:"available"`
Reason string `json:"reason,omitempty"`
Version string `json:"version,omitempty"`
Protocols []string `json:"protocols,omitempty"`
Active int `json:"active"`
}
VideoEnhancementSupport is detection, not successful execution. Its protocols are the transports verified for this backend version.
type ViewerActivity ¶
type ViewerActivity struct {
Title Title `json:"title"`
At time.Time `json:"at"`
Completed bool `json:"completed"`
Progress float64 `json:"progress"` // 0..1
}
ViewerActivity is one recent viewing.
type ViewerInvalidator ¶ added in v0.2.7
type ViewerInvalidator interface{ Invalidate(userID int64) }
ViewerInvalidator exposes the optional Invalidate capability.
type ViewerLayout ¶
type ViewerLayout struct {
UserID int64 `json:"userId"`
Pages []LayoutPage `json:"pages"`
}
type ViewerStats ¶
type ViewerStats struct {
UserID int64 `json:"userId"`
LastActive time.Time `json:"lastActive,omitempty"`
Plays30d int `json:"plays30d"`
HoursWatched30d float64 `json:"hoursWatched30d"`
}
ViewerStats is a viewer's recent activity summary.
type WatchStatus ¶
type WatchStatus struct {
Key RatingKey `json:"key"`
ViewOffset time.Duration `json:"viewOffset"`
ViewCount int `json:"viewCount"`
LastViewedAt time.Time `json:"lastViewedAt,omitempty"`
UserRating float64 `json:"userRating,omitempty"` // 0-10
Duration time.Duration `json:"duration,omitempty"`
UpdatedAt time.Time `json:"updatedAt"`
}
Source Files
¶
- align.go
- background.go
- capabilities.go
- collectionspec.go
- customformats.go
- debrid.go
- downloads.go
- hls_ladder.go
- homelayout.go
- hubcontext.go
- interfaces.go
- keyframes.go
- libraryfilter.go
- personalhooks.go
- personalize.go
- personalize_owner.go
- playback.go
- playbackcore.go
- prewarm.go
- ratingkey.go
- release.go
- repair.go
- session_activity.go
- trickplay.go
- types.go
- variants.go
- vibesearch.go
- video_enhancement.go