diff --git a/src/main/auth/authService.ts b/src/main/auth/authService.ts index 65b7dc2..1289798 100644 --- a/src/main/auth/authService.ts +++ b/src/main/auth/authService.ts @@ -49,8 +49,8 @@ export class AuthService extends EventEmitter { } /** - * Startet den Device Code Flow. Liefert die Anzeigedaten sofort zurück - * und pollt im Hintergrund. Statuswechsel werden als 'auth-event' emittiert. + * Starts the Device Code Flow. Returns display data immediately + * and polls in the background. Status changes are emitted as 'auth-event'. */ async startDeviceFlow(): Promise { if (!this.isConfigured()) { @@ -86,8 +86,8 @@ export class AuthService extends EventEmitter { } /** - * Liefert ein gültiges Access-Token, refresht falls nötig. - * Wird vom Helix-Client benutzt. + * Returns a valid access token, refreshing it if necessary. + * Used by the Helix client. */ async getValidAccessToken(): Promise { if (!this.tokens) return null @@ -100,7 +100,7 @@ export class AuthService extends EventEmitter { await saveTokens(this.tokens) return this.tokens.accessToken } catch { - // Refresh fehlgeschlagen → User muss sich neu einloggen. + // Refresh failed — user must log in again. this.tokens = null await clearTokens() return null diff --git a/src/main/auth/deviceCodeFlow.ts b/src/main/auth/deviceCodeFlow.ts index ffae0f1..dc62d7e 100644 --- a/src/main/auth/deviceCodeFlow.ts +++ b/src/main/auth/deviceCodeFlow.ts @@ -1,5 +1,5 @@ // Twitch OAuth 2.0 Device Code Flow. -// Doku: https://dev.twitch.tv/docs/authentication/getting-tokens-oauth/#device-code-grant-flow +// Docs: https://dev.twitch.tv/docs/authentication/getting-tokens-oauth/#device-code-grant-flow const DEVICE_ENDPOINT = 'https://id.twitch.tv/oauth2/device' const TOKEN_ENDPOINT = 'https://id.twitch.tv/oauth2/token' @@ -100,7 +100,7 @@ export async function pollForToken(opts: PollOptions): Promise { payload = json as ErrorResponse } } catch { - /* nicht-JSON Antwort, ignorieren */ + /* non-JSON response, ignore */ } const msg = (payload.message ?? '').toLowerCase() diff --git a/src/main/index.ts b/src/main/index.ts index 661fe8f..976ea98 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -64,15 +64,15 @@ function createWindow(): BrowserWindow { } /** - * CORS-Bypass für Twitch CDN und HLS-Streams. - * hls.js im Renderer sendet Requests von localhost/file:// — Twitch CDN erwartet - * Origin: https://www.twitch.tv. Wir setzen Origin + Referer auf ausgehenden Requests - * und ergänzen CORS-Response-Header, damit Chromium die Antworten akzeptiert. + * CORS bypass for Twitch CDN and HLS streams. + * hls.js in the renderer sends requests from localhost/file:// — Twitch CDN expects + * Origin: https://www.twitch.tv. We set Origin + Referer on outgoing requests + * and inject CORS response headers so Chromium accepts the responses. * - * HLS-Cache-Unterdrückung: xhrSetup in hls.js setzt Cache-Control: no-store auf jedem - * XHR. Das löst einen CORS-Preflight aus (Cache-Control ist kein safelisted header). - * Damit der Preflight durchkommt, muss onHeadersReceived auch ttvnw.net-Domains abdecken - * (Segment-CDN: *.j.cloudfront.hls.ttvnw.net — enthält nicht "cloudfront.net"). + * HLS cache suppression: xhrSetup in hls.js sets Cache-Control: no-store on every + * XHR. This triggers a CORS preflight (Cache-Control is not a safelisted header). + * For the preflight to pass, onHeadersReceived must also cover ttvnw.net domains + * (segment CDN: *.j.cloudfront.hls.ttvnw.net — does not contain "cloudfront.net"). */ function setupTwitchCors(): void { const isTwitchCdn = (url: string): boolean => diff --git a/src/main/input/gamepadReader.ts b/src/main/input/gamepadReader.ts index 615af7b..a5c0bd1 100644 --- a/src/main/input/gamepadReader.ts +++ b/src/main/input/gamepadReader.ts @@ -1,32 +1,32 @@ /** - * Liest Gamepad-Events via Linux evdev (/dev/input/event*). + * Reads gamepad events via Linux evdev (/dev/input/event*). * - * Erkennt Controller über /dev/input/js* und öffnet deren zugehöriges - * event*-Interface. Das evdev-Interface liefert standardisierte, benannte - * Button-Codes (BTN_SOUTH, BTN_NORTH, …) die über alle Controller-Typen - * (Xbox, PlayStation, Nintendo) und Verbindungsarten (USB, Bluetooth) - * konsistent sind – unabhängig davon, welchen Treiber der Kernel nutzt. + * Detects controllers via /dev/input/js* and opens their corresponding + * event* interface. The evdev interface provides standardized, named + * button codes (BTN_SOUTH, BTN_NORTH, …) that are consistent across all + * controller types (Xbox, PlayStation, Nintendo) and connection methods + * (USB, Bluetooth), regardless of which driver the kernel uses. * - * Damit entfällt das alte Problem mit der button-Nummerierung im Joystick-API, - * bei dem Xbox-One-Controller via Bluetooth (Share-Button verschiebt die - * Nummerierung) X-Button als Nummer 3 statt 2 lieferten. + * This eliminates the old button-numbering problem in the Joystick API, + * where Xbox One controllers via Bluetooth (Share button shifts the + * numbering) reported the X button as number 3 instead of 2. * - * Unterstützt mehrere Gamepads gleichzeitig (Steam Deck + Bluetooth Xbox). - * Hotplug wird durch periodisches Scannen von /dev/input/ abgedeckt. + * Supports multiple gamepads simultaneously (Steam Deck + Bluetooth Xbox). + * Hotplug is handled by periodically scanning /dev/input/. * - * BTN_*-Codes sind konsistent für alle Controller: + * BTN_* codes are consistent across all controllers: * BTN_SOUTH (0x130) = A / Cross * BTN_EAST (0x131) = B / Circle * BTN_NORTH (0x133) = X / Square * BTN_WEST (0x134) = Y / Triangle * BTN_TL (0x136) = LB / L1 * BTN_TR (0x137) = RB / R1 - * BTN_TL2 (0x138) = LT / L2 (als Button bei manchen Controllern) - * BTN_TR2 (0x139) = RT / R2 (als Button bei manchen Controllern) - * ABS_HAT0X (16) = D-Pad X-Achse - * ABS_HAT0Y (17) = D-Pad Y-Achse - * ABS_X/Y (0/1) = Linker Stick - * ABS_Z/RZ (2/5) = LT/RT als Analog-Achse + * BTN_TL2 (0x138) = LT / L2 (as button on some controllers) + * BTN_TR2 (0x139) = RT / R2 (as button on some controllers) + * ABS_HAT0X (16) = D-Pad X-axis + * ABS_HAT0Y (17) = D-Pad Y-axis + * ABS_X/Y (0/1) = Left stick + * ABS_Z/RZ (2/5) = LT/RT as analog axis */ import * as fs from 'node:fs' @@ -35,8 +35,8 @@ import type { BrowserWindow } from 'electron' import { DEDUP_WINDOW_MS, HOTPLUG_SCAN_INTERVAL_MS, TRIGGER_THRESHOLD } from '../constants/input' import log from 'electron-log/main' -// evdev input_event auf 64-bit Linux: -// { u64 tv_sec (8 Bytes), u64 tv_usec (8 Bytes), u16 type (2), u16 code (2), s32 value (4) } = 24 Bytes +// evdev input_event on 64-bit Linux: +// { u64 tv_sec (8 bytes), u64 tv_usec (8 bytes), u16 type (2), u16 code (2), s32 value (4) } = 24 bytes const INPUT_EVENT_SIZE = 24 const EV_KEY = 0x01 @@ -52,7 +52,7 @@ const BTN_TR = 0x137 const BTN_TL2 = 0x138 const BTN_TR2 = 0x139 -// ABS-Achsen-Codes +// ABS axis codes const ABS_X = 0 const ABS_Y = 1 const ABS_Z = 2 // LT / L2 @@ -84,8 +84,8 @@ interface AxisState { } /** - * Sucht das zugehörige evdev-Device (/dev/input/eventN) für ein - * Joystick-Device (/dev/input/jsN) über den /sys-Dateisystem-Pfad. + * Finds the corresponding evdev device (/dev/input/eventN) for a + * joystick device (/dev/input/jsN) via the /sys filesystem path. */ function findEventDevice(jsDevicePath: string): string | null { try { @@ -223,9 +223,9 @@ class EvdevReader { } /** - * Fallback-Reader für den Fall, dass kein evdev-Device gefunden wird. - * Nutzt das alte Joystick-API (8-Byte-Events, Joystick-Nummern-Mapping). - * Deckt Controller ab, die kein /sys-Event-Device-Mapping haben. + * Fallback reader used when no evdev device is found. + * Uses the legacy Joystick API (8-byte events, numeric button mapping). + * Covers controllers that have no /sys event device mapping. */ class JoystickFallbackReader { private stream: fs.ReadStream | null = null @@ -350,13 +350,13 @@ class JoystickFallbackReader { } /** - * Startet das Lesen aller /dev/input/js* Devices (evdev-Interface bevorzugt). - * Gibt eine Cleanup-Funktion zurück. + * Starts reading all /dev/input/js* devices (evdev interface preferred). + * Returns a cleanup function. */ export function startGamepadReader(getWindow: () => BrowserWindow | null): () => void { if (process.platform !== 'linux') return () => {} - // Keyed by js-Device-Pfad + // Keyed by js device path const readers = new Map() const lastKeyAt = new Map() diff --git a/src/main/playback/streamlink.ts b/src/main/playback/streamlink.ts index cc2f1e2..473aff2 100644 --- a/src/main/playback/streamlink.ts +++ b/src/main/playback/streamlink.ts @@ -32,8 +32,8 @@ function sortQualities(qualities: string[]): string[] { } /** - * Fragt streamlink nach allen verfügbaren Qualitätsstufen für eine URL (--json). - * Gibt eine sortierte Liste zurück; bei Fehler leeres Array. + * Queries streamlink for all available quality levels for a URL (--json). + * Returns a sorted list; empty array on error. */ export function getAvailableQualities(url: string): Promise { return new Promise((resolve) => { @@ -56,8 +56,8 @@ export function getAvailableQualities(url: string): Promise { } /** - * Fragt streamlink nach der direkten HLS-URL für einen Stream oder VOD. - * Wird für Live-Streams (twitch.tv/) und VODs (twitch.tv/videos/) verwendet. + * Queries streamlink for the direct HLS URL of a stream or VOD. + * Used for live streams (twitch.tv/) and VODs (twitch.tv/videos/). */ export function getStreamUrl(url: string, quality: string): Promise { return new Promise((resolve, reject) => { diff --git a/src/main/twitch/helixClient.ts b/src/main/twitch/helixClient.ts index 4297b09..164a3fb 100644 --- a/src/main/twitch/helixClient.ts +++ b/src/main/twitch/helixClient.ts @@ -157,7 +157,7 @@ export class HelixClient { boxArtUrl: g.box_art_url.replace('{width}', '285').replace('{height}', '380') })) - // Zuschauerzahlen: pro Kategorie die Top-100-Streams abrufen (parallel) + // Viewer counts: fetch the top 100 streams per category in parallel const headers = await this.authHeaders() const viewerCounts = await Promise.all( games.map(async (g) => { diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 1acf4d9..83e344f 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -20,14 +20,14 @@ export default function App(): JSX.Element { return off }, [refresh]) - // Browser Gamepad API (funktioniert im Desktop-Modus / Windows) + // Browser Gamepad API (works in desktop mode / Windows) useEffect(() => { if (navigator.userAgent.toLowerCase().includes('linux')) return gamepadService.start() return () => gamepadService.stop() }, []) - // Main-Process Gamepad Reader (liest /dev/input/js* direkt, funktioniert in Gaming Mode) + // Main-process gamepad reader (reads /dev/input/js* directly, works in Gaming Mode) useEffect(() => { return window.t4sd.gamepad.onInput((key) => { const target = document.activeElement ?? document.body diff --git a/src/renderer/src/screens/ChannelScreen.tsx b/src/renderer/src/screens/ChannelScreen.tsx index 2479e14..7bccdf1 100644 --- a/src/renderer/src/screens/ChannelScreen.tsx +++ b/src/renderer/src/screens/ChannelScreen.tsx @@ -588,7 +588,7 @@ export default function ChannelScreen({ channel, onBack }: Props): JSX.Element { )} - {/* HTML5 Video Player — rendert innerhalb des Electron-Fensters */} + {/* HTML5 video player — renders inside the Electron window */} {hlsPayload && (