[webapp] Add retry logic and improve renderer registration

- 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)
This commit is contained in:
2026-04-06 00:08:50 +02:00
parent 23b1ce7661
commit 19451068f1
5 changed files with 68 additions and 27 deletions

2
Cargo.lock generated
View File

@@ -4,7 +4,7 @@ version = 4
[[package]]
name = "PMOMusic"
version = "0.3.35"
version = "0.3.36"
dependencies = [
"axum 0.8.7",
"console-subscriber",

View File

@@ -1,6 +1,6 @@
[package]
name = "PMOMusic"
version = "0.3.35"
version = "0.3.37"
edition = "2024"
[dependencies]

View File

@@ -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(

View File

@@ -73,17 +73,41 @@ export function useWebRenderer() {
let onConnectedCallback: (() => void) | null = null;
async function register(): Promise<void> {
// 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 {

View File

@@ -1 +1 @@
0.3.35
0.3.37