|
1 | 1 | import { absoluteUrl, SITE_NAME } from "./site"; |
| 2 | +import { youtubeId } from "./youtube"; |
| 3 | + |
| 4 | +function cleanDescription( |
| 5 | + desc?: string | null, |
| 6 | + fallback?: string | null, |
| 7 | +): string | undefined { |
| 8 | + const trimmed = desc?.trim(); |
| 9 | + if (trimmed && trimmed.toLowerCase() !== "fix me") { |
| 10 | + return trimmed; |
| 11 | + } |
| 12 | + const fb = fallback?.trim(); |
| 13 | + return fb && fb.toLowerCase() !== "fix me" ? fb : undefined; |
| 14 | +} |
2 | 15 |
|
3 | 16 | /** |
4 | 17 | * JSON-LD builders, ported from the Next app's lib/structured-data.ts. |
@@ -97,7 +110,7 @@ export function articleSchema( |
97 | 110 | "@type": content.articleType ?? "TechArticle", |
98 | 111 | "@id": `${url}#article`, |
99 | 112 | headline: content.title ?? undefined, |
100 | | - description: content.excerpt ?? undefined, |
| 113 | + description: cleanDescription(content.excerpt, content.title), |
101 | 114 | ...(content.imageUrl ? { image: content.imageUrl } : {}), |
102 | 115 | ...(content.date ? { datePublished: content.date } : {}), |
103 | 116 | ...(content._updatedAt ? { dateModified: content._updatedAt } : {}), |
@@ -204,19 +217,27 @@ export function videoObjectSchema( |
204 | 217 | durationIso = d; |
205 | 218 | } |
206 | 219 |
|
| 220 | + const ytId = youtubeId(video.youtubeUrl); |
| 221 | + const resolvedThumb = |
| 222 | + video.thumbnailUrl || |
| 223 | + (ytId ? `https://i.ytimg.com/vi/${ytId}/hqdefault.jpg` : undefined); |
| 224 | + const embedUrl = ytId |
| 225 | + ? `https://www.youtube.com/embed/${ytId}` |
| 226 | + : video.youtubeUrl |
| 227 | + .replace("watch?v=", "embed/") |
| 228 | + .replace("youtu.be/", "www.youtube.com/embed/"); |
| 229 | + |
207 | 230 | return { |
208 | 231 | "@type": "VideoObject", |
209 | 232 | "@id": `${url}#video`, |
210 | 233 | name: video.title ?? undefined, |
211 | | - description: video.description ?? undefined, |
212 | | - ...(video.thumbnailUrl ? { thumbnailUrl: [video.thumbnailUrl] } : {}), |
| 234 | + description: cleanDescription(video.description, video.title), |
| 235 | + ...(resolvedThumb ? { thumbnailUrl: [resolvedThumb] } : {}), |
213 | 236 | ...(video.uploadDate ? { uploadDate: video.uploadDate } : {}), |
214 | 237 | ...(durationIso ? { duration: durationIso } : {}), |
215 | 238 | ...(video.transcript ? { transcript: video.transcript } : {}), |
216 | 239 | contentUrl: video.youtubeUrl, |
217 | | - embedUrl: video.youtubeUrl |
218 | | - .replace("watch?v=", "embed/") |
219 | | - .replace("youtu.be/", "www.youtube.com/embed/"), |
| 240 | + embedUrl, |
220 | 241 | }; |
221 | 242 | } |
222 | 243 |
|
@@ -326,7 +347,7 @@ export function podcastEpisodeSchema( |
326 | 347 | "@id": `${url}#episode`, |
327 | 348 | url, |
328 | 349 | name: content.title ?? undefined, |
329 | | - description: content.excerpt ?? undefined, |
| 350 | + description: cleanDescription(content.excerpt, content.title), |
330 | 351 | ...(content.date ? { datePublished: content.date } : {}), |
331 | 352 | ...(content.imageUrl ? { image: content.imageUrl } : {}), |
332 | 353 | ...(content.season ? { seasonNumber: content.season } : {}), |
|
0 commit comments