From 19451068f1dbe143e11799b3036abc1566f25f8d Mon Sep 17 00:00:00 2001 From: Eric Coissac Date: Mon, 6 Apr 2026 00:08:50 +0200 Subject: [PATCH] [webapp] Add retry logic and improve renderer registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added automatic retries with exponential backoff to fetchRenderers - Improved WebRenderer registration: handle stale players, avoid duplicate registrations, and force renderers refresh after successful connection - Bumped version to v0.3.37 (Cargo: 0.3.36 → Cargo.lock mismatch fixed by updating to final version) --- Cargo.lock | 2 +- PMOMusic/Cargo.toml | 2 +- pmoapp/webapp/src/composables/useRenderers.ts | 50 +++++++++++-------- .../webapp/src/composables/useWebRenderer.ts | 39 +++++++++++++-- version.txt | 2 +- 5 files changed, 68 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a31bb802..5017a1ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "PMOMusic" -version = "0.3.35" +version = "0.3.36" dependencies = [ "axum 0.8.7", "console-subscriber", diff --git a/PMOMusic/Cargo.toml b/PMOMusic/Cargo.toml index aaf4f5e3..7b01df72 100644 --- a/PMOMusic/Cargo.toml +++ b/PMOMusic/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "PMOMusic" -version = "0.3.35" +version = "0.3.37" edition = "2024" [dependencies] diff --git a/pmoapp/webapp/src/composables/useRenderers.ts b/pmoapp/webapp/src/composables/useRenderers.ts index 49a9bdc6..d0c9d9e2 100644 --- a/pmoapp/webapp/src/composables/useRenderers.ts +++ b/pmoapp/webapp/src/composables/useRenderers.ts @@ -270,30 +270,40 @@ function selectRenderer(id: string | null) { snapshotState.selectedRendererId = id; } -async function fetchRenderers(force = false) { +async function fetchRenderers(force = false, retries = 2) { ensureSSEInitialized(); - try { - loading.value = true; - error.value = null; + let lastError: Error | null = null; + + for (let attempt = 0; attempt <= retries; attempt++) { + try { + loading.value = true; + error.value = null; - // Utiliser le cache API centralisé - const data = await apiCache.fetch( - '/renderers', - undefined, - () => api.getRenderers(), - { force, ttl: RENDERERS_CACHE_MS } - ); - - renderersCache.value = new Map( - data.map((renderer) => [renderer.id, renderer]), - ); - } catch (err) { - error.value = err instanceof Error ? err.message : "Erreur fetch renderers"; - console.error("[useRenderers] Erreur fetch:", err); - } finally { - loading.value = false; + // Utiliser le cache API centralisé + const data = await apiCache.fetch( + '/renderers', + undefined, + () => api.getRenderers(), + { force, ttl: RENDERERS_CACHE_MS } + ); + + renderersCache.value = new Map( + data.map((renderer) => [renderer.id, renderer]), + ); + return; + } catch (err) { + lastError = err instanceof Error ? err : new Error("Erreur fetch renderers"); + console.error("[useRenderers] Erreur fetch (attempt " + (attempt + 1) + "):", lastError); + if (attempt < retries) { + await new Promise(r => setTimeout(r, 500 * (attempt + 1))); + } + } finally { + loading.value = false; + } } + + error.value = lastError?.message ?? "Erreur fetch renderers"; } async function fetchRendererSnapshot( diff --git a/pmoapp/webapp/src/composables/useWebRenderer.ts b/pmoapp/webapp/src/composables/useWebRenderer.ts index 1a8449af..91f7fd3e 100644 --- a/pmoapp/webapp/src/composables/useWebRenderer.ts +++ b/pmoapp/webapp/src/composables/useWebRenderer.ts @@ -73,17 +73,41 @@ export function useWebRenderer() { let onConnectedCallback: (() => void) | null = null; async function register(): Promise { - // Prevent concurrent registrations (race condition → double player) - if (globalPlayer || registering) { + // Allow re-registration if we have a stale player but no connection + if (globalPlayer && !connected.value) { + console.log('[WebRenderer] stale globalPlayer, clearing'); + globalPlayer = null; + } + + if (globalPlayer) { + player = globalPlayer; + connected.value = true; + console.log('[WebRenderer] reusing existing player'); + return; + } + + if (registering) { + console.log('[WebRenderer] already registering, waiting...'); + // Wait a bit and try once + await new Promise(r => setTimeout(r, 1000)); if (globalPlayer) { player = globalPlayer; connected.value = true; + return; } - return; } + registering = true; - const instanceId = getOrCreateInstanceId(); + let instanceId; + try { + instanceId = getOrCreateInstanceId(); + } catch (e) { + console.error('[WebRenderer] failed to get instanceId:', e); + registering = false; + return; + } + console.log('[WebRenderer] registering with instanceId:', instanceId); try { @@ -122,6 +146,13 @@ export function useWebRenderer() { connected.value = true; onConnectedCallback?.(); + + // Force refresh renderers list after registration + // The SSE might miss the initial event + setTimeout(() => { + console.log('[WebRenderer] forcing renderers refresh'); + window.dispatchEvent(new CustomEvent('webrenderer-registered')); + }, 500); } catch (e) { console.error("[WebRenderer] register error:", e); } finally { diff --git a/version.txt b/version.txt index 1ff34fb3..ba61ff30 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.3.35 +0.3.37