Prefer cached stills and stream first frames in the grid

This commit is contained in:
Fisto
2026-09-16 13:44:34 +02:00
parent 0081224954
commit 1e064b69d0
3 changed files with 71 additions and 47 deletions
+47 -38
View File
@@ -412,17 +412,6 @@
return el(className ?? "figcaption", "vk-media-banner", parts.join(" · "));
}
/**
* Grid thumbnail source: cached preview, then the local file.
* Attachment URLs are direct files; embed URLs are pages, never rendered as media.
*/
function gridSource(item) {
if (item.thumbUrl) return item.thumbUrl;
if (item.localUrl) return item.localUrl;
if (item.source !== "embed") return item.url;
return null;
}
function placeholder(media, item) {
media.replaceChildren(el("span", "vk-media-card-kind", item.kind.toUpperCase()));
}
@@ -447,36 +436,18 @@
function renderCard(item) {
const figure = el("figure", "vk-media-figure");
const media = el("div", "vk-media-media");
const source = gridSource(item);
const videoSource = item.localUrl || item.playUrl || (item.source !== "embed" ? item.url : null);
const playableVideo =
!!videoSource &&
(/\.(mp4|webm|m4v)(\?|$)/i.test(videoSource) || videoSource === item.playUrl);
const imageSource = item.localUrl || item.thumbUrl;
const fallbackSource = item.source !== "embed" ? item.url : null;
const streamPath = `/api/media/${item.scrapeId}/${item.itemId}/stream`;
const animatedGif =
item.kind === "gif" && item.remotePreviewUrl && /\.gif(\?|$)/i.test(item.remotePreviewUrl)
? item.remotePreviewUrl
: null;
if (item.kind === "video" && videoSource && playableVideo) {
// Discord has no server-side video thumbnails; the browser shows the first frame.
const video = document.createElement("video");
video.preload = "metadata";
video.muted = true;
video.playsInline = true;
video.src = videoSource;
video.addEventListener(
"loadedmetadata",
() => {
if (video.duration) video.currentTime = Math.min(0.1, video.duration / 2);
},
{ once: true }
);
video.addEventListener("error", () => placeholder(media, item), { once: true });
media.append(video);
} else if (source && /^(https?:|\/)/.test(source)) {
if (imageSource) {
const img = document.createElement("img");
img.loading = "lazy";
img.src = source;
img.src = imageSource;
img.alt = item.kind;
img.addEventListener("error", () => placeholder(media, item), { once: true });
@@ -486,10 +457,34 @@
img.src = animatedGif;
});
media.addEventListener("mouseleave", () => {
img.src = source;
img.src = imageSource;
});
}
media.append(img);
} else if (item.kind === "video") {
// No thumbnail: take the first frame through the same-origin stream endpoint,
// which refreshes expired URLs and avoids cross-origin blocking.
const video = document.createElement("video");
video.preload = "metadata";
video.muted = true;
video.playsInline = true;
video.src = streamPath;
video.addEventListener(
"loadedmetadata",
() => {
if (video.duration) video.currentTime = Math.min(0.1, video.duration / 2);
},
{ once: true }
);
video.addEventListener("error", () => placeholder(media, item), { once: true });
media.append(video);
} else if (fallbackSource && /^(https?:|\/)/.test(fallbackSource)) {
const img = document.createElement("img");
img.loading = "lazy";
img.src = fallbackSource;
img.alt = item.kind;
img.addEventListener("error", () => placeholder(media, item), { once: true });
media.append(img);
} else {
placeholder(media, item);
@@ -1124,7 +1119,7 @@
const streamUrl = `/api/media/${item.scrapeId}/${item.itemId}/stream`;
const source = item.localUrl || item.thumbUrl || streamUrl;
const embed = embedUrlFor(item);
const directVideo = item.localUrl || item.playUrl || (item.source !== "embed" ? streamUrl : null);
const directVideo = item.localUrl || (item.kind === "video" ? streamUrl : null);
const canPlay = !!embed || (item.kind === "video" && !!directVideo);
// A play request on something we cannot play falls back to the external page.
@@ -1156,13 +1151,27 @@
{ once: true }
);
media.append(video);
} else if (item.kind === "video") {
// No still available: show the first frame through the stream endpoint.
const video = document.createElement("video");
video.preload = "metadata";
video.muted = true;
video.playsInline = true;
video.src = streamUrl;
video.addEventListener(
"loadedmetadata",
() => {
if (video.duration) video.currentTime = Math.min(0.1, video.duration / 2);
},
{ once: true }
);
media.append(video);
media.append(viewerPlayIcon(item));
} else if (/^(https?:|\/)/.test(source)) {
const img = document.createElement("img");
img.src = source;
img.alt = item.kind;
media.append(img);
if (item.kind === "video") media.append(viewerPlayIcon(item));
} else {
media.append(el("span", "vk-media-card-kind", item.kind.toUpperCase()));
}