* chore(deps): upgrade Angular to 22.1 and Nx to 23.2
* fix(deps): complete Angular migrations after rebasing on master
* fix(ci): use the Node pin for Windows runtime refresh
* docs(deps): synchronize the workspace-shell Node requirements
Settings > General gains "Window on startup" (normal / maximized /
fullscreen), Electron only, mirrored into the main-process config by
SETTINGS_UPDATE and applied at the next window creation. `--fullscreen`
forces one fullscreen launch (consumed by the first window). F11 toggles
OS-level fullscreen through WINDOW:TOGGLE_FULLSCREEN — the exit path on
Windows/Linux where the title bar is hidden — and is skipped while the
player owns document.fullscreenElement.
attachWindowStateEvents tracks native and HTML fullscreen as two flags,
since Electron leaves only the HTML state when the window was already
natively fullscreen. macOS ignores the constructor `fullscreen` option on a
hidden window, so ready-to-show repeats the request after show(). Toggles
are decided by an observe-only, event-fed tracker
(native-fullscreen-transitions.ts), never against isFullScreen().
Closes#1455
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(playback): make playback keyboard shortcuts work without shared controls
With the default configuration (Video.js, webPlayerSharedControls off) the
playback shortcuts advertised in the in-app help and README — Space/K, F,
arrow seek/volume, M — silently did nothing: ControlsShortcuts only exists
inside app-player-controls, which never renders on the preference-off path.
Attach a LegacyPlayerShortcuts wrapper (same arbitration and ignore rules)
in the vendor-chrome HTML5, Video.js, and ArtPlayer players, forwarding the
commands to each engine's own API. Seek stays gated on authoritative VOD
metadata plus a finite positive duration, and a visible playback diagnostic
disables the keys. The legacy ArtPlayer chrome now passes hotkey:false —
its focus-scoped vendor hotkeys ignore defaultPrevented and would
double-handle every key — with its Escape-exits-web-fullscreen behavior
restored by the new wiring.
The playback entries in the in-app shortcut help and README drop their
embedded-MPV-only qualifier, since the keys now work in every runtime.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014h2cZi5DcFSbcmV7WgB6qB
* fix(playback): restore audible volume when M unmutes at zero volume
Addresses the Codex review finding on #1398: after arrowing the volume
down to zero (which mutes), M flipped muted off while leaving the volume
at 0, so the player looked unmuted but stayed silent — in all three
legacy engine adapters.
Mirror the shared controls' ControlsVolume semantics with a per-adapter
LegacyMuteMemory: muting remembers the audible volume, and unmuting while
the volume sits at zero restores it, with the same 0.5 fallback when
nothing was remembered.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014h2cZi5DcFSbcmV7WgB6qB
---------
Co-authored-by: Claude <noreply@anthropic.com>
Integrate the community-contributed Hungarian translation by
Tibor Hermann (@htibcsike) as the 19th locale:
- add apps/web/src/assets/i18n/hu.json (1,142 of 1,175 keys translated;
32 keys added after the contribution fall back to English, plus the
new LANGUAGES.HUNGARIAN endonym)
- register HUNGARIAN = 'hu' in the Language enum, SUPPORTED_LANGS,
Angular date locale registration, and the TMDB language map (hu-HU)
- add LANGUAGES.HUNGARIAN = "Magyar" to en.json and all other locales
via tools/i18n/fill-missing.mjs
- update README.md and CLAUDE.md language counts to 19
Closes#1192, refs #1140.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Revert to the shields Codecov badge for visual consistency with the
other for-the-badge badges (native badge has no matching style). Pin
branch=master, add the Codecov logo, and keep the canonical
app.codecov.io link. Note: the shields Codecov proxy is occasionally
slow and may briefly render 'unknown'.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The shields.io Codecov proxy intermittently rendered 'unknown' (extra
API hop, cached by GitHub's camo). Switch to Codecov's native master
branch badge and point the link at app.codecov.io directly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The single donation button used a 'Buy Me A Coffee' image that actually
linked to GitHub Sponsors. Split it into two correctly labeled badges:
GitHub Sponsors and Ko-fi (ko-fi.com/4gray, same as the blog).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The workflow status badge pointed at a non-existent build-and-test.yaml.
Point it at the actual CI workflow, link it to the workflow page, and
label it CI.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the flat feature list with seven themed groups, drop granular
release-note-level items, and surface recent capabilities (TMDB
enrichment, embedded MPV, download manager, global search, dashboard,
auto-updater, remote control) with desktop-only markers.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add blog post warning about scam sites misusing the IPTVnator name to
sell IPTV services or push suspicious downloads (official sources + safety)
- Add "official sources only" callout to README linking to the post
- Replace enumerated language list with a count (18) linking to i18n files
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(security): harden Electron IPC against MITM, SSRF, path and injection risks
S1 TLS: validate certs by default on playlist/EPG fetches (opt-out via IPTVNATOR_ALLOW_INSECURE_TLS); new util/secure-https.ts.
S2: write-file IPC restricted to save-dialog-authorized paths.
S3: XTREAM_PROBE_URL guarded by assertRemoteUrlAllowed + maxRedirects:0; new events/url-safety.ts (+19 tests).
S4: EPG titles rendered via interpolation, not [innerHTML].
S5: downloads reveal/play limited to recorded download paths.
S6: Stalker cmd encoded (slash-preserving) to block query injection.
EPG-worker and Stalker fetches reject file://-style/credentialed URLs; LAN/self-hosted targets remain allowed.
* perf(player): lazy-load web video players via @defer
Wrap Video.js/HTML5/ArtPlayer in @defer (on immediate) so video.js, hls.js,
artplayer and mpegts.js split into a deferred chunk loaded on first playback
instead of eagerly on the player route. Embedded MPV (native) stays eager.
Spec uses DeferBlockBehavior.Playthrough.
* fix(player): remove leaked HTML video listeners on destroy
volumechange used a mismatched removeEventListener reference, while
loadedmetadata and timeupdate were never removed at all. Bind all three to
stable handler fields used for both add and remove, and add a teardown
regression test asserting each listener is detached on destroy.
* refactor(dashboard): extract pure navigation helpers from DashboardDataService
Move the 8 stateless link/navigation-state/type-kind helpers into a new
dashboard-navigation.util.ts so the routing logic is independently testable and
the 1260-line god-service shrinks. DashboardDataService keeps the public methods
as thin delegators (facade) so the public API and the single consumer
(workspace-dashboard-rails) are unchanged. First slice of the DashboardDataService
decomposition; verified by the existing service spec (33/33) and the app typecheck.
* fix(review): address PR feedback (IPv6 link-local, write-path cap, @defer placeholder)
- url-safety: broaden IPv6 link-local detection to the full fe80::/10 range
(fe80:: through febf::), not just the fe80:: prefix (+ regression tests).
- playlist.events: cap authorizedWritePaths (evict oldest past 32) so a save
dialog opened without a following write cannot accumulate entries until restart.
- web-player-view: add a @placeholder to each @defer (on immediate) player block
to avoid the one-frame blank/layout-shift before the chunk resolves.
* fix(security): close Electron network and download gaps
* test(downloads): cover cancellation and restart cleanup
* fix(downloads): address Greptile review gaps
* test(security): reproduce remaining Greptile findings
* fix(security): close remaining Greptile findings
* test(downloads): reproduce early database queue stall
* fix(downloads): release queue after setup failures
* test(downloads): reproduce completion queue stall
* fix(downloads): release queue after completion failures
Adds a top-level TRADEMARK.md spelling out that the IPTVnator name and
logo are unregistered trademarks reserved to the project, separate from
the MIT-licensed source code, and documenting what forks may and may
not do plus where to report misuse. README gets a short Trademark
section pointing to the new file.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 3f453cc0085a
Add a Troubleshooting section to the README covering two common
platform-specific launch issues and how to resolve them.
- macOS: document Gatekeeper "App is damaged and can't be opened"
and show how to clear the quarantine flag with xattr.
- Linux: document chrome-sandbox SUID permission error and provide
two solutions — fix permissions (chown/chmod) for packaged installs,
or run with --no-sandbox by editing the desktop launcher or using the
command line.
These instructions reduce user confusion and support requests by
providing clear, actionable steps to get IPTVnator running on affected
systems.