Documentation
¶
Overview ¶
Package experience contains protocol-independent viewer experience logic.
Index ¶
- Constants
- Variables
- func ArrangeHome(ctx context.Context, source core.Recommender, viewer *core.User, ...) ([]int, error)
- func ArrangeLayout(candidates []LayoutCandidate, ...) ([]int, error)
- func ArrangeNeutralHome(ctx context.Context, source core.NeutralHomeLayoutSource, viewer *core.User, ...) ([]int, error)
- func AssembleHome(candidates []HomeCandidate, includeContinue, promotedOnly bool, ...)
- func AssemblePostPlay[P any](ctx context.Context, selection PostPlaySelection, viewer *core.User, ...) ([]P, error)
- func CandidateObservationSource(releaseID string) (string, bool)
- func CandidatePurpose(h core.Hub) core.RowPurpose
- func CandidateRows(candidates []RankedCandidate) []frontend.Row
- func CanonicalTMDBPerson(id string) (int64, bool)
- func ColdHomeRows(ctx context.Context, viewer *core.User, libraries []Library, ...) ([]frontend.Row, error)
- func CompleteHome(includePlaylists bool, playlists, discovery, live, selectRows, layout func())
- func DetailCanPlay(ctx context.Context, req frontend.ItemRequest, protocol core.Protocol, ...) *bool
- func DetailExtras(ctx context.Context, source ExtrasSource, parent *core.Title, language string, ...) ([]extras.Extra, error)
- func DiscoveryAvailabilityRow(id string) bool
- func DiscoverySearch(source *discovery.Features) func(context.Context, *core.User, string, int) ([]core.Title, bool)
- func DownloadsAllowed(viewer *core.User, ready, allowFriends bool, policy AccessPolicy) bool
- func FavoriteAllowed(ctx context.Context, viewer *core.User, subject FavoriteSubject, ...) bool
- func FavoriteTitlesCached(ctx context.Context, viewer *core.User, keys []core.RatingKey, ...) (bool, error)
- func HomePurpose(h core.Hub) core.RowPurpose
- func HubRows(hubs []core.Hub) []frontend.Row
- func IndexVirtualPaths(ctx context.Context, catalog VirtualPathCatalog, viewer *core.Viewer, ...) (map[core.RatingKey]string, error)
- func ItemMarkers(ctx context.Context, source MarkerSource, key core.RatingKey, ...) []frontend.Marker
- func LayoutRows(ctx context.Context, source core.NeutralHomeLayoutSource, libraries []Library, ...) ([]frontend.Row, error)
- func LibrarySection(libraries []Library, kind core.MediaType) (int, bool)
- func LiveEntries(ctx context.Context, viewer *core.User, source LiveGuideSource, ...) ([]frontend.RowEntry, error)
- func LoadSeriesNextUp(ctx context.Context, series core.RatingKey, resumable bool, ...) (*core.Title, error)
- func MarkerMedia(viewer *core.User, device core.Device, key core.RatingKey, ...) core.MediaInfo
- func MaySetViewerRating(actor *core.Viewer, active *core.User) bool
- func MergeShareFilter(filter, share core.Filter) (out core.Filter, none bool)
- func ObserveVersionOffers(ctx context.Context, port VersionObservationPort, parent core.RatingKey, ...) ([]*time.Time, error)
- func PickAudio(info core.MediaInfo, langs []string) int
- func PlaylistDuration(items []core.RatingKey, get func(core.RatingKey) *core.Title) time.Duration
- func PlaylistEntries(ctx context.Context, viewer *core.User, source PlaylistRowSource, limit int) ([]frontend.RowEntry, bool, error)
- func PreviewAvailability(ctx context.Context, u *core.User, limit int, ...) []core.Hub
- func PrioritizeFavoriteShows(titles []core.Title, favorites map[core.RatingKey]bool)
- func PruneReceiptShard(ctx context.Context, db *sql.DB, now time.Time, shard int64) (int64, error)
- func RecentPlaylists(ctx context.Context, viewer *core.User, source PlaylistRowSource, limit int) ([]core.Playlist, bool, error)
- func RecentlyAdded(ctx context.Context, viewer *core.User, kind core.MediaType, count int, ...) ([]core.Title, bool, error)
- func RecentlyViewed(ctx context.Context, viewer *core.User, libraryID int, visibility Visibility, ...) ([]core.Title, error)
- func ResolveAudio(want *core.StreamInfo, pos int, info core.MediaInfo) int
- func ResolveSubtitle(want *core.StreamInfo, pos int, info core.MediaInfo) int
- func RowEntryIdentity(e frontend.RowEntry) string
- func RowViewCollectionMembers(ctx context.Context, source RowViewCollectionSource, user *core.User, ...) bool
- func RowViewHydrateEntries(ctx context.Context, viewer *core.User, refs []core.NeutralEntryRef, ...) ([]frontend.RowEntry, error)
- func RowViewHydrateLegacy(ctx context.Context, viewer *core.User, refs []core.NeutralEntryRef, ...) ([]frontend.RowEntry, error)
- func RowViewMembers(ctx context.Context, items RowViewCollectionItems, viewer *core.User, ...) bool
- func RowViewResolveBatchTitles(ctx context.Context, viewer *core.User, device core.Device, ...) ([]frontend.RowEntry, error)
- func RowViewRevision(ctx context.Context, db *sql.DB, user *core.User) (string, error)
- func SameDayUnavailable(ctx context.Context, title *core.Title, now time.Time, ...) bool
- func SectionAllowed(viewer *core.User, id int) bool
- func SelectCinemaTrailers(feature core.RatingKey, candidates []CinemaCandidate, limit int) []frontend.Extra
- func SeriesNextUp(eps []core.Title, st map[core.RatingKey]core.WatchStatus, ...) *core.Title
- func SeriesNextUpQuery() frontend.ItemQuery
- func ShowOnDeck(eps []core.Title, st map[core.RatingKey]core.WatchStatus, ...) *core.Title
- func TitleAllowed(policy AccessPolicy, viewer *core.User, title *core.Title) bool
- func UniqueRelated(ctx context.Context, viewer *core.User, visibility Visibility, ...) []core.Title
- func ViewerRestricted(policy AccessPolicy, u *core.User) bool
- func VirtualPath(ctx context.Context, catalog VirtualPathCatalog, viewer *core.Viewer, ...) (string, error)
- func VirtualPaths(ctx context.Context, catalog VirtualPathCatalog, viewer *core.Viewer, ...) (map[core.RatingKey]string, error)
- type AccessPolicy
- type AdditionalRows
- type CandidateComposer
- type CandidateRanker
- type CandidateRowSource
- type CinemaCandidate
- type Continue
- func (c Continue) InProgress(ctx context.Context, viewer *core.User, t core.MediaType, limit int) ([]core.Title, map[core.RatingKey]core.WatchStatus)
- func (c Continue) Row(ctx context.Context, viewer *core.User, kind core.MediaType, limit int) ([]core.Title, map[core.RatingKey]core.WatchStatus)
- func (c Continue) Shelf(ctx context.Context, viewer *core.User, kind core.MediaType, limit int) ([]core.Title, map[core.RatingKey]core.WatchStatus, bool)
- type DetailRelated
- type DiscoveryCandidate
- type DiscoveryRowSource
- type DiscoverySelection
- type EstimatePlanner
- type ExtrasSource
- type FavoriteKind
- type FavoriteSources
- type FavoriteSubject
- type FirstObservedLedger
- func (l *FirstObservedLedger) FirstObservedAt(ctx context.Context, parent core.RatingKey, source string) (*time.Time, error)
- func (l *FirstObservedLedger) Observe(ctx context.Context, parent core.RatingKey, source string, observed time.Time) (time.Time, error)
- func (l *FirstObservedLedger) ObserveBatch(ctx context.Context, parent core.RatingKey, sources []string, ...) (map[string]time.Time, error)
- type HomeAssembly
- type HomeCandidate
- type HomeSelection
- type LayoutCandidate
- type Library
- type LibraryPageExclusions
- type LiveGuideSource
- type MarkerSource
- type PeopleRead
- type PersonCandidate
- type PlaylistRowSource
- type PlaylistSelection
- type PlexIssuedIdentity
- type PostPlayRow
- type PostPlaySelection
- type PreviewRows
- type RankedCandidate
- type ReceiptOptions
- type ReceiptSubject
- type Receipts
- func (s *Receipts) Issue(ctx context.Context, viewer *core.User, device core.Device, ...) (string, error)
- func (s *Receipts) IssueBatch(ctx context.Context, viewer *core.User, device core.Device, ...) ([]string, error)
- func (s *Receipts) Report(ctx context.Context, interaction frontend.RowInteraction) error
- func (s *Receipts) ReportRowInteraction(ctx context.Context, i frontend.RowInteraction) error
- type Recent
- type RelatedArtworkDeps
- type RelatedArtworkService
- type RelatedCandidate
- type ResumeCandidates
- type RowEntryPolicy
- type RowInteractionPolicy
- type RowSelection
- type RowServiceDeps
- type RowSource
- type RowViewCollectionItems
- type RowViewCollectionPage
- type RowViewCollectionSource
- type RowViewEntry
- type RowViewGuide
- type RowViewOptions
- type RowViewService
- type RowViewSnapshot
- type RowViewTitleBatch
- type RowViewTitleRead
- type RowsService
- type RuntimeRowViews
- type ScopedCollection
- func (s ScopedCollection) Counts(ctx context.Context, viewer *core.User, collections []core.Collection, ...) []core.Collection
- func (s ScopedCollection) Get(ctx context.Context, viewer *core.User, key core.RatingKey, device string, ...) (*core.Collection, error)
- func (s ScopedCollection) Items(ctx context.Context, viewer *core.User, key core.RatingKey, deviceClass string) ([]core.Title, error)
- func (s ScopedCollection) Page(ctx context.Context, viewer *core.User, collection *core.Collection, ...) (core.Page, error)
- func (s ScopedCollection) Placeholder(ctx context.Context, viewer *core.User, key core.RatingKey, ...) (*core.Title, core.RatingKey, error)
- type Search
- func (s Search) Find(ctx context.Context, viewer *core.User, query string, types []core.MediaType, ...) ([]core.Title, error)
- func (s Search) Keyword(ctx context.Context, viewer *core.User, query string, types []core.MediaType, ...) ([]core.Title, error)
- func (s Search) Resolve(ctx context.Context, viewer *core.User, query string, types []core.MediaType, ...) (SearchResults, error)
- type SearchResults
- type Service
- func (s *Service) AddPlaylistItems(ctx context.Context, viewer *core.Viewer, key core.RatingKey, ...) error
- func (s *Service) Ancestors(ctx context.Context, viewer *core.Viewer, device core.Device, ...) ([]core.Title, error)
- func (s *Service) AuthorizeArtwork(ctx context.Context, viewer *core.Viewer, device core.Device, ...) (*core.Viewer, error)
- func (s *Service) Children(ctx context.Context, viewer *core.Viewer, device core.Device, ...) (core.Page, error)
- func (s *Service) Collections(ctx context.Context, viewer *core.Viewer, _ core.Device, ...) (frontend.CollectionPage, error)
- func (s *Service) CreatePlaylist(ctx context.Context, viewer *core.Viewer, name string, items []core.RatingKey) (*core.Playlist, error)
- func (s *Service) DeletePlaylist(ctx context.Context, viewer *core.Viewer, key core.RatingKey) error
- func (s *Service) EntityFavorites(ctx context.Context, viewer *core.Viewer, refs []frontend.FavoriteRef) (map[frontend.FavoriteRef]bool, error)
- func (s *Service) Facets(ctx context.Context, viewer *core.Viewer, device core.Device, ...) ([]core.FacetValue, error)
- func (s *Service) FavoriteItem(ctx context.Context, viewer *core.Viewer, _ core.Device, key core.RatingKey) (frontend.Membership, error)
- func (s *Service) Item(ctx context.Context, req frontend.ItemRequest) (frontend.ItemDetail, error)
- func (s *Service) LatestAdded(ctx context.Context, viewer *core.Viewer, device core.Device, viewID string, ...) ([]frontend.LatestItem, error)
- func (s *Service) LatestAddedGrouped(ctx context.Context, viewer *core.Viewer, device core.Device, viewID string, ...) ([]frontend.LatestItem, error)
- func (s *Service) LibraryCount(ctx context.Context, viewer *core.Viewer, viewID string) (int, error)
- func (s *Service) LibraryItems(ctx context.Context, viewer *core.Viewer, device core.Device, viewID string, ...) (core.Page, error)
- func (s *Service) LocalEpisodes(ctx context.Context, viewer *core.Viewer, key core.RatingKey) ([]core.Title, bool, error)
- func (s *Service) MovePlaylistItem(ctx context.Context, viewer *core.Viewer, key core.RatingKey, from, to int) error
- func (s *Service) NamedFacetIDs(ctx context.Context, viewer *core.Viewer, _ core.Device, key core.RatingKey, ...) (map[string]string, error)
- func (s *Service) NotInterested(ctx context.Context, viewer *core.Viewer, _ core.Device, q frontend.PageQuery) (frontend.NotInterestedPage, error)
- func (s *Service) NotInterestedItem(ctx context.Context, viewer *core.Viewer, _ core.Device, key core.RatingKey) (frontend.NotInterestedEntry, error)
- func (s *Service) PageArtworkTitles(ctx context.Context, viewer *core.Viewer, keys []core.RatingKey) ([]core.Title, error)
- func (s *Service) People(ctx context.Context, viewer *core.Viewer, device core.Device, ...) (result []frontend.PersonDetail, err error)
- func (s *Service) Person(ctx context.Context, viewer *core.Viewer, device core.Device, id string, ...) (result frontend.PersonDetail, err error)
- func (s *Service) PersonCredit(ctx context.Context, viewer *core.Viewer, device core.Device, ...) (result frontend.PersonDetail, err error)
- func (s *Service) PersonFacetIDs(ctx context.Context, viewer *core.Viewer, _ core.Device, key core.RatingKey, ...) (map[int64]string, error)
- func (s *Service) Playlists(ctx context.Context, viewer *core.Viewer, q frontend.PageQuery) (frontend.PlaylistPage, error)
- func (s *Service) RemoveFromContinue(ctx context.Context, v *core.Viewer, k core.RatingKey) error
- func (s *Service) RemovePlaylistItem(ctx context.Context, viewer *core.Viewer, key core.RatingKey, index int) error
- func (s *Service) ResolveEventSubject(ctx context.Context, viewer *core.Viewer, _ core.Device, ...) (frontend.ResolvedEventSubject, error)
- func (s *Service) ResolveExternal(ctx context.Context, viewer *core.Viewer, _ core.Device, ...) (core.RatingKey, error)
- func (s *Service) ResolveFacet(ctx context.Context, viewer *core.Viewer, device core.Device, ...) (core.FacetValue, error)
- func (s *Service) Resume(ctx context.Context, viewer *core.Viewer, _ core.Device, viewID string, ...) (frontend.ActivityPage, error)
- func (s *Service) Rows(ctx context.Context, req frontend.RowRequest) ([]frontend.Row, error)
- func (s *Service) Search(ctx context.Context, viewer *core.Viewer, device core.Device, query string, ...) (frontend.SearchPage, error)
- func (s *Service) SetEntityFavorite(ctx context.Context, viewer *core.Viewer, ref frontend.FavoriteRef, value bool) error
- func (s *Service) SetFavorite(ctx context.Context, v *core.Viewer, k core.RatingKey, on bool) error
- func (s *Service) SetLikes(ctx context.Context, viewer *core.Viewer, key core.RatingKey, likes *bool) error
- func (s *Service) SetNotInterested(ctx context.Context, v *core.Viewer, k core.RatingKey, on bool) error
- func (s *Service) SetPlayed(ctx context.Context, v *core.Viewer, k core.RatingKey) error
- func (s *Service) SetRating(ctx context.Context, v *core.Viewer, k core.RatingKey, rating float64) error
- func (s *Service) SetUnplayed(ctx context.Context, v *core.Viewer, k core.RatingKey) error
- func (s *Service) SetWatchlist(ctx context.Context, v *core.Viewer, k core.RatingKey, on bool) error
- func (s *Service) Similar(ctx context.Context, viewer *core.Viewer, device core.Device, ...) (frontend.SearchPage, error)
- func (s *Service) SnapshotRows(ctx context.Context, req frontend.RowRequest) ([]frontend.Row, error)
- func (s *Service) StoredItem(ctx context.Context, req frontend.ItemRequest) (frontend.ItemDetail, error)
- func (s *Service) UnplayedEpisodeCounts(ctx context.Context, viewer *core.Viewer, keys []core.RatingKey) (map[core.RatingKey]int, error)
- func (s *Service) UserData(ctx context.Context, viewer *core.Viewer, keys []core.RatingKey) (map[core.RatingKey]core.WatchStatus, error)
- func (s *Service) Views(ctx context.Context, viewer *core.Viewer) ([]frontend.View, error)
- func (s *Service) WatchlistItem(ctx context.Context, viewer *core.Viewer, _ core.Device, key core.RatingKey) (frontend.Membership, error)
- func (s *Service) WithPersonCredit(ctx context.Context, viewer *core.Viewer, device core.Device, ...) error
- type ServiceDeps
- type StoredEpisodeDeps
- type TrackPreferenceSource
- type TrackPreferences
- type TrackSelection
- type UnsupportedPlexTrackIdentityError
- type VersionObservationPort
- type VersionOffer
- type VibeSearch
- type VibeSource
- type VirtualPathCatalog
- type VirtualPathPage
- type VirtualPathView
- type Visibility
- func (v Visibility) Available(ctx context.Context, viewer *core.User, titles []core.Title) []core.Title
- func (v Visibility) CollectionAllowed(viewer *core.User, c *core.Collection) bool
- func (v Visibility) Playable(ctx context.Context, viewer *core.User, title *core.Title) bool
- func (v Visibility) TitleAllowed(viewer *core.User, title *core.Title) bool
- func (v Visibility) TitlesAllowed(viewer *core.User, titles []core.Title) []core.Title
- func (v Visibility) UpcomingItems(ctx context.Context, viewer *core.User, items []core.Title, ...) []core.Title
- func (v Visibility) Visible(ctx context.Context, viewer *core.User, title *core.Title) bool
Constants ¶
const PlaylistExpandLimit = 500
const ReceiptCapabilityNamespace = "experience.receipt"
ReceiptCapabilityNamespace is the shared header namespace for experience receipts. It is not the ticket namespace and it is not an authority.
const SeriesNextUpLimit = frontend.SeriesNextUpLimit
SeriesNextUpLimit is the indexed window for one series' next-up listing. The lookup is keyed by the series, never the library.
Variables ¶
var ErrReceipt = errors.New("experience: invalid interaction receipt")
var ErrRowViewNotFound = fmt.Errorf("experience: row view unavailable: %w", frontend.ErrNotFound)
var ErrRowViewStale = frontend.ErrRowViewStale
Functions ¶
func ArrangeHome ¶
func ArrangeHome(ctx context.Context, source core.Recommender, viewer *core.User, hc core.HubContext, section string, rows []core.Hub, candidates []core.NeutralHomeRow) ([]int, error)
ArrangeHome selects original producer indices. Providers use their declared layout contract directly; rendering sidecars never enter a neutral layout.
func ArrangeLayout ¶
func ArrangeLayout(candidates []LayoutCandidate, finalize func([]LayoutCandidate) ([]string, error)) ([]int, error)
ArrangeLayout returns indices into candidates in the engine's final order. Duplicate identifiers retain the last candidate, matching existing layout rendering. Unknown identifiers are ignored.
func ArrangeNeutralHome ¶
func ArrangeNeutralHome(ctx context.Context, source core.NeutralHomeLayoutSource, viewer *core.User, situation core.HomeSituation, section string, rows []core.NeutralHomeRow) ([]int, error)
ArrangeNeutralHome returns candidate indices in the shared layout's order. Indices point at the caller's original sidecars; duplicate producer IDs use the last sidecar, matching the established Home layout behavior.
func AssembleHome ¶
func AssembleHome(candidates []HomeCandidate, includeContinue, promotedOnly bool, continueID string, renderContinue func(), render func(HomeCandidate) string, accept func(HomeCandidate))
AssembleHome places the continue row at its ranked position and deduplicates rendered identifiers. The renderer returns the stable identifier it actually used; this can differ from the producer's candidate identifier.
func AssemblePostPlay ¶
func AssemblePostPlay[P any](ctx context.Context, selection PostPlaySelection, viewer *core.User, key core.RatingKey, count int, next func() (core.RatingKey, P, bool), picks func(PostPlayRow) (P, bool), render func(core.Hub) P) ([]P, error)
AssemblePostPlay retains next-up, personal picks and related row ordering. Rendering runs at each original boundary, before fetching the next source.
func CandidateObservationSource ¶
CandidateObservationSource names a genuine candidate release without exposing its raw identity. Empty, whitespace-only or unbounded IDs cannot identify a stable offer and have no observation timestamp.
func CandidatePurpose ¶
func CandidatePurpose(h core.Hub) core.RowPurpose
func CandidateRows ¶
func CandidateRows(candidates []RankedCandidate) []frontend.Row
CandidateRows projects already-authorized neutral selection data; source-specific Context is deliberately absent from the candidate contract.
func CanonicalTMDBPerson ¶
CanonicalTMDBPerson accepts only a "tmdb:" prefix plus the canonical base-10 form of a positive int64. Leading zeros, signs, and surrounding space fail.
func ColdHomeRows ¶
func ColdHomeRows(ctx context.Context, viewer *core.User, libraries []Library, visibility Visibility, limit int, excluded []core.RatingKey, browse func(context.Context, core.BrowseQuery) (core.Page, error)) ([]frontend.Row, error)
ColdHomeRows fills a neutral Home that has no ranked shelves left after policy filtering. Plex does not call it. Ordering follows date added: a movie or episode uses its own release or air date, and a series or season uses its latest aired episode. The page defaults to 50 and never exceeds 100.
func CompleteHome ¶
func CompleteHome(includePlaylists bool, playlists, discovery, live, selectRows, layout func())
CompleteHome orders the remaining home producers and finalizes the response. The adapter owns rendered metadata and only exposes these operations here.
func DetailCanPlay ¶
func DetailCanPlay(ctx context.Context, req frontend.ItemRequest, protocol core.Protocol, capabilities frontend.Capabilities, broker EstimatePlanner) *bool
DetailCanPlay asks the broker about authorization without preparing a source. A missing trusted peer or an indeterminate broker failure stays unknown.
func DetailExtras ¶
func DetailExtras(ctx context.Context, source ExtrasSource, parent *core.Title, language string, wanted map[int]bool) ([]extras.Extra, error)
DetailExtras selects enabled clips in source order. Numeric dialect filters are parsed by the adapter; an empty selection retains the complete list.
func DiscoveryAvailabilityRow ¶
Apply source suitability to discovery, including mixed Home shelves. Personal saved lists and playback state must never disappear because sources change.
func DiscoverySearch ¶
func DiscoverySearch(source *discovery.Features) func(context.Context, *core.User, string, int) ([]core.Title, bool)
DiscoverySearch uses natural search only when enabled and successful.
func DownloadsAllowed ¶
func DownloadsAllowed(viewer *core.User, ready, allowFriends bool, policy AccessPolicy) bool
DownloadsAllowed is shared capability admission; handlers still enforce it.
func FavoriteAllowed ¶
func FavoriteAllowed(ctx context.Context, viewer *core.User, subject FavoriteSubject, visibility Visibility, sources FavoriteSources) bool
FavoriteAllowed decides whether the viewer may favorite this subject from current dependencies. Callers still validate the active viewer. Person credits use the supplied Visibility, including whatever title lookup it has.
func FavoriteTitlesCached ¶
func FavoriteTitlesCached(ctx context.Context, viewer *core.User, keys []core.RatingKey, visibility Visibility, cached func(context.Context, []core.RatingKey) (map[core.RatingKey]core.Title, error)) (bool, error)
FavoriteTitlesCached rechecks title policy using only the injected local catalogue read. It does not call FavoriteSources and replaces Visibility's title lookup for this call so a parent read cannot leave the cache. A cache error is returned and the decision is false, so the caller fails closed. A missing title or a hidden or missing parent is a deny without an error.
func HomePurpose ¶
func HomePurpose(h core.Hub) core.RowPurpose
homePurpose translates producer identifiers at the Plex boundary; no wire context or hub identifier is included in the neutral candidate.
func HubRows ¶
HubRows converts trusted recommender candidates to neutral rows; wire contexts stay on the producer side. Authorization and pagination are performed by experience.RowsService before returning these candidates.
func IndexVirtualPaths ¶
func IndexVirtualPaths(ctx context.Context, catalog VirtualPathCatalog, viewer *core.Viewer, device core.Device) (map[core.RatingKey]string, error)
IndexVirtualPaths walks each authorized library once and returns every path. Callers cache the map and look up pages in it.
func ItemMarkers ¶
func ItemMarkers(ctx context.Context, source MarkerSource, key core.RatingKey, info core.MediaInfo) []frontend.Marker
ItemMarkers converts validated marker ranges from known media into the neutral detail representation. Estimates do not identify a release timeline.
func LayoutRows ¶
func LayoutRows(ctx context.Context, source core.NeutralHomeLayoutSource, libraries []Library, req frontend.RowRequest, sit core.HomeSituation, rows []frontend.Row) ([]frontend.Row, error)
LayoutRows applies shared Home placement to neutral frontend rows.
func LibrarySection ¶
LibrarySection maps a title or episode to the configured library of that kind. Seasons and episodes belong to the show library. Unknown kinds use the movie library.
func LiveEntries ¶
func LiveEntries(ctx context.Context, viewer *core.User, source LiveGuideSource, now, until time.Time, image func(livetv.Channel) string) ([]frontend.RowEntry, error)
LiveEntries projects allowed channels and their current and next programme. The caller supplies the guide horizon; stream and provider URLs never leave this projection. Image is an optional server-controlled logo resolver.
func LoadSeriesNextUp ¶
func LoadSeriesNextUp(ctx context.Context, series core.RatingKey, resumable bool, children func(context.Context, core.RatingKey, frontend.ItemQuery) (core.Page, error), userData func(context.Context, []core.RatingKey) (map[core.RatingKey]core.WatchStatus, error)) (*core.Title, error)
LoadSeriesNextUp loads one series' episodes through Children and selects the next episode. The children callback is invoked only with that series key.
func MarkerMedia ¶
func MarkerMedia(viewer *core.User, device core.Device, key core.RatingKey, estimate core.MediaInfo, lookup func(int64, string, core.RatingKey) (*core.PlaySession, bool)) core.MediaInfo
MarkerMedia selects an existing source only for the authenticated viewer, device and title. A missing device never selects a session.
func MaySetViewerRating ¶
MaySetViewerRating is the owner-console rule for changing a household rating cap. Delegated sessions and non-owners cannot write it.
func MergeShareFilter ¶
MergeShareFilter narrows filter by a plex.tv share predicate. Allow lists intersect, so a client's own contentRating filter can only narrow a share; deny lists and collection names add; rating bounds tighten. none reports that the two allow lists have no rating in common.
func ObserveVersionOffers ¶
func ObserveVersionOffers(ctx context.Context, port VersionObservationPort, parent core.RatingKey, offers []VersionOffer, slotCount int, observed time.Time) ([]*time.Time, error)
ObserveVersionOffers attaches persisted first-observation times for offers paired by index with slotCount result slots. Offers past slotCount are not observed. An empty observable set does not call the port. A port error returns no timestamps. Returned times are copies. Sources sent to the port keep offer order and contain only candidate hashes, keyed with the parent.
func PlaylistDuration ¶
PlaylistDuration uses cached runtimes and visits every member so a cache can schedule missing titles. The total stays unknown until every runtime is known.
func PlaylistEntries ¶
func PlaylistEntries(ctx context.Context, viewer *core.User, source PlaylistRowSource, limit int) ([]frontend.RowEntry, bool, error)
PlaylistEntries returns newest playlists first. Cross-viewer records are discarded even if a source accidentally returns them.
func PreviewAvailability ¶
func PreviewAvailability(ctx context.Context, u *core.User, limit int, load func(context.Context, int) []core.Hub, filter func(context.Context, *core.User, []core.Title) ([]core.Title, error)) []core.Hub
PreviewAvailability filters suggested titles before the preview's row cap. A viewer with no exclusions retains the source's exact first response.
func PrioritizeFavoriteShows ¶
PrioritizeFavoriteShows stably promotes episodes of favorite shows without changing order within groups.
func PruneReceiptShard ¶
PruneReceiptShard retires at most HeaderBatchMax expired receipts, including abandoned devices. The header DELETE trigger retires typed payloads and claims atomically. It uses the same shard lock as issuance before touching either table; live receipts are never evicted to recover capacity.
func RecentPlaylists ¶
func RecentPlaylists(ctx context.Context, viewer *core.User, source PlaylistRowSource, limit int) ([]core.Playlist, bool, error)
RecentPlaylists preserves source records for adapters that render item counts.
func RecentlyAdded ¶
func RecentlyAdded(ctx context.Context, viewer *core.User, kind core.MediaType, count int, visibility Visibility, browse func(context.Context, core.BrowseQuery) (core.Page, error)) ([]core.Title, bool, error)
RecentlyAdded loads the catalogue candidates for a library in added order.
func RecentlyViewed ¶
func RecentlyViewed(ctx context.Context, viewer *core.User, libraryID int, visibility Visibility, history func(context.Context, int64, time.Time, int) ([]core.HistoryEntry, error), titles func(context.Context, []core.RatingKey) []core.Title, libraryFor func(core.MediaType) (int, bool)) ([]core.Title, error)
RecentlyViewed loads unique history entries in their most recent order.
func ResolveAudio ¶
ResolveAudio maps an issued audio identity onto a position in the current MediaInfo.Streams. Callers needing an encoder index must translate that position through the selected StreamInfo.Index. The caller must supply a non-nil issued stream identity.
func ResolveSubtitle ¶
ResolveSubtitle maps a stored choice onto a stream position, or -1. The choice is language, codec, and source (sidecar URL, or embedded versus external). A matching track at a new index beats whatever now sits in the old slot: variant lists reorder, and that slot is not the track the viewer picked.
func RowEntryIdentity ¶
RowEntryIdentity returns the stable neutral identity of a well-formed entry.
func RowViewCollectionMembers ¶
func RowViewCollectionMembers(ctx context.Context, source RowViewCollectionSource, user *core.User, collection core.RatingKey, refs []core.NeutralEntryRef) bool
RowViewCollectionMembers adapts one call's collection source into neutral member keys. A nil source stays a nil port and fails closed.
func RowViewHydrateEntries ¶
func RowViewHydrateEntries(ctx context.Context, viewer *core.User, refs []core.NeutralEntryRef, one RowViewEntry, batch RowViewTitleBatch, read RowViewTitleRead, visibility Visibility, guide RowViewGuide) ([]frontend.RowEntry, error)
RowViewHydrateEntries rehydrates titles and inherited parents for this pass only. A title or parent batch error falls back to RowViewHydrateLegacy for the entire original ref list; the guide memo from this call survives that fallback and is not shared with any other viewer, device or pass. A home-release read failure is not-found and does not fall back. Non-title and virtual-season entries use the legacy port. Batch-built title entries leave Handle empty. Visibility is copied per chunk so a parent read cannot leak into the caller's policy or a later chunk.
func RowViewHydrateLegacy ¶
func RowViewHydrateLegacy(ctx context.Context, viewer *core.User, refs []core.NeutralEntryRef, one RowViewEntry, guide RowViewGuide) ([]frontend.RowEntry, error)
RowViewHydrateLegacy resolves mixed entries and loads the live guide at most once in this call. A later call loads the guide again. Source errors are returned unchanged, with a nil entry slice. An empty ref list returns a non-nil empty slice.
func RowViewMembers ¶
func RowViewMembers(ctx context.Context, items RowViewCollectionItems, viewer *core.User, collection core.RatingKey, refs []core.NeutralEntryRef) bool
RowViewMembers reports whether every title ref is still a member of the viewer-visible collection. The scan is bounded and fails closed when membership cannot be established. It keeps no result for a later pass.
func RowViewResolveBatchTitles ¶
func RowViewResolveBatchTitles(ctx context.Context, viewer *core.User, device core.Device, protocol core.Protocol, refs []core.NeutralEntryRef, one func(context.Context, *core.User, core.Device, core.Protocol, core.NeutralEntryRef) (frontend.RowEntry, error), batch func(context.Context, []core.RatingKey) (map[core.RatingKey]core.Title, error), get func(context.Context, *core.User, core.RatingKey) (*core.Title, error), visibility Visibility, live func(context.Context, *core.User) ([]frontend.RowEntry, error)) ([]frontend.RowEntry, error)
RowViewResolveBatchTitles binds this call's viewer, device and protocol into the neutral title hydrator. Nothing in the adapter is reused for a later pass.
func RowViewRevision ¶
RowViewRevision reads shared preference and layout versions, so snapshots created by another node fail closed after a relevant change.
func SameDayUnavailable ¶
func SameDayUnavailable(ctx context.Context, title *core.Title, now time.Time, filter func(context.Context, []core.Title) []core.Title, source core.AvailabilitySource) bool
SameDayUnavailable preserves the release-evidence check used for next-up.
func SectionAllowed ¶
SectionAllowed applies a viewer's library restrictions to an existing section. An empty restriction list means all sections, as it does for an owner.
func SelectCinemaTrailers ¶
func SelectCinemaTrailers(feature core.RatingKey, candidates []CinemaCandidate, limit int) []frontend.Extra
SelectCinemaTrailers picks up to limit trailers of other unwatched movies. It checks at most twelve candidates, matching cinema-trailer selection: the feature itself, non-movies, and watched titles are skipped, and each movie contributes its first playable trailer or teaser.
func SeriesNextUp ¶
func SeriesNextUp(eps []core.Title, st map[core.RatingKey]core.WatchStatus, series core.RatingKey, resumable bool, unavailable func(*core.Title) bool) *core.Title
SeriesNextUp selects that series' next regular episode. Specials are skipped. When resumable, an in-progress episode wins; otherwise the first unwatched episode after the last completed one is returned, or S1E1 when none are watched. Episodes of any other series are ignored.
func SeriesNextUpQuery ¶
SeriesNextUpQuery is the bounded children request for one series' episodes.
func ShowOnDeck ¶
func ShowOnDeck(eps []core.Title, st map[core.RatingKey]core.WatchStatus, unavailable func(*core.Title) bool) *core.Title
ShowOnDeck selects the resumed or next regular episode in the viewer's order.
func TitleAllowed ¶
TitleAllowed applies the mandatory content guards and the owner's policy.
func UniqueRelated ¶
func UniqueRelated(ctx context.Context, viewer *core.User, visibility Visibility, items []core.Title, seen map[core.RatingKey]bool, limit int) []core.Title
UniqueRelated excludes previously served titles, recording all newly seen keys in row order so later rows cannot repeat them.
func ViewerRestricted ¶
func ViewerRestricted(policy AccessPolicy, u *core.User) bool
ViewerRestricted chooses inherited visibility for limited or unknown policies.
func VirtualPath ¶
func VirtualPath(ctx context.Context, catalog VirtualPathCatalog, viewer *core.Viewer, device core.Device, title core.Title) (string, error)
VirtualPath builds the relative path for one already identified title. The caller supplies the title; this function does not look up or enrich it. Sibling names still come from the authorized library and children. A title absent from that tree is not found. A port or page error returns no path.
func VirtualPaths ¶
func VirtualPaths(ctx context.Context, catalog VirtualPathCatalog, viewer *core.Viewer, device core.Device, keys []core.RatingKey) (map[core.RatingKey]string, error)
VirtualPaths resolves at most 50 distinct keys from at most 256 inputs. Hidden or unsupported keys are omitted. One requested set shares each library scan. A malformed or truncated page, or any port error, returns no paths.
Types ¶
type AccessPolicy ¶
AccessPolicy adds owner-defined restrictions to the mandatory title guard.
type AdditionalRows ¶
type AdditionalRows struct {
Playlists PlaylistRowSource
Watchlist DiscoveryRowSource
Personal DiscoveryRowSource
Live LiveGuideSource
Visibility Visibility
Restricted func(*core.User) bool
Now func() time.Time
}
AdditionalRows composes viewer-owned playlists, discovery and live guide rows. Sources are invoked in that order; a failure stops subsequent sources.
func (AdditionalRows) Rows ¶
func (a AdditionalRows) Rows(ctx context.Context, req frontend.RowRequest) ([]frontend.Row, error)
type CandidateComposer ¶
type CandidateComposer struct {
Ranker CandidateRanker
Resume ResumeCandidates
Libraries []Library
Visibility Visibility
Restricted func(*core.User) bool
}
CandidateComposer selects one placement and incorporates continuation before the row service authorizes entries and issues interaction receipts.
func (CandidateComposer) Rows ¶
func (c CandidateComposer) Rows(ctx context.Context, req frontend.RowRequest, sit core.HomeSituation) ([]RankedCandidate, error)
type CandidateRanker ¶
type CandidateRanker interface {
Home(context.Context, *core.User, core.HomeSituation, int, func(context.Context, *core.User, *core.Title) bool) ([]RankedCandidate, error)
Library(context.Context, *core.User, core.MediaType, core.HomeSituation, int, func(context.Context, *core.User, *core.Title) bool) ([]RankedCandidate, error)
}
CandidateRanker assembles one ranked pool for a visit. A non-nil predicate must be applied to the generated pools before ranking and limiting.
type CandidateRowSource ¶
type CandidateRowSource struct {
Load func(context.Context, frontend.RowRequest) ([]RankedCandidate, error)
Additional func(context.Context, frontend.RowRequest) ([]frontend.Row, error)
Kind func(string) string
}
func (CandidateRowSource) Rows ¶
func (s CandidateRowSource) Rows(ctx context.Context, req frontend.RowRequest) ([]frontend.Row, error)
type CinemaCandidate ¶
CinemaCandidate is one movie already authorized for the viewer.
type Continue ¶
type Continue struct {
Filter func(context.Context, *core.User, []core.Title) []core.Title
State core.State
EpisodeOrder interface {
Grouped(context.Context, *core.User, []core.RatingKey) (map[core.RatingKey]bool, error)
}
Now func() time.Time
Grandchildren func(context.Context, core.RatingKey) ([]core.Title, error)
NextEpisode func(context.Context, core.RatingKey) (*core.Title, error)
Visible func(context.Context, *core.User, *core.Title) bool
GetTitles func(context.Context, []core.RatingKey) []core.Title
SectionAllowed func(*core.User, core.MediaType) bool
OnError func(error)
FavoriteShows func(context.Context, *core.User) (map[core.RatingKey]bool, error)
// Unrestricted is a trusted policy capability; nil fails closed. It is
// supplied only when the catalogue and policy authorize the legacy path.
Unrestricted func(*core.User) bool
}
Continue coordinates resume and next-episode selection using viewer-scoped dependencies.
func (Continue) InProgress ¶
type DetailRelated ¶
type DetailRelated struct {
Source core.Recommender
Visibility Visibility
Get func(context.Context, core.RatingKey) (*core.Title, error)
}
DetailRelated owns source authorization and title selection for detail shelves. Parent and availability checks during rendering retain their established dialect semantics; postplay uses its explicit selection policy.
func (DetailRelated) SourceVisible ¶
type DiscoveryCandidate ¶
type DiscoveryCandidate struct {
Index int
Purpose core.RowPurpose
Items []core.Title
}
DiscoveryCandidate contains a source row's neutral purpose and item candidates.
func FilterDiscovery ¶
func FilterDiscovery(ctx context.Context, viewer *core.User, visibility Visibility, rows []DiscoveryCandidate) []DiscoveryCandidate
FilterDiscovery applies viewer visibility to each row before it is returned.
func LoadDiscovery ¶
func LoadDiscovery(ctx context.Context, viewer *core.User, visibility Visibility, limit int, load func(context.Context, int) []DiscoveryCandidate) []DiscoveryCandidate
LoadDiscovery expands full candidate pages when visibility filtering leaves a row short. Sources with fewer results than requested signal exhaustion.
type DiscoveryRowSource ¶
type DiscoveryRowSource interface {
Rows(context.Context, *core.User, int) ([]RankedCandidate, error)
}
DiscoveryRowSource returns context-free row candidates for one viewer.
type DiscoverySelection ¶
type DiscoverySelection struct {
Visibility Visibility
Unrestricted func(*core.User) bool
}
DiscoverySelection chooses visible candidates before frontend rendering. Unrestricted is a trusted host policy capability; nil fails closed.
func (DiscoverySelection) Load ¶
func (s DiscoverySelection) Load(ctx context.Context, viewer *core.User, limit int, load func(context.Context, int) []DiscoveryCandidate) []DiscoveryCandidate
type EstimatePlanner ¶
type EstimatePlanner interface {
Plan(context.Context, frontend.PlanRequest) (frontend.PlayPlan, error)
}
EstimatePlanner answers whether a viewer may play an item without opening a source.
type ExtrasSource ¶
type FavoriteKind ¶
type FavoriteKind string
FavoriteKind is the neutral subject class for a favorite decision.
const ( FavoriteKindTitle FavoriteKind = "title" FavoriteKindPerson FavoriteKind = "person" FavoriteKindCollection FavoriteKind = "collection" FavoriteKindChannel FavoriteKind = "channel" )
type FavoriteSources ¶
type FavoriteSources struct {
Title func(context.Context, core.RatingKey) (*core.Title, error)
Collection func(context.Context, *core.User, core.RatingKey) (*core.Collection, error)
// ChannelAllowed checks one channel. It is the live-TV path; title,
// person, and collection decisions must not call it.
ChannelAllowed func(context.Context, *core.User, string) (bool, error)
// ChannelIDs lists every allowed channel. ChannelAllowed replaces it.
ChannelIDs func(context.Context, *core.User) ([]string, error)
PersonPage func(context.Context, int64, int, int) ([]core.Title, error)
}
FavoriteSources are the current-policy lookups. A nil function is a missing dependency and fails closed. These functions are not used by the cached path.
type FavoriteSubject ¶
type FavoriteSubject struct {
Kind FavoriteKind
ID string
}
FavoriteSubject is a favorite identity with no protocol or storage attached.
type FirstObservedLedger ¶
type FirstObservedLedger struct {
// contains filtered or unexported fields
}
FirstObservedLedger records genuine offer-observation times in the shared database. It neither discovers nor resolves releases. Source identities are hashed at rest; callers must supply a stable, source-qualified identity.
func NewFirstObservedLedger ¶
func NewFirstObservedLedger(db *sql.DB) (*FirstObservedLedger, error)
func (*FirstObservedLedger) FirstObservedAt ¶
func (l *FirstObservedLedger) FirstObservedAt(ctx context.Context, parent core.RatingKey, source string) (*time.Time, error)
FirstObservedAt returns nil when no genuine observation has been recorded.
func (*FirstObservedLedger) Observe ¶
func (l *FirstObservedLedger) Observe(ctx context.Context, parent core.RatingKey, source string, observed time.Time) (time.Time, error)
Observe records the first committed genuine observation atomically. The caller must supply the time the offer was actually observed, not its catalogue date, publication date or a time invented for an unknown offer.
func (*FirstObservedLedger) ObserveBatch ¶
func (l *FirstObservedLedger) ObserveBatch(ctx context.Context, parent core.RatingKey, sources []string, observed time.Time) (map[string]time.Time, error)
ObserveBatch records a bounded group of source-qualified offers in one transaction. A failure rolls back the entire group. Empty input does not write or infer an observation.
type HomeAssembly ¶
type HomeAssembly[P any] struct { Render func(core.Hub) P Identity func(P) string Report func(P) (core.Hub, core.NeutralHomeRow) Continue func() (core.Hub, P) Playlists func() (P, bool) Discovery func() []core.Hub Live func() (P, bool) InLibraries func(core.Hub) bool Keep func(P) bool Layout func([]P, []core.Hub, []core.NeutralHomeRow) []P }
HomeAssembly owns row inclusion, deduplication and ordering while adapters supply rendered projections and preserve their exact producer sidecars. Report records layout facts at paint time. Layout receives those facts and must not read the wire value again.
type HomeCandidate ¶
type HomeCandidate struct {
Index int
Purpose core.RowPurpose
Promoted bool
InLibraries bool
}
HomeCandidate carries neutral row placement information. Index identifies the producer's candidate without exposing its wire representation.
type HomeSelection ¶
type HomeSelection struct {
Source core.Recommender
Visibility Visibility
Restricted bool
}
HomeSelection obtains ranked shelves from the configured source. Policy-aware sources filter before ranking; finite custom sources are checked afterwards.
type LayoutCandidate ¶
type LayoutCandidate struct {
ID string
Title string
Type core.MediaType
Purpose core.RowPurpose
ItemCount int
}
LayoutCandidate is a row available to a layout engine. ItemCount includes non-title entries such as live channels or playlists.
type Library ¶
type Library struct {
ID int
Name string
Type core.MediaType
// Virtual is true only when the host asserts this library has no physical
// locations. False means the host did not assert virtual origin.
Virtual bool
}
Library is a host-configured, protocol-neutral library.
type LibraryPageExclusions ¶
type LibraryPageExclusions struct {
Keys []core.RatingKey
ExcludeGenres, ExcludeKeywords []string
Movie, Television core.Filter
}
LibraryPageExclusions is what an indexed library page can push into SQL.
type LiveGuideSource ¶
type LiveGuideSource interface {
AllowedChannels(context.Context, *core.User) ([]livetv.Channel, error)
Guide(context.Context, *core.User, time.Time, time.Time) ([]livetv.Programme, error)
}
LiveGuideSource must enforce channel and programme access for the viewer.
type MarkerSource ¶
type MarkerSource interface {
CachedMarkers(context.Context, core.RatingKey, core.MediaInfo) ([]markers.Marker, error)
}
MarkerSource finds locally cached ranges in an already-probed release.
type PeopleRead ¶
type PeopleRead struct {
Viewer *core.User
SectionFor func(core.MediaType) (int, bool)
Policy AccessPolicy
FilterAvailable func(context.Context, []core.Title) []core.Title
Titles func(context.Context, []core.RatingKey) (map[core.RatingKey]core.Title, error)
Finish func(context.Context) error
Close func()
}
PeopleRead owns request-local policy and title facts. Finish must reject any intervening durable policy change; Close releases the host policy lease. Neither callback may publish partial results on failure.
type PersonCandidate ¶
PersonCandidate is a credited person whose name a people search matched.
type PlaylistRowSource ¶
PlaylistRowSource is scoped to a viewer's own playlists.
type PlaylistSelection ¶
type PlaylistSelection struct {
Playlists core.Playlists
Collections core.Collections
Visibility Visibility
Children func(context.Context, core.RatingKey) ([]core.Title, error)
Grandchildren func(context.Context, core.RatingKey) ([]core.Title, error)
}
PlaylistSelection expands containers through viewer-bound metadata sources. URI parsing and playlist presentation belong to the requesting adapter.
type PlexIssuedIdentity ¶
type PlexIssuedIdentity struct {
Audio *core.StreamInfo
AudioPosition int
AudioSet bool
Subtitle *core.StreamInfo
SubtitlePosition int
SubtitleSet bool
}
PlexIssuedIdentity is the selection resolvePlaySelection already holds after the Plex adapter has accepted a stream ID from the item-scoped issued registry. Positions are indexes into the full MediaInfo.Streams slice from that issuance. They are not StreamInfo.Index values and they are not audio-only ordinals.
TranslatePlexTrackIdentity does not look up stream IDs, does not see the item, and does not authorize a caller-built StreamInfo. Copy Audio and Subtitle only from a registry reference whose item is the one being played.
AudioSet must equal AudioPosition >= 0, which is how the play resolver derives audio set-ness. Pass AudioPosition -1 and AudioSet false when the client omitted audio. The zero value is not an omission.
SubtitleSet false is omission. SubtitleSet true with a nil subtitle and position -1 is explicit subtitle-off. A nil subtitle with SubtitleSet true and any other position, including an out-of-range or wrong-type slot, is a legacy position-only choice and is preserved.
The translator does not retain either pointer and does not pin a source.
type PostPlayRow ¶
type PostPlayRow struct {
Purpose core.RowPurpose
Items []core.Title
}
PostPlayRow is a viewer-scoped set of ranked recommendations. The presentation and protocol-specific identifiers belong to the frontend.
func LoadPostPlay ¶
func LoadPostPlay(ctx context.Context, viewer *core.User, visibility Visibility, count int, seen map[core.RatingKey]bool, load func(context.Context, int) []core.PostPlayPick) PostPlayRow
LoadPostPlay expands the candidate prefix when policy or deduplication leaves the requested row short. The source signals exhaustion by returning fewer candidates than requested. Work is capped at 1000 candidates.
func NextUp ¶
func NextUp(ctx context.Context, viewer *core.User, key core.RatingKey, next func(context.Context, core.RatingKey) (*core.Title, error), visibility Visibility) PostPlayRow
NextUp selects a permitted successor to an episode. Missing successors and lookup failures produce no row, matching the optional nature of next-up.
func PostPlay ¶
func PostPlay(ctx context.Context, picks []core.PostPlayPick, viewer *core.User, visibility Visibility, seen map[core.RatingKey]bool, limit int) PostPlayRow
PostPlay constructs a recommendation row in the supplied rank order.
type PostPlaySelection ¶
type PostPlaySelection struct {
Visibility Visibility
Unrestricted bool
Picks func(context.Context, int) []core.PostPlayPick
Related func(context.Context, int) ([]core.Hub, error)
}
PostPlaySelection preserves the established owner selection semantics while restricted viewers use inherited visibility before deduplication and limits. Unrestricted is a trusted host policy decision, never a request option.
func (PostPlaySelection) RelatedRows ¶
type PreviewRows ¶
type PreviewRows struct {
Availability func(context.Context, int) []core.Hub
Filter func(context.Context, *core.User, []core.Title) ([]core.Title, error)
Watchlist func(context.Context, *core.User, int) ([]core.Hub, error)
Personal func(context.Context, *core.User, int) ([]core.Hub, error)
Live func(context.Context, *core.User, int) (core.Hub, error)
LiveEnabled func() bool
}
PreviewRows composes the owner preview using the same source ordering and section projection.
type RankedCandidate ¶
type RankedCandidate struct {
ID string
Purpose core.RowPurpose
Title string
Type core.MediaType
Style string
Promoted bool
Items []core.Title
More bool
Key core.RatingKey
ItemCount int
}
RankedCandidate carries protocol-neutral selection and presentation data.
func DetailRows ¶
func DetailRows(ctx context.Context, viewer *core.User, key core.RatingKey, limit int, visibility Visibility, next func(context.Context, core.RatingKey) (*core.Title, error), postPlay func(context.Context, int) []core.PostPlayPick, related func(context.Context, int) ([]RankedCandidate, error)) ([]RankedCandidate, error)
DetailRows composes policy-filtered next-up, post-play and related shelves. Related supplies neutral candidates; no renderer-specific context is exposed.
type ReceiptOptions ¶
type ReceiptOptions struct {
DB *sql.DB
Active func(context.Context, int64) (*core.User, error)
Allowed func(context.Context, *core.User, core.Device, core.Protocol, ReceiptSubject) bool
AllowedAction func(context.Context, *core.User, core.Device, core.Protocol, ReceiptSubject, frontend.RowInteractionKind) bool
Record func(context.Context, *core.User, core.Device, core.Protocol, ReceiptSubject, frontend.RowInteractionKind) error
Now func() time.Time
TTL time.Duration
MaxPerDevice int
PolicyLease func(context.Context) (func(), error)
// Generation, when set, is stored on the authority row and rechecked on
// report. Nil stores 0 and does not recheck, which is the mixed-version
// behavior for callers that have no generation.
Generation func(context.Context, *core.User, core.Device, core.Protocol) (int64, error)
// contains filtered or unexported fields
}
type ReceiptSubject ¶
type ReceiptSubject struct {
Kind, ID string
Purpose core.RowPurpose
Rank int
Impression *core.NeutralImpression
}
ReceiptSubject is server-owned neutral information, never supplied by a client report.
type Receipts ¶
type Receipts struct {
// contains filtered or unexported fields
}
func NewReceipts ¶
func NewReceipts(o ReceiptOptions) (*Receipts, error)
func (*Receipts) Issue ¶
func (s *Receipts) Issue(ctx context.Context, viewer *core.User, device core.Device, protocol core.Protocol, subject ReceiptSubject) (string, error)
Issue creates an opaque, surface-scoped receipt. A device identifier is required.
func (*Receipts) IssueBatch ¶
func (s *Receipts) IssueBatch(ctx context.Context, viewer *core.User, device core.Device, protocol core.Protocol, subjects []ReceiptSubject) ([]string, error)
IssueBatch atomically issues receipts and shared admission in input order.
func (*Receipts) Report ¶
Report rechecks membership and authorization. Duplicate actions are ignored across nodes. Recording is at-most-once: a sink failure is not retried.
func (*Receipts) ReportRowInteraction ¶
type Recent ¶
type Recent struct {
Visibility Visibility
Unrestricted func(*core.User) bool
}
Recent owns recent-list selection. Unrestricted is a trusted host policy capability; nil enforces inherited visibility and availability.
type RelatedArtworkDeps ¶
type RelatedArtworkDeps struct {
Authorize func(context.Context, *core.Viewer, core.Device, core.RatingKey) (*core.Viewer, error)
List func(context.Context, *core.Viewer, core.Device, core.RatingKey) ([]frontend.Image, bool, error)
}
RelatedArtworkDeps uses the same cache-only authorization and descriptor source as item artwork; it never resolves media or fetches catalogue data.
type RelatedArtworkService ¶
type RelatedArtworkService struct {
// contains filtered or unexported fields
}
func NewRelatedArtworkService ¶
func NewRelatedArtworkService(deps RelatedArtworkDeps) (*RelatedArtworkService, error)
func (*RelatedArtworkService) RelatedArtwork ¶
type RelatedCandidate ¶
RelatedCandidate contains protocol-neutral item candidates for one row. Index identifies the source row for its rendering adapter.
func LoadRelated ¶
func LoadRelated(ctx context.Context, viewer *core.User, visibility Visibility, count int, seen map[core.RatingKey]bool, load func(context.Context, int) ([]RelatedCandidate, error)) ([]RelatedCandidate, error)
LoadRelated filters before deduplication and limits, expanding candidate requests while a full source page leaves a row short.
type ResumeCandidates ¶
type ResumeCandidates interface {
Continue(context.Context, *core.User, int) ([]core.Title, error)
}
ResumeCandidates supplies viewer-scoped continuation titles.
type RowEntryPolicy ¶
type RowEntryPolicy struct {
Visibility Visibility
Favorite func(context.Context, *core.User, FavoriteSubject) bool
Playlist func(context.Context, *core.User, core.RatingKey) bool
}
RowEntryPolicy shares admission between rows and persisted row interactions.
type RowInteractionPolicy ¶
type RowInteractionPolicy struct {
Libraries []Library
Visibility Visibility
Get func(context.Context, core.RatingKey) (*core.Title, error)
AllowEntry func(context.Context, *core.User, string, string) bool
CollectionMembers func(context.Context, *core.User, core.RatingKey, []core.NeutralEntryRef) bool
Impression func(*core.User, core.NeutralImpression)
DetailOpen func(context.Context, *core.User, *core.Title)
}
RowInteractionPolicy rechecks persisted impressions against current scope before recording personalization events.
func (RowInteractionPolicy) Allowed ¶
func (p RowInteractionPolicy) Allowed(ctx context.Context, u *core.User, subject ReceiptSubject) bool
func (RowInteractionPolicy) Record ¶
func (p RowInteractionPolicy) Record(ctx context.Context, u *core.User, subject ReceiptSubject, kind frontend.RowInteractionKind) error
type RowSelection ¶
type RowSelection struct {
Visibility Visibility
PrioritizeAvailable func(context.Context, []core.Title) []core.Title
}
RowSelection applies product policy before wire rendering, preserving source order.
type RowServiceDeps ¶
type RowServiceDeps struct {
Source RowSource
Visibility Visibility
Active func(context.Context, *core.User) bool
AllowRow func(context.Context, *core.User, frontend.Row) bool
AllowEntry func(context.Context, *core.User, frontend.RowEntry) bool
Layout func(context.Context, frontend.RowRequest, []frontend.Row) ([]frontend.Row, error)
Receipts *Receipts
Situation func(context.Context, frontend.RowRequest) (core.HomeSituation, error)
// HomeFallback supplies bounded library rows when a neutral Home has no
// discovery shelves left after policy filtering. Plex does not use it.
HomeFallback func(context.Context, frontend.RowRequest) ([]frontend.Row, error)
// RankedSource disables retries when the source ranks policy-filtered candidates once.
RankedSource bool
}
RowServiceDeps are host-owned dependencies. AllowRow and AllowEntry must recheck current membership and visibility, including non-title entries.
type RowViewCollectionItems ¶
type RowViewCollectionItems func(ctx context.Context, viewer *core.User, collection core.RatingKey, offset, limit int) (RowViewCollectionPage, error)
RowViewCollectionItems loads one page of a viewer's collection. A nil function means membership cannot be established.
type RowViewCollectionPage ¶
RowViewCollectionPage is one viewer-scoped page of collection member keys. It carries no protocol or wire context.
type RowViewCollectionSource ¶
type RowViewEntry ¶
RowViewEntry resolves one entry the batch path does not own. Callers bind device and protocol outside this port. The returned entry is passed through, including a handle if the caller set one. This hydrator does not mint one.
type RowViewGuide ¶
RowViewGuide returns the viewer's current live guide for a single policy pass.
type RowViewOptions ¶
type RowViewOptions struct {
DB *sql.DB
TTL time.Duration
Now func() time.Time
Active func(context.Context, int64) (*core.User, error)
Build func(context.Context, frontend.RowViewRequest) ([]RowViewSnapshot, error)
// ValidRow checks current placement, collection and viewer preference scope,
// including the snapshot revision; it must fail closed on uncertainty.
ValidRow func(context.Context, *core.User, core.Device, core.Protocol, RowViewSnapshot) bool
ResolveEntry func(context.Context, *core.User, core.Device, core.Protocol, core.NeutralEntryRef) (frontend.RowEntry, error)
// ResolveEntries may batch rehydration; every call must perform fresh policy checks.
ResolveEntries func(context.Context, *core.User, core.Device, core.Protocol, []core.NeutralEntryRef) ([]frontend.RowEntry, error)
Receipts *Receipts
}
type RowViewService ¶
type RowViewService struct {
// contains filtered or unexported fields
}
func NewRowViewService ¶
func NewRowViewService(o RowViewOptions) (*RowViewService, error)
func NewRuntimeRowViews ¶
func NewRuntimeRowViews(d RuntimeRowViews) (*RowViewService, error)
func (*RowViewService) ListRowViews ¶
func (s *RowViewService) ListRowViews(ctx context.Context, req frontend.RowViewRequest) ([]frontend.RowView, error)
func (*RowViewService) ResolveRowView ¶
func (s *RowViewService) ResolveRowView(ctx context.Context, req frontend.RowViewPageRequest) (frontend.RowViewPage, error)
type RowViewSnapshot ¶
type RowViewSnapshot struct {
Row frontend.Row
Situation core.HomeSituation
Placement frontend.Placement
ViewID string
Item core.RatingKey
Entries []core.NeutralEntryRef
Truncated bool
Revision string
}
RowViewSnapshot is produced by the trusted row ranker after policy and layout. Entries are identities only; ResolveEntry must rehydrate them.
type RowViewTitleBatch ¶
type RowViewTitleBatch func(ctx context.Context, keys []core.RatingKey) (map[core.RatingKey]core.Title, error)
RowViewTitleBatch loads titles by key from the local catalogue.
type RowViewTitleRead ¶
RowViewTitleRead loads one title, including an existing home-release admission.
type RowsService ¶
type RowsService struct {
// contains filtered or unexported fields
}
RowsService produces viewer-scoped, policy-checked rows.
func NewRowsService ¶
func NewRowsService(d RowServiceDeps) (*RowsService, error)
func (*RowsService) Rows ¶
func (s *RowsService) Rows(ctx context.Context, req frontend.RowRequest, protocol core.Protocol) ([]frontend.Row, error)
Rows filters each tagged entry before limiting and never accepts client-made interaction identifiers. Candidate requests expand to a bounded prefix when filtering would otherwise leave a row short.
func (*RowsService) SnapshotRows ¶
func (s *RowsService) SnapshotRows(ctx context.Context, req frontend.RowRequest, protocol core.Protocol) ([]frontend.Row, error)
SnapshotRows builds and authorizes the same ranked rows without issuing impressions; a paged row view issues receipts only for delivered entries.
type RuntimeRowViews ¶
type RuntimeRowViews struct {
DB *sql.DB
CurrentAvailable func(context.Context, []core.Title) []core.Title
Protocol core.Protocol
Receipts *Receipts
Visibility Visibility
Libraries []Library
Active func(context.Context, int64) (*core.User, error)
CachedGet func(context.Context, core.RatingKey) (*core.Title, error)
CachedGetMany func(context.Context, []core.RatingKey) (map[core.RatingKey]core.Title, error)
CachedVirtualSeason func(context.Context, *core.User, core.RatingKey) (*core.Title, error)
Collections core.Collections
Playlists core.Playlists
Live LiveGuideSource
Situation func(frontend.RowRequest) core.HomeSituation
Snapshot func(context.Context, frontend.RowRequest, core.HomeSituation) ([]frontend.Row, error)
}
RuntimeRowViews binds persistence, snapshot construction and current policy revalidation inside Experience. The app supplies source capabilities only.
type ScopedCollection ¶
type ScopedCollection struct {
Collections core.Collections
Visibility Visibility
Now func() time.Time
// Unrestricted is a trusted host capability; nil enforces inherited policy.
Unrestricted func(*core.User) bool
}
ScopedCollection selects upcoming members only through an enabled, viewer-and-device-scoped collection capability.
func (ScopedCollection) Counts ¶
func (s ScopedCollection) Counts(ctx context.Context, viewer *core.User, collections []core.Collection, device string) []core.Collection
func (ScopedCollection) Get ¶
func (s ScopedCollection) Get(ctx context.Context, viewer *core.User, key core.RatingKey, device string, count bool) (*core.Collection, error)
Get authorizes a collection and optionally calculates its upcoming count.
func (ScopedCollection) Placeholder ¶
func (s ScopedCollection) Placeholder(ctx context.Context, viewer *core.User, key core.RatingKey, requested *core.RatingKey, device string) (*core.Title, core.RatingKey, error)
Placeholder resolves a collection-scoped future title and authorizes both the title and its containing library before returning the source record.
type Search ¶
type Search struct {
Visibility Visibility
Vibe VibeSearch
Natural func(context.Context, *core.User, string, int) ([]core.Title, bool)
Catalog func(context.Context, string, []core.MediaType, int) ([]core.Title, error)
// Unrestricted is a trusted host policy capability. Nil uses the
// restricted, inherited-policy and availability path.
Unrestricted func(*core.User) bool
}
Search resolves discovery-first keyword searches for the viewer's libraries. Natural is optional; false falls back to the catalogue.
func (Search) Find ¶
func (s Search) Find(ctx context.Context, viewer *core.User, query string, types []core.MediaType, limit int) ([]core.Title, error)
Find is an explicit search: catalogue rules the owner limited to browsing do not apply, so those titles can still be found.
func (Search) Keyword ¶
func (s Search) Keyword(ctx context.Context, viewer *core.User, query string, types []core.MediaType, limit int) ([]core.Title, error)
Keyword preserves the catalogue's candidate window and applies title policy. Section searches use this fixed window rather than natural search or refill.
type SearchResults ¶
type SearchResults struct {
Collection *core.Collection
Items []core.Title
CollectionSearch bool
}
SearchResults retains whether an optional collection source handled the query. A handled empty result must not fall back to keyword search.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
func NewService ¶
func NewService(d ServiceDeps) (*Service, error)
func (*Service) AddPlaylistItems ¶
func (*Service) Ancestors ¶
func (s *Service) Ancestors(ctx context.Context, viewer *core.Viewer, device core.Device, key core.RatingKey) ([]core.Title, error)
Ancestors returns only authorized parents, failing closed if a parent is hidden.
func (*Service) AuthorizeArtwork ¶
func (*Service) Collections ¶
func (s *Service) Collections(ctx context.Context, viewer *core.Viewer, _ core.Device, q frontend.CollectionQuery) (frontend.CollectionPage, error)
Collections filters member visibility before calculating totals and paging.
func (*Service) CreatePlaylist ¶
func (*Service) DeletePlaylist ¶
func (*Service) EntityFavorites ¶
func (*Service) Facets ¶
func (s *Service) Facets(ctx context.Context, viewer *core.Viewer, device core.Device, viewID, name string) ([]core.FacetValue, error)
Facets are computed only from titles the viewer may see.
func (*Service) FavoriteItem ¶
func (*Service) Item ¶
func (s *Service) Item(ctx context.Context, req frontend.ItemRequest) (frontend.ItemDetail, error)
func (*Service) LatestAdded ¶
func (s *Service) LatestAdded(ctx context.Context, viewer *core.Viewer, device core.Device, viewID string, limit int) ([]frontend.LatestItem, error)
LatestAdded returns library roots, with TV grouped as series.
func (*Service) LatestAddedGrouped ¶
func (*Service) LibraryCount ¶
func (s *Service) LibraryCount(ctx context.Context, viewer *core.Viewer, viewID string) (int, error)
LibraryCount is the indexed size of one library. It does not browse or hydrate titles. A viewer who cannot open the library gets zero.
func (*Service) LibraryItems ¶
func (s *Service) LibraryItems(ctx context.Context, viewer *core.Viewer, device core.Device, viewID string, q frontend.ItemQuery) (core.Page, error)
LibraryItems scans catalogue pages and filters before applying the requested page.
func (*Service) LocalEpisodes ¶
func (s *Service) LocalEpisodes(ctx context.Context, viewer *core.Viewer, key core.RatingKey) ([]core.Title, bool, error)
LocalEpisodes lists aired episodes already stored for a show or season. The bool is false when the catalogue page was capped, so a caller that needs every episode can omit a count instead of inventing one. It does not refresh the show or its seasons.
func (*Service) MovePlaylistItem ¶
func (*Service) NamedFacetIDs ¶
func (s *Service) NamedFacetIDs(ctx context.Context, viewer *core.Viewer, _ core.Device, key core.RatingKey, kind string, names []string) (map[string]string, error)
NamedFacetIDs maps exact credited genre or studio names on one visible title to the same identifier namespace as library facets.
func (*Service) NotInterested ¶
func (*Service) NotInterestedItem ¶
func (*Service) PageArtworkTitles ¶
func (s *Service) PageArtworkTitles(ctx context.Context, viewer *core.Viewer, keys []core.RatingKey) ([]core.Title, error)
PageArtworkTitles loads one grid and drops titles this viewer cannot see.
func (*Service) PersonCredit ¶
func (*Service) PersonFacetIDs ¶
func (s *Service) PersonFacetIDs(ctx context.Context, viewer *core.Viewer, _ core.Device, key core.RatingKey, ids []int64) (map[int64]string, error)
PersonFacetIDs maps requested credits on one currently visible title to the same identifiers used by library person facets. An episode-only credit may have no corresponding library-wide facet and therefore may not resolve via ResolveFacet for that library.
func (*Service) Playlists ¶
func (s *Service) Playlists(ctx context.Context, viewer *core.Viewer, q frontend.PageQuery) (frontend.PlaylistPage, error)
Playlists lists the viewer's own playlists. Membership is the titles this viewer is allowed to see.
func (*Service) RemoveFromContinue ¶
func (*Service) RemovePlaylistItem ¶
func (*Service) ResolveEventSubject ¶
func (s *Service) ResolveEventSubject(ctx context.Context, viewer *core.Viewer, _ core.Device, ref frontend.FavoriteRef) (frontend.ResolvedEventSubject, error)
ResolveEventSubject rechecks visibility on delivery rather than trusting the event's subject. Unknown and inaccessible entities return the same error.
func (*Service) ResolveExternal ¶
func (s *Service) ResolveExternal(ctx context.Context, viewer *core.Viewer, _ core.Device, req frontend.ExternalRequest) (core.RatingKey, error)
ResolveExternal resolves an IMDb ID to a currently visible catalogue item. Episode suffixes use canonical TMDB numbering, regardless of viewer display order.
func (*Service) ResolveFacet ¶
func (s *Service) ResolveFacet(ctx context.Context, viewer *core.Viewer, device core.Device, viewID, kind, id string) (core.FacetValue, error)
ResolveFacet only resolves identifiers among the viewer's current visible facets.
func (*Service) Resume ¶
func (s *Service) Resume(ctx context.Context, viewer *core.Viewer, _ core.Device, viewID string, types []core.MediaType, offset, limit int, requireExact bool) (frontend.ActivityPage, error)
Resume reads only in-progress state, without next-up or Home Designer data.
func (*Service) SetEntityFavorite ¶
func (*Service) SetFavorite ¶
func (*Service) SetLikes ¶
func (s *Service) SetLikes(ctx context.Context, viewer *core.Viewer, key core.RatingKey, likes *bool) error
SetLikes delegates to the state's atomic shared-rating operation.
func (*Service) SetNotInterested ¶
func (*Service) SetUnplayed ¶
func (*Service) SetWatchlist ¶
func (*Service) Similar ¶
func (s *Service) Similar(ctx context.Context, viewer *core.Viewer, device core.Device, key core.RatingKey, q frontend.ItemQuery) (frontend.SearchPage, error)
Similar applies policy before pagination and marks truncated candidate sets unknown.
func (*Service) SnapshotRows ¶
func (s *Service) SnapshotRows(ctx context.Context, req frontend.RowRequest) ([]frontend.Row, error)
SnapshotRows is the same authorized Home as Rows without issuing impressions. A second surface that paints those rows, such as Silo Discover, uses it so the bandit is not charged twice for one visit.
func (*Service) StoredItem ¶
func (s *Service) StoredItem(ctx context.Context, req frontend.ItemRequest) (frontend.ItemDetail, error)
StoredItem reads a title already in the catalogue. Resume, Next Up, and other lists use it so one stale show cannot refresh from a provider.
func (*Service) UnplayedEpisodeCounts ¶
func (s *Service) UnplayedEpisodeCounts(ctx context.Context, viewer *core.Viewer, keys []core.RatingKey) (map[core.RatingKey]int, error)
UnplayedEpisodeCounts is the aired-episode badge for a list of shows and seasons. It does not load those episodes.
func (*Service) WatchlistItem ¶
func (*Service) WithPersonCredit ¶
func (s *Service) WithPersonCredit(ctx context.Context, viewer *core.Viewer, device core.Device, item core.RatingKey, tmdb int64, read func(*core.User, frontend.PersonDetail, core.Title) error) error
WithPersonCredit keeps a host's cache-only descriptor read in the same durable policy proof as the exact current credit. The callback must not publish data; its output is valid only after this method returns success.
type ServiceDeps ¶
type ServiceDeps struct {
// BeginPeopleRead supplies fresh, fenced policy facts for the optional People API.
BeginPeopleRead func(context.Context, *core.User) (PeopleRead, error)
NotInterestedSource func(context.Context, *core.User) ([]frontend.NotInterestedEntry, error)
ByExternalID func(context.Context, string, string) (*core.Title, error)
Active func(context.Context, int64) (*core.User, error)
Libraries []Library
// LiveLibraries, when set, is read on each surface call so configured
// names stay current. IDs and media types stay stable. Nil uses Libraries.
LiveLibraries func() []Library
Visibility Visibility
Get func(context.Context, *core.User, core.RatingKey) (*core.Title, error)
// CachedGet applies catalogue visibility without a network refresh.
CachedGet func(context.Context, core.RatingKey) (*core.Title, error)
// CachedGetMany reads current cached rows in bounded batches without refresh.
CachedGetMany func(context.Context, []core.RatingKey) (map[core.RatingKey]core.Title, error)
// ParentTitles resolves distinct parents using the same semantics as GetTitle.
ParentTitles func(context.Context, []core.RatingKey) (map[core.RatingKey]core.Title, error)
// CachedVirtualSeason resolves a viewer's already-computed episode layout.
CachedVirtualSeason func(context.Context, *core.User, core.RatingKey) (*core.Title, error)
Children func(context.Context, *core.User, core.RatingKey) ([]core.Title, error)
Grandchildren func(context.Context, *core.User, core.RatingKey) ([]core.Title, error)
// SeasonEpisodes pages one catalogue season. ok is false when this viewer
// uses a custom episode group and the caller must use Grandchildren.
SeasonEpisodes func(context.Context, *core.User, core.RatingKey, int, int, int) (core.Page, bool, error)
Browse func(context.Context, core.BrowseQuery) (core.Page, error)
// PageExclusions are viewer-specific SQL exclusions for an indexed library
// page: hidden titles, blocked genres, blocked keywords, the policy
// content-rating cap, and parsed plex.tv share filters. Nil keeps the
// catalogue scan, which still applies filters the SQL page cannot see.
PageExclusions func(context.Context, *core.User) LibraryPageExclusions
// CountBrowsable is the indexed library size. The view list uses it instead
// of browsing titles.
CountBrowsable func(context.Context, core.MediaType) (int, error)
// CreditItems enumerates cached credit-title keys without browsing or hydration.
CreditItems func(context.Context, core.RatingKey, int) ([]core.RatingKey, error)
// PeopleIndex lists credited people with a name word starting with every
// term, most credited first. PersonCredits lists a person's most popular
// credited titles of the given types. With both, people searches use the
// name index instead of walking every credited title. Optional.
PeopleIndex func(context.Context, []string, int) ([]PersonCandidate, error)
PersonCredits func(context.Context, int64, []core.MediaType, int) ([]core.RatingKey, error)
AiredEpisodes func(context.Context, core.RatingKey, int, int) (core.Page, error)
// StoredEpisodes answers a recursive episode listing from the stored
// episodes in key order. Optional; without it the listing walks the
// library.
StoredEpisodes StoredEpisodeDeps
AddedTimes func(context.Context, []core.RatingKey) (map[core.RatingKey]time.Time, error)
// RecentAir is the indexed date-added window. Nil keeps the catalogue scan.
RecentAir func(context.Context, core.MediaType, int, int) ([]core.Title, bool, error)
// CountUnplayedAired counts aired episodes with no play. Nil keeps the episode list.
CountUnplayedAired func(context.Context, int64, []core.RatingKey, string) (map[core.RatingKey]int, error)
SimilarSource func(context.Context, core.RatingKey, int) ([]core.Title, error)
// Related is the same detail recommender Plex uses. Jellyfin and Emby
// Similar flatten its policy-filtered rows.
Related core.Recommender
FilterRecommendations func(context.Context, *core.User, []core.Title) ([]core.Title, error)
Collections core.Collections
Playlists core.Playlists
// Scheduled lists metadata-only future episodes for one season. Nil means
// the host has not opted in. Callers must not resolve a release from it.
Scheduled func(context.Context, core.RatingKey) ([]core.Title, error)
// Grouped reports shows whose episode order is not the canonical season
// tree. Future episodes stay off those shows.
Grouped func(context.Context, *core.User, []core.RatingKey) (map[core.RatingKey]bool, error)
State core.State
Search Search
Rows *RowsService
Protocol core.Protocol
// Favorites manages the stable, per-viewer Favorites playlist mapping.
Favorites func(context.Context, *core.User, core.RatingKey, bool) (bool, error)
IsFavorite func(context.Context, *core.User, core.RatingKey) (bool, error)
IsFavoriteMany func(context.Context, *core.User, []core.RatingKey) (map[core.RatingKey]bool, error)
IsNotInterestedMany func(context.Context, *core.User, []core.RatingKey) (map[core.RatingKey]bool, error)
IsWatchlistedMany func(context.Context, int64, []core.RatingKey) (map[core.RatingKey]bool, error)
FavoriteMembership func(context.Context, *core.User, core.RatingKey) (frontend.Membership, error)
IsNotInterested func(context.Context, *core.User, core.RatingKey) (bool, error)
SetNotInterestedSource func(context.Context, *core.User, core.RatingKey, bool) error
IsWatchlisted func(context.Context, *core.User, core.RatingKey) (bool, error)
WatchlistMembership func(context.Context, *core.User, core.RatingKey) (frontend.Membership, error)
SetWatchlistSource func(context.Context, *core.User, core.RatingKey, bool) error
RemoveContinue func(context.Context, *core.User, core.RatingKey) error
CanFavoriteEntity func(context.Context, *core.User, frontend.FavoriteRef) bool
SetEntityFavoriteSource func(context.Context, *core.User, frontend.FavoriteRef, bool) error
EntityFavoritesSource func(context.Context, *core.User, []frontend.FavoriteRef) (map[frontend.FavoriteRef]bool, error)
// Enrich must use cached or estimated data; it must never resolve releases.
Enrich func(context.Context, *core.User, core.Device, *core.Title) (frontend.ItemDetail, error)
}
ServiceDeps contains trusted host dependencies, never provided by a frontend.
type StoredEpisodeDeps ¶
type StoredEpisodeDeps struct {
// Page returns stored, aired, non-adult episodes after a key, in key
// order, of the series a browse filter admits.
Page func(context.Context, core.Filter, core.RatingKey, int) ([]core.Title, error)
// Count counts Page's whole set.
Count func(context.Context, core.Filter) (int, error)
// Unranked reports that no stored episode has a popularity.
Unranked func(context.Context) (bool, error)
// Customized reports that the viewer, or the owner's defaults, change
// some series' episode set (an episode group or hidden specials).
Customized func(context.Context, *core.User) (bool, error)
}
StoredEpisodeDeps let a recursive episode listing read the catalogue's stored episodes in key order instead of walking every series. All are required for that path; without them the listing walks the library.
type TrackPreferenceSource ¶
type TrackPreferenceSource interface {
Select(context.Context, int64, core.RatingKey, core.MediaInfo, string) (trackprefs.Selection, error)
GetRules(context.Context, int64) (trackprefs.Rules, error)
}
TrackPreferenceSource supplies persisted viewer rules and show choices.
type TrackPreferences ¶
type TrackPreferences struct {
Source TrackPreferenceSource
GetTitle func(context.Context, core.RatingKey) (*core.Title, error)
}
TrackPreferences applies persisted choices beneath explicit client choices. GetTitle is optional; an episode with no original language uses its show's language when it can be loaded, and otherwise retains the empty language.
func (TrackPreferences) AudioFilters ¶
func (p TrackPreferences) AudioFilters(ctx context.Context, viewer *core.User, info core.MediaInfo, selected TrackSelection) ([]string, error)
AudioFilters applies the viewer's rules for the selected stream's channel count. Missing preferences return no filters; lookup failures are reported.
func (TrackPreferences) Select ¶
func (p TrackPreferences) Select(ctx context.Context, viewer *core.User, key core.RatingKey, info core.MediaInfo, original string, selected TrackSelection) (TrackSelection, error)
Select returns the original selection unchanged if preferences are absent or their lookup fails. A lookup error is returned for the caller to log.
type TrackSelection ¶
TrackSelection holds positions in MediaInfo.Streams, not StreamInfo.Index. Set flags distinguish an omitted selection from position zero, and an explicit subtitle-off selection (-1) from an omitted subtitle choice.
func TranslatePlexTrackIdentity ¶
func TranslatePlexTrackIdentity(issued PlexIssuedIdentity, probed core.MediaInfo) (TrackSelection, error)
TranslatePlexTrackIdentity maps an issued Plex selection onto the current probed MediaInfo the way plex resolvePlaySelection does, before viewer preferences and before applySelections.
A nil audio identity leaves AudioPosition unchanged, including when it is negative, out of range, or not an audio stream. Preferences still see a nonnegative legacy position as set. A non-nil audio identity is passed to ResolveAudio only when that position is nonnegative. An unmatched result is position -1 and AudioSet false so preferences can run. The identity's Type is not rechecked.
A nil subtitle identity leaves SubtitleSet and SubtitlePosition unchanged. SubtitleSet false becomes position -1. A non-nil subtitle identity is passed to ResolveSubtitle only when SubtitleSet is true. An unmatched result is position -1 and SubtitleSet false. Type is not rechecked.
The result positions are indexes in probed.Streams. Encoder indexes still come from the selected StreamInfo.Index.
type UnsupportedPlexTrackIdentityError ¶
type UnsupportedPlexTrackIdentityError struct {
Reason string
}
UnsupportedPlexTrackIdentityError means the issued selection is not a shape plex resolvePlaySelection produces. Callers must handle it themselves. The returned TrackSelection is empty and must not be applied. Every value of this type matches errors.Is, whatever Reason is set.
func (UnsupportedPlexTrackIdentityError) Error ¶
func (e UnsupportedPlexTrackIdentityError) Error() string
func (UnsupportedPlexTrackIdentityError) Is ¶
func (UnsupportedPlexTrackIdentityError) Is(target error) bool
type VersionObservationPort ¶
type VersionObservationPort interface {
ObserveBatch(context.Context, core.RatingKey, []string, time.Time) (map[string]time.Time, error)
}
VersionObservationPort records first-seen times for privacy-preserving candidate sources. ObserveBatch is one atomic group for a parent title: a failure leaves every offer's observation unknown.
type VersionOffer ¶
type VersionOffer struct {
ID string
Name string
Edition string
Media core.MediaInfo
AddedAt time.Time
Publication *time.Time
// contains filtered or unexported fields
}
VersionOffer is one candidate without protocol fields. ID is the existing ReleaseVariant.ID, unchanged. It is not a scoped client handle: Jellyfin and Emby pass it to VersionHandles.Issue, which replaces it before MediaSources, and Plex maps the release to an integer Media/Part id. Publication is the release publication time when known. It is not a file modification time. source is the hashed observation identity only.
Final merge: profile SelectionKey and remembered-release invalidation are absent on b5315d63131029ddb5df7b91161e31240367f602. When they land they must not copy ReleaseVariant.ID or source onto an unscoped client id, and must keep this ID pass-through and parent+source observations.
func VersionOfferFrom ¶
func VersionOfferFrom(title *core.Title, variant core.ReleaseVariant) VersionOffer
VersionOfferFrom converts one candidate. It does not resolve a file, part list, etag, or modification time, and it does not record an observation or replace the candidate id.
func VersionOffersFrom ¶
func VersionOffersFrom(title *core.Title, variants []core.ReleaseVariant) []VersionOffer
VersionOffersFrom converts candidates in their existing order.
type VibeSearch ¶
type VibeSearch struct {
Source VibeSource
Visibility Visibility
// Unrestricted is a trusted host policy capability; nil fails closed.
Unrestricted func(*core.User) bool
}
VibeSearch resolves an optional natural-language collection source.
type VibeSource ¶
type VibeSource interface {
VibeCollection(context.Context, *core.User, string) (*core.Collection, []core.Title, bool, error)
}
VibeSource returns a viewer-scoped ephemeral collection and candidate items.
type VirtualPathCatalog ¶
type VirtualPathCatalog struct {
Views func(context.Context, *core.Viewer) ([]VirtualPathView, error)
LibraryItems func(context.Context, *core.Viewer, core.Device, string, int, int) (VirtualPathPage, error)
Children func(context.Context, *core.Viewer, core.Device, core.RatingKey, int, int) (VirtualPathPage, error)
}
VirtualPathCatalog is the authorized browse port for virtual library paths. Views, LibraryItems and Children must already enforce viewer policy. A nil function fails closed. This service does not resolve a source, release or playback detail; names come from libpath, which depends only on core and is shared with WebDAV rather than reimplemented here.
type VirtualPathPage ¶
VirtualPathPage is one authorized page of catalogue titles.
type VirtualPathView ¶
VirtualPathView is one viewer-authorized library section. It carries no protocol or wire context.
type Visibility ¶
type Visibility struct {
Policy AccessPolicy
SectionFor func(core.MediaType) (int, bool)
GetTitle func(context.Context, core.RatingKey) (*core.Title, error)
FilterAvailable func(context.Context, []core.Title) []core.Title
Sessions func() []*core.PlaySession
}
Visibility applies content, library and inherited show restrictions. The section lookup returns false for media types with no configured library.
func BindVisibility ¶
func BindVisibility(policy AccessPolicy, get func(context.Context, core.RatingKey) (*core.Title, error), available func(context.Context, []core.Title) []core.Title, sessions func() []*core.PlaySession, libraries []Library) Visibility
BindVisibility is the shared library, policy and availability port. Frontends supply sources; they do not decide which library a media type uses.
func (Visibility) Available ¶
func (v Visibility) Available(ctx context.Context, viewer *core.User, titles []core.Title) []core.Title
Available filters discovery evidence, retaining a viewer's open sources. Callers must separately enforce title policy.
func (Visibility) CollectionAllowed ¶
func (v Visibility) CollectionAllowed(viewer *core.User, c *core.Collection) bool
CollectionAllowed checks the library restriction for a collection.
func (Visibility) Playable ¶
Playable checks inherited show policy without requiring discovery evidence.
func (Visibility) TitleAllowed ¶
func (Visibility) TitlesAllowed ¶
TitlesAllowed filters the title/section guard without discovery filtering.
Source Files
¶
- additional_rows.go
- adjacent.go
- audio_identity.go
- candidate_rows.go
- cinema.go
- cold_home.go
- collection_preferences.go
- continue.go
- detail_extras.go
- detail_related.go
- detail_rows.go
- discovery.go
- download_policy.go
- event_subject.go
- external.go
- favorite_policy.go
- first_observed.go
- home.go
- home_selection.go
- layout.go
- library_activity.go
- library_index.go
- live_playlist_rows.go
- markers.go
- membership.go
- named_facets.go
- people.go
- person_facets.go
- playability.go
- playlist_library.go
- playlist_selection.go
- plex_track_identity.go
- policy.go
- postplay.go
- postplay_selection.go
- preview_rows.go
- receipt.go
- receipt_batch.go
- receipt_maintenance.go
- receipt_relay.go
- receipt_schema.go
- recent.go
- recent_availability.go
- related.go
- related_artwork.go
- row_candidates.go
- row_entry_policy.go
- row_interaction_policy.go
- row_producers.go
- row_selection.go
- row_view_hydration.go
- row_views.go
- rows_service.go
- runtime_row_views.go
- scheduled.go
- scoped_collection.go
- search.go
- search_discovery.go
- series_next_up.go
- service.go
- show_on_deck.go
- stored_episodes.go
- track_preferences.go
- tracks.go
- version_offers.go
- vibe.go
- viewer_lifecycle.go
- virtual_paths.go