adrian-altner.de/scripts/fetch-favicons.mjs
Adrian Altner 5bb63bacf5
All checks were successful
Deploy / deploy (push) Successful in 49s
Initial commit: Astro 6 static blog site
- German (default) and English i18n support
- Categories and tags
- Blog posts with hero images
- Dark/light theme switcher
- View Transitions removed to fix reload ghost images
- Webmentions integration
- RSS feeds per locale

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-22 10:55:29 +02:00

74 lines
1.9 KiB
JavaScript

/**
* Fetches favicons for all linked domains and saves them locally to public/favicons/.
* Run before astro build so favicons are served statically instead of via Google S2.
* Idempotent: skips domains that already have a cached favicon.
*/
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = fileURLToPath(new URL(".", import.meta.url));
const root = join(__dirname, "..");
const linksPath = join(root, "src/content/links/links.json");
const outDir = join(root, "public/favicons");
function getDomain(url) {
try {
return new URL(url).hostname.replace(/^www\./, "");
} catch {
return null;
}
}
function faviconPath(domain) {
return join(outDir, `${domain}.png`);
}
async function fetchFavicon(domain) {
const url = `https://www.google.com/s2/favicons?domain=${domain}&sz=32`;
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const buf = await res.arrayBuffer();
return Buffer.from(buf);
}
async function main() {
const links = JSON.parse(readFileSync(linksPath, "utf-8"));
const domains = [
...new Set(links.map((l) => getDomain(l.url)).filter(Boolean)),
];
if (!existsSync(outDir)) mkdirSync(outDir, { recursive: true });
let fetched = 0;
let skipped = 0;
await Promise.all(
domains.map(async (domain) => {
const dest = faviconPath(domain);
if (existsSync(dest)) {
skipped++;
return;
}
try {
const data = await fetchFavicon(domain);
writeFileSync(dest, data);
fetched++;
console.log(`${domain}`);
} catch (err) {
console.warn(`${domain}: ${err.message}`);
}
}),
);
console.log(
`Favicons: ${fetched} fetched, ${skipped} cached, ${domains.length} total`,
);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});