* fix(i18n): translate hard-coded labels, snackbars and missing keys
Catalog screens, snackbars and external player messages showed English
in every locale, and seven keys used in code were missing from en.json,
so ngx-translate rendered them raw.
- Catalog: "All items", item and channel counts, channel sort menus and
tooltips, unnamed-category and empty-category labels, LIVE/PAUSED
badges and the Xtream global search summary are translated. The Xtream
and Stalker stores no longer bake an English name into the every-item
sentinel; the facades return a null title and the view translates it.
- Snackbars: Xtream/Stalker request failures, the 413 upload error,
backup export/import results and the category visibility failure use
keys; every "Close" action uses CLOSE.
- External player: the main process sends an error code instead of an
English sentence (player-error event, session errorCode, and a tag in
rejected launch errors); the renderer, dock and VOD primary button
translate it. The external player info dialog is translated.
- Adds the seven missing keys and 43 new ones, translated in all 18
locales; seven legitimately identical values are baselined.
- tools/i18n/check-usage.mjs fails on keys used in code but missing from
en.json; it runs in i18n:check (and so in CI through i18n:validate).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(i18n): translate catalog counts in the template and skip inline template comments
- The category subtitle hands a key to the translate pipe instead of
caching translate.instant(), so a cold start re-renders it once the
language file loads.
- The Xtream live root count uses the singular/plural item keys, so one
result no longer reads "1 channels".
- check-usage.mjs strips HTML comments inside inline templates of
TypeScript files, so a commented-out key no longer fails the check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(i18n): check keys in parenthesised translate pipe operands
`(expanded() ? 'SHOW_LESS' : 'SHOW_MORE') | translate` yields keys that
neither precede the pipe directly nor contain a dot, so the usage check
missed them. It now reads the ternary and fallback branches of a
parenthesised operand; a literal compared in the condition is not read
as a key.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(e2e): expect the translated external player failure in the dock
A launch failure without an error code now shows the translated generic
status in the playback dock, with the raw main-process detail as its
tooltip. The ClearKey DASH flow asserted the raw English text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(playback): keep the IPC error tag out of the stored session detail
A tagged launch failure reached ExternalPlayerSession.error unchanged, so
the dock tooltip showed "[iptvnator:external-player:start-failed]". The
registry now strips the tag when it stores the detail; the rejected IPC
error keeps it for the renderer to read the code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(web): expect translated portal request failure toasts
#1861's new resolved-failure specs asserted the English toast text; the
toasts now go through PORTALS.REQUEST_ERRORS keys, so the specs check
the key and its message/status params.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(i18n): read only returned branches of a grouped translate operand
A literal at the start of a condition, as in
`('ERROR' === status() ? 'CLOSE' : 'CLOSE') | translate`, was taken for
a translation key, so a valid template could fail the usage check. A
grouped literal now counts only when it ends its operand (end of group,
`:`, `||` or `??`).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(i18n): translate the remaining hard-coded labels
Follow-up to the UI-26 pass. The remaining English UI literals now come
from translation keys, translated in all 18 locales:
- Windows/Linux window controls, playlist switcher title and actions
menu, the portal status tooltip, dashboard carousel roles and rail
scroll buttons, the search placeholder, the card remove tooltip, the
EPG offset unit, the REC chip, the Stalker EPG source label and the
export file type.
- Embedded MPV failures raised in the renderer and the release-notes
dialog without a bridge. Settings never showed its English reasons,
so they are dropped.
- Unnamed Stalker episodes: data access leaves the title empty and the
series view labels it after the TMDB overlay. Synthetic season names
stay, because they key persisted episode progress.
- The phone remote-control page, which follows the first browser
language with a translation and sets <html lang>.
Removes the dead getStatusMessage(), the unused favorites layout and the
translateWithFallback() helpers whose keys now exist.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(remote-control): keep the remote build off shared-interfaces
The language resolver imported the Language enum, which made
remote-control-web depend on shared-interfaces. Its build-performance
chain then hit Nx's recursive-invocation guard through the existing
shared-interfaces/shared-logging build loop, failing the Electron E2E
and performance journey builds. The resolver now keeps its own list of
translation codes, and a spec checks it against the locale files the page
loads.
Also drops the deleted favorites layout from the zoneless checklist,
whose guard spec requires ticked entries to exist.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf(journeys): raise the launch DOM mutation baseline for translated attributes
Launch now sets 569 DOM mutations before the first dashboard card instead
of 557 (identical in all three CI iterations). The extra twelve come from
attributes this PR moved from static English to translated bindings on
the launch path: the window-control labels and tooltips on Linux, the
hero carousel and slide roles, and the rail scroll-button labels. A
translated attribute is a binding set after the element is attached, so
each costs a mutation. Accepted as a deliberate trade-off; it needs the
perf-baseline-increase label.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(portals): preserve live channel navigation while browsing
* test(portals): await media source assertion in remote E2E
* fix(xtream): capture destination queue for live auto-open
* feat(remote-control): cover live collections, honest volume, status resets
Remote control previously worked only on the three routed live layouts
(M3U player, Xtream live, Stalker ITV); playing live TV from favorites,
recently viewed, or the global collections left the mobile remote inert.
- Wire channel up/down, number select, and status publishing into the
unified live tab, covering per-portal and global favorites/recent for
M3U, Xtream, and Stalker; navigation follows the search-filtered,
sorted list exactly as rendered (shared deriveVisibleFavoriteChannels)
- Treat non-live status updates as snapshots in the main process so
stale now-playing fields are cleared instead of merged forever
- Publish a reset snapshot from every integration on destroy, so
leaving a live view clears the remote instead of freezing it
- Report M3U supportsVolume only for built-in inline playback and no-op
volume commands while MPV/VLC/Embedded MPV owns the audio
- Publish live status for Stalker radio (same layout, same handlers)
and fix its channel-number lookup for non-numeric radio ids
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmMT33wgK52QL6JAz468eH
* fix(remote-control): review-loop hardening for status honesty
- Make the non-live status update an authoritative reset in the main
process: only portal survives, supportsVolume is forced false, stray
now-playing fields from callers are dropped (Copilot review)
- Stop Stalker radio status from leaking an unrelated TV channel's EPG:
the ITV-keyed bulk cache survives itv->radio navigation and Ministra
ids collide across the two lists, so EPG fields publish for itv only
- Publish the reset snapshot when the M3U active channel clears in
place (e.g. quitting external MPV), not only on route destroy
- Consider a live external session in the M3U volume gate: a
diagnostic-recovery MPV/VLC launch owns the audio even while a web
player is configured; republish capability on session start/end
- Share one REMOTE_CONTROL_RESET_STATUS constant across all four
integrations instead of four hand-copied literals
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmMT33wgK52QL6JAz468eH
* fix(remote-control): external session outranks DASH in M3U volume gate
The managed clear-DASH MPV/VLC fallback (Shaka browser-support preflight
failure) leaves activeChannelIsDash() true while the external session
owns the audio, so the DASH shortcut bypassed the session check and kept
advertising remote volume support. The live-session check now precedes
the DASH branch; radio stays first because its inline audio element is
always mounted and remains audible.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmMT33wgK52QL6JAz468eH
---------
Co-authored-by: Claude <noreply@anthropic.com>
settings.component.ts had grown to 819 lines — past the CLAUDE.md target (<300)
and hard maximum, passing lint only because it sat in the max-lines baseline.
The behaviour moves into facades the template binds to directly, following the
precedent already in this folder: new app-update (218), form (197), epg (123),
embedded-mpv (74) and remote-control (37) facades, with playlist-reset extended
to 143 and settings-options to 200. The component is now a 259-line coordinator
holding capability flags, section nav, players() and the cross-facade flows.
settings.component.ts is removed from the max-lines baseline.
No behaviour change. One ordering detail: applyChangedSettings now applies
language/theme before kicking off the EPG re-fetch; changeTheme only touches DOM
theme sync and translate.use does not touch the form, so the two are
independent.
Adds contract-focused regression coverage for the Electron HTTP server,
remote-control events, settings events, and managed download paths, and makes
Tier A coverage fail closed when instrumentation fails or a runtime-owning
production file disappears from a project or from the merged Istanbul report.
The old `coverage:ci` exited 0 despite a `Failed to collect coverage`
diagnostic: libs/m3u-state/src/lib/effects.ts was simply absent from the merged
map. All 30 Tier A reports are now required, the merged map covers 710 files,
and effects.ts is reported as 0/159 instead of silently disappearing.
Also fixes remote static-file path containment for encoded, malformed, NUL,
POSIX and Win32-style traversal inputs, with behavior-preserving testability
seams.
Statements 69.27% -> 69.54%; http-server.ts 0% -> 90.21%,
remote-control.events.ts 0% -> 96.55%, settings.events.ts 59.25% -> 96.29%.
- Moved Stalker-related components and services from apps/web to libs/portal for better modularity.
- Introduced SQLite DB Worker to handle non-EPG database operations, improving UI responsiveness.
- Updated documentation for the Workspace Dashboard and Shell, detailing current implementation and routing structure.
- Added new EPG fixture scenarios to the Xtream mock server for testing purposes.
- Enhanced the overall architecture documentation to reflect recent changes and improvements.