Documentation
¶
Overview ¶
Package collections is Ether's collection engine. It implements core.Collections (what Plex sees in the Collections tab), core.CollectionRows (owner collections offered to the personalization engine as Home and section rows) and the Collection Studio behind the owner UI.
A collection is a core.CollectionSpec: candidates gathered from sources (the catalogue, TMDB lists, collections, discover, keywords, companies, networks, people and streaming services, public Trakt, Letterboxd and MDBList lists, hand-picked titles), filtered by rules, sorted, limited, optionally rotated, and published with generated artwork. External titles join the browsable catalogue so every member is playable. Specs refresh on a schedule in the background, or live on request.
TMDB franchises (the catalogue's TMDB collections with two or more titles) are listed alongside, as before.
Index ¶
- Constants
- Variables
- func BuiltinKey(slug string) core.RatingKey
- func ImportTitlesTx(ctx context.Context, tx *sql.Tx, id, userID int64, name string, ...) (int64, error)
- func Normalize(s *core.CollectionSpec)
- func ParseVibe(query string) (core.VibeQuery, bool)
- func Templates() []core.CollectionTemplate
- func ValidTMDBImagePath(p string) bool
- func Validate(s *core.CollectionSpec) error
- type Chatter
- type ExternalBases
- type LookAhead
- type Option
- func WithArtwork(r *artwork.Renderer, im *artwork.Images) Option
- func WithAuth(a core.Auth) Option
- func WithCatalogConfig(fn func() config.Catalog) Option
- func WithEphemeralLimits(ttl time.Duration, max int) Option
- func WithExternalBases(b ExternalBases) Option
- func WithHTTPClient(c *http.Client) Option
- func WithIndexTTL(d time.Duration) Option
- func WithLLM(c Chatter) Option
- func WithLiveTTL(d time.Duration) Option
- func WithNow(fn func() time.Time) Option
- func WithSchedulerTick(d time.Duration) Option
- func WithScorer(sc core.PersonalScorer) Option
- func WithState(st core.State) Option
- func WithTMDB(c TMDB) Option
- func WithTraktConfig(fn func() config.Trakt) Option
- func WithUserAgent(ua string) Option
- func WithVibeRefiner(r core.VibeRefiner) Option
- func WithoutDefaults() Option
- type Service
- func (s *Service) AllowsPlaceholders(ctx context.Context, user *core.User, collection core.RatingKey, device string) bool
- func (s *Service) Artwork(ctx context.Context, id string, w int) ([]byte, string, time.Time, error)
- func (s *Service) BrowseTitles(ctx context.Context, q core.BrowseQuery) (core.Page, error)
- func (s *Service) CollectionArtwork(ctx context.Context, user *core.User, key core.RatingKey, element string, ...) ([]byte, string, bool, error)
- func (s *Service) CreateSmart(ctx context.Context, c core.Collection) (*core.Collection, error)
- func (s *Service) CreateSpec(ctx context.Context, sp core.CollectionSpec) (*core.CollectionSpec, error)
- func (s *Service) DeleteSmart(ctx context.Context, key core.RatingKey) error
- func (s *Service) DeleteSpec(ctx context.Context, id string) error
- func (s *Service) Facets(ctx context.Context, t core.MediaType, facet string) ([]core.FacetValue, error)
- func (s *Service) FindPlaceholder(ctx context.Context, user *core.User, key core.RatingKey, device string) (*core.Title, core.RatingKey, error)
- func (s *Service) Generate(ctx context.Context, prompt, mediaType string) (*core.CollectionSpec, string, error)
- func (s *Service) Get(ctx context.Context, user *core.User, key core.RatingKey) (*core.Collection, error)
- func (s *Service) GetSpec(ctx context.Context, id string) (*core.CollectionSpec, error)
- func (s *Service) HomeRowsFor(ctx context.Context, user *core.User, hc core.HubContext) ([]core.CollectionRow, error)
- func (s *Service) Items(ctx context.Context, user *core.User, key core.RatingKey, offset, limit int) (core.Page, error)
- func (s *Service) LastChance(ctx context.Context, user *core.User, key core.RatingKey) (time.Time, error)
- func (s *Service) LayoutRow(ctx context.Context, user *core.User, hc core.HubContext, id string) (*core.CollectionRow, error)
- func (s *Service) List(ctx context.Context, user *core.User, t core.MediaType) ([]core.Collection, error)
- func (s *Service) ListComplete(ctx context.Context, user *core.User, typ core.MediaType) ([]core.Collection, error)
- func (s *Service) ListSpecs(ctx context.Context) ([]core.CollectionSpec, error)
- func (s *Service) LookAheads(ctx context.Context) ([]LookAhead, error)
- func (s *Service) Lookup(ctx context.Context, kind, q, region string) ([]core.LookupResult, error)
- func (s *Service) Placeholder(ctx context.Context, user *core.User, collection, key core.RatingKey, ...) (*core.Title, error)
- func (s *Service) Preview(ctx context.Context, sp core.CollectionSpec, n int) (*core.CollectionPreview, error)
- func (s *Service) PromoteEphemeral(ctx context.Context, user *core.User, key core.RatingKey) (*core.Collection, bool, error)
- func (s *Service) Prune(ctx context.Context) (int64, error)
- func (s *Service) Rebuild(ctx context.Context, id string) (*core.CollectionSpec, error)
- func (s *Service) Run(ctx context.Context)
- func (s *Service) RunDue(ctx context.Context) int
- func (s *Service) SearchTitles(ctx context.Context, q string, t core.MediaType, limit int) ([]core.Title, error)
- func (s *Service) SetAuth(a core.Auth)
- func (s *Service) SetBus(bus clusterbus.Bus)
- func (s *Service) SetLLM(c Chatter)
- func (s *Service) SetScorer(sc core.PersonalScorer)
- func (s *Service) SetState(st core.State)
- func (s *Service) SetVibeRefiner(r core.VibeRefiner)
- func (s *Service) Templates() []core.CollectionTemplate
- func (s *Service) TitleDetail(ctx context.Context, key core.RatingKey) (*core.Title, error)
- func (s *Service) UpdateSpec(ctx context.Context, id string, sp core.CollectionSpec) (*core.CollectionSpec, error)
- func (s *Service) VibeCollection(ctx context.Context, user *core.User, query string) (*core.Collection, []core.Title, bool, error)
- type TMDB
- type TitleUpserter
- type ValidationError
Constants ¶
const ( MediaMovie = "movie" MediaShow = "show" MediaMixed = "mixed" MatchAll = "all" MatchAny = "any" RefreshLive = "live" RefreshHourly = "hourly" RefreshDaily = "daily" RefreshWeekly = "weekly" )
Field, op, sort and source vocabularies.
Variables ¶
var ErrBuiltIn = errors.New("built-in collections can be disabled but not deleted")
ErrBuiltIn is returned when deleting a built-in collection.
var ErrLLMDisabled error = notConfigured{errors.New("the LLM is not enabled (Settings → LLM)")}
ErrLLMDisabled is returned by Generate when no LLM is configured.
var ErrNoArtwork = errors.New("no artwork for this collection")
ErrNoArtwork means no poster could be produced (no renderer, no members).
Functions ¶
func BuiltinKey ¶
BuiltinKey returns the rating key of a built-in collection by slug (e.g. "trending-week-movie"), or 0.
func ImportTitlesTx ¶
func ImportTitlesTx(ctx context.Context, tx *sql.Tx, id, userID int64, name string, keys []core.RatingKey, now int64) (int64, error)
ImportTitlesTx is the collections-owned writer for a reviewed import. The caller commits its migration bookkeeping in the same transaction. Imported lists retain stable IDs and can never replace built-ins or another viewer's.
func Normalize ¶
func Normalize(s *core.CollectionSpec)
Normalize fills defaults in place: an empty source list means the whole catalogue, empty match means "all", and so on. It never fails.
func ParseVibe ¶
ParseVibe turns a natural-language "vibe" query ("90s thrillers under 100 mins", "korean horror", "shows like the office") into a structured filter plus a human title. It is a deterministic, conservative grammar: every word of the query must be consumed by a known pattern (genre, decade/year, runtime, rating, language, media type, mood, person/studio shape or a "like X" seed), otherwise ok is false and the query is treated as a plain title search. No I/O: people and "like" seeds come back unresolved (PersonName / SimilarSeed) for the caller to look up.
func Templates ¶
func Templates() []core.CollectionTemplate
Templates are curated, complete specs the Collection Studio starts from. They name TMDB entities by id and label; labels are resolved by name if an id ever changes. Each previews well on a real catalogue: rules lean on what the catalogue mirror knows (genres, years, ratings, votes, languages), and anything it only partly knows (keywords, runtime, certifications, streaming services) comes from TMDB.
func ValidTMDBImagePath ¶
ValidTMDBImagePath reports whether p looks like a TMDB image path ("/abc123.jpg"). It rejects traversal and anything that is not a single file name.
func Validate ¶
func Validate(s *core.CollectionSpec) error
Validate checks a normalized spec. It returns a *ValidationError.
Types ¶
type Chatter ¶
type Chatter interface {
Enabled() bool
Chat(ctx context.Context, msgs []llm.Message, jsonMode bool) (string, error)
}
Chatter is the LLM the generator drafts specs with; *llm.Client satisfies it.
type ExternalBases ¶
type ExternalBases struct {
TraktAPI string // https://api.trakt.tv
Letterboxd string // https://letterboxd.com
MDBList string // https://mdblist.com
AniListAPI string // https://graphql.anilist.co
AnimeIDs string // the anime id mapping document (anilist -> TMDB/TVDB/IMDb)
StevenLu string // https://popular-movies-data.stevenlu.com
}
ExternalBases are the origins external list sources are fetched from. Tests point them at httptest servers; the URLs owners paste are always validated against the real hosts.
type LookAhead ¶ added in v0.3.0
LookAhead is a server-wide collection that lists upcoming releases.
type Option ¶
type Option func(*Service)
Option customises a Service.
func WithArtwork ¶
WithArtwork enables generated collection posters.
func WithCatalogConfig ¶
WithCatalogConfig supplies the catalogue settings (region for streaming services, whether unreleased titles are shown).
func WithEphemeralLimits ¶
WithEphemeralLimits bounds the ephemeral registry (tests).
func WithExternalBases ¶
func WithExternalBases(b ExternalBases) Option
WithExternalBases points external list fetches at other origins (tests).
func WithHTTPClient ¶
WithHTTPClient sets the client for the external list sites.
func WithIndexTTL ¶
WithIndexTTL sets how often the catalogue index checks for changes (default 2m; 0 checks on every use).
func WithLiveTTL ¶
WithLiveTTL sets how long a live collection's evaluation is reused (default 2m).
func WithSchedulerTick ¶
WithSchedulerTick sets how often Run looks for collections due a refresh.
func WithScorer ¶
func WithScorer(sc core.PersonalScorer) Option
WithScorer sets the personal scorer used by the "personal" sort.
func WithTraktConfig ¶
WithTraktConfig supplies the Trakt client id used to read public lists.
func WithUserAgent ¶
WithUserAgent identifies Ether to list sites.
func WithVibeRefiner ¶
func WithVibeRefiner(r core.VibeRefiner) Option
WithVibeRefiner installs the optional LLM refiner consulted when the deterministic parser cannot read a query.
func WithoutDefaults ¶
func WithoutDefaults() Option
WithoutDefaults skips seeding the starter collections (tests).
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is the collection engine.
func New ¶
New creates the collection engine, migrating v1 smart collections and seeding built-ins and starter collections.
func (*Service) AllowsPlaceholders ¶ added in v0.2.37
func (*Service) Artwork ¶
Artwork renders (or fetches, for tmdb/custom modes) a collection's poster at width w. It returns the image, its content type and when it last changed.
func (*Service) BrowseTitles ¶ added in v0.2.9
BrowseTitles lists the released local catalogue with filters and pagination.
func (*Service) CollectionArtwork ¶
func (s *Service) CollectionArtwork(ctx context.Context, user *core.User, key core.RatingKey, element string, w int) ([]byte, string, bool, error)
CollectionArtwork serves generated posters to Plex for owner collections (the thumb/poster elements). ok is false for anything else, so the caller falls back to TMDB artwork.
func (*Service) CreateSmart ¶
func (s *Service) CreateSmart(ctx context.Context, c core.Collection) (*core.Collection, error)
CreateSmart persists a filter-based smart collection (core.Filter) as a spec. OwnerUserID 0 = server-wide; otherwise only that user sees it.
func (*Service) CreateSpec ¶
func (s *Service) CreateSpec(ctx context.Context, sp core.CollectionSpec) (*core.CollectionSpec, error)
CreateSpec validates, saves and builds a new collection. The build is bounded; if it runs long it finishes in the background.
func (*Service) DeleteSmart ¶
DeleteSmart removes an owner or user collection. Built-ins and franchises cannot be deleted.
func (*Service) DeleteSpec ¶
DeleteSpec deletes an owner collection.
func (*Service) Facets ¶
func (s *Service) Facets(ctx context.Context, t core.MediaType, facet string) ([]core.FacetValue, error)
Facets lists filter values over the browsable catalogue: genre, decade, contentRating, studio, country, language (and year).
func (*Service) FindPlaceholder ¶ added in v0.2.37
func (*Service) Generate ¶
func (s *Service) Generate(ctx context.Context, prompt, mediaType string) (*core.CollectionSpec, string, error)
Generate drafts a spec from a plain-English request with the LLM, resolves the people, keywords and companies it names to TMDB ids, validates it, and explains it.
func (*Service) Get ¶
func (s *Service) Get(ctx context.Context, user *core.User, key core.RatingKey) (*core.Collection, error)
Get returns one collection by key.
func (*Service) HomeRowsFor ¶
func (s *Service) HomeRowsFor(ctx context.Context, user *core.User, hc core.HubContext) ([]core.CollectionRow, error)
func (*Service) Items ¶
func (s *Service) Items(ctx context.Context, user *core.User, key core.RatingKey, offset, limit int) (core.Page, error)
Items returns a page of a collection's titles as the user sees them.
func (*Service) LastChance ¶
func (s *Service) LastChance(ctx context.Context, user *core.User, key core.RatingKey) (time.Time, error)
LastChance reports the next configured rotation boundary only when a title belongs to this viewer's current subset and is absent from the next subset. It means leaving a rotating collection row, never disappearing from Ether. The method uses already-built members and the current local index. It never triggers a build, fetches a source, or invents a media-availability deadline.
func (*Service) LayoutRow ¶
func (s *Service) LayoutRow(ctx context.Context, user *core.User, hc core.HubContext, id string) (*core.CollectionRow, error)
LayoutRow serves a collection explicitly selected by the owner's Home template or viewer override, even when its default publication targets do not include Home. Private ownership and viewer content limits still apply. The layout policy supplies the publication scope and schedule.
func (*Service) List ¶
func (s *Service) List(ctx context.Context, user *core.User, t core.MediaType) ([]core.Collection, error)
List returns the collections a user sees in a section's Collections tab: owner collections targeted at them, built-ins, and TMDB franchises.
func (*Service) ListComplete ¶ added in v0.3.0
func (s *Service) ListComplete(ctx context.Context, user *core.User, typ core.MediaType) ([]core.Collection, error)
ListComplete builds every eligible collection or returns an error. Unlike List, it never suppresses refresh or franchise-list failures; consumers that advertise an exact total must not use a partial result.
func (*Service) LookAheads ¶ added in v0.3.0
LookAheads returns the enabled server-wide collections that admit upcoming releases, with their last built members shared with every viewer. It reads stored builds only.
func (*Service) Lookup ¶
Lookup finds TMDB people, keywords, companies, networks, collections or streaming services by name for the rule and source pickers.
func (*Service) Placeholder ¶ added in v0.2.37
func (*Service) Preview ¶
func (s *Service) Preview(ctx context.Context, sp core.CollectionSpec, n int) (*core.CollectionPreview, error)
Preview evaluates an unsaved spec and returns the first n titles. Titles come from the in-memory catalogue index, so a preview over the whole catalogue never waits on the database.
func (*Service) PromoteEphemeral ¶
func (s *Service) PromoteEphemeral(ctx context.Context, user *core.User, key core.RatingKey) (*core.Collection, bool, error)
PromoteEphemeral implements core.VibeSearcher: rating an ephemeral collection saves it as a persisted personal smart collection with the same title. The ephemeral key keeps working as an alias.
func (*Service) Prune ¶
Prune deletes stale rows from collections_names and collections_external. Specs, members and owner data are never touched. It is meant to be called periodically.
func (*Service) Run ¶
Run refreshes scheduled collections until ctx ends: anything never built or edited since its last build right away, then hourly/daily/weekly per spec. Live collections are evaluated on request instead.
func (*Service) RunDue ¶
RunDue rebuilds every collection that is due and returns how many were built.
func (*Service) SearchTitles ¶
func (s *Service) SearchTitles(ctx context.Context, q string, t core.MediaType, limit int) ([]core.Title, error)
SearchTitles searches the catalogue (reaching all of TMDB).
func (*Service) SetBus ¶
func (s *Service) SetBus(bus clusterbus.Bus)
SetBus connects the cluster bus (nil disconnects). A standalone server never calls it.
func (*Service) SetScorer ¶
func (s *Service) SetScorer(sc core.PersonalScorer)
SetScorer installs (or replaces) the personal scorer after construction, for when the personalization engine is built after the collections.
func (*Service) SetVibeRefiner ¶
func (s *Service) SetVibeRefiner(r core.VibeRefiner)
SetVibeRefiner installs the refiner after construction.
func (*Service) Templates ¶
func (s *Service) Templates() []core.CollectionTemplate
Templates returns the curated starting points.
func (*Service) TitleDetail ¶
TitleDetail returns a title with credits and keywords (fetching details from TMDB when the mirror only has a summary). The owner console also inspects titles viewers cannot open yet, such as an upcoming movie on the release calendar, so an unreleased title Get refuses is read from the mirror instead. Adult titles stay hidden.
func (*Service) UpdateSpec ¶
func (s *Service) UpdateSpec(ctx context.Context, id string, sp core.CollectionSpec) (*core.CollectionSpec, error)
UpdateSpec replaces a collection. Built-ins accept only enabled, targets, artwork, rotate and limit.
type TMDB ¶
TMDB is the slice of the TMDB client the collection engine needs: one generic, rate-limited, cached GET. *tmdb.Client satisfies it.
type TitleUpserter ¶
type TitleUpserter interface {
UpsertTitle(ctx context.Context, t core.Title, browsable bool) error
}
TitleUpserter adds titles to the catalogue mirror; *catalog.Catalog satisfies it. Collection members found outside the catalogue are joined to the browsable set through it.
type ValidationError ¶
ValidationError lists the problems with a spec, keyed by field path ("name", "sources[1].url", "rules[0].value"). The admin API renders it as a 400 with per-field messages.
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string
func (*ValidationError) InvalidFields ¶
func (e *ValidationError) InvalidFields() map[string]string
InvalidFields exposes the field errors without importing this package.
Source Files
¶
- air_hydration.go
- anilist.go
- builtins.go
- capability_assertions.go
- cluster.go
- collections.go
- complete.go
- defaults.go
- engine.go
- external.go
- generate.go
- helpers.go
- import.go
- index.go
- lastchance.go
- lookahead.go
- names.go
- prune.go
- rotate.go
- rows.go
- rules.go
- sources.go
- spec.go
- store.go
- studio.go
- templates.go
- titles.go
- tmdbapi.go
- vibe_ephemeral.go
- vibe_parse.go
- vibe_tables.go