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

catalog

package
v0.2.23 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: 21 Imported by: 0

Documentation

Overview

Package catalog implements core.Catalog: a SQLite mirror of a filtered TMDB slice plus on-demand fetches for anything else.

Index

Constants

View Source
const DefaultCrowdTTL = 72 * time.Hour

DefaultCrowdTTL is how long a stored co-engagement list is fresh.

Variables

View Source
var ErrSyncInProgress = fmt.Errorf("catalog: sync already in progress")

Sync (re)builds the browsable set from TMDB discover. A second call while one is running returns ErrSyncInProgress; use SyncProgress to poll.

Functions

func EstimateTitles

func EstimateTitles(minPopularity float64, minVotes int) int

EstimateTitles is a convenience wrapper returning only the total.

func MeetsThreshold

func MeetsThreshold(cfg config.Catalog, t core.Title, now time.Time) bool

MeetsThreshold reports whether a title qualifies for the browsable set under cfg: popularity >= MinPopularity OR votes >= MinVotes, and released.

func RatingExceeds

func RatingExceeds(rating, max string) bool

RatingExceeds reports whether rating is stricter than max ("" max = unrestricted).

func RatingRank

func RatingRank(rating string) int

Content-rating ordering. All scales map onto a unified severity rank:

1  everyone      G, TV-G, TV-Y, U
2  parental      PG, TV-PG, TV-Y7
3  teens         PG-13, TV-14, 12/15-ish, M
4  adults        R, TV-MA, MA15+
5  adults-only   NC-17, 18/R18+

Unknown / unrated ratings rank 0. Restricted viewers cannot access them.

func WithinRatingLimit

func WithinRatingLimit(rating, max string) bool

WithinRatingLimit is the negation, for readability at call sites.

Types

type Catalog

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

Catalog implements core.Catalog.

func New

func New(db *sql.DB, client TMDB, cfg config.Catalog, log *slog.Logger, opts ...Option) (*Catalog, error)

New creates a catalogue. client may be nil (mirror-only / offline mode).

func (*Catalog) Browse

func (c *Catalog) Browse(ctx context.Context, q core.BrowseQuery) (core.Page, error)

Browse queries the browsable mirror entirely in SQL, with generation-stamped in-memory count and page caching so repeated library/recommendation reads return without hitting disk.

func (*Catalog) ByExternalID

func (c *Catalog) ByExternalID(ctx context.Context, source, id string) (*core.Title, error)

ByExternalID looks up by imdb ("tt123") or tvdb id.

func (*Catalog) CachedDiscoveryEpisodes added in v0.2.9

func (c *Catalog) CachedDiscoveryEpisodes(ctx context.Context, shows []core.RatingKey) (map[core.RatingKey][]core.Title, error)

CachedDiscoveryEpisodes returns regular episode metadata without hydration or remote lookups. Unknown/future dates are retained so callers can distinguish an unmirrored premiere from a known but unreleased episode.

func (*Catalog) CachedTitle

func (c *Catalog) CachedTitle(ctx context.Context, key core.RatingKey) (*core.Title, error)

CachedTitle returns a mirrored title without contacting TMDB.

func (*Catalog) CachedTitles added in v0.2.6

func (c *Catalog) CachedTitles(ctx context.Context, keys []core.RatingKey) ([]core.Title, error)

CachedTitles returns released, non-adult mirrored titles in requested order, without refreshing remote metadata. Batch consumers such as recommendation builders use this to avoid one full hydration round trip per history entry.

func (*Catalog) Children

func (c *Catalog) Children(ctx context.Context, key core.RatingKey) ([]core.Title, error)

Children: seasons of a show, episodes of a season.

func (*Catalog) Collection

func (c *Catalog) Collection(ctx context.Context, tmdbCollection int64) (string, []core.Title, error)

Collection returns a TMDB franchise and its parts sorted by release date.

func (*Catalog) CrowdGraphBatch added in v0.2.7

func (c *Catalog) CrowdGraphBatch(ctx context.Context, keys []core.RatingKey, maxPerKey, maxTargets int) (map[core.RatingKey][]core.RatingKey, map[core.RatingKey]core.Title, error)

CrowdGraphBatch reads stored co-engagement lists from catalog_crowd for a batch of keys without contacting TMDB, and resolves up to maxTargets of the referenced titles from the local mirror. Used for 2-hop crowd expansion.

func (*Catalog) CrowdRecommendations

func (c *Catalog) CrowdRecommendations(ctx context.Context, key core.RatingKey, pages int, fetch bool) (titles []core.Title, fetched bool, err error)

CrowdRecommendations returns TMDB's crowd co-engagement list for a title (/recommendations only: never metadata similarity), best first, from up to pages pages (1–2). A stored list younger than the crowd TTL with enough pages is served from the local mirror. Otherwise, with fetch set, TMDB is asked and fetched reports it (callers budget those calls); without fetch, or when TMDB fails, a stale list is served when there is one and nil otherwise. Titles not yet released are dropped.

func (*Catalog) Facet

func (c *Catalog) Facet(ctx context.Context, t core.MediaType, facet string) ([]core.FacetValue, error)

Facet returns value counts over the browsable set.

func (*Catalog) FilterVisibility added in v0.2.12

func (c *Catalog) FilterVisibility(ctx context.Context, ts []core.Title) []core.Title

FilterVisibility enriches remote summaries with already-known local facets; there are no detail fetches or indexer calls. Unknown metadata stays unknown.

func (*Catalog) Get

func (c *Catalog) Get(ctx context.Context, key core.RatingKey) (*core.Title, error)

Get returns a title from the mirror, fetching on demand when missing/stale.

func (*Catalog) Grandchildren

func (c *Catalog) Grandchildren(ctx context.Context, key core.RatingKey) ([]core.Title, error)

Grandchildren: all episodes of a show.

func (*Catalog) List

func (c *Catalog) List(ctx context.Context, list string, t core.MediaType, limit int) ([]core.Title, error)

List returns TMDB curated/trending lists with a short in-memory TTL.

func (*Catalog) NextEpisode

func (c *Catalog) NextEpisode(ctx context.Context, episode core.RatingKey) (*core.Title, error)

NextEpisode returns the next regular episode (specials, season 0, are skipped as a target; calling it on a special jumps to S1E1-order sequence).

func (*Catalog) PreviewVisibility added in v0.2.12

func (c *Catalog) PreviewVisibility(ctx context.Context, specs []core.LibraryExclusion) (any, error)

func (*Catalog) RecentEpisodes

func (c *Catalog) RecentEpisodes(ctx context.Context, show core.RatingKey, since time.Time) ([]core.Title, error)

RecentEpisodes returns only local, released episode identities for artwork badges. Callers apply viewer state through core.State instead of a SQL join.

func (*Catalog) RecentMovies added in v0.2.9

func (c *Catalog) RecentMovies(ctx context.Context, since time.Time) ([]core.Title, error)

RecentMovies reads the release-date index, without refreshing any title.

func (*Catalog) RecentShows added in v0.2.9

func (c *Catalog) RecentShows(ctx context.Context, since time.Time) ([]core.Title, error)

RecentShows only reads the local premiere-date index.

func (*Catalog) ReleaseMatches added in v0.2.8

func (c *Catalog) ReleaseMatches(ctx context.Context, releases []core.Release) (map[string][]core.Title, error)

ReleaseMatches joins a feed page to local metadata in one narrow scan. It never spends a metadata API call per feed item. Ambiguous names are ignored; explicit identifier conflicts always defeat a textual match. Season packs are expanded only to locally known episodes, never to invented episode identities.

func (*Catalog) Run

func (c *Catalog) Run(ctx context.Context)

Run performs an initial sync and then refreshes on RefreshInterval until ctx ends. Intended to be run in its own goroutine by the app wiring. With WithResumeSchedule the initial sync waits until the interval has passed since the last recorded sync.

func (*Catalog) RunTrends added in v0.2.12

func (c *Catalog) RunTrends(ctx context.Context)

RunTrends has an independent schedule: a slow catalogue crawl cannot delay charts. App lifetime owns and joins it, and only the cluster leader polls.

func (*Catalog) Search

func (c *Catalog) Search(ctx context.Context, query string, types []core.MediaType, limit int) ([]core.Title, error)

Search reaches all of TMDB; matched titles join the browsable set.

func (*Catalog) SeedGenres

func (c *Catalog) SeedGenres(mediaType string, m map[int]string)

SeedGenres pre-populates the genre id->name cache (tests, offline warm-up).

func (*Catalog) ShowTrajectories added in v0.2.7

func (c *Catalog) ShowTrajectories(ctx context.Context, shows []core.RatingKey) (map[core.RatingKey]core.ShowTrajectory, error)

ShowTrajectories computes season/episode rating trajectories for a batch of shows from local mirror data without contacting TMDB.

func (*Catalog) Similar

func (c *Catalog) Similar(ctx context.Context, key core.RatingKey, limit int) ([]core.Title, error)

Similar returns TMDB recommendations ∪ similar (recommendations first).

func (*Catalog) StandaloneEpisodes added in v0.2.7

func (c *Catalog) StandaloneEpisodes(ctx context.Context, minRating float64, limit int) ([]core.Title, error)

StandaloneEpisodes returns acclaimed mirrored episodes from anthology series or top-rated standalone chapters that can be watched without a multi-season commitment.

func (*Catalog) Stats

func (c *Catalog) Stats(ctx context.Context) (Stats, error)

Stats returns mirror counts, last sync time and current sync progress.

func (*Catalog) Sync

func (c *Catalog) Sync(ctx context.Context) error

func (*Catalog) SyncProgress

func (c *Catalog) SyncProgress() SyncProgress

SyncProgress returns the current sync progress snapshot.

func (*Catalog) TrendStatus added in v0.2.12

func (c *Catalog) TrendStatus(ctx context.Context) []TrendStatus

func (*Catalog) UpdateConfig

func (c *Catalog) UpdateConfig(cfg config.Catalog)

UpdateConfig swaps the catalogue config (wire to config.Store.Subscribe).

func (*Catalog) UpsertTitle

func (c *Catalog) UpsertTitle(ctx context.Context, t core.Title, browsable bool) error

UpsertTitle stores a title (exported for tests/seeders and other components that need to insert known titles). Browsable/added_at are preserved once set.

func (*Catalog) Visible added in v0.2.12

func (c *Catalog) Visible(t *core.Title) bool

func (*Catalog) WarmDetails added in v0.2.7

func (c *Catalog) WarmDetails(ctx context.Context, limit int) (int, error)

WarmDetails enriches up to limit browsable titles whose detailed metadata (credits, crew, keywords, studios, runtime, content rating, seasons and crowd recommendations) has not been fetched yet, plus returning series whose detail TTL has expired. New releases (<120 days old) are warmed first, followed by the most popular and most-voted titles. Settled titles are marked detail=1 and never re-queried.

type Estimate

type Estimate struct {
	Movies int `json:"movies"`
	Shows  int `json:"shows"`
	Total  int `json:"total"`
}

Estimate is a rough catalogue-size prediction for given thresholds. It is a heuristic from TMDB's documented popularity behaviour (a daily- recomputed, heavily right-skewed score) and public catalogue counts. Treat it as order-of-magnitude guidance for the setup UI, not a measurement.

func EstimateCatalogSize

func EstimateCatalogSize(minPopularity float64, minVotes int) Estimate

EstimateCatalogSize estimates browsable titles with MeetsThreshold's OR rule. The union of the popularity set and vote set overlaps substantially; the estimate uses max + 40% of the smaller set as the non-overlapping remainder.

type Mirror

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

Mirror exposes bounded local reads for catalogue consumers. It owns schema knowledge; callers never compose SQL against catalog_* tables.

func NewMirror

func NewMirror(db *sql.DB, now func() time.Time) *Mirror

func (*Mirror) CollectionArt

func (m *Mirror) CollectionArt(ctx context.Context, id int64) (core.Collection, error)

func (*Mirror) Fingerprint

func (m *Mirror) Fingerprint(ctx context.Context) (MirrorFingerprint, error)

func (*Mirror) Franchises

func (m *Mirror) Franchises(ctx context.Context) ([]core.Collection, error)

func (*Mirror) Snapshot

func (m *Mirror) Snapshot(ctx context.Context) ([]MirrorItem, error)

func (*Mirror) Titles

func (m *Mirror) Titles(ctx context.Context, keys []core.RatingKey) ([]core.Title, error)

Titles returns hydrated, released, non-adult mirror entries in requested order. It never fetches remote metadata, so collection serving stays bounded and offline.

type MirrorFingerprint

type MirrorFingerprint struct {
	Count            int
	MaxFetch, MaxAdd int64
}

type MirrorItem

type MirrorItem struct {
	Title    core.Title
	AddedAt  int64
	Detailed bool
}

type Option

type Option func(*Catalog)

Option customises a Catalog.

func WithCrowdTTL

func WithCrowdTTL(d time.Duration) Option

WithCrowdTTL overrides how long co-engagement lists stay fresh.

func WithListTTL

func WithListTTL(d time.Duration) Option

func WithNow

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

func WithResumeSchedule

func WithResumeSchedule() Option

WithResumeSchedule makes Run continue the refresh schedule recorded in the database instead of syncing as soon as it starts: the first sync waits until RefreshInterval has passed since the last successful sync (by any node). A cluster uses it because Run restarts on every leader handoff; with no recorded sync Run still syncs at once.

func WithStaleAfter

func WithStaleAfter(d time.Duration) Option

func WithSyncYears

func WithSyncYears(from, to int) Option

WithSyncYears bounds the year slices used when discover exceeds 500 pages.

type Stats

type Stats struct {
	Trending        []TrendStatus `json:"trending"`
	Movies          int           `json:"movies"`
	Shows           int           `json:"shows"`
	Seasons         int           `json:"seasons"`
	Episodes        int           `json:"episodes"`
	BrowsableMovies int           `json:"browsableMovies"`
	BrowsableShows  int           `json:"browsableShows"`
	Collections     int           `json:"collections"`
	LastSync        time.Time     `json:"lastSync,omitempty"`
	Sync            SyncProgress  `json:"sync"`
}

Stats is the admin-UI snapshot of the mirror.

type SyncProgress

type SyncProgress struct {
	Running     bool      `json:"running"`
	Phase       string    `json:"phase,omitempty"` // e.g. "movie/popularity", "tv/votes/1994"
	PagesDone   int       `json:"pagesDone"`
	TitlesSeen  int       `json:"titlesSeen"`
	TitlesAdded int       `json:"titlesAdded"`
	StartedAt   time.Time `json:"startedAt,omitempty"`
	FinishedAt  time.Time `json:"finishedAt,omitempty"`
	LastError   string    `json:"lastError,omitempty"`
}

SyncProgress describes a running or last-finished catalogue sync.

type TMDB

type TMDB interface {
	MovieDetails(ctx context.Context, id int64) (*tmdb.MovieDetails, error)
	TVDetails(ctx context.Context, id int64) (*tmdb.TVDetails, error)
	SeasonDetails(ctx context.Context, tvID int64, season int) (*tmdb.SeasonDetails, error)
	EpisodeExternalIDs(ctx context.Context, tvID int64, season, episode int) (*tmdb.ExternalIDs, error)
	CollectionDetails(ctx context.Context, id int64) (*tmdb.CollectionDetails, error)
	DiscoverMovies(ctx context.Context, p tmdb.DiscoverParams) (*tmdb.Paged[tmdb.Movie], error)
	DiscoverTV(ctx context.Context, p tmdb.DiscoverParams) (*tmdb.Paged[tmdb.TVShow], error)
	SearchMovies(ctx context.Context, q string, page int) (*tmdb.Paged[tmdb.Movie], error)
	SearchTV(ctx context.Context, q string, page int) (*tmdb.Paged[tmdb.TVShow], error)
	SearchMulti(ctx context.Context, q string, page int) (*tmdb.Paged[tmdb.MultiResult], error)
	TrendingMovies(ctx context.Context, window string, page int) (*tmdb.Paged[tmdb.Movie], error)
	TrendingTV(ctx context.Context, window string, page int) (*tmdb.Paged[tmdb.TVShow], error)
	TrendingAll(ctx context.Context, window string, page int) (*tmdb.Paged[tmdb.MultiResult], error)
	MovieList(ctx context.Context, list string, page int) (*tmdb.Paged[tmdb.Movie], error)
	TVList(ctx context.Context, list string, page int) (*tmdb.Paged[tmdb.TVShow], error)
	MovieSimilar(ctx context.Context, id int64, page int) (*tmdb.Paged[tmdb.Movie], error)
	MovieRecommendations(ctx context.Context, id int64, page int) (*tmdb.Paged[tmdb.Movie], error)
	TVSimilar(ctx context.Context, id int64, page int) (*tmdb.Paged[tmdb.TVShow], error)
	TVRecommendations(ctx context.Context, id int64, page int) (*tmdb.Paged[tmdb.TVShow], error)
	FindByExternalID(ctx context.Context, source, id string) (*tmdb.FindResult, error)
	Genres(ctx context.Context, mediaType string) ([]tmdb.Genre, error)
}

TMDB is the subset of the TMDB client the catalogue needs. *tmdb.Client satisfies it.

type TrendStatus added in v0.2.12

type TrendStatus struct {
	Source      string         `json:"source"`
	Scope       string         `json:"scope"`
	Window      string         `json:"window"`
	MediaType   core.MediaType `json:"mediaType"`
	Titles      int            `json:"titles"`
	FetchedAt   *time.Time     `json:"fetchedAt,omitempty"`
	AttemptedAt *time.Time     `json:"attemptedAt,omitempty"`
	State       string         `json:"state"`
}

type VisibilityPreview added in v0.2.12

type VisibilityPreview struct {
	Total          int          `json:"total"`
	Hidden         int          `json:"hidden"`
	Movies         int          `json:"movies"`
	Shows          int          `json:"shows"`
	UnknownCountry int          `json:"unknownCountry"`
	Items          []core.Title `json:"items"`
}

Jump to

Keyboard shortcuts

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