/** * Stops older dumps from showing a site's icon as their thumbnail. The metadata * fetch used to fall back on the page's icon, and then on a guessed * `/favicon.ico`, so most thumbnails were a 16×16 icon, or a 404, that the UI * stretched over a 128×72 box. It now stops at real artwork, and a missing * `thumbnailUrl` means none, which the frontend draws a placeholder for. This * gives the older rows that meaning, by moving their icons out of * `rich_content.thumbnailUrl` into the new `faviconUrl`. * * It judges the stored URL alone and fetches nothing, so it doesn't fill * `accentColor`. The frontend derives a hue from the hostname when it is * missing, and refreshing a dump's metadata fetches the real one. * * @module */ import type { DatabaseSync } from "node:sqlite"; /** * Checks whether a stored thumbnail URL is a site icon. It is loose on * purpose, since artwork taken for an icon still shows whole on a tinted field, * only not cropped to fill the box. 0012 fixes the rows it got wrong. */ function isIconUrl(raw: string): boolean { let pathname: string; try { pathname = new URL(raw).pathname; } catch { return false; } return /favicon|apple-touch-icon|\/icons?\//i.test(pathname) || /\.(ico|svg)$/i.test(pathname); } /** * Run once by `runMigrations`. It leaves alone a row that already has a * `faviconUrl`, and one whose `rich_content` doesn't parse. 0012 undoes its * mistakes. */ export function up(db: DatabaseSync): void { const rows = db.prepare( `SELECT id, rich_content FROM dumps WHERE kind = 'url' AND rich_content IS NOT NULL;`, ).all() as { id: string; rich_content: string }[]; const update = db.prepare( `UPDATE dumps SET rich_content = ? WHERE id = ?;`, ); let patched = 0; for (const row of rows) { let rich: { thumbnailUrl?: string; faviconUrl?: string }; try { rich = JSON.parse(row.rich_content); } catch { continue; } if (rich.faviconUrl || !rich.thumbnailUrl) continue; if (!isIconUrl(rich.thumbnailUrl)) continue; const { thumbnailUrl: _dropped, ...rest } = rich; update.run( JSON.stringify({ ...rest, faviconUrl: rich.thumbnailUrl }), row.id, ); patched++; } if (patched > 0) { console.log( `[migrate] 0011: reclassified ${patched} favicon thumbnail(s)`, ); } }