diff --git a/pmoapp/webapp/src/composables/useMediaServers.ts b/pmoapp/webapp/src/composables/useMediaServers.ts index 299b0e68..cfaf03d0 100644 --- a/pmoapp/webapp/src/composables/useMediaServers.ts +++ b/pmoapp/webapp/src/composables/useMediaServers.ts @@ -36,9 +36,36 @@ function ensureSSEConnected() { const serverId = event.server_id switch (event.type) { - case 'global_updated': - // Invalider tout le cache de ce serveur - console.log(`[useMediaServers] GlobalUpdated pour ${serverId}`) + case 'online': + // Nouveau serveur découvert + console.log(`[useMediaServers] Serveur ${serverId} (${event.friendly_name}) est maintenant en ligne`) + + // Ajouter au cache avec les infos disponibles + const server: MediaServerSummary = { + id: serverId, + friendly_name: event.friendly_name, + model_name: event.model_name, + online: true, + } + serversCache.value.set(serverId, server) + + // Fetch la liste complète pour avoir les bonnes infos + // On ne le fait pas ici car on pourrait déclencher trop de requêtes + // La liste se mettra à jour au prochain refresh automatique + break + + case 'offline': + // Serveur déconnecté + console.log(`[useMediaServers] Serveur ${serverId} est maintenant hors ligne`) + + // Marquer comme offline dans le cache + const existingServer = serversCache.value.get(serverId) + if (existingServer) { + existingServer.online = false + serversCache.value.set(serverId, existingServer) + } + + // Invalider tout le cache browse de ce serveur const keysToDelete: string[] = [] browseCache.value.forEach((_, key) => { if (key.startsWith(serverId + '/')) { @@ -48,6 +75,18 @@ function ensureSSEConnected() { keysToDelete.forEach(key => browseCache.value.delete(key)) break + case 'global_updated': + // Invalider tout le cache de ce serveur + console.log(`[useMediaServers] GlobalUpdated pour ${serverId}`) + const globalKeysToDelete: string[] = [] + browseCache.value.forEach((_, key) => { + if (key.startsWith(serverId + '/')) { + globalKeysToDelete.push(key) + } + }) + globalKeysToDelete.forEach(key => browseCache.value.delete(key)) + break + case 'containers_updated': // Invalider les containers spécifiques console.log(`[useMediaServers] ContainersUpdated pour ${serverId}:`, event.container_ids) diff --git a/pmoapp/webapp/src/composables/useRenderers.ts b/pmoapp/webapp/src/composables/useRenderers.ts index ca6e715c..dd27c5de 100644 --- a/pmoapp/webapp/src/composables/useRenderers.ts +++ b/pmoapp/webapp/src/composables/useRenderers.ts @@ -45,6 +45,63 @@ function ensureSSEConnected() { sse.onRendererEvent((event) => { const rendererId = event.renderer_id const timestamp = Date.parse(event.timestamp ?? '') || Date.now() + + // Gérer les événements Online/Offline différemment + if (event.type === 'online') { + // Nouveau renderer découvert + console.log(`[useRenderers] Renderer ${rendererId} (${event.friendly_name}) est maintenant en ligne`) + + // Ajouter au cache avec les infos disponibles + // Note: on n'a pas toutes les infos (capabilities, protocol) donc on fetch ensuite + const renderer: RendererSummary = { + id: rendererId, + friendly_name: event.friendly_name, + model_name: event.model_name, + protocol: 'upnp', // Valeur par défaut, sera mise à jour par le fetch + capabilities: { + has_avtransport: false, + has_avtransport_set_next: false, + has_rendering_control: false, + has_connection_manager: false, + has_linkplay_http: false, + has_arylic_tcp: false, + has_oh_playlist: false, + has_oh_volume: false, + has_oh_info: false, + has_oh_time: false, + has_oh_radio: false, + }, + online: true, + } + renderersCache.value.set(rendererId, renderer) + + // Fetch la liste complète pour avoir les bonnes infos + void fetchRenderers(true) + + // Fetch le snapshot complet pour ce renderer + void fetchRendererSnapshot(rendererId, { force: true }) + return + } + + if (event.type === 'offline') { + // Renderer déconnecté + console.log(`[useRenderers] Renderer ${rendererId} est maintenant hors ligne`) + + // Marquer comme offline dans le cache + const renderer = renderersCache.value.get(rendererId) + if (renderer) { + renderer.online = false + renderersCache.value.set(rendererId, renderer) + } + + // Supprimer le snapshot (il n'est plus valide) + snapshotState.snapshots.delete(rendererId) + snapshotState.lastSnapshotAt.delete(rendererId) + snapshotState.lastEventAt.delete(rendererId) + return + } + + // Pour les autres événements, comportement existant snapshotState.lastEventAt.set(rendererId, timestamp) const lastSnapshot = snapshotState.lastSnapshotAt.get(rendererId) ?? 0 if (!snapshotState.snapshots.has(rendererId) || timestamp > lastSnapshot) { diff --git a/pmoapp/webapp/src/services/pmocontrol/types.ts b/pmoapp/webapp/src/services/pmocontrol/types.ts index e9f338f9..519ab02c 100644 --- a/pmoapp/webapp/src/services/pmocontrol/types.ts +++ b/pmoapp/webapp/src/services/pmocontrol/types.ts @@ -176,10 +176,14 @@ export type RendererEventPayload = | { type: 'metadata_changed'; renderer_id: string; title: string | null; artist: string | null; album: string | null; album_art_uri: string | null; timestamp: string } | { type: 'queue_updated'; renderer_id: string; queue_length: number; timestamp: string } | { type: 'binding_changed'; renderer_id: string; server_id: string | null; container_id: string | null; timestamp: string } + | { type: 'online'; renderer_id: string; friendly_name: string; model_name: string; manufacturer: string; timestamp: string } + | { type: 'offline'; renderer_id: string; timestamp: string } export type MediaServerEventPayload = | { type: 'global_updated'; server_id: string; system_update_id: number | null; timestamp: string } | { type: 'containers_updated'; server_id: string; container_ids: string[]; timestamp: string } + | { type: 'online'; server_id: string; friendly_name: string; model_name: string; manufacturer: string; timestamp: string } + | { type: 'offline'; server_id: string; timestamp: string } export type UnifiedEventPayload = | { category: 'renderer' } & RendererEventPayload