Cache audio waveforms and rescue oversized GIF previews

This commit is contained in:
Fisto
2026-09-16 23:48:17 +02:00
parent caf9b7db3a
commit 8f30ec14ad
7 changed files with 123 additions and 5 deletions
+35
View File
@@ -12,6 +12,7 @@ import {
gifPoster,
previewGapMs,
probeDimensions,
remoteFrame,
videoPoster,
type DownloadOptions
} from "./download.js";
@@ -387,6 +388,24 @@ export class ScrapeEngine {
return;
}
// Audio has no image proxy: render a waveform strip with ffmpeg.
if (row.kind === "audio" && row.source !== "embed") {
const waveform = await remoteFrame(row.url, join(dir, "thumbs"), { waveform: true });
if (waveform) {
store.markPreview(row.id, {
hash: waveform.hash,
bytes: waveform.bytes,
ext: "webp"
});
meta.previewed = (meta.previewed ?? 0) + 1;
} else {
store.markPreviewError(row.id, "waveform failed");
}
this.persist(dir, meta);
await this.sleep(previewGapMs(options));
continue;
}
// Some providers (fxtwitter, vxReddit, ...) ship no thumbnail in Discord's data;
// resolve one from the provider or the page before caching it.
let previewRow = row;
@@ -429,6 +448,22 @@ export class ScrapeEngine {
mkdirSync(dirname(target), { recursive: true });
copyFileSync(join(dir, "thumbs", `${hash}.${ext}`), target);
}
} else if (
outcome.state === "skipped" &&
outcome.error === "too large" &&
/\.gif(\?|$)/i.test(previewRow.preview_url ?? previewRow.url)
) {
// Oversized animated GIF: let ffmpeg read the stream and keep one frame.
const frame = await remoteFrame(previewRow.preview_url ?? previewRow.url, join(dir, "thumbs"));
if (frame) {
store.markPreview(row.id, { hash: frame.hash, bytes: frame.bytes, ext: "webp" });
meta.previewed = (meta.previewed ?? 0) + 1;
} else {
store.markPreviewError(row.id, "too large");
}
} else if (outcome.state === "failed") {
// Transient network failure: leave the row pending for the next pass.
this.log(`${meta.id}: preview ${row.id} failed: ${outcome.error ?? "unknown"}`);
} else {
store.markPreviewError(row.id, outcome.error ?? outcome.state);
}