Resolve thumbnail-less embeds, including Discord CDN unfurls

This commit is contained in:
Fisto
2026-09-17 09:07:07 +02:00
parent c6a16a63f7
commit a7db9cb200
5 changed files with 87 additions and 5 deletions
+2 -1
View File
@@ -392,7 +392,8 @@ export class BackfillQueue {
job.processed += progressed + result.ratelimited;
const elapsed = Math.max(1, this.now() - (job.startedAt ?? this.now()));
job.perMinute = job.processed / (elapsed / 60_000);
// Early in a run the measured rate is noise; report null until it settles.
job.perMinute = elapsed >= 30_000 ? job.processed / (elapsed / 60_000) : null;
const after = this.engine.rateLimitStatus();
if (after.active) this.limitedUntil = after.until ?? this.now() + after.remainingMs;
+15
View File
@@ -34,6 +34,11 @@ async function getText(url: string, options: DownloadOptions): Promise<string |
signal: AbortSignal.timeout(TIMEOUT_MS)
});
if (!response.ok) return null;
// Never pull media bytes into memory just to look for an og:image.
const type = response.headers.get("content-type") ?? "";
if (type && !/text\/html|application\/xhtml|text\/plain/i.test(type)) return null;
return (await response.text()).slice(0, MAX_HTML_BYTES);
} catch {
return null;
@@ -74,6 +79,16 @@ export async function resolveEmbedPreview(
const host = parsed.hostname.replace(/^www\./, "").toLowerCase();
// Discord unfurls a pasted attachment link into an embed. The URL is still a CDN file, so
// the proxy can render a still (and a video frame) exactly like a plain attachment.
if ((host === "cdn.discordapp.com" || host === "media.discordapp.net") && parsed.pathname.startsWith("/attachments/")) {
parsed.hostname = "media.discordapp.net";
parsed.searchParams.set("width", "320");
parsed.searchParams.set("height", "320");
parsed.searchParams.set("format", "webp");
return parsed.toString();
}
if (host === "youtube.com" || host === "m.youtube.com" || host === "youtu.be") {
const data = await getJson(
`https://www.youtube.com/oembed?url=${encodeURIComponent(media.url)}&format=json`,
+3 -3
View File
@@ -356,9 +356,9 @@ export class ScrapeStore {
pendingPreviews(limit = 5000, includeEmbeds = false): MediaRow[] {
const local =
"((source IS NULL OR source != 'embed') AND kind IN ('image', 'gif', 'video', 'audio'))";
const condition = includeEmbeds
? `(${local} OR (source = 'embed' AND preview_url IS NOT NULL))`
: local;
// With embeds on, a thumbnail-less embed still needs a pass: the engine resolves one
// from the provider or the page (or the Discord proxy for CDN attachment embeds).
const condition = includeEmbeds ? `(${local} OR source = 'embed')` : local;
return this.db
.prepare(