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

catalog

package
v0.2.6 Latest Latest
Warning

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

Go to latest
Published: Oct 21, 2026 License: GPL-3.0 Imports: 16 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.

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

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 {
	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.

Jump to

Keyboard shortcuts

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