Documentation
¶
Overview ¶
Package cinemeta is Mosaic's default metadata provider: a client of Cinemeta, Stremio's public film and television metadata service, filling the metadata, search and catalog provider roles (ADR 0027).
It is a **core module** (ADR 0062) under the guarantee clause. Metadata and search are a required capability class (ADR 0035) — a Mosaic that cannot identify or find content reads as broken rather than as unconfigured — so at least one provider must be present in every binary, with no install step that can fail and no configuration that can be omitted. This module is that provider. It needs no credential, no key and no addon URL: constructing it is all the configuration there is.
The tier is a delivery decision, not a contract decision. This module is shaped exactly like an extension module — its own Go repository importing only the published SDK and the standard library — and it does not know which tier it is in.
Why it exists rather than the arrangement it replaces ¶
The metadata Mosaic shipped with was a Cinemeta addon URL bundled *inside* module-stremio-addons, which is an extension module: a default belonging to something a deployment might not install, reached through a general addon protocol and a user-managed addon list that could be emptied. ADR 0035 recorded that placement as unresolved — "whether the default belongs to the Platform or to the module is a question this record answers one way and the code answers the other" — and ADR 0062 answered it the other way by making a metadata provider core. A guarantee cannot be delegated to a module that is not guaranteed to be there.
So this is not a second Stremio client. It is a direct client of one service, with no manifest negotiation, no addon list, no resource discovery and no ordering policy — none of which a fixed, known upstream needs. What it gives up in generality it gains in being unable to be misconfigured.
What it provides ¶
- RoleMetadata — descriptive detail for a ref: overview, genres, IMDb rating, runtime, poster/backdrop/clearlogo, billed cast names, and for a series a per-episode preview with stills and synopses.
- RoleSearch — free-text search over film and television. The other half of the required class, and the half that produces a ref at all: without it nothing could name the content the metadata role answers for, which is why ADR 0035 makes the two one class rather than two.
- RoleCatalog — Cinemeta's Popular and Featured collections for both types, so a fresh install has rails to render rather than an empty home screen.
It fills no stream or subtitle role, and it fills no settings-UI role. Cinemeta describes content and does not index or host it, so an import through this module materialises a Work and its season/episode tree with **no Parts** — the meta-only shape the Platform already supports. A deployment that wants something to play installs a stream source alongside it.
What it deliberately cannot do ¶
Cinemeta has no clearart or banner artwork, no franchise collections, no "similar titles", and no character names or headshots on its cast. Those are ADR 0034's recorded gaps and they are structural to the source, not decoding this module skipped. A TMDB- or Fanart-class provider is what closes them; this module reports honestly empty fields rather than inventing.
It owns no schema (ADR 0012): everything it writes goes through ContentService, acting as the Caller the Platform hands it (ADR 0017).
Index ¶
- Constants
- type Capability
- func (c *Capability) CatalogItems(ctx context.Context, req v1.CatalogItemsRequest) (v1.CatalogItemsResponse, error)
- func (c *Capability) Catalogs(ctx context.Context, req v1.CatalogsRequest) (v1.CatalogsResponse, error)
- func (c *Capability) Import(ctx context.Context, svc v1.ContentService, req v1.ImportRequest) (v1.ImportResult, error)
- func (c *Capability) Manifest() v1.Manifest
- func (c *Capability) Metadata(ctx context.Context, req v1.MetadataRequest) (v1.ContentMetadata, error)
- func (c *Capability) Search(ctx context.Context, req v1.SearchRequest) (v1.SearchResponse, error)
- type CatalogDecl
- type Client
- func (c *Client) CatalogItems(ctx context.Context, catalogID, nativeType, genre string, skip int) ([]Preview, bool, error)
- func (c *Client) Catalogs() []CatalogDecl
- func (c *Client) Meta(ctx context.Context, nativeType, id string) (Title, error)
- func (c *Client) Search(ctx context.Context, text string, nativeTypes []string) ([]Preview, error)
- type Episode
- type Preview
- type Title
Constants ¶
const ( // CapabilityID is the id the Platform registers this module under, the id a // ref names to route an import back here, and the key any settings document // would be stored under (ADR 0021). There is no settings document — see New. CapabilityID = "cinemeta" )
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Capability ¶
type Capability struct {
// contains filtered or unexported fields
}
Capability is the Cinemeta metadata module. It holds an HTTP client and nothing else.
func New ¶
func New(httpClient *http.Client) *Capability
New builds the capability over an HTTP client (nil for a default). The Platform passes its own, which carries the netguard dial guard and the outbound telemetry seam (ADR 0055).
It takes no settings, and that is the point rather than an omission. Every provider role receives the module's settings document on each invocation (ADR 0021) and this module ignores it, because a guarantee-clause core module (ADR 0062) that can be configured is one that can be misconfigured: there is no key to be missing, no URL to be wrong, and no list a user can empty. The client is therefore built once here rather than per invocation, unlike a module whose configuration can change between two calls.
func (*Capability) CatalogItems ¶
func (c *Capability) CatalogItems(ctx context.Context, req v1.CatalogItemsRequest) (v1.CatalogItemsResponse, error)
CatalogItems lists one collection's entries as virtual candidates (ADR 0028). It touches no part of the object graph: browsing a source must not flood the library with everything the source knows about, and Cinemeta knows about everything.
func (*Capability) Catalogs ¶
func (c *Capability) Catalogs(ctx context.Context, req v1.CatalogsRequest) (v1.CatalogsResponse, error)
Catalogs lists the collections this module exposes (RoleCatalog). The set is fixed rather than fetched: Cinemeta declares its catalogs in a manifest, and half of them require an argument a browse surface addressing a catalog by id alone has no way to supply, so which of them are usable is a decision that belongs at this boundary rather than being rediscovered per request.
func (*Capability) Import ¶
func (c *Capability) Import(ctx context.Context, svc v1.ContentService, req v1.ImportRequest) (v1.ImportResult, error)
Import materialises the virtual item named by req.Ref — a result a search or catalog browse produced (ADR 0028) — into the object graph.
It creates the Work with its artwork and external id, binds the source, and builds the containment tree: a film as Work → feature item, a series as Work → season container → episode item. It attaches **no Parts**, which is the shape rather than a gap: Cinemeta knows what exists, not where to get it, so an import through this module is a described library that needs a stream source installed beside it before anything plays.
func (*Capability) Manifest ¶
func (c *Capability) Manifest() v1.Manifest
Manifest is the module's self-declaration, including the provider roles it fills (ADR 0027).
Three roles, and the three it does not declare are as deliberate: no stream and no subtitles, because Cinemeta describes content rather than indexing it; no settings UI, because there is nothing to set.
func (*Capability) Metadata ¶
func (c *Capability) Metadata(ctx context.Context, req v1.MetadataRequest) (v1.ContentMetadata, error)
Metadata resolves descriptive detail for a ref (RoleMetadata). It backs the detail screen for both a virtual result and an in-library node, and it is the detail Import draws on (ADR 0027, ADR 0034).
It is the role the required capability class (ADR 0035) is really about: with no provider filling it, a Mosaic can list what it owns and describe none of it.
func (*Capability) Search ¶
func (c *Capability) Search(ctx context.Context, req v1.SearchRequest) (v1.SearchResponse, error)
Search returns virtual candidates for free text (RoleSearch).
The media-type filter selects which of Cinemeta's catalogs is asked rather than filtering what came back, so a narrowed search is one request instead of two and cannot return a row it then has to discard.
type CatalogDecl ¶
CatalogDecl is one collection this module exposes.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a Cinemeta API client. It holds only an HTTP client: there is nothing to configure, so there is nothing else to hold.
func NewClient ¶
NewClient builds a client over an HTTP client (nil for a default). The Platform passes its own, which carries the netguard dial guard and the outbound telemetry seam (ADR 0055); a module that builds its own bypasses both.
func (*Client) CatalogItems ¶
func (c *Client) CatalogItems(ctx context.Context, catalogID, nativeType, genre string, skip int) ([]Preview, bool, error)
CatalogItems lists one catalog's entries. skip is an item offset, which is what Cinemeta's own paging parameter means, so it passes through untranslated. genre narrows the listing when non-empty, as the addon protocol's `genre` extra; the caller has already checked it against what the catalog declared.
It reports whether another page exists, and this is the **weaker** of the two statements the SDK describes: Cinemeta returns no total, so a full page is all there is to go on. Only the provider can make even that claim, because only the provider knows the page size — and its cost is bounded and visible, a final page of exactly a hundred asking for one more that comes back empty. The alternative is what was here before: paging built, and dead, because nothing ever said there was more.
func (*Client) Catalogs ¶
func (c *Client) Catalogs() []CatalogDecl
Catalogs is the set of collections this module exposes.
It is a curated subset of what Cinemeta's manifest declares, and the omissions are deliberate rather than an oversight. Cinemeta's "New" catalogs require a year to be supplied as a genre parameter, and its "Last videos" and "Calendar videos" catalogs require a list of series ids the caller must already hold — neither fits a browse surface that addresses a catalog by id alone (ADR 0028). What is left is the four that answer with no argument.
The names are Cinemeta's own, qualified by type. Its manifest calls both of the "top" catalogs "Popular", which is unambiguous in a client that shows film and television separately and useless in a flat list of collections.
func (*Client) Meta ¶
Meta fetches one title's full record.
One request is the whole story here, and it is worth contrasting with a metadata API that splits a record across endpoints: Cinemeta returns the description, artwork, rating, runtime, cast and — for a series — every episode of every season in a single document. That is the one thing this source is unambiguously better at.
func (*Client) Search ¶
Search queries Cinemeta's search-capable catalogs for free text and returns the union, de-duplicated by id.
nativeTypes selects which catalogs are asked — film, television or both. The media-type filter therefore *chooses the endpoint* rather than filtering results afterwards, which is one fewer round trip when a caller has narrowed the query. A type that errors is skipped rather than failing the search: half an answer beats none when the other half is a transient 502.
type Episode ¶
type Episode struct {
Season int
Episode int
Title string
Overview string
Thumbnail string
Released string
}
Episode is one episode of a series.
type Preview ¶
Preview is one search or catalog result — enough to render a row and to address the title afterwards.
type Title ¶
type Title struct {
ID string
NativeType string
Title string
Year int
Overview string
Poster string
Backdrop string
Logo string
Genres []string
Rating float64
Runtime string
// Cast is billed names, and names only. Cinemeta carries no character and no
// headshot for a credit, which is one of ADR 0034's recorded gaps rather than
// something this decode drops — so the type says so instead of offering
// fields that would always be empty.
Cast []string
// Episodes is populated for a series, in season/episode order.
Episodes []Episode
}
Title is one film or series, as fully as Cinemeta describes it.