Files
pmomusic/Blackboard/Report/bug_images_cache_webapp.md
Eric Coissac c9f9eb4ee5 Fix cache image loading bug in webapp
Fix the bug where cached images were not displaying in the webapp due to synchronous load events. Added checkImageComplete() function to detect already loaded images from cache and updated all affected components (CurrentTrack, QueueItem, MediaItem, RendererCard) to properly handle cached image loading.
2026-01-21 20:02:10 +01:00

4.1 KiB

Rapport : Correction du bug d'affichage des images en cache dans la webapp

Tâche originale

Les images de couverture d'album ne s'affichaient plus dans l'application web Control Point. Seuls les logos SVG de fallback étaient visibles, bien que les URLs soient correctes et les images accessibles.

Analyse

Pattern d'affichage existant

Les composants utilisaient le pattern suivant :

<img
    v-if="item.album_art_uri && !imageError"
    v-show="imageLoaded"
    :src="item.album_art_uri"
    @load="handleImageLoad"
    @error="handleImageError"
/>
<div v-if="!item.album_art_uri || imageError || !imageLoaded" class="placeholder">
    <Music :size="20" />
</div>

Avec :

const imageLoaded = ref(false);

watch(() => props.item.album_art_uri, () => {
    imageLoaded.value = false;
    imageError.value = false;
});

function handleImageLoad() {
    imageLoaded.value = true;
}

Cause du bug

Lorsqu'une image est déjà en cache du navigateur, elle peut se charger de manière synchrone avant que Vue n'ait attaché l'écouteur d'événement @load. Dans ce cas :

  1. L'image est créée dans le DOM (via v-if)
  2. Le navigateur charge l'image immédiatement depuis le cache
  3. L'événement load se déclenche avant que Vue n'ait attaché @load
  4. imageLoaded reste à false
  5. L'image reste cachée par v-show="imageLoaded"
  6. Le placeholder SVG s'affiche à la place

Ce comportement est particulièrement fréquent avec des images déjà visitées ou après un rechargement de page.

Correction appliquée

Solution

Ajout d'une fonction checkImageComplete() qui vérifie si l'image est déjà chargée via les propriétés natives de l'élément <img> :

const coverImageRef = ref<HTMLImageElement | null>(null);

function checkImageComplete() {
    nextTick(() => {
        if (coverImageRef.value?.complete && coverImageRef.value?.naturalWidth > 0) {
            imageLoaded.value = true;
            imageError.value = false;
        }
    });
}

onMounted(() => {
    checkImageComplete();
});

watch(() => metadata.value?.album_art_uri, (newUri) => {
    imageLoaded.value = false;
    imageError.value = false;
    if (newUri) {
        checkImageComplete();
    }
});

Et ajout de la référence sur l'élément <img> :

<img ref="coverImageRef" ... />

Fichiers modifiés

Fichier Modification
pmoapp/webapp/src/components/pmocontrol/CurrentTrack.vue Ajout checkImageComplete(), coverImageRef, onMounted
pmoapp/webapp/src/components/pmocontrol/QueueItem.vue Ajout checkImageComplete(), coverImageRef, onMounted
pmoapp/webapp/src/components/pmocontrol/MediaItem.vue Ajout checkImageComplete(), coverImageRef, onMounted
pmoapp/webapp/src/components/pmocontrol/RendererCard.vue Ajout complet de la gestion d'état image (était absent)

Détail des modifications par composant

CurrentTrack.vue

  • Import de onMounted, nextTick
  • Ajout de coverImageRef
  • Ajout de checkImageComplete()
  • Modification du watch pour appeler checkImageComplete() après changement d'URL
  • Ajout de onMounted(() => checkImageComplete())
  • Ajout de ref="coverImageRef" sur l'élément <img>

QueueItem.vue

  • Mêmes modifications que CurrentTrack.vue

MediaItem.vue

  • Mêmes modifications que CurrentTrack.vue

RendererCard.vue

  • Ce composant n'avait pas de gestion d'état de chargement d'image
  • Ajout complet : imageLoaded, imageError, coverImageRef, checkImageComplete()
  • Ajout des handlers @load et @error
  • Modification de hasCover pour inclure !imageError
  • Ajout de v-show="imageLoaded" sur l'image
  • Modification de la condition du placeholder

Vérification

  • Build webpack réussi sans erreur
  • Compilation TypeScript OK

Remarque technique

La propriété HTMLImageElement.complete retourne true si :

  • L'image a fini de charger (succès ou erreur)
  • L'attribut src est vide ou absent

C'est pourquoi on vérifie également naturalWidth > 0 pour s'assurer que l'image a bien été chargée avec succès (une image en erreur a naturalWidth === 0).