Fix: add fallback to placeholder if loading thumbnail fails

This commit is contained in:
arabcoders 2025-12-18 18:58:11 +03:00
parent b36d9fd641
commit d5727ca387
3 changed files with 35 additions and 17 deletions

View file

@ -271,17 +271,17 @@
<figure :class="['image', thumbnail_ratio]"> <figure :class="['image', thumbnail_ratio]">
<span v-if="'finished' === item.status && item.filename" @click="playVideo(item)" class="play-overlay"> <span v-if="'finished' === item.status && item.filename" @click="playVideo(item)" class="play-overlay">
<div class="play-icon"></div> <div class="play-icon"></div>
<img @load="(e: Event) => pImg(e)" :src="getImage(item)" v-if="getImage(item)" /> <img @load="pImg" @error="onImgError" :src="getImage(item)" v-if="getImage(item)" />
<img v-else src="/images/placeholder.png" /> <img v-else src="/images/placeholder.png" />
</span> </span>
<span v-else-if="isEmbedable(item.url)" @click="embed_url = getEmbedable(item.url) as string" <span v-else-if="isEmbedable(item.url)" @click="embed_url = getEmbedable(item.url) as string"
class="play-overlay"> class="play-overlay">
<div class="play-icon embed-icon"></div> <div class="play-icon embed-icon"></div>
<img @load="(e: Event) => pImg(e)" :src="getImage(item)" v-if="getImage(item)" /> <img @load="pImg" @error="onImgError" :src="getImage(item)" v-if="getImage(item)" />
<img v-else src="/images/placeholder.png" /> <img v-else src="/images/placeholder.png" />
</span> </span>
<template v-else> <template v-else>
<img @load="(e: Event) => pImg(e)" v-if="getImage(item)" :src="getImage(item)" /> <img @load="pImg" @error="onImgError" v-if="getImage(item)" :src="getImage(item)" />
<img v-else src="/images/placeholder.png" /> <img v-else src="/images/placeholder.png" />
</template> </template>
</figure> </figure>
@ -847,6 +847,14 @@ const pImg = (e: Event) => {
} }
} }
const onImgError = (e: Event) => {
const target = e.target as HTMLImageElement
if (target.src.endsWith('/images/placeholder.png')) {
return
}
target.src = '/images/placeholder.png'
}
watch(video_item, (v) => { watch(video_item, (v) => {
if (!bg_enable.value) { if (!bg_enable.value) {
return return

View file

@ -193,13 +193,13 @@
<span v-if="isEmbedable(item.url)" @click="embed_url = getEmbedable(item.url) as string" <span v-if="isEmbedable(item.url)" @click="embed_url = getEmbedable(item.url) as string"
class="play-overlay"> class="play-overlay">
<div class="play-icon embed-icon"></div> <div class="play-icon embed-icon"></div>
<img @load="(e: Event) => pImg(e)" <img @load="pImg" @error="onImgError"
:src="uri('/api/thumbnail?id=' + item._id + '&url=' + encodePath(item.extras.thumbnail))" :src="uri('/api/thumbnail?id=' + item._id + '&url=' + encodePath(item.extras.thumbnail))"
v-if="item.extras?.thumbnail" /> v-if="item.extras?.thumbnail" />
<img v-else src="/images/placeholder.png" /> <img v-else src="/images/placeholder.png" />
</span> </span>
<template v-else> <template v-else>
<img @load="(e: Event) => pImg(e)" v-if="item.extras?.thumbnail" <img @load="pImg" @error="onImgError" v-if="item.extras?.thumbnail"
:src="uri('/api/thumbnail?id=' + item._id + '&url=' + encodePath(item.extras.thumbnail))" /> :src="uri('/api/thumbnail?id=' + item._id + '&url=' + encodePath(item.extras.thumbnail))" />
<img v-else src="/images/placeholder.png" /> <img v-else src="/images/placeholder.png" />
</template> </template>
@ -579,6 +579,14 @@ const pImg = (e: Event) => {
} }
} }
const onImgError = (e: Event) => {
const target = e.target as HTMLImageElement
if (target.src.endsWith('/images/placeholder.png')) {
return
}
target.src = '/images/placeholder.png'
}
watch(embed_url, v => { watch(embed_url, v => {
if (!bg_enable.value) { if (!bg_enable.value) {
return return

View file

@ -89,7 +89,8 @@
<figure class="media-left"> <figure class="media-left">
<figure class="image is-16by9 queue-thumb" :class="{ 'is-clickable': isEmbedable(entry.item.url) }" <figure class="image is-16by9 queue-thumb" :class="{ 'is-clickable': isEmbedable(entry.item.url) }"
role="presentation" @click="openPlayer(entry.item)"> role="presentation" @click="openPlayer(entry.item)">
<img :src="resolveThumbnail(entry)" :alt="entry.item.title || 'Video thumbnail'" loading="lazy"> <img :src="resolveThumbnail(entry)" :alt="entry.item.title || 'Video thumbnail'" loading="lazy"
@error="onImgError">
<span v-if="getDurationLabel(entry.item)" class="queue-thumb__badge"> <span v-if="getDurationLabel(entry.item)" class="queue-thumb__badge">
{{ getDurationLabel(entry.item) }} {{ getDurationLabel(entry.item) }}
</span> </span>
@ -688,18 +689,19 @@ const loadMoreHistory = async (): Promise<void> => {
} }
} }
// Setup intersection observer for infinite scroll const onImgError = (e: Event) => {
useIntersectionObserver( const target = e.target as HTMLImageElement
loadMoreTrigger, if (target.src.endsWith('/images/placeholder.png')) {
([entry]) => { return
if (entry?.isIntersecting && !paginationInfo.value.isLoading && paginationInfo.value.page < paginationInfo.value.total_pages) {
loadMoreHistory()
}
},
{
threshold: 0.5,
} }
) target.src = '/images/placeholder.png'
}
useIntersectionObserver(loadMoreTrigger, ([entry]) => {
if (entry?.isIntersecting && !paginationInfo.value.isLoading && paginationInfo.value.page < paginationInfo.value.total_pages) {
loadMoreHistory()
}
}, { threshold: 0.5 })
</script> </script>