Commit Graph
11 Commits
Author SHA1 Message Date
4gray 0e4e1d2169 chore(release): begin 0.25 development and publish 0.24 article (#1674) 2026-09-26 17:13:13 +02:00
4grayandClaude Opus 5 cb37f628ed fix(pwa): drop the retired demo URL from backend CORS and og:url defaults
The public demo deployment is paused for good, so the production CLIENT_URL
fallback allowed an origin nobody can reach — a manual (non-Docker) self-host
failed every provider request with a CORS error. Default to the documented
http://localhost:4333 instead, and point og:url at the project website.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 09:58:06 +02:00
4grayandClaude Fable 5 dc05a2566e feat(tmdb): opt-in TMDB metadata enrichment for Xtream and Stalker portals (#1123)
* feat(tmdb): opt-in TMDB metadata enrichment for Xtream and Stalker portals

Adds an opt-in TMDB integration (Settings > Metadata) that enriches
detail views with a field-level merge — the provider stays authoritative
for stream data, TMDB fills editorial fields when the match is confident.

Enrichment:
- Movie/series details: plot, cast (avatar chips), director, genres,
  rating, poster/backdrop, official YouTube trailers
- Confidence-gated matching: provider tmdb_id trusted; otherwise
  normalized-title search with year gate (±1; series accept earlier
  premieres), season-suffix stripping, Cyrillic search-language override,
  and language-prefix fallback variants
- Lazy season/episode enrichment: real episode names, overviews, stills
- "Similar" rail (Xtream): TMDB recommendations matched to the catalog
- Actor pages per portal with full filmography, availability filter and
  an Electron-only "All portals" scope backed by a batched DB_MATCH_TITLES
  worker op over the trigram FTS index

Infrastructure:
- SQLite cache table tmdb_metadata (details, search verdicts, seasons,
  persons; per-language, TTL-guarded), in-memory fallback for the PWA
- Settings: enable toggle, own-API-key override with a live "check key"
  button; TMDB attribution in Settings and About
- Embedded key stays an empty placeholder; CI injects TMDB_API_KEY via
  tools/tmdb/inject-tmdb-key.mjs when the secret is configured
- normalizeTitle shared between renderer and DB worker
- CSP: allow YouTube embeds (frame-src was 'none'; trailers never worked)

Fixes and refactors along the way:
- fix(stalker): Advanced Search sent bare get_ordered_list requests and
  skipped the auth handshake when isFullStalkerPortal was missing on the
  active-playlist meta — full portals answered "Authorization failed."
  and search looked empty; now mirrors the catalog request shape and
  routes through makeAuthenticatedRequest with URL-based detection
- fix(stalker): TMDB fields survive info re-normalization; detail views
  prefer the store copy patched by async enrichment over stale snapshots
- refactor(xtream): split oversized vod/serial detail components into
  component-scoped playback services; detail routes re-initialize on
  route param changes (router reuses them for detail-to-detail nav)
- i18n: all new keys translated across the 18 locales

Docs: docs/architecture/tmdb-metadata-enrichment.md + CLAUDE.md updates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(tmdb): provide route params observable to inline collection details, linearize regexes

The global-collection inline detail host builds a fake ActivatedRoute for
VodDetailsRouteComponent/SerialDetailsComponent with only snapshot.params.
Since the detail components now read route.params via toSignal() (detail->
detail re-init), the missing observable crashed component construction and
the content hero never rendered — broke dashboard-activation, favorites and
recent Electron E2E on all platforms. Provide the params observable
alongside the snapshot and assert it in the component spec.

Also resolves both CodeQL js/polynomial-redos alerts: bracket-stripping in
normalizeTitle now excludes opening delimiters inside the classes, and
youtubeEmbedUrl extracts watch?v= ids with a linear two-pass match instead
of "watch\?.*v=". Combining-diacritics range rewritten as explicit \u
escapes (greptile note).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(tmdb): surface TMDB-only VOD score in the rating badge, drop youtube.com from CSP

Review follow-ups on PR #1123: the Xtream VOD detail badge renders
rating_imdb, but the merge wrote the TMDB score only into `rating`, so a
TMDB-only score was never displayed (Codex P2) — fill rating_imdb when the
provider left it empty, mirroring the Stalker merge. All trailer iframes
are normalized to youtube-nocookie.com, so the extra youtube.com frame-src
allowance was dead surface (greptile) — removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(tmdb): resolve confirmed review findings — matching correctness, race guards, cache schema

Fixes the confirmed findings from the PR #1123 code review:

- Stalker search: setSelectedContentType now runs BEFORE setSelectedItem,
  so the TMDB enrichment gate in the selection hook no longer sees the
  content type of the previously open tab (wrong/no enrichment after
  ITV -> search -> movie).
- Title normalization is now two-tier (normalizeTitleKeys): the exact
  normalized form keeps a trailing year, the base form strips it and
  remembers the tag. Year stripping is anchored to the end of the title
  ("2001: A Space Odyssey" keeps its year) and language-prefix stripping
  is UPPERCASE-only ("It: Chapter Two" is no longer amputated).
- All catalog matching (similar rail, actor pages, DB worker
  DB_MATCH_TITLES) compares exact forms first and only accepts
  year-stripped matches when the stripped tag is year-compatible (+-1)
  with the TMDB year — "Blade Runner" (1982) can no longer claim a
  catalog "Blade Runner 2049". CatalogTitleMatch carries the stripped
  trailingYear so the renderer can apply the guard to worker matches.
- mergedBackdrops tolerates a plain-string backdrop_path; enrichment
  merge+patch blocks are wrapped in try/catch so a malformed provider
  payload can no longer become an unhandled rejection.
- loadGlobalMatches (both actor routes) guards against actor->actor
  navigation races — a slow match for the previous person no longer
  overwrites the current one's results.
- tmdb_metadata media_type CHECK widened to ('movie','tv','person') and
  person rows now use the honest 'person' type (TmdbCacheMediaType).
  Pre-release dev DBs with the narrow CHECK are rebuilt in place — the
  table is a pure cache, so the migration is a self-healing
  drop-and-recreate keyed off sqlite_master.

Docs updated (tmdb-metadata-enrichment.md, CLAUDE.md). New regression
coverage: title-normalization.util.spec.ts, two-tier cases in
tmdb-similar.util.spec.ts and title-match.operations.spec.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 17:07:46 +02:00
4gray fc449ca73e feat(electron): harden renderer security
Scope playback request header overrides to active stream origins and document the Electron runtime security contract.
2026-05-22 23:31:41 +03:00
4gray 32cc0b74e8 feat: add self-hosted PWA backend runtime (#944)
* feat(web-backend): add self-hosted proxy app

* feat(web): enable runtime PWA configuration

* test(web-e2e): cover self-hosted backend proxy

* test(web-e2e): clean self-hosted lint warnings

* fix(web-backend): validate proxied provider URLs

* fix(web-backend): proxy through registered provider targets

* fix(web-backend): document CodeQL SSRF validation boundary

* test(web-e2e): mock provider target registration

* fix(web): address self-hosted review feedback
2026-05-16 00:10:03 +02:00
4grayandClaude Opus 4.7 bbbe7b4036 perf(renderer): inline splash + sync language hint + drop legacy polyfill
Three first-paint fixes for cold app start.

#1 — Inline splash in index.html
Cold start used to show a blank Material-grey background until the
~1.5MB preloaded chunk + 300KB styles.css downloaded and Angular
bootstrapped. On slow disks/networks that's 1-3+ s of "is the app
frozen?" before any pixel of UI appears.

Add a self-contained splash (inline CSS, no extra HTTP, no assets) —
"IPTVnator" wordmark + a CSS spinner — that paints immediately from
the parsed HTML. Removed in main.ts after bootstrapApplication()
resolves, gated on requestAnimationFrame so the swap happens after
AppComponent's first paint (no flash of empty background between
splash removal and the real UI).

Respects prefers-reduced-motion (spinner stops, opacity dims).

#3 — Drop legacy `global = window` polyfill
The inline <script> in <head> ("if (global === undefined) var global =
window") was a workaround for an older library expecting Node's
`global`. Verified nothing in the current bundle reads window.global
(the only matches are TypeScript `declare global { ... }` blocks and
unrelated route paths). Removes one render-blocking inline <script>
parse from the critical path.

#5 — Synchronous language hint
Settings live in IndexedDB (via @ngx-pwa/local-storage), which the
TranslateModule cannot read synchronously at bootstrap. So the first
render historically used English regardless of the user's preference,
then re-rendered every TranslatePipe in the tree once initSettings()
finished — visible flash for non-English users.

Mirror the active language to localStorage in initSettings() once the
IDB-backed settings resolve. Add getInitialLanguage() in app.config.ts
that reads that key synchronously before bootstrap and passes it to
TranslateModule.forRoot({ defaultLanguage }). First-ever boot still
falls back to English (no hint yet); every subsequent boot renders in
the saved language from the very first frame.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f957cd9849e0
2026-05-03 00:00:06 +02:00
4gray a17ed73806 feat(fonts): replace Google Fonts with bundled font imports and update typography settings 2026-03-28 15:45:06 +01:00
4gray e279172dfe fix(ui): correct fonts, semicolons and preload target
- Add missing semicolon in apps/web/src/styles.scss to fix invalid CSS
  that could break style parsing for .scroll-viewport.
- Change preconnect in apps/web/src/index.html from fonts.gstatic.com to
  fonts.googleapis.com and consolidate multiple Google Fonts links into 
  a single stylesheet URL to reduce redundant requests and ensure 
  Material Icons and selected font families load together.
  Remove duplicated @import of Outfit in
  
  libs/ui/components/src/lib/multi-epg/multi-epg-container.component.scss, 
  switch component font-family to Roboto (with fallback) and update 
  button font to match, ensuring consistent typography and avoiding 
  external import duplication across components. ese changes fix a 
  ntax error, optimize font loading, and standardize
typography across the UI.
2025-11-23 18:50:46 +01:00
4gray 09ad2c3b1b refactor(ui): tidy imports, simplify cleanup, and format styles 2025-11-23 18:50:46 +01:00
4gray 7028ff4049 refactor(nx): app restructuring 2025-10-13 00:24:54 +02:00
4gray 99ed5df404 chore: back to electron + nx 2025-10-07 18:02:13 +02:00