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
This commit is contained in:
Andre
2026-05-11 19:40:50 +02:00
parent 82ff6dbbf2
commit 78d67de35a
9 changed files with 271 additions and 21 deletions
+22 -8
View File
@@ -5,6 +5,7 @@ import { HelixClient } from './twitch/helixClient'
import { PlaybackService } from './playback/playbackService'
import { registerIpcHandlers } from './ipc/handlers'
import { startGamepadReader } from './input/gamepadReader'
import { getHlsCacheEnabled } from './prefs/hlsCachePref'
const isDev = !app.isPackaged
@@ -53,11 +54,18 @@ function createWindow(): BrowserWindow {
return win
}
function isHlsStreamContent(url: string): boolean {
const pathEnd = url.indexOf('?')
const path = pathEnd === -1 ? url : url.slice(0, pathEnd)
return /\.(m3u8|ts|m4s|aac)$/i.test(path)
}
/**
* 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.
* Zusätzlich: cache-control: no-store für HLS-Inhalte wenn hlsCacheEnabled=false.
*/
function setupTwitchCors(): void {
const isTwitchCdn = (url: string): boolean =>
@@ -75,18 +83,24 @@ function setupTwitchCors(): void {
})
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
if (isTwitchCdn(details.url)) {
const headers: Record<string, string[]> = {}
for (const [k, v] of Object.entries(details.responseHeaders ?? {})) {
headers[k.toLowerCase()] = Array.isArray(v) ? v : [v]
}
const isCdn = isTwitchCdn(details.url)
const applyNoStore = isHlsStreamContent(details.url) && !getHlsCacheEnabled()
if (!isCdn && !applyNoStore) { callback({}); return }
const headers: Record<string, string[]> = {}
for (const [k, v] of Object.entries(details.responseHeaders ?? {})) {
headers[k.toLowerCase()] = Array.isArray(v) ? v : [v]
}
if (isCdn) {
headers['access-control-allow-origin'] = ['*']
headers['access-control-allow-headers'] = ['*']
headers['access-control-allow-methods'] = ['GET, HEAD, OPTIONS']
callback({ responseHeaders: headers })
} else {
callback({})
}
if (applyNoStore) {
headers['cache-control'] = ['no-store']
}
callback({ responseHeaders: headers })
})
}