From 9776b83ac5a6f3715453219cccd8f2708f579822 Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Thu, 16 Apr 2026 20:09:21 +0200 Subject: [PATCH] fix: client bug fixes from review - FileBrowser: reload hidden files when profile changes - WebSocket: wrap JSON.parse in try-catch - WebSocket: exponential backoff on reconnect (2s -> 30s max) - WebSocket: clean up connection on destroy Co-Authored-By: Claude Opus 4.6 --- client/src/components/FileBrowser.svelte | 8 +++++ client/src/lib/ws.ts | 38 +++++++++++++++--------- client/src/routes/+page.svelte | 3 +- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/client/src/components/FileBrowser.svelte b/client/src/components/FileBrowser.svelte index de698a4..c19bef0 100644 --- a/client/src/components/FileBrowser.svelte +++ b/client/src/components/FileBrowser.svelte @@ -16,6 +16,14 @@ } }); + // Reload hidden files when profile changes + $effect(() => { + void $profile; + if (selectedRoot) { + loadFiles(); + } + }); + async function loadFiles() { $files = await getFiles(selectedRoot); const hidden = await getHidden($profile); diff --git a/client/src/lib/ws.ts b/client/src/lib/ws.ts index a143650..5d99ba7 100644 --- a/client/src/lib/ws.ts +++ b/client/src/lib/ws.ts @@ -2,30 +2,40 @@ import { getServer } from "./api"; import { exportStatus, exportCompleted } from "./stores"; let socket: WebSocket | null = null; +let reconnectDelay = 2000; export function connectExportWs() { const wsUrl = getServer().replace(/^http/, "ws") + "/ws/export"; socket = new WebSocket(wsUrl); + socket.onopen = () => { + reconnectDelay = 2000; // reset backoff on successful connect + }; + socket.onmessage = (event) => { - const msg = JSON.parse(event.data); - switch (msg.type) { - case "clip_done": - exportCompleted.update(n => n + 1); - break; - case "all_done": - exportStatus.set("done"); - break; - case "error": - exportStatus.set("error"); - console.error("Export error:", msg.msg); - break; + try { + const msg = JSON.parse(event.data); + switch (msg.type) { + case "clip_done": + exportCompleted.update(n => n + 1); + break; + case "all_done": + exportStatus.set("done"); + break; + case "error": + exportStatus.set("error"); + console.error("Export error:", msg.msg); + break; + } + } catch (e) { + console.error("Failed to parse WebSocket message:", e); } }; socket.onclose = () => { - // Reconnect after 2s - setTimeout(connectExportWs, 2000); + // Reconnect with exponential backoff, max 30s + setTimeout(connectExportWs, reconnectDelay); + reconnectDelay = Math.min(reconnectDelay * 2, 30000); }; } diff --git a/client/src/routes/+page.svelte b/client/src/routes/+page.svelte index f10280b..53ab6c5 100644 --- a/client/src/routes/+page.svelte +++ b/client/src/routes/+page.svelte @@ -6,7 +6,7 @@ import ProfileBar from "../components/ProfileBar.svelte"; import { mpvStart, mpvLoad, mpvSeek, mpvPause, mpvResume, mpvSetLoop, mpvClearLoop, mpvTimePos, mpvDuration } from "$lib/mpv"; import { streamUrl, audioUrl, deleteExport, getMarkers } from "$lib/api"; - import { connectExportWs } from "$lib/ws"; + import { connectExportWs, disconnectExportWs } from "$lib/ws"; import { loadSettings, saveSettings } from "$lib/settings"; import { currentFile, cursor, duration, playPos, playing, quality, @@ -44,6 +44,7 @@ onDestroy(() => { clearInterval(pollInterval); + disconnectExportWs(); }); // Load file into mpv when currentFile OR quality changes