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

collections

package
v0.2.28 Latest Latest
Warning

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

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

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

View Source
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

View Source
var ErrBuiltIn = errors.New("built-in collections can be disabled but not deleted")

ErrBuiltIn is returned when deleting a built-in collection.

View Source
var ErrLLMDisabled error = notConfigured{errors.New("the LLM is not enabled (Settings → LLM)")}

ErrLLMDisabled is returned by Generate when no LLM is configured.

View Source
var ErrNoArtwork = errors.New("no artwork for this collection")

ErrNoArtwork means no poster could be produced (no renderer, no members).

Functions

func BuiltinKey

func BuiltinKey(slug string) core.RatingKey

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

func ParseVibe(query string) (core.VibeQuery, bool)

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

func ValidTMDBImagePath(p string) bool

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
}

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 Option

type Option func(*Service)

Option customises a Service.

func WithArtwork

func WithArtwork(r *artwork.Renderer, im *artwork.Images) Option

WithArtwork enables generated collection posters.

func WithAuth

func WithAuth(a core.Auth) Option

WithAuth lets previews evaluate per-user rules as the owner.

func WithCatalogConfig

func WithCatalogConfig(fn func() config.Catalog) Option

WithCatalogConfig supplies the catalogue settings (region for streaming services, whether unreleased titles are shown).

func WithEphemeralLimits

func WithEphemeralLimits(ttl time.Duration, max int) Option

WithEphemeralLimits bounds the ephemeral registry (tests).

func WithExternalBases

func WithExternalBases(b ExternalBases) Option

WithExternalBases points external list fetches at other origins (tests).

func WithHTTPClient

func WithHTTPClient(c *http.Client) Option

WithHTTPClient sets the client for Letterboxd, MDBList and Trakt.

func WithIndexTTL

func WithIndexTTL(d time.Duration) Option

WithIndexTTL sets how often the catalogue index checks for changes (default 2m; 0 checks on every use).

func WithLLM

func WithLLM(c Chatter) Option

WithLLM enables the natural-language collection generator.

func WithLiveTTL

func WithLiveTTL(d time.Duration) Option

WithLiveTTL sets how long a live collection's evaluation is reused (default 2m).

func WithNow

func WithNow(fn func() time.Time) Option

WithNow sets the clock (tests).

func WithSchedulerTick

func WithSchedulerTick(d time.Duration) Option

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 WithState

func WithState(st core.State) Option

WithState enables per-user rules (watched, in progress).

func WithTMDB

func WithTMDB(c TMDB) Option

WithTMDB enables TMDB-backed sources, lookups and rule pushdown.

func WithTraktConfig

func WithTraktConfig(fn func() config.Trakt) Option

WithTraktConfig supplies the Trakt client id used to read public lists.

func WithUserAgent

func WithUserAgent(ua string) Option

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

func New(db *sql.DB, cat core.Catalog, log *slog.Logger, opts ...Option) (*Service, error)

New creates the collection engine, migrating v1 smart collections and seeding built-ins and starter collections.

func (*Service) Artwork

func (s *Service) Artwork(ctx context.Context, id string, w int) ([]byte, string, time.Time, error)

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

func (s *Service) BrowseTitles(ctx context.Context, q core.BrowseQuery) (core.Page, error)

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

func (s *Service) DeleteSmart(ctx context.Context, key core.RatingKey) error

DeleteSmart removes an owner or user collection. Built-ins and franchises cannot be deleted.

func (*Service) DeleteSpec

func (s *Service) DeleteSpec(ctx context.Context, id string) error

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) 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) GetSpec

func (s *Service) GetSpec(ctx context.Context, id string) (*core.CollectionSpec, error)

GetSpec returns one collection by id (number or built-in slug).

func (*Service) HomeRowsFor

func (s *Service) HomeRowsFor(ctx context.Context, user *core.User, hc core.HubContext) ([]core.CollectionRow, error)

HomeRowsFor returns owner collections promoted to Home or section rows for a user in a context: enabled, targeted at the user, inside their schedule (days, hours, devices), with the user's content-rating limit, personal rules, personal order and rotation applied. Each row says whether it belongs on Home, in section hubs, or both; Hub.Type tells the caller which section ("" for mixed collections).

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) ListSpecs

func (s *Service) ListSpecs(ctx context.Context) ([]core.CollectionSpec, error)

ListSpecs returns every collection, owner-made first, then built-ins.

func (*Service) Lookup

func (s *Service) Lookup(ctx context.Context, kind, q, region string) ([]core.LookupResult, error)

Lookup finds TMDB people, keywords, companies, networks, collections or streaming services by name for the rule and source pickers.

func (*Service) Preview

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

func (s *Service) Prune(ctx context.Context) (int64, error)

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) Rebuild

func (s *Service) Rebuild(ctx context.Context, id string) (*core.CollectionSpec, error)

Rebuild evaluates a collection now.

func (*Service) Run

func (s *Service) Run(ctx context.Context)

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

func (s *Service) RunDue(ctx context.Context) int

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) SetAuth

func (s *Service) SetAuth(a core.Auth)

SetAuth lets previews apply per-user rules as the owner.

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) SetLLM

func (s *Service) SetLLM(c Chatter)

SetLLM installs the model the collection generator drafts with.

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) SetState

func (s *Service) SetState(st core.State)

SetState installs watch state for per-user rules (watched, in progress).

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

func (s *Service) TitleDetail(ctx context.Context, key core.RatingKey) (*core.Title, error)

TitleDetail returns a title with credits and keywords (fetching details from TMDB when the mirror only has a summary).

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.

func (*Service) VibeCollection

func (s *Service) VibeCollection(ctx context.Context, user *core.User, query string) (*core.Collection, []core.Title, bool, error)

VibeCollection implements core.VibeSearcher.

type TMDB

type TMDB interface {
	GetJSON(ctx context.Context, path string, q url.Values, out any) error
}

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

type ValidationError struct {
	Fields map[string]string
}

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.

Jump to

Keyboard shortcuts

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