mirror of
https://github.com/Andre-Bzk/twitch4steamdeck.git
synced 2026-11-15 09:33:09 +01:00
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:
+22
-8
@@ -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 })
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { app, BrowserWindow, ipcMain } from 'electron'
|
||||
import { app, BrowserWindow, ipcMain, session } from 'electron'
|
||||
import { setHlsCacheEnabled } from '../prefs/hlsCachePref'
|
||||
import { appendFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import type { AuthEvent, AuthService } from '../auth/authService'
|
||||
@@ -35,7 +36,11 @@ export const IPC = {
|
||||
/** main → renderer */
|
||||
playbackEvent: 'playback:event',
|
||||
/** main → renderer: HLS-URL + Metadaten für den Renderer-seitigen Video-Player */
|
||||
playbackHlsUrl: 'playback:hls-url'
|
||||
playbackHlsUrl: 'playback:hls-url',
|
||||
|
||||
appGetCacheSize: 'app:get-cache-size',
|
||||
appClearCache: 'app:clear-cache',
|
||||
appSetHlsCacheEnabled: 'app:set-hls-cache-enabled'
|
||||
} as const
|
||||
|
||||
export function registerIpcHandlers(
|
||||
@@ -47,6 +52,12 @@ export function registerIpcHandlers(
|
||||
app.quit()
|
||||
})
|
||||
|
||||
ipcMain.handle(IPC.appGetCacheSize, () => session.defaultSession.getCacheSize())
|
||||
ipcMain.handle(IPC.appClearCache, () => session.defaultSession.clearCache())
|
||||
ipcMain.handle(IPC.appSetHlsCacheEnabled, (_e, enabled: boolean) => {
|
||||
setHlsCacheEnabled(Boolean(enabled))
|
||||
})
|
||||
|
||||
ipcMain.handle(IPC.authStatus, () => auth.getStatus())
|
||||
ipcMain.handle(IPC.authConfigured, () => auth.isConfigured())
|
||||
ipcMain.handle(IPC.authStart, () => auth.startDeviceFlow())
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
let hlsCacheEnabled = false
|
||||
|
||||
export function getHlsCacheEnabled(): boolean {
|
||||
return hlsCacheEnabled
|
||||
}
|
||||
|
||||
export function setHlsCacheEnabled(enabled: boolean): void {
|
||||
hlsCacheEnabled = enabled
|
||||
}
|
||||
Reference in New Issue
Block a user