Website overhaul: nav, accessibility, shared components, SEO, Reaper silence detection
Website: - Add persistent top nav across all pages - Add skip-to-content links, focus-visible styles, ARIA on audio player - Fix text contrast for WCAG AA compliance - Add 600px breakpoint, mobile typography scaling - Extract shared footer.js, player.js, episode.js components - Episode pagination (10 + Load More), featured clip dedup - Worker meta injection for social crawler OG tags - Unify Plausible analytics proxy across all pages - Sanitize innerHTML for XSS safety - Custom 404 page, enhanced llms.txt, fix sitemap - Bump cache versions to v=4 Reaper: - Add dual silence threshold: 2.5s for speaker transitions, 6s for same-speaker gaps Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -86,6 +86,67 @@ export default {
|
||||
});
|
||||
}
|
||||
|
||||
// Social crawler meta injection for episode pages
|
||||
if (url.pathname === "/episode.html" && url.searchParams.get("slug")) {
|
||||
const ua = (request.headers.get("User-Agent") || "").toLowerCase();
|
||||
const isCrawler = /facebookexternalhit|twitterbot|linkedinbot|slackbot|discordbot|telegrambot|whatsapp|pinterest|redditbot/i.test(ua);
|
||||
|
||||
if (isCrawler) {
|
||||
const slug = url.searchParams.get("slug");
|
||||
try {
|
||||
const feedResp = await fetch("https://podcast.macneilmediagroup.com/@LukeAtTheRoost/feed.xml", {
|
||||
signal: AbortSignal.timeout(5000),
|
||||
});
|
||||
if (feedResp.ok) {
|
||||
const feedXml = await feedResp.text();
|
||||
const items = feedXml.split("<item>");
|
||||
let title = "";
|
||||
let description = "";
|
||||
|
||||
for (let i = 1; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
const linkMatch = item.match(/<link>(.*?)<\/link>/);
|
||||
if (linkMatch) {
|
||||
const itemSlug = linkMatch[1].split("/episodes/").pop()?.replace(/\/$/, "");
|
||||
if (itemSlug === slug) {
|
||||
const titleMatch = item.match(/<title>(.*?)<\/title>/);
|
||||
title = titleMatch ? titleMatch[1].replace(/<!\[CDATA\[|\]\]>/g, "").trim() : "";
|
||||
const descMatch = item.match(/<description>([\s\S]*?)<\/description>/);
|
||||
description = descMatch
|
||||
? descMatch[1].replace(/<!\[CDATA\[|\]\]>/g, "").replace(/<[^>]+>/g, "").trim().slice(0, 200)
|
||||
: "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (title) {
|
||||
const pageResp = await env.ASSETS.fetch(request);
|
||||
let html = await pageResp.text();
|
||||
|
||||
const escTitle = title.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
||||
const escDesc = description.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
||||
const canonicalUrl = `https://lukeattheroost.com/episode.html?slug=${slug}`;
|
||||
|
||||
html = html.replace(/<meta property="og:title"[^>]*>/, `<meta property="og:title" content="${escTitle}">`);
|
||||
html = html.replace(/<meta property="og:description"[^>]*>/, `<meta property="og:description" content="${escDesc}">`);
|
||||
html = html.replace(/<meta property="og:url"[^>]*>/, `<meta property="og:url" content="${canonicalUrl}">`);
|
||||
html = html.replace(/<meta name="twitter:title"[^>]*>/, `<meta name="twitter:title" content="${escTitle}">`);
|
||||
html = html.replace(/<meta name="twitter:description"[^>]*>/, `<meta name="twitter:description" content="${escDesc}">`);
|
||||
html = html.replace(/<title[^>]*>.*?<\/title>/, `<title>${escTitle} — Luke at the Roost</title>`);
|
||||
|
||||
return new Response(html, {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "text/html;charset=UTF-8" },
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Fall through to static page
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All other requests — serve static assets
|
||||
return env.ASSETS.fetch(request);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user