mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
docs(tmdb): clarify user-provided API key policy (#1581)
This commit is contained in:
1 parent
d438f5c655
commit
40092f6efc
4 files changed
+31
-20
No files matched your search
@@ -1602,7 +1602,7 @@ stream_id`); it drops `series_id`/`movie_id`, so the builder pins the
|
||||
- Discover pages (clickable metadata chips, issue #1449): year, genre, and country chips on all four detail views (Xtream VOD/series, shared Stalker detail, Stalker series) are clickable when TMDB matched the item — the merges emit structured `tmdb_genres`/`tmdb_countries` (+ `tmdb_media_type` on Stalker), the year chip renders from provider data so it is gated on the navigation TARGET instead of the item's identity — `createDiscoverFacetNavigation()` offers a facet only when a playlist resolves AND `TmdbEnrichmentService.isEnabled()`, since Discover reads its results from TMDB (gating on `typeof tmdb_id === 'number'` is WRONG: the field is `number | string` and a provider-sent number satisfied it with enrichment never having run) — and clicks navigate via `discoverLink()` (`libs/portal/shared/util`) to the portal-scoped `discover` route (`?type&year&genre&genreLabel&country&countryLabel`). The route containers (`XtreamDiscoverRouteComponent`, `StalkerDiscoverRouteComponent`) clone the actor-page pattern: TMDB `/discover` top-5-pages by popularity via `TmdbDiscoverService` (session-only Map cache, never persisted to `tmdb_metadata`), matched against the catalog (Xtream in-memory index / all-portals `DB_MATCH_TITLES`; Stalker search-prefill), rendered by `DiscoverViewComponent`. Facets change via query params on the same route instance, so the discover load is guarded by recency (`createLatestRequestGuard()`) — A→B→A leaves two in-flight requests sharing one `discoverFacetKey()` — while the catalog match uses the guard for its spinner and the facet key for its results; availability also waits for catalog readiness (in-flight flags, not `isContentInitialized`, so a failed import still settles). See "Discover Pages" in `docs/architecture/tmdb-metadata-enrichment.md`
|
||||
- Actor page "All portals" scope (Electron only): batched `DB_MATCH_TITLES` worker op (trigram FTS over all imported Xtream playlists, `apps/electron-backend/src/app/database/operations/title-match.operations.ts`); `normalizeTitle` is shared renderer/worker via `libs/shared/interfaces/src/lib/title-normalization.util.ts`
|
||||
- All `DB_MATCH_TITLES` consumers (Trending rail, "Because you watched" recommendations rail, cross-portal Similar rail, actor "All portals" scope) resolve the worker's flat result list through the shared `groupTitleMatchesByKey()` + `pickTitleMatch()` in `libs/services/src/lib/catalog-title-match.service.ts`. The grouping keeps EVERY row per `type:exactNormalizedTitle` on purpose — the year that separates same-titled rows belongs to the lookup, which the grouping cannot see, so collapsing first made a catalog holding both "Dune 1984" and "Dune 2021" drop whichever copy the user actually owns. `pickTitleMatch` then ranks year-compatible rows by evidence (exact year → untagged → any compatible) across all title aliases at once; only the recommendations rail passes an alias (TMDB `original_title`, via `candidateLookup()`). Multi-source VOD discovery deliberately stays off these helpers: there every copy is a distinct selectable source, not one best answer
|
||||
- Opt-in via `Settings > Metadata (TMDB)` (sends titles to TMDB); the section also has a "check key" button and a cache panel (row count + payload size, with a clear button); optional user API key overrides the embedded default (`DEFAULT_TMDB_API_KEY` in `libs/services/src/lib/tmdb/tmdb-config.ts` — an empty placeholder in the repo by design; the real key lives in the `TMDB_API_KEY` GitHub Actions secret and is injected at CI build time by `tools/tmdb/inject-tmdb-key.mjs`)
|
||||
- Opt-in via `Settings > Metadata (TMDB)` (sends titles to TMDB); the section also has a "check key" button and a cache panel (row count + payload size, with a clear button). Distributed builds ship without a shared key; users supply their own. `DEFAULT_TMDB_API_KEY` in `libs/services/src/lib/tmdb/tmdb-config.ts` is empty by default; `tools/tmdb/inject-tmdb-key.mjs` still supports optional CI injection from `TMDB_API_KEY`, but is a no-op when it is unset. A user key takes precedence over any injected default; without either key, enrichment stays inactive even when enabled. Requiring a personal key is project policy, not a categorical TMDB terms restriction; see the canonical "Settings and API Key" section in `docs/architecture/tmdb-metadata-enrichment.md`.
|
||||
- Match confidence: a provider `tmdb_id` is a strong hint, not gospel — its payload is weighed against the item (`assessProviderId`: title or year agrees → use it; both years known and incompatible → the search may take over; title-only mismatch → keep it, since TMDB localizes titles). A 404 marks the id dead (`badProviderId:<id>` row); transient failures never do. Without a usable id: normalized-title + year (±1) search with a strict gate — no confident match means no enrichment
|
||||
- Detail views render provider data immediately; enrichment patches the selection asynchronously (staleness-guarded)
|
||||
- Cached in SQLite `tmdb_metadata` (Electron, via DB worker ops `DB_GET/SET_TMDB_METADATA`, plus `DB_GET_TMDB_CACHE_STATS` / `DB_CLEAR_TMDB_METADATA` behind the settings cache panel) or in-memory (PWA); localized via the app language setting. Search-match lookup keys are versioned, and connection startup removes obsolete unversioned rows once through the `migration:tmdb-search-lookup-v2-cache-cleanup:v1` app-state marker.
|
||||
|
||||
@@ -53,7 +53,7 @@ The application is a cross-platform, open-source project built with Electron and
|
||||
**Discovery & metadata**
|
||||
|
||||
- Global search across live TV, movies, and series _(desktop)_
|
||||
- TMDB enrichment (opt-in) — plots, cast & crew, trailers, ratings, artwork, a "Similar" rail, clickable actor pages, and a trending dashboard rail _(trending rail: desktop)_
|
||||
- TMDB enrichment (opt-in, requires your own TMDB API key) — plots, cast & crew, trailers, ratings, artwork, a "Similar" rail, clickable actor pages, and a trending dashboard rail _(trending rail: desktop)_
|
||||
- Dashboard with recently watched & continue-watching
|
||||
|
||||
**Organization**
|
||||
|
||||
@@ -24,7 +24,8 @@ Related:
|
||||
"Movie Recognition (VOD Detail View)" in
|
||||
`docs/architecture/m3u-playlist-module.md`.
|
||||
- Enrichment is **opt-in** via `Settings > Metadata (TMDB)` because it sends
|
||||
movie/series titles to a third-party API. Default: disabled.
|
||||
movie/series titles to a third-party API. Default: disabled. Users supply
|
||||
their own TMDB API key; distributed builds ship without a shared key.
|
||||
- The detail view renders provider data **immediately**; enrichment runs
|
||||
asynchronously and patches the selected item once TMDB responds. A
|
||||
staleness guard drops responses that arrive after the user navigated away.
|
||||
@@ -44,7 +45,7 @@ store imports):
|
||||
|
||||
| File | Responsibility |
|
||||
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `tmdb-config.ts` | API/image base URLs, embedded default API key, cache TTLs, app-language → TMDB-language mapping |
|
||||
| `tmdb-config.ts` | API/image base URLs, empty default API key placeholder, cache TTLs, app-language → TMDB-language mapping |
|
||||
| `tmdb.types.ts` | TMDB v3 response shapes (search, details with credits) |
|
||||
| `tmdb-api.service.ts` | Thin `fetch`-based client (TMDB supports CORS; works in Electron renderer and PWA). Accepts v3 keys (`api_key` param) and v4 tokens (Bearer) |
|
||||
| `tmdb-matcher.ts` | Title normalization, year extraction, and the match-confidence gate (pure functions) |
|
||||
@@ -505,8 +506,7 @@ The panel's full path — settings button, preload bridge, DB worker, real
|
||||
SQLite — is covered by `@settings @electron @persistence sizes and clears
|
||||
the TMDB metadata cache` in `apps/electron-backend-e2e/src/settings.e2e.ts`.
|
||||
It seeds a row through `dbSetTmdbMetadata` rather than through enrichment,
|
||||
which needs an API key that builds outside the release pipeline do not
|
||||
carry.
|
||||
so the test does not depend on a TMDB API key or external API access.
|
||||
|
||||
The PWA uses a session-scoped in-memory map (acceptable for phase 1; TMDB
|
||||
supports CORS so the PWA calls the API directly).
|
||||
@@ -515,7 +515,7 @@ supports CORS so the PWA calls the API directly).
|
||||
|
||||
`Settings.tmdb?: { enabled: boolean; apiKey?: string }`
|
||||
(`libs/shared/interfaces/src/lib/tmdb.interface.ts`). The settings page has
|
||||
a "Metadata (TMDB)" section: enable toggle, optional API key override with a
|
||||
a "Metadata (TMDB)" section: enable toggle, user-provided API key with a
|
||||
"check key" button (validates against `/configuration`), the M3U
|
||||
movie-recognition toggle (root-level `Settings.m3uVodDetails`, shown only
|
||||
while TMDB is enabled and bound via `[formControl]` because it is not part of
|
||||
@@ -525,19 +525,30 @@ lot. Sizing is a full table scan, so it runs only once that section is the
|
||||
active one, and a failed read or clear says so instead of showing an empty
|
||||
cache.
|
||||
|
||||
The embedded default key lives in `DEFAULT_TMDB_API_KEY`
|
||||
(`libs/services/src/lib/tmdb/tmdb-config.ts`) and is an **empty placeholder
|
||||
in the repository by design**: the real key is stored in the `TMDB_API_KEY`
|
||||
GitHub Actions secret and injected at CI build time by
|
||||
`tools/tmdb/inject-tmdb-key.mjs` (step "Inject TMDB API key" in
|
||||
`build-and-make.yaml`, before the frontend build). Rationale: TMDB keys are
|
||||
free and extractable from any client binary regardless, but keeping the key
|
||||
out of the public repo prevents trivial scraping and fork propagation. Never
|
||||
commit a real key; never reuse keys found in other repositories.
|
||||
Distributed builds ship **without a shared TMDB API key**. To use
|
||||
metadata enrichment, users enable it and enter their own key under
|
||||
`Settings > Metadata (TMDB)`. Key registration is available through the
|
||||
[TMDB account settings](https://www.themoviedb.org/settings/api); see the
|
||||
[TMDB API FAQ](https://developer.themoviedb.org/docs/faq).
|
||||
|
||||
With no key available (empty default and no user override in settings),
|
||||
enrichment stays inactive even when the toggle is on — fork PRs and local
|
||||
dev builds fall into this mode automatically.
|
||||
This is a project distribution policy, not a claim that TMDB's terms
|
||||
categorically prohibit embedding an application key. A key shipped in a
|
||||
client can be extracted and misused; revoking a shared key could interrupt
|
||||
metadata access for everyone using it. TMDB also applies
|
||||
[service rate limits](https://developer.themoviedb.org/docs/rate-limiting),
|
||||
so a personal key does not remove the need to respect throttling.
|
||||
|
||||
`DEFAULT_TMDB_API_KEY` in `libs/services/src/lib/tmdb/tmdb-config.ts` is
|
||||
empty by default. The build tooling still supports optional injection:
|
||||
`tools/tmdb/inject-tmdb-key.mjs` reads `TMDB_API_KEY` during the "Inject
|
||||
TMDB API key" step in `.github/workflows/build-and-make.yaml`. With no
|
||||
non-empty secret, that step is a no-op. Its presence does not mean release
|
||||
builds include a key; leave the secret unset for the no-shared-key policy.
|
||||
Never commit a real key or reuse keys found in other repositories.
|
||||
|
||||
At runtime, a non-empty user key takes precedence over an injected default.
|
||||
With neither available, enrichment stays inactive even when the toggle is
|
||||
on. This applies to release, local development, and fork builds alike.
|
||||
|
||||
## Failure Behavior
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ Canonical titles in grids, rating/year badges on cards, genre/decade browse, fam
|
||||
|
||||
**Blockers, all hard:**
|
||||
|
||||
- **The shared release key.** `.github/workflows/build-and-make.yaml:402-406` injects one `TMDB_API_KEY` into every installed copy. Bulk backfill on that key is exactly the traffic shape TMDB throttles ("upper limits to help mitigate needlessly high bulk scraping"). Must be **hard-gated to a user-supplied personal key**, not merely "explicit consent".
|
||||
- **User-supplied key and throttling.** Distributed builds ship without a shared key; the workflow retains optional injection support (see [Settings and API Key](./tmdb-metadata-enrichment.md#settings-and-api-key)). Bulk backfill must require a user-supplied key even in a build with an injected default. A personal key does not exempt bulk traffic from [TMDB rate limits](https://developer.themoviedb.org/docs/rate-limiting); explicit consent alone is insufficient.
|
||||
- **No 429/backoff machinery exists** (Theme A5 is a prerequisite).
|
||||
- **Per-item IPC.** 40k warm writes = 40k+ worker round-trips. Batched cache ops are a prerequisite, not an optimization.
|
||||
- **6-month retention** must ship _with_ it, not after — bulk warming is precisely a mechanism for maximizing held TMDB data.
|
||||
|
||||
Reference in new issue
Block a user