Commit Graph
58 Commits
Author SHA1 Message Date
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
4grayandClaude Fable 5 57ba1977a1 test: close coverage gaps in persistence, portal stores, EPG, players, and E2E (#1124)
* test(db): cover playback positions, recently viewed, and connection migrations

Add specs for the previously untested persistence paths: playback-position
and recently-viewed operations (upsert/dedup/ordering/scoped deletes),
shared-database path-utils, createTables and the tolerant column/index
migrations incl. Xtream cache deduplication. Exposes createTables through
the existing __databaseConnectionTestHooks object.

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

* test(xtream): cover the Electron DB-first data source and favorites guards

Add specs for electron-xtream-data-source (DB-hit vs cold-cache paths,
concurrent request dedup, error propagation, full method delegation) and
extend the favorites feature spec with the invalid-input and
content-not-found guard paths.

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

* test(stalker): cover favorites and recent store features

Add specs for with-stalker-favorites and with-stalker-recent: payload
normalization and id/title fallbacks, series-mode category forcing, meta
sync dispatches, snackbar/callback side effects, and error paths.

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

* test(epg): cover archive/summary utils and the EPG worker service

Add specs for the pure catch-up window and summary-progress helpers shared
by the EPG panels, and for epg-worker.service: in-flight dedup by URL,
double-settle guard, progress-aware timeouts, worker lifecycle and error
broadcasting. Also settle an interrupted fetch in epg.events.spec that
caused "Cannot log after tests are done" in longer runs.

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

* test(player): cover the VLC session service

Mirror the MPV session spec patterns for VLC: enqueue-command building and
RC response parsing, launch argv construction, instance reuse over the RC
socket, exit-code handling, and the retry-without-RC fallback.

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

* test(e2e): add downloads page and EPG timeline interaction coverage

Downloads: empty state without sources, and a full lifecycle - authorize a
folder via a stubbed native dialog, download from a local server, verify
the completed item and file on disk, remove it from the UI. Timeline: zoom
changes block widths and the on-air info affordance opens the programme
dialog with the correct title and watch-live action.

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

* test: split oversized specs to meet the file-size guideline

Address review feedback: extract shared drizzle mocks into
operations.test-helpers.ts and split the Electron data-source delegation
spec into delegation + user-data files. Pure reorganization - test counts
and assertions unchanged (29/13/10), all files now under 300 lines.

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

* test(e2e): wait for DB readiness before opening the downloads page

On slow CI runners (macOS) the renderer can query SQLite while the DB
worker is still creating tables, leaving the downloads page on its
skeleton state forever. Poll a playlist read until it succeeds before
navigating on a cold profile.

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

* fix(build): exclude *.test-helpers.ts from the electron-backend app tsconfig

The new operations.test-helpers.ts uses jest globals and broke the webpack
build and tsc typecheck, which compile every non-spec file in the app.
Exclude the test-helpers pattern alongside the existing spec exclusions.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 15:45:06 +02:00
4gray 007ae7028d fix(xtream): key catchup probes by output formats 2026-06-27 13:00:03 +02:00
4gray 1679373bb2 feat(xtream): auto-select stream output format 2026-06-27 12:42:53 +02:00
4gray 12f3f1fc05 fix(xtream): resolve catchup timeshift variants 2026-06-27 11:51:40 +02:00
4gray 89916af9d8 feat(search): add workspace global search with M3U support
Moves global search into a routed workspace view, adds M3U live/radio results, lazy pagination, and DB-backed matching improvements.
2026-06-20 23:05:48 +02:00
4gray ffc07146fe feat(xtream): sort and filter the VOD/series catalog by IMDb rating
Adds IMDb rating sort and minimum-rating filtering for Xtream VOD/series catalogs, consolidates refinement controls, and guards rating controls away from Live TV.
2026-06-20 13:50:56 +02:00
4gray 20d0f01428 fix(xtream): address portal review feedback 2026-06-14 13:47:42 +02:00
4gray 254879f92b fix(xtream): improve portal compatibility 2026-06-14 13:21:56 +02:00
4gray dfdb5bb8ad fix(playlist): save xtream details in pwa
- save Xtream playlist details through browser-safe metadata persistence in PWA\n- keep PWA Xtream data source cache in sync with current playlist metadata\n- cover dialog close timing, stale cache, and PWA data-source bootstrap regression
2026-06-13 16:04:54 +02:00
4gray 82c725ea5a refactor(playback): add playback position runtime bridge (#1009)
* refactor(playback): add playback position runtime bridge

* test(services): avoid angular testing import in playback specs
2026-05-25 01:14:11 +03:00
4gray 8cfc40f59b refactor(xtream): gate data source by sqlite capability 2026-05-22 13:35:03 +03:00
4gray 5cf7ab9358 refactor(xtream): use epg runtime capability only 2026-05-22 13:34:37 +03:00
4gray c571c57c5b refactor(runtime): centralize platform capabilities 2026-05-22 11:00:34 +03:00
4gray 36bce47764 merge: resolve master conflicts for pwa hardening
- merge origin/master into PR #964 and keep embedded MPV test on the isolated playback sub-entrypoint

- centralize EPG capability through DataService.supportsEpg and update PWA web-e2e expectations

- split BrowserAccessError copy between Electron and PWA diagnostics
2026-05-22 10:20:03 +03:00
4gray 5d355b6f10 fix(web): address strict mode review feedback 2026-05-22 03:11:14 +03:00
4gray 4ab8915483 chore(web): enable strict TypeScript mode 2026-05-22 02:58:12 +03:00
4gray 55efc24608 fix(pwa): harden self-hosted runtime boundaries 2026-05-22 02:09:57 +03:00
4gray 69b7bca9df fix(pwa): prefer xtream collection snapshots 2026-05-21 21:13:58 +03:00
4gray 6438eec49f fix(pwa): align xtream content identity 2026-05-21 20:18:39 +03:00
4gray ed0b6833d6 fix(pwa): persist xtream collection snapshots 2026-05-21 20:00:38 +03:00
4gray 9097ff5bc1 fix(pwa): align xtream recent clearing 2026-05-21 19:45:07 +03:00
4gray d1e057887f fix(pwa): avoid persisting xtream passwords 2026-05-21 19:41:16 +03:00
4gray ec098f98b1 fix(pwa): hydrate xtream user collections 2026-05-21 19:35:36 +03:00
4gray 1ea6914f98 fix(pwa): harden xtream identity normalization 2026-05-21 19:27:40 +03:00
4gray 1d4d793aac fix(pwa): restore xtream user collections 2026-05-21 19:23:25 +03:00
4gray 6829c667bd fix(pwa): restore xtream rail and vod navigation 2026-05-21 14:18:11 +03:00
4gray d24c77a143 chore(nx): enforce scoped workspace boundaries (#942) 2026-05-15 09:59:06 +02:00
4gray 8b3b1c9bb7 chore: merge master into xtream timezone fix 2026-05-09 16:15:01 +02:00
4gray 9261794b4a Merge pull request #902 from weeco/feat/xtream-xmltv-epg-fallback
feat(xtream): fall back to uploaded XMLTV when provider has no EPG
2026-05-09 13:06:51 +02:00
4gray 4020822b90 fix(xtream): prevent stale epg queue fetches 2026-05-09 12:08:14 +02:00
Eliran Sapir a3a4c6b5ea Fix Xtream section search across categories 2026-05-07 17:42:29 -04:00
weeco 505e75b67e fix(xtream): address review feedback on XMLTV fallback PR
- pruneEphemeralMaps now uses getCached() so TTL-expired entries also
  drop their XMLTV preview / channel-id mapping, instead of clinging
  to them until the next explicit cache read.
- portal-channels-list attaches a .catch() to the now-async enqueue so
  any future internal rejection surfaces in the console rather than
  bubbling silently as an unhandled promise rejection.
- XtreamXmltvFallbackService binds the bridge receiver when calling
  the extracted function reference, in case a preload wrapper relies
  on `this`.

Adds a regression test for the TTL-aware prune.
2026-05-07 08:52:59 -07:00
weeco e751e82c07 feat(xtream): fall back to uploaded XMLTV when provider has no EPG
Live TV channels in Xtream playlists go blank whenever the provider's
get_short_epg returns nothing, even when the user has working XMLTV
URLs in Settings. The XMLTV pipeline already populates epg_programs
but only the M3U module was reading it.

Wire those uploads up as a fallback for Xtream too: when the provider
returns nothing for a channel with an epg_channel_id, look it up in
the local table. A settings toggle flips the priority for users whose
curated XMLTV is better than the provider's auto guide.

EpgQueueService.enqueue() became async to batch the XMLTV lookup once
per viewport change. To keep the queue consistent under fast scroll,
all shared-state mutations now happen behind a generation counter so
only the latest call commits. Per-method bridge gating in the new
XtreamXmltvFallbackService keeps each path working when the preload
exposes only one of the two endpoints.
2026-05-07 08:52:59 -07:00
4gray c14b5d5aff fix(xtream): search all vod and series items
Entire-Checkpoint: 3f453cc0085a
2026-05-02 00:35:37 +02:00
4gray cbedb22698 refactor: enhance live stream layout with loading state and caching
- Added `XtreamCachedContentScope` type to manage content loading states.
- Updated live stream layout to show a loading skeleton when content is being fetched.
- Introduced styles for loading content in the live stream layout.
- Enhanced unit tests to cover loading states and caching behavior.
- Modified `XtreamWorkspaceRouteSession` to handle cached content loading and state management.
- Improved handling of optional connection values in playlist data.
- Updated workspace context panel to reflect import states and loading conditions.

Entire-Checkpoint: c2cea9c530e6
2026-04-30 18:54:58 +02:00
Eliran SapirandClaude Sonnet 4.6 c6562e6882 fix(xtream): guard against invalid timezone strings in catchup formatter
Intl.DateTimeFormat throws RangeError for non-IANA timezone values
(e.g. 'UTC+5', offset strings) that some Xtream servers return in
server_info.timezone. Wrap the formatter in a try-catch and fall back
to client local-time formatting on failure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 05:58:41 -04:00
Eliran SapirandClaude Sonnet 4.6 30b8533e3a fix(xtream): use server timezone when constructing catchup/timeshift URLs
Previously the server timezone returned by the Xtream API (`server_info.timezone`)
was fetched but never applied when formatting the start timestamp in catchup URLs.
This caused timeshift requests to use the client's local time instead of the
server's local time, resulting in wrong or missing archive content.

Changes:
- Store `server_info.timezone` on the playlist state after a successful portal
  status check (`with-portal.feature.ts`)
- Thread `serverTimezone?` through `resolveCatchupUrl` → `constructCatchupUrl`
  → `formatCatchupStartTime` in `XtreamUrlService`
- `formatCatchupStartTime` now uses `Intl.DateTimeFormat` with the server
  timezone when available, falling back to local time when not set
- Pass `playlist.serverTimezone` from `LiveStreamLayoutComponent.playCatchup`
- Add `serverTimezone?` field to `XtreamPlaylistData` interface

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 05:51:42 -04:00
4gray a2e2959b89 fix(xtream): pagination behavior (back & scroll)
Entire-Checkpoint: c6e522b4276c
2026-04-25 14:11:36 +02:00
4gray 7caa18308d feat: enhance unified collection with workspace commands and backdrop handling
- Added WorkspaceViewCommandService to UnifiedCollectionPageComponent for workspace layout commands.
- Implemented clear current view command for favorites and recent items.
- Updated unified favorites data service to use buildXtreamCollectionUid for UID generation.
- Enhanced unified recent data service to normalize timestamps and handle backdrop URLs.
- Introduced withRecentItems feature to manage recent item additions with backdrop support.
- Updated VodDetailsRouteComponent and SerialDetailsComponent to backfill content backdrops.
- Modified IXtreamDataSource interface to include backdrop URL handling for favorites and recent items.
- Added tests for recent items feature to ensure correct functionality and backdrop handling.
2026-04-22 22:23:50 +02:00
4gray c8c93798f9 feat: enhance playlist backup export functionality and UI feedback
Entire-Checkpoint: c6e522b4276c
2026-04-21 08:54:03 +02:00
4gray ff605fc024 fix(xtream): enhance content retrieval by adding content type filtering for Xtream ID lookups #756
Entire-Checkpoint: c6e522b4276c
2026-04-19 22:33:49 +02:00
4gray faa4e2beb2 refactor: update searchContent method to accept flexible parameters and clean up XtreamStore
Entire-Checkpoint: c6e522b4276c
2026-04-16 21:57:18 +02:00
4gray 4175fc57b1 feat(xtream): enhance VOD details handling with fallback mechanisms and metadata validation
- Updated XtreamUrlService to handle missing stream IDs and extensions gracefully.
- Modified XtreamSelectionItem interface to allow for empty info arrays and null values.
- Introduced findVodCatalogItem function in XtreamStore for better catalog item retrieval.
- Added unit tests for fallback view model generation in vod-details-fallback.util.spec.ts.
- Created vod-details-fallback.util.ts to manage fallback view model construction and metadata checks.
- Updated vod-details-route.component.html to display fallback information when metadata is unavailable.
- Enhanced styling for fallback notes and status indicators in vod-details-route.component.scss.
- Implemented comprehensive tests for VodDetailsRouteComponent to ensure proper rendering of fallback and detailed views.
- Refactored xtream-vod-details.interface.ts to include new utility functions for metadata extraction.
2026-04-11 22:32:50 +02:00
4gray 76e4e1b032 feat: enhance playlist management by adding optional title input and updating URL submission logic 2026-04-06 17:57:04 +02:00
4gray c58c7468e2 feat: add offline cache availability checks and enhance hasUsableOfflineCache method 2026-04-06 12:57:54 +02:00
4gray 12eca90e4a feat: enhance Xtream data models and services with EPG support, including new fields for content and improved API handling 2026-04-05 19:04:40 +02:00
4gray a0a084fef7 feat: enhance content loading state management and improve recent playlists component tests 2026-04-04 20:42:22 +02:00
4gray 91cd650c1a feat: enhance EPG and portal features with error suppression and status resolution
- Added suppressErrorLog option to EPG API calls in withEpg feature.
- Introduced resolvePortalStatus function to streamline portal status checks in withPortal feature.
- Updated checkPortalStatus to return PortalStatusType instead of void.
- Enhanced XtreamStore to preserve cancelled content initialization block.
- Expanded XtreamState to include contentInitBlockReason for better state management.
- Added XtreamContentGateComponent to manage content initialization states.
- Implemented tests for XtreamContentGateComponent to ensure correct rendering based on contentInitBlockReason.
- Created PlaylistRefreshService to handle playlist refresh operations and events.
- Introduced new IPC commands for playlist refresh functionality.
- Added interfaces for playlist refresh events and payloads to improve type safety.
2026-04-04 13:40:34 +02:00
4gray a15ac7f7c1 test(e2e): enhance category management dialog and update playlist handling 2026-04-01 22:41:15 +02:00