mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-11 11:06:16 -08:00
agent/skill-implementation-sync
65
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
063662028a |
feat(portals): find the same movie in your other playlists (#1286)
* feat(portals): find the same movie in your other playlists A movie that exists in several imported Xtream playlists now shows a "Sources N" chip on its detail page and in the player. Switching playlist mid-film keeps the timecode, a preferred source can be pinned per movie, and a failed stream offers the alternatives instead of a dead end. The governing rule is that a guess is never presented as a fact. Every metadata value carries where it came from — `api` (the provider said so), `parsed` (inferred from the title) or `probe` (we contacted the stream). Facts render as plain tags, guesses are prefixed `~` in a warning colour, and an unknown value renders no tag at all plus a "check" affordance. Ranking and failover read through `factualOnly()`, so a filename claiming 4K is structurally unable to outrank a source that was actually reached. A probe that could not complete reports "unknown", never "unavailable". Scope is deliberately narrow: Xtream to Xtream, movies only, Electron only. Stalker never reaches the `content` table and M3U is a JSON blob whose search forces live content; both are additive later, since the candidate type already carries all three portal kinds. In the PWA every entry point is gated off and the chip renders nothing. Auto-failover is opt-in and off by default. Each source is tried at most once per session, so it terminates structurally, and the switch is never silent — the toast names the new playlist, offers an undo, and warns that the dub may differ only when both sides state an audio track as fact. Notable details: - Playlist names are routinely the pasted URL, credentials included. They are never rendered raw; a short host-only label is derived instead. - Quality is derived from pixel width, not height: a 2.39:1 1080p master is 1920x800, and bucketing that by height would publish "720p" as a fact. - Switching is a single `inlinePlayback.set()` so the player and engine survive and re-seek; the carried position is read before the 15s persistence throttle so it does not rewind. - Sources from one playlist collapse into a group, since the same film often appears there several times under different stream ids. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(portals): stop stale source resolutions from committing Addresses three defects Greptile found in the multi-source review. **Concurrent switches committed out of order.** Selecting a second source before the first resolution returned let the slower request overwrite the newer selection and repoint Undo at itself. `switchTo` now takes a sequence number and drops its result if a newer switch already committed. **Stale switches crossed movie sessions.** Navigating to another film while a resolution was in flight let the continuation activate the old film's source inside the new controller — and restart it from that session's zero resume position. The controller is now snapshotted per operation and the movie session is revalidated after every await. `check()` had the same hazard across its two awaits and is guarded the same way. **Short titles skipped discovery entirely.** The trigram tokenizer cannot index tokens under three characters, so "Up", "It" or "Us" produced an empty MATCH expression and the query was discarded before SQLite was consulted — the chip could never appear for those films. Discovery now falls back to a bounded scan when FTS structurally cannot serve the title; the existing two-tier normalized confirmation still rejects loose hits like "Upgrade". Each fix carries a regression test; all three were mutation-checked by removing the guard and confirming exactly those tests fail. The previous test asserting that short titles return nothing encoded the bug and has been replaced. The host spec passed 400 lines, so its fixtures moved to a shared module and the race suite into its own file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(portals): make the pin decide playback and keep failover going Second round of Greptile review findings. **A pin had no behavioural effect.** Loading a stored pin only decorated the row: Play still started the route's playlist and failover ranking ignored `isPinned`, so "make this the main source" survived a restart as an icon and nothing else. The primary action now starts from the pinned source when one is set, and the pin outranks everything else in failover ranking. **Failover stopped at the first unresolvable candidate.** An expired account or a failing `get_vod_info` on the top-ranked source ended the attempt, and since production calls `failover()` only once — on the original playback failure — a healthy lower-ranked source was never reached. It now continues through untried candidates. `switchTo` reports why it stopped so the loop can tell "could not resolve, try the next one" from "something newer owns the screen"; without that distinction a superseded switch would have spun forever, because only the former marks the candidate tried. **Identity ignored enrichment.** The key was `playlistId:contentId:title`, so when `get_vod_info` added a TMDB id and release year to an unchanged title the host saw no change, never reloaded, and kept yearless discovery and title-only pin keys — a `tmdb:`-keyed pin could never be found. The key now covers every field that affects matching. **A server refusing HEAD read as unavailable.** Some stream hosts answer 405 or 501 to HEAD yet serve the media over GET. The probe now retries once with the ranged GET the main process already supported, instead of caching a working source as failed and penalising it during failover. Greptile also flagged a missing token check after the resolve await in `switchTo`; that guard landed in |
||
|
|
deae0a2a4d |
fix(xtream): keep sparse VOD details playable (#1303)
* fix(xtream): keep sparse VOD details playable * fix(xtream): scope VOD fallback to active playlist * fix(xtream): render sparse VOD before recovery * fix(xtream): recover Similar VOD provider categories |
||
|
|
a2fafcfc08 |
test(performance): add end-to-end Xtream benchmark harness (#1300)
* docs(performance): plan Xtream benchmark * feat(xtream-mock-server): add deterministic 100k fixture * style(xtream-mock-server): apply repository formatting * fix(xtream-mock-server): harden performance fixture data * feat(xtream-mock-server): add performance control plane * docs(performance): correct Xtream capture plan * fix(xtream-mock-server): harden performance controls * fix(xtream-mock-server): harden control lifecycle * feat(performance): add Xtream preload markers * feat(performance): trace Xtream main phases * feat(performance): mark Xtream store publications * feat(performance): trace Xtream database phases * feat(performance): trace Xtream delete cancellation * feat(performance): capture Xtream phase attribution * feat(performance): mark Sources Xtream refresh * test(performance): define Xtream benchmark evidence contracts * test(performance): add Xtream benchmark runner * test(performance): surface failure evidence writes * test(performance): align database read clock * test(performance): preserve capture failure contracts |
||
|
|
0273ded8e2 |
fix(tmdb): resolve season number from title markers for per-season series slices (#1229)
* fix(tmdb): resolve season number from title markers for per-season series slices
Providers often slice a show into per-season catalog items ("The
Mandalorian (2 season)", "Пацаны 2 сезон", "The Boys S05") and renumber
the single contained season to 1, so season enrichment fetched the wrong
TMDB season (season 1 metadata for a season 2 item).
- new season-marker.util.ts in shared/interfaces: extractSeasonFromTitle
(word-first, number-first and S-form markers, bracketed or trailing)
and resolveEnrichmentSeasonNumber (title marker wins only for
single-season items whose provider number disagrees)
- wired into Xtream enrichSerialSeasonWithTmdb and the Stalker
series-view season service (cache/overlay still keyed by provider
season key)
- SEASON_SUFFIX_PATTERN now also strips number-first season suffixes
("2 season", "2 сезон", "2-й сезон" incl. NFD-decomposed ordinals) so
such titles match the show at all
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(stalker): wait for the season map before TMDB season fetch
The TMDB match can arrive before the async season resource; fetching
then passed seasonCount 0, suppressed the title-marker override and
cached the wrong season forever (fetchSeason is idempotent). The effect
now reads the season map tracked and skips while it is empty —
overlay-driven re-runs are safe because fetchSeason early-returns per
(tmdbId, seasonKey).
Addresses Greptile review on #1229.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(stalker): reset season selection on detail-to-detail navigation
The router reuses the series view for detail-to-detail navigation, and a
retained season selection let the NEW item's tmdb_id pair with the
PREVIOUS series' season context in the TMDB fetch effect, poisoning its
idempotent per-season cache before the new season resource loaded. The
selection is now a linkedSignal keyed on the displayed item's identity —
compared inside the computation, since displayItem produces a fresh
object on every recomputation.
Addresses Greptile review round 2 on #1229.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(stalker): include the resolved season in the TMDB season cache identity
Per-season slices of one show share (tmdbId, provider season key "1")
but resolve to different TMDB seasons — plain key idempotency served the
first slice's episodes to every later slice. Each cache entry now
records the resolved season it was fetched for: a call resolving a
different season refetches and overwrites (also self-healing a fetch
made with stale navigation context), an in-flight marker dedups
concurrent runs, and a superseded fetch may not store its result.
Failed fetches stay uncached so later triggers retry.
Addresses Codex review (P1) and Greptile review round 3 on #1229.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(stalker): drop a mismatched season cache entry before its replacement fetch
If a replacement fetch (same key, different resolved season) failed, the
previous slice's entry stayed visible indefinitely through overlay() and
descriptions(). The mismatched entry is now removed up front, so a
failed replacement falls back to provider data until a retry succeeds.
Addresses Codex review (P2) on #1229.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(stalker): title-based season reset identity and o_name marker support
- The season-selection reset identity now combines provider id and title:
distinct items can share or lack provider ids, and an id-only identity
retained the previous item's selection across such navigation
- The season marker is read from whichever title field carries it via
pickSeasonMarkedTitle: providers put the descriptive title in o_name
while name stays generic, and the show-level match already used o_name
Addresses Greptile review round 4 (P1) and Codex review (P2) on #1229.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(stalker): gate TMDB season fetch on resource coherence, not selection resets
Resetting the parent season selection on item identity change (previous
round) silently disabled enrichment after detail-to-detail navigation
between items sharing one season-key set: the season container keeps its
own selection and deduplicates seasonSelected emissions, so the parent
key stayed null forever. The reset is gone; instead the fetch effect
gates on coherence — it waits while the season resource reloads (the
window in which a reused component pairs the new item's tmdb_id with the
previous item's map) and requires the selected key to exist in the map
with episodes. Stale-snapshot fetches remain self-healing through the
resolution-aware cache.
Addresses Codex review (P2) and Greptile review round 5 on #1229.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
bc6e7018e0 |
fix(epg): harden three latent edges from the #1165 manual-mapping review (#1214)
* fix(epg): harden three latent edges from the #1165 manual-mapping review Follow-up to #1165 (manual EPG-to-channel mapping). Three minor but real issues flagged by the bot reviews on #1173, all in already-merged #1165 code rather than the Stalker delta: 1. EPG program dedup ignored source_url. The unique index and upsert key (channel_id, start, title) collapsed programmes imported from different XMLTV sources that shared those columns, and the upsert reassigned source_url to the last importer — so source-scoped queries could miss a programme and source-scoped deletes could drop another source's row. The key and index now include source_url (migrated via a _v2 index that drops the old source-blind one); the upsert no longer overwrites source_url. 2. Xtream getMapping fallback capped candidate streams at an unordered first five, so a mapping saved under a later stream sharing the provider epg_channel_id was silently ignored. Replaced the two-step fetch-then-lookup with a single content⋈categories⋈mappings join that finds a mapping under any matching stream, with no arbitrary cap. 3. The Xtream mapping dialog did not refresh after closing, unlike the Stalker path, so a remapped visible/selected channel kept its stale preview until the 5-minute TTL or a rescroll. Added EpgQueueService.invalidate(streamId) and a before/after mapping compare in the channel-list dialog flow that invalidates the cache and refetches the current viewport when the mapping actually changed. Tests: source-aware dedup index/upsert assertions; join-based getMapping resolution regardless of stream position; EpgQueueService.invalidate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(epg): address review feedback on the #1165 follow-up - portal-channels-list: forward the already-validated playlistId into the mapping dialog instead of re-reading currentPlaylist() after the async getEpgMapping roundtrip (which could return undefined on navigation) - EpgQueueService.invalidate(): bump a per-stream invalidation epoch and clear inFlight so a request already running when the mapping changes has its (pre-change) result discarded via an epoch check in fetchEpg, and the immediate re-enqueue can schedule a fresh mapping-aware fetch - getMapping Xtream fallback: order the join deterministically before limit(1) so the resolved mapping is stable; documented that this backend layer has no caller playlist context and is a best-effort net behind the renderer's playlist-scoped resolution Tests: in-flight staleness discard for invalidate(). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(epg): split EpgQueueService invalidation specs under the max-lines limit The added invalidate() tests pushed epg-queue.service.spec.ts to 415 lines, over the 400-line ESLint cap (and the baseline must not grow). Moved them to a focused epg-queue-invalidation.spec.ts; both files are now under the limit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(epg): resolve second-order review findings on the mapping follow-up Two P2 issues Codex raised on the previous fixes: - Unscoped program lookups could return the same programme twice now that the dedup index preserves per-source rows: the M3U timeline calls getChannelPrograms without sourceUrls, so two sources sharing channel/start/title both surfaced. Added toEpgProgams(), which collapses duplicate channel|start|title slots after mapping/validation, applied at every getChannelPrograms return. - EpgQueueService.fetchEpg unconditionally cleared the in-flight marker in its finally, which could drop a marker a re-enqueued request took over after invalidate(). It now only releases the marker when the completing request still owns it (epoch unchanged), preserving per-stream dedup and concurrency accounting. Tests: unscoped duplicate-slot collapse; stale request preserving a fresh in-flight marker. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(epg): deduplicate program rows in SQL before applying row limits Codex follow-up: the JS-level slot dedup ran after the SQL LIMIT, so duplicate cross-source rows consumed the cap and truncated real data. Moved the dedup into SQL with GROUP BY, applied before the limits: - selectChannelPrograms / selectLegacyChannelPrograms: GROUP BY (channel_id, start, title) before ORDER BY start LIMIT 500, so the timeline cap counts distinct programmes rather than duplicate rows - selectCurrentProgramsForChannelIds: GROUP BY channel_id before LIMIT channelIds.length, so duplicate cross-source current slots can't starve other channels of their current-programme preview The JS toEpgPrograms() dedup stays as a safety net (e.g. legacy NULL-source rows the unique index treats as distinct). Test query-chain mocks updated for the new groupBy link. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ec6fc403a3 |
feat: manual EPG-to-channel mapping with mapping fallback (#1165)
* feat(epg): manual EPG-to-channel mapping with mapping fallback in all EPG paths
* fix: epg mapping in live tv list
* fix(epg): harden manual EPG mapping — upgrade safety, perf, playlist-scoped keys
Follow-up fixes on top of the manual EPG-to-channel mapping feature:
- epg-database: dedupe existing epg_programs rows before creating the
unique (channel_id, start, title) index — a plain CREATE UNIQUE INDEX
crashed the EPG worker on upgrade when historical duplicates exist;
replace INSERT OR REPLACE with ON CONFLICT DO UPDATE so the
epg_programs_fts delete trigger is not bypassed (REPLACE skips delete
triggers unless recursive_triggers is on), with a plain-INSERT
fallback when the index cannot be created
- db: add idx_content_epg_channel — the mapping fallback scanned the
whole content table on every single-channel EPG lookup
- keys: scope Xtream mapping keys per playlist via shared
buildXtreamEpgMappingKey (xtream:{playlistId}:{id}) — bare stream ids
collide across portals; the backend fallback now joins categories to
resolve the playlist id
- pwa: hide "Map EPG channel" entries behind the supportsEpgMapping
capability — the menu item was a dead end in the PWA
- parser: parse the XMLTV offset sign from the string — Math.sign(0)
dropped the minutes of ±00:xx offsets
- cleanup: typed window.electron access instead of ad-hoc casts, drop
unused resolveChannelId and dialog data field, shared
EpgMappingDialogComponent.open() for all seven call sites
- dialog UX: minimum-characters search hint, save/remove snackbars,
current mapping shows the EPG channel display name,
takeUntilDestroyed on the search stream
- i18n: fill the new keys in all 17 locales
- tests: cover mapping CRUD/search escaping, the dedup-index guard,
offset parsing and playlist-scoped keys; update stale stream-resolver
specs for the new 50-item limit and 10s timeout
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(epg): escape backslashes in EPG channel search LIKE pattern
CodeQL js/incomplete-sanitization: a lone trailing backslash in the
search term paired with the closing wildcard under the ESCAPE clause
and corrupted the pattern.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* perf(epg): batch mapping lookups in the viewport preview queue
Expose the existing getEpgMappingsBatch operation over a new
EPG_MAPPING_GET_BATCH IPC channel and use it in resolveManualMappings —
the per-entry lookup issued one IPC round-trip per visible channel on
every scroll event.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* perf(epg): batch mapping prefetch in the collection preview loader
Resolve all candidate mapping keys for an Xtream preview batch with a
single getEpgMappingsBatch IPC call instead of per-channel lookups.
Also fix a worker early-exit: a channel without tvgId/name returned out
of the shared-iterator loop and silently killed one of the three
concurrent preview workers.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: 4gray <serega05@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
5cae310430 |
fix(logging): redact sensitive portal and Electron diagnostics (#1182)
* fix: redact sensitive log data * fix(ci): keep logging preload self-contained * fix(logging): preserve shared diagnostics * fix(logging): close trace redaction gaps * fix(logging): redact Xtream path credentials * fix(logging): close credential redaction gaps * fix(logging): suppress external player arguments * fix(logging): harden URL and date redaction * fix(logging): redact map keys and URL fragments * fix(logging): redact sensitive map values * fix(logging): redact credentials in diagnostic text * fix(logging): close remaining credential leaks |
||
|
|
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> |
||
|
|
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> |
||
|
|
007ae7028d | fix(xtream): key catchup probes by output formats | ||
|
|
1679373bb2 | feat(xtream): auto-select stream output format | ||
|
|
12f3f1fc05 | fix(xtream): resolve catchup timeshift variants | ||
|
|
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. |
||
|
|
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. |
||
|
|
20d0f01428 | fix(xtream): address portal review feedback | ||
|
|
254879f92b | fix(xtream): improve portal compatibility | ||
|
|
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 |
||
|
|
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 |
||
|
|
8cfc40f59b | refactor(xtream): gate data source by sqlite capability | ||
|
|
5cf7ab9358 | refactor(xtream): use epg runtime capability only | ||
|
|
c571c57c5b | refactor(runtime): centralize platform capabilities | ||
|
|
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 |
||
|
|
5d355b6f10 | fix(web): address strict mode review feedback | ||
|
|
4ab8915483 | chore(web): enable strict TypeScript mode | ||
|
|
55efc24608 | fix(pwa): harden self-hosted runtime boundaries | ||
|
|
69b7bca9df | fix(pwa): prefer xtream collection snapshots | ||
|
|
6438eec49f | fix(pwa): align xtream content identity | ||
|
|
ed0b6833d6 | fix(pwa): persist xtream collection snapshots | ||
|
|
9097ff5bc1 | fix(pwa): align xtream recent clearing | ||
|
|
d1e057887f | fix(pwa): avoid persisting xtream passwords | ||
|
|
ec098f98b1 | fix(pwa): hydrate xtream user collections | ||
|
|
1ea6914f98 | fix(pwa): harden xtream identity normalization | ||
|
|
1d4d793aac | fix(pwa): restore xtream user collections | ||
|
|
6829c667bd | fix(pwa): restore xtream rail and vod navigation | ||
|
|
d24c77a143 | chore(nx): enforce scoped workspace boundaries (#942) | ||
|
|
8b3b1c9bb7 | chore: merge master into xtream timezone fix | ||
|
|
9261794b4a |
Merge pull request #902 from weeco/feat/xtream-xmltv-epg-fallback
feat(xtream): fall back to uploaded XMLTV when provider has no EPG |
||
|
|
4020822b90 | fix(xtream): prevent stale epg queue fetches | ||
|
|
a3a4c6b5ea | Fix Xtream section search across categories | ||
|
|
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. |
||
|
|
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. |
||
|
|
c14b5d5aff |
fix(xtream): search all vod and series items
Entire-Checkpoint: 3f453cc0085a |
||
|
|
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 |
||
|
|
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> |
||
|
|
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> |
||
|
|
a2e2959b89 |
fix(xtream): pagination behavior (back & scroll)
Entire-Checkpoint: c6e522b4276c |
||
|
|
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. |
||
|
|
c8c93798f9 |
feat: enhance playlist backup export functionality and UI feedback
Entire-Checkpoint: c6e522b4276c |
||
|
|
ff605fc024 |
fix(xtream): enhance content retrieval by adding content type filtering for Xtream ID lookups #756
Entire-Checkpoint: c6e522b4276c |
||
|
|
faa4e2beb2 |
refactor: update searchContent method to accept flexible parameters and clean up XtreamStore
Entire-Checkpoint: c6e522b4276c |