Commit Graph
47 Commits
Author SHA1 Message Date
Andre 50a14197b7 style: translate all remaining German code comments to English
German comments had survived the earlier translation pass. Mixing
languages in comments creates inconsistency and makes the codebase
harder to read for non-German contributors.
2026-05-16 08:41:47 +02:00
Andre 123809fb9a fix: suppress HLS disk cache via Fetch API loader instead of XHR headers
Chromium evaluates the Fetch API's cache mode before the disk cache
storage decision; XHR request headers and onHeadersReceived patches do
not reliably prevent storage. The old approach silently allowed caching
regardless of the setting.

Moving cache suppression to a custom hls.js NoCacheLoader (Fetch with
cache: 'no-store') also eliminates the CORS preflight that Cache-Control
as a request header was triggering, which lets the main-process CORS
handler drop the applyNoStore branch entirely.
2026-05-15 11:31:32 +02:00
Andre c7bc1a23c0 feat: add file logging toggle and logout confirmation
File logging defaulted to always-on meant the log grew silently on every
run with no way to turn it off. Changing the default to error-only and
exposing the toggle in Settings lets users opt in only when debugging.

The logout button previously triggered immediately without warning, making
accidental logouts unrecoverable without re-running the device code flow.
A confirmation step (Ja/Nein, gamepad-navigable) prevents that.
2026-05-15 10:15:30 +02:00
Andre a5eaafbed3 feat: unify logging via electron-log and add IPC error boundary
Bug reports from Steam Deck (Flatpak) arrived without any useful context:
no terminal, no log file, no trace of what failed. Silent catches in
helixClient hid quota and token errors entirely. IPC handler exceptions
reached the renderer as rejected promises with no server-side record.

electron-log now writes to a rotating log file (~1 MB, 5 files) that
users can attach to bug reports. All IPC handlers are wrapped in
safeHandle, which logs and re-throws at the boundary. Optional-feature
failures (chapters, profile images, viewer counts) emit log.warn with
enough context to diagnose. A React ErrorBoundary catches render crashes
app-wide and logs them before showing a reload prompt.
2026-05-15 09:26:03 +02:00
Andre 9acf503afa style: translate all source code comments to English
German inline comments alongside English identifiers create a language
mismatch that makes the code harder to read for anyone not fluent in
German. All 28 affected comments across 14 files are now in English,
consistent with the rest of the project's written artifacts.
2026-05-15 08:49:52 +02:00
Andre 13b28fdc91 refactor: consolidate IPC types into src/shared/types.ts
The 12 cross-process data types were manually copied across
src/preload/index.ts and src/renderer/src/types/t4sd.d.ts. A silent
field addition or rename in either file would not produce a compile
error in the other — the divergence only surfaces at runtime.

A shared module makes the compiler enforce consistency. Preload imports
from shared/types and re-exports for downstream consumers. The renderer's
t4sd.d.ts re-exports the same types from shared/types, so all existing
screen imports continue to work without modification.

Both tsconfigs are extended to include src/shared so the module is
reachable from both composite projects.
2026-05-15 08:49:31 +02:00
Andre 211174f889 refactor: extract IPC channel names into a single source of truth
Duplicate raw string literals across handlers.ts, preload/index.ts made
typos undetectable at compile time and renames a multi-file grep problem.
A single channels.ts now owns all channel names; both files import from it.

Also fixes two bugs found during testing:
- LoginScreen and AccountScreen buttons were not triggerable via gamepad
  A-button: synthetic KeyboardEvents are untrusted and don't fire native
  button clicks. Added explicit keydown handlers that call the action directly.
- AppShell loading overlay could disappear while hlsPayload was still null
  if the playback:event 'started' IPC message arrived before playback:hls-url.
  Overlay now stays visible until hlsPayload is set, regardless of playState.
2026-05-14 12:57:29 +02:00
Andre bb635366b2 refactor: extract magic values into named constant modules
Scattered numeric literals had no names at their use sites, making it
impossible to know whether two identical values were intentionally the
same or coincidentally equal. Moving them to typed constants makes the
relationship explicit and ensures a single edit propagates everywhere.
2026-05-14 12:17:47 +02:00
Andre 0e38f930e7 refactor: centralize format utilities in lib/formatting.ts
formatViewers, formatCount, formatDuration, formatDate, formatWatchedAt,
formatTimestamp, and formatViewersFull were duplicated across four files.
Divergence was silent — fixing a rounding edge case or locale string in
one copy left the others stale.
2026-05-14 12:13:08 +02:00
Andre 086bda9b9c refactor: extract usePlaybackSession hook shared by AppShell and ChannelScreen
Both components managed identical playback state — HLS payload, play state,
quality panel, error message, videoRef — plus duplicate IPC subscriptions
and duplicate startLive/startVod/stop/getQualities sequences.

A shared hook removes that duplication and makes the two-context architecture
legible at a glance: both components call usePlaybackSession, AppShell with
active: !selectedChannel and ChannelScreen with active: true. The ownership
rule is now visible in two lines instead of requiring a cross-file search.
2026-05-13 19:59:05 +02:00
Andre 7a6a58054a refactor: replace playback-initiated ref with conditional IPC subscription
A mutable ref guarded AppShell's IPC listeners against events fired by
ChannelScreen's playback session. The ref had to be set and cleared in
five separate places, making the control flow hard to follow.

The two contexts are already mutually exclusive at the UI level, so the
guard is unnecessary. Subscribing only when no ChannelScreen is open
expresses the same invariant through React's effect lifecycle instead.
2026-05-13 19:38:35 +02:00
Andre ed790dc3cd fix: surface playback errors and stabilize disk cache size
Playback errors in the global overlay (direct-start from Browse/Category/
StreamList screens) were silently discarded — the stream disappeared with
no user feedback. Now shows an error overlay with the streamlink message
and a dismiss hint; hls.js fatal errors use the same path.

Chromium computes disk-cache-size dynamically from free disk space, so
the value differs between runs. The cache index then contains a size that
no longer matches, producing "Invalid cache (current) size" on shutdown.
A fixed 500 MB cap keeps the value stable across restarts.

The cache classification thresholds (500 MB / 1 GB) were designed for an
uncapped cache. With a fixed 500 MB limit, the cache can never enter those
states, leaving dead UI states and unused logic behind.
2026-05-12 21:02:21 +02:00
Andre 4fa308b9e6 refactor: remove duplicate VOD start and chapter panel helpers
handleWatchVodFromChapter was identical to handleWatchVod except for an
optional startSeconds argument that the bridge already supports. Merged
into a single function with startSeconds?: number.

openChapterPanel was a wrapper around showChapterPanel whose two branches
both executed the same code. Removed the wrapper; call sites now call
showChapterPanel directly.

Also removes stale mpv references from CLAUDE.md that were superseded by
the hls.js migration commit.
2026-05-12 20:34:05 +02:00
Andre 1fe2093ebe chore: remove mpv and its build dependencies from Flatpak manifest
mpv was replaced by hls.js in v0.4.0 but its five build modules
(libass, libplacebo, libXpresent, Lua 5.2, mpv itself) and the
ffmpeg7 patch were never removed — dead weight in every build since.
2026-05-12 20:20:00 +02:00
Andre 78d67de35a feat: add HLS cache toggle and cache management to Settings
Chromium's HTTP disk cache accumulates HLS segments indefinitely on
Steam Deck — single-use segments have ~0% cache-hit rate but cause
unbounded SSD writes and disk growth. This wires up a runtime toggle
(default: cache disabled) so the user can control the behaviour without
restarting the app.

- Introduce src/main/prefs/hlsCachePref.ts as a shared mutable so
  index.ts and handlers.ts don't form a circular import
- Restructure onHeadersReceived in setupTwitchCors: CORS-bypass and
  no-store now run as independent blocks; no-store fires only for HLS
  extensions (.m3u8/.ts/.m4s/.aac, path before ?) when the toggle is off
- Add IPC handlers app:get-cache-size, app:clear-cache,
  app:set-hls-cache-enabled; wire through preload bridge and t4sd.d.ts
- Extend SettingsContext with hlsCacheEnabled (default false); push
  persisted value to main on mount and on every change
- Add "Speicher" section to SettingsScreen with HLS toggle row and
  cache-size display + clear button; gamepad-navigable
0.4.5
2026-05-11 19:40:50 +02:00
Andre 82ff6dbbf2 feat: switch gamepad reader to Linux evdev BTN_* codes
The Joystick API (/dev/input/js*) numbers buttons depending on driver
and connection type. Xbox One controllers via Bluetooth with a Share
button shifted X from button number 2 to 3, silently breaking the
mapping.

The evdev interface provides standardized BTN_* codes (BTN_SOUTH=A,
BTN_NORTH=X, etc.) that remain consistent across Xbox, PlayStation,
and Nintendo controllers over both USB and Bluetooth.

js* devices remain the discovery path, while /sys/class/input/jsN is
used to resolve the corresponding event* device. If the /sys lookup
fails, JoystickFallbackReader falls back to the old Joystick API.
2026-05-11 17:29:54 +02:00
Andre 1251cc278a feat: add quality selection, improve chapter panel UX, and show VOD view counts
Quality selection during playback:
- Add getAvailableQualities() in streamlink via --json, sorted by fixed order
- Expose playback:get-qualities IPC channel (handlers, preload, type declarations)
- Add quality panel in PlaybackOverlay:
  - Button + dropdown
  - Keyboard navigation (X: open, arrows: navigate, Enter: confirm, Escape: close)
- Quality change triggers streamlink restart; VOD resumes at current position
- Available in ChannelScreen (Live + VOD) and AppShell global overlay (Live)
- Add QualityIcon

Chapter panel UX improvements:
- Video no longer pauses when chapter panel opens
- PlaybackOverlay is hidden via conditional render instead
- Remove wasPlayingBeforeChapters state (no longer needed)
- X button seeks to video start when no chapters are found, with hint in panel UI

VOD cards and defaults:
- Show view count with eye icon on VOD cards (matching playback overlay style)
- Change default streamBadgeMode from 'language' to 'both'
0.4.2
2026-05-10 12:13:34 +02:00
Andre 23eeb9912c feat: add viewer metrics and dynamic category display to playback overlay
- Live streams: show viewer count and current category inline next to LIVE indicator
- VODs: display clapperboard badge, total view count, and chapter-based category
- Chapter-based category updates automatically based on playback position
- Chapters are preloaded on VOD start; fallback category is "Unbekannt" if missing
- Add icons: EyeIcon, GamepadIcon, ClapperboardIcon
0.4.1
2026-05-10 09:55:23 +02:00
Andre 48ab6e551f feat: replace mpv with hls.js HTML5 video player (v0.4.0)
- Remove mpvController.ts and hlsTrimmer.ts entirely
  - Rewrite PlaybackService: streamlink --stream-url → HLS-URL → IPC event playback:hls-url
  - Add VideoPlayer.tsx (hls.js <video> wrapper, forwardRef with seek/seekTo/pause/play/stop/getCurrentTime)
  - Integrate VideoPlayer into ChannelScreen with direct videoRef calls (no IPC roundtrip for seek/pause)
  - Add global AppShell playback overlay for direct stream start (A-button in Browse/Category/StreamList)
  - Propagate onStartLive callback through BrowseScreen, CategoryScreen, StreamListScreen
  - Add CORS bypass for Twitch CDN (webRequest.onBeforeSendHeaders) and autoplay-policy switch
  - Update CSP: add media-src 'self' blob: for hls.js MediaSource API
  - Open chapter panel without stopping playback (pause + resume instead of stop/restart)
  - Drop IPC channels: seek, seekTo, resume, togglePause, getCurrentPosition, onTimeUpdate
  - Add IPC channel: playback:hls-url event with HlsUrlPayload
0.4.0
2026-05-10 08:33:07 +02:00
Andre cde5fec9ff feat: playback overlay with seek bar, auto-hide and double-tap skip (v0.3.4)
New PlaybackOverlay component replaces the old hint bar in ChannelScreen.
The overlay sits as a fullscreen layer above the app during playback:

- Auto-hides after 5 s while playing; stays visible when paused;
  reactivated by any gamepad key press or mouse movement
- Center play/pause button, channel avatar, title, LIVE badge
- VOD: seek bar with pointer-capture scrubbing, current time / total duration
- Live: pulsing LIVE indicator instead of seek bar
- Double-tap zones left/right (±10 s) with animated skip feedback
- Gamepad hints in the top bar
- New icons: PlayIcon, PauseIcon, StopIcon in Icons.tsx
- New IPC channel `playback:time-update` (~1 Hz) pushes absolute playback
  position from main process to renderer; adds PlaybackTimeUpdate type
  and onTimeUpdate bridge in preload + t4sd.d.ts
- PlaybackEvent extended with durationSeconds and isLive fields
0.3.4
2026-05-07 17:24:48 +02:00
Andre 87eb37c60c docs: rewrite README in English and German, add missing features
Add VOD chapters, language-filtered streams, account screen, quit dialog, and docked TV fullscreen to feature list. Update gamepad layout table.
2026-05-06 17:58:05 +02:00
Andre 2e6c61b2c7 feat: maximize window on Linux to fill screen, bump version to 0.3.3
On Linux, the primary display resolution is queried and the window is  maximized on startup. This ensures a fullscreen experience both on the Steam Deck display and on external monitors/TVs when docked.
Windows/dev behavior unchanged.
0.3.3
2026-05-06 17:47:43 +02:00
Andre f365170ded feat: Account-Screen mit Nutzerprofil, App-Version aus package.json
- AccountScreen zeigt jetzt Profilbild, Anzeigename und @Login des eingeloggten Twitch-Nutzers (via neuem IPC-Kanal twitch:get-own-user)
- CSS: account__avatar zeigt Profilbild, neue Klassen für identity, name, login und error
0.3.2
2026-04-25 16:14:59 +02:00
Andre eeddcb9dff feat: Quit-Dialog und Xbox-Button-Prompts ergänzen 0.3.1 2026-04-25 15:12:45 +02:00
Andre e035ec9ac9 fix: Kapitelwahl und Seeking bei laufenden Live-VODs korrigieren 0.3.0 2026-04-25 12:22:44 +02:00
Andre 312097e205 docs: README.md mit Projektbeschreibung hinzugefügt 2026-04-13 19:39:21 +02:00
Andre 174f525145 Fix: Entdoppelte Gamepad-Eingaben bei externen Controllern
- Browser-Gamepad-Pfad unter Linux deaktiviert
- nahezu zeitgleiche identische Eingaben aus mehreren /dev/input/js*-Devices entdoppelt
- doppelte D-Pad-Bewegungen bei Bluetooth-/USB-Controllern verhindert
2026-04-12 15:43:29 +02:00
Andre 028e56e2cb Fix für Resume und R2-Seeking bei fMP4-VODs auf dem Steam Deck
- ffmpeg-full im Flatpak eingebunden für vollständiges H.264/VAAPI-Decoding
  - Trigger-Repeat für L2/R2 deaktiviert, um gestapelte 300s-Seeks zu vermeiden
  - fMP4-HLS-Playlists über EXT-X-MAP erkannt
  - große fMP4-Seeks nicht mehr per In-Stream-Seek, sondern per Reload einer getrimmten Playlist
  - getrimmte Playlists über localhost statt file:// ausgeliefert
  - absoluten Playback-Offset eingeführt, damit mehrfaches R2 korrekt weiter vorspult
  - B Button ermöglicht, mit der selektierung zurück zum sidemenu zurückzukehren
  - Logging Schalter dem Einstellungs Screen hinzugefügt
2026-04-12 15:19:26 +02:00
Andre 276d27ca89 Update auf mpv 0.41.0 mit lua support 0.2.0 2026-04-12 11:48:08 +02:00
Andre b0a2d9446e Fix versuch: Seeking und Resume von VODs ohne Kapitel
streamlink.ts — 3 neue mpv-Argumente für VODs
  - --demuxer-max-bytes=150M / --demuxer-max-back-bytes=75M — größerer Buffer, verhindert Flush bei Seeks
  - --hr-seek=no — Keyframe-only Seeks, zuverlässiger für HLS
  mpvController.ts — 2 Änderungen                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       - Relative Seeks jetzt relative+keyframes (statt relative) — konsistent mit seekAbsolute                                                                                                                                                                                                   - Neue pollTimePos()-Methode als Fallback wenn observe_property nicht feuert
  playbackService.ts — Kerndfix

  - playback-restart statt file-loaded für den initialen Resume-Seek — wartet bis der erste Frame dekodiert ist, statt nur auf das Manifest
  - Seek-Verify nach 5s — prüft ob der Seek geklappt hat, Retry falls nicht
  - Poll-Fallback nach 10s — startet Polling falls time-pos nie über observe_property kommt
  - Cleanup in stop() für die neuen Timer/Callbacks
2026-04-12 09:30:33 +02:00
Andre 77e74a5994 Fix: Seeking optimiert? 0.1.0 2026-04-12 08:35:18 +02:00
Andre c84b8e16fe Fix: Sidebar Darstellung korrigert
- Zusätzlich Menueinträge Top Streams
- Topstreams DE und EN  anzeige filterte Livestreams.
- Einstellung Sidebarbreite und Flaggenbadge (Slider) hinzugefügt.
2026-04-11 22:05:24 +02:00
Andre cfb8a0b790 Phase 6 abgeschlossen
- Fix: VOD-Kapitelwechsel und Following-Layout korrigiert
2026-04-11 13:03:31 +02:00
Andre 56d212a3b7 Phase 6 Feat: VOD-Kapitelauswahl
- Kapitel werden via Twitch GQL API geladen
- Kapitelauswahl als Modal-Overlay zentriert dargestellt
- A-Taste pausiert/setzt VOD-Wiedergabe fort (war: Stop)
2026-04-11 11:11:02 +02:00
Andre ffad3b045d Phase 6 Fix 4
- Unter "Du folgst" wurde immer der erste Kanal aufgerufen, unabhängig vom ausgewählten Kanal
- Abbrechen der Wiedergabe auch unter "Durchsuchen" und Top-Stream möglich.
2026-04-11 09:59:31 +02:00
Andre f592de7aeb Phase 6 Fix 3
- Gamepad steamdeck repariert
- Navigation , Start und Stopp der Wiedergabe ist jetzt möglich
2026-04-10 22:03:42 +02:00
Andre ac9f9ea94f Phase 6 Fix 2
-Wiedergabe Livestream und VOD im Desktopmodus
2026-04-10 19:43:37 +02:00
Andre 0c88bded01 Phase 6 Fix 1
- Flatpak build erfolgreich
- Auf Steamdeck lauffähig
- Stream obder VOD Wiedergabe nicht möglich
2026-04-10 16:05:36 +02:00
Andre f31bad7cc9 Phase 6 ongoing WSL2-Vorbereitung 2026-04-09 16:36:38 +02:00
Andre 897f6ab727 Phase 5.6 abgeschlossen
- Seitenmenu um Einstellungen ergänzt
- Sprach-Badge und Flaggen anzeige im Thumbnail des Streams anzeigbar.
-  Sprach und Flaggen Einstellungen hinzugefügt.
2026-04-08 21:37:20 +02:00
Andre 1f40d25771 Phase 5.5 abgeschlossen
- Durchsuchen Menu
- Top Live Streams
- Top Kategorie
- Anzeige Zuschaueranzahl getTopGames(), technisch bedingt mit einer gewissen unschärfe zu den tatsächliche Zuschauer der Kategorie
2026-04-08 21:05:34 +02:00
Andre 2cd5e2fb0c Phase 5 abgeschlossen
- SQLite-Setup: `src/main/store/db.ts` (better-sqlite3, Migration)
- Lokaler VOD-Verlauf (Resume)
- mpv `time-pos`-Observer schreibt alle ~5s in DB
- Auto-Resume: beim VOD-Start Position aus DB lesen → `--start=<s>`
 -Resume-Indikator auf VOD-Karte
 - Completed-Indikator (dunkles Overlay + Checkmark)
2026-04-08 20:05:24 +02:00
Andre ed1f710866 Phase 4 abgeschlossen
- VOD Historie in Channelscreen implementiert
- VOD-Wiedergabe + Stop-Verhalten verifiziert
2026-04-08 17:49:30 +02:00
Andre 3e8e5c2078 Phase 3 abgeschlossen
Fix: Streamlink auth header entfernt, mpv-Pfad explizit aufgelöst
Livestream wird kann jetzt abgespielt werden
2026-04-08 17:44:48 +02:00
Andre 60d4b4ace7 Vorbereitung Phase 3
- Plan und Todo überarbeitet
2026-04-07 21:14:59 +02:00
Andre 1f5d1254db Phase 2
- Twitch Helix-Client + Followed-Channels-Home-Screen
- Navigation über Sidebar
- Karten-Design überarbeitet
2026-04-07 20:57:25 +02:00
Andre 7d19ccf652 Projektstart
- Phase 1 "Twitch Login" abgeschlossen
2026-04-07 20:11:00 +02:00