fix(og): match plain URL slugs; fix Funkwhale GC wipe
Build & Deploy / build-and-deploy (push) Successful in 1m19s
Build & Deploy / build-and-deploy (push) Successful in 1m19s
og-fix transform was matching date-based URL segments (/type/yyyy/MM/dd/slug/) that this site never uses — posts live at /type/slug/. Every post therefore fell through to the default OG image. Fixed by updating the regex to /type/slug/index.html and deriving the OG slug as the bare last URL segment, which matches the filename og.js already generates. The ogSlug filter is simplified accordingly. Funkwhale GC bug: gcFunkwhaleImages() deleted the entire image cache whenever _activeFilenames was empty — which happens if the API returns valid stats but no listenings with cover URLs. Guard added: GC is skipped when no images were referenced this build. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+10
-17
@@ -403,19 +403,18 @@ export default function (eleventyConfig) {
|
|||||||
if (!outputPath || !outputPath.endsWith(".html")) return content;
|
if (!outputPath || !outputPath.endsWith(".html")) return content;
|
||||||
|
|
||||||
// Derive correct page URL and OG slug from outputPath (immune to race condition)
|
// Derive correct page URL and OG slug from outputPath (immune to race condition)
|
||||||
// Content pages match: .../type/yyyy/MM/dd/slug/index.html
|
// Content pages match: .../type/slug/index.html (plain URLs, no date segments)
|
||||||
const dateMatch = outputPath.match(
|
const postMatch = outputPath.match(
|
||||||
/\/([\w-]+)\/(\d{4})\/(\d{2})\/(\d{2})\/([\w-]+)\/index\.html$/
|
/\/([\w-]+)\/([\w-]+)\/index\.html$/
|
||||||
);
|
);
|
||||||
|
|
||||||
if (dateMatch) {
|
if (postMatch) {
|
||||||
const [, type, year, month, day, slug] = dateMatch;
|
const [, type, slug] = postMatch;
|
||||||
const pageUrlPath = `/${type}/${year}/${month}/${day}/${slug}/`;
|
const pageUrlPath = `/${type}/${slug}/`;
|
||||||
const correctFullUrl = `${siteUrl}${pageUrlPath}`;
|
const correctFullUrl = `${siteUrl}${pageUrlPath}`;
|
||||||
const ogSlug = `${year}-${month}-${day}-${slug}`;
|
const hasOg = hasOgImage(slug);
|
||||||
const hasOg = hasOgImage(ogSlug);
|
|
||||||
const ogImageUrl = hasOg
|
const ogImageUrl = hasOg
|
||||||
? `${siteUrl}/og/${ogSlug}.png`
|
? `${siteUrl}/og/${slug}.png`
|
||||||
: `${siteUrl}/images/og-default.png`;
|
: `${siteUrl}/images/og-default.png`;
|
||||||
const twitterCard = hasOg ? "summary_large_image" : "summary";
|
const twitterCard = hasOg ? "summary_large_image" : "summary";
|
||||||
|
|
||||||
@@ -433,7 +432,7 @@ export default function (eleventyConfig) {
|
|||||||
content = content.replace(/__OG_IMAGE_PLACEHOLDER__/g, ogImageUrl);
|
content = content.replace(/__OG_IMAGE_PLACEHOLDER__/g, ogImageUrl);
|
||||||
content = content.replace(/__TWITTER_CARD_PLACEHOLDER__/g, twitterCard);
|
content = content.replace(/__TWITTER_CARD_PLACEHOLDER__/g, twitterCard);
|
||||||
} else {
|
} else {
|
||||||
// Non-date pages (homepage, about, etc.): use defaults
|
// Non-post pages (homepage, archives, etc.): use defaults
|
||||||
content = content.replace(
|
content = content.replace(
|
||||||
/__OG_IMAGE_PLACEHOLDER__/g,
|
/__OG_IMAGE_PLACEHOLDER__/g,
|
||||||
`${siteUrl}/images/og-default.png`
|
`${siteUrl}/images/og-default.png`
|
||||||
@@ -916,16 +915,10 @@ export default function (eleventyConfig) {
|
|||||||
|
|
||||||
// Derive OG slug from page.url (reliable) instead of page.fileSlug
|
// Derive OG slug from page.url (reliable) instead of page.fileSlug
|
||||||
// (which suffers from Nunjucks race conditions in Eleventy 3.x parallel rendering).
|
// (which suffers from Nunjucks race conditions in Eleventy 3.x parallel rendering).
|
||||||
// OG images are named with the full date prefix to match URL segments exactly.
|
// URLs are plain (/type/slug/), so the slug is the last path segment.
|
||||||
eleventyConfig.addFilter("ogSlug", (url) => {
|
eleventyConfig.addFilter("ogSlug", (url) => {
|
||||||
if (!url) return "";
|
if (!url) return "";
|
||||||
const segments = url.split("/").filter(Boolean);
|
const segments = url.split("/").filter(Boolean);
|
||||||
// Date-based URL: /type/yyyy/MM/dd/slug/ → 5 segments → "yyyy-MM-dd-slug"
|
|
||||||
if (segments.length === 5) {
|
|
||||||
const [, year, month, day, slug] = segments;
|
|
||||||
return `${year}-${month}-${day}-${slug}`;
|
|
||||||
}
|
|
||||||
// Fallback: last segment (for pages, legacy URLs)
|
|
||||||
return segments[segments.length - 1] || "";
|
return segments[segments.length - 1] || "";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,10 @@ export async function cacheFunkwhaleImage(url) {
|
|||||||
*/
|
*/
|
||||||
export function gcFunkwhaleImages() {
|
export function gcFunkwhaleImages() {
|
||||||
if (!existsSync(CACHE_DIR)) return;
|
if (!existsSync(CACHE_DIR)) return;
|
||||||
|
// If no images were referenced this build, skip GC — likely the API returned no
|
||||||
|
// cover URLs (empty listenings, null covers, or stats-only response). Deleting
|
||||||
|
// everything from an empty _activeFilenames set would wipe a valid cache.
|
||||||
|
if (_activeFilenames.size === 0) return;
|
||||||
let deleted = 0;
|
let deleted = 0;
|
||||||
for (const file of readdirSync(CACHE_DIR)) {
|
for (const file of readdirSync(CACHE_DIR)) {
|
||||||
if (!_activeFilenames.has(file)) {
|
if (!_activeFilenames.has(file)) {
|
||||||
|
|||||||
@@ -126,7 +126,8 @@ function formatDate(dateStr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use the full filename (with date prefix) as the OG image slug.
|
* Use the filename (without extension) as the OG image slug.
|
||||||
|
* Matches the last URL path segment, which Eleventy derives from the filename.
|
||||||
*/
|
*/
|
||||||
function toOgSlug(filename) {
|
function toOgSlug(filename) {
|
||||||
return filename;
|
return filename;
|
||||||
|
|||||||
Reference in New Issue
Block a user