[webapp] Optimise le chargement des snapshots renderers avec batch controlé
- Ajoute la fonction fetchBatchSnapshots pour limiter le nombre de requêtes simultanées (concurrency=3) et évite la saturation réseau avec un délai entre les batches - Remplace le chargement séquentiel des snapshots par une approche batchée dans DashboardView.vue - Introduit un état de chargement (isLoadingSnapshots) et une indiqueur visuel avec Loader2 animé - Charge les renderers/servers en parallèle au montage via Promise.all
This commit is contained in:
@@ -345,6 +345,46 @@ async function fetchRendererSnapshot(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch les snapshots de plusieurs renderers en parallèle controlée.
|
||||
* - Limite le nombre de requêtes simultanées (concurrency)
|
||||
* - Ajoute un délai entre chaque batch pour ne pas saturer le réseau
|
||||
* - Continue même si certaines requêtes échouent
|
||||
*/
|
||||
async function fetchBatchSnapshots(
|
||||
rendererIds: string[],
|
||||
options: {
|
||||
concurrency?: number; // Nombre max de requêtes parallèles (défaut: 3)
|
||||
batchDelay?: number; // Délai entre les batches en ms (défaut: 100ms)
|
||||
force?: boolean; // Forcer le refetch même en cache
|
||||
} = {}
|
||||
): Promise<void> {
|
||||
const { concurrency = 3, batchDelay = 100, force = false } = options;
|
||||
|
||||
// Filtrer les rendererIds valides
|
||||
const validIds = rendererIds.filter(id => id && typeof id === 'string');
|
||||
|
||||
if (validIds.length === 0) return;
|
||||
|
||||
// Fonction pour traiter un batch
|
||||
const processBatch = async (batch: string[]): Promise<void> => {
|
||||
await Promise.allSettled(
|
||||
batch.map(id => fetchRendererSnapshot(id, { force }))
|
||||
);
|
||||
};
|
||||
|
||||
// Exécuter par batches avec controlled concurrency
|
||||
for (let i = 0; i < validIds.length; i += concurrency) {
|
||||
const batch = validIds.slice(i, i + concurrency);
|
||||
await processBatch(batch);
|
||||
|
||||
// Délai entre les batches (sauf pour le dernier)
|
||||
if (i + concurrency < validIds.length) {
|
||||
await new Promise(resolve => setTimeout(resolve, batchDelay));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transport controls
|
||||
async function play(id: string) {
|
||||
await api.play(id);
|
||||
@@ -477,6 +517,7 @@ export function useRenderers() {
|
||||
// Fetchers
|
||||
fetchRenderers,
|
||||
fetchRendererSnapshot,
|
||||
fetchBatchSnapshots,
|
||||
// Transport controls
|
||||
play,
|
||||
resumeOrPlayFromQueue,
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useRenderers } from '@/composables/useRenderers'
|
||||
import { useMediaServers } from '@/composables/useMediaServers'
|
||||
import RendererCard from '@/components/pmocontrol/RendererCard.vue'
|
||||
import MediaServerCard from '@/components/pmocontrol/MediaServerCard.vue'
|
||||
import { Radio, Server } from 'lucide-vue-next'
|
||||
import { Radio, Server, Loader2 } from 'lucide-vue-next'
|
||||
|
||||
const {
|
||||
allRenderers: renderers,
|
||||
onlineRenderers,
|
||||
getStateById,
|
||||
fetchRenderers,
|
||||
fetchRendererSnapshot
|
||||
fetchBatchSnapshots
|
||||
} = useRenderers()
|
||||
|
||||
const {
|
||||
@@ -20,15 +20,34 @@ const {
|
||||
fetchServers
|
||||
} = useMediaServers()
|
||||
|
||||
// État de chargement pour l'UI
|
||||
const isLoadingSnapshots = ref(false)
|
||||
|
||||
// Charger les données au montage
|
||||
onMounted(async () => {
|
||||
await fetchRenderers()
|
||||
await fetchServers()
|
||||
// Charger les listes de renderers et servers en parallèle
|
||||
await Promise.all([
|
||||
fetchRenderers(),
|
||||
fetchServers()
|
||||
])
|
||||
|
||||
for (const renderer of renderers.value) {
|
||||
fetchRendererSnapshot(renderer.id, { force: true })
|
||||
}
|
||||
// Charger les snapshots en batch controlé
|
||||
// On récupère les IDs des renderers découverts
|
||||
const rendererIds = renderers.value.map(r => r.id)
|
||||
|
||||
if (rendererIds.length > 0) {
|
||||
isLoadingSnapshots.value = true
|
||||
|
||||
// Appel batch avec concurrency controlée
|
||||
await fetchBatchSnapshots(rendererIds, {
|
||||
concurrency: 3, // Max 3 requêtes simultanées
|
||||
batchDelay: 100, // 100ms entre chaque batch
|
||||
force: true
|
||||
})
|
||||
|
||||
isLoadingSnapshots.value = false
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -55,6 +74,10 @@ onMounted(async () => {
|
||||
<Radio :size="24" />
|
||||
<span>Renderers Audio</span>
|
||||
</h2>
|
||||
<span v-if="isLoadingSnapshots" class="loading-indicator">
|
||||
<Loader2 :size="16" class="spin" />
|
||||
Chargement...
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div v-if="renderers.length" class="renderers-grid">
|
||||
@@ -175,6 +198,24 @@ onMounted(async () => {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* Loading indicator */
|
||||
.loading-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-xs);
|
||||
font-size: var(--text-sm);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.loading-indicator .spin {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Grids */
|
||||
.renderers-grid,
|
||||
.servers-grid {
|
||||
|
||||
Reference in New Issue
Block a user