/** * OG Image metadata utilities. * * Builds the /api/og URL with appropriate query params for dynamic * Open Graph image generation. Used by page-level metadata exports. */ import { BOOK_NAMES, CC_LILAS } from "@/lib/constants"; import { decodeTextFragment, extractMatchedText } from "@/lib/text-highlight"; const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://prabhupadasays.org"; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- export type OgParams = { title: string; detail?: string; quote?: string; }; // --------------------------------------------------------------------------- // Fallback descriptions — used when no quote/description is provided // --------------------------------------------------------------------------- const SITE_DESCRIPTION = "Browse the original, unedited books, letters, and teachings of His Divine Grace A.C. Bhaktivedanta Swami Prabhupāda — presented as they are."; const BOOK_DESCRIPTIONS: Record = { bg: "The definitive guide to the science of self-realization, spoken by Lord Śrī Kṛṣṇa to His devotee Arjuna on the Battlefield of Kurukṣetra.", sb: "The ripened fruit of the tree of Vedic literature — 18,000 verses describing the pastimes of the Supreme Lord and His devotees.", cc: "The life and teachings of Śrī Caitanya Mahāprabhu, the most munificent incarnation of Godhead who distributed love of God freely to all.", sb1962: "The original 1962 edition of Śrīmad-Bhāgavatam — Śrīla Prabhupāda's first major literary contribution in English.", iso: "Eighteen mantras revealing the essential teachings of the Vedas — the relationship between God, the living entity, and the material world.", noi: "Eleven verses by Śrīla Rūpa Gosvāmī providing essential instructions for serious practitioners of devotional service.", nod: "A comprehensive guide to the science of bhakti-yoga, summarizing Śrīla Rūpa Gosvāmī's Bhakti-rasāmṛta-sindhu.", bs: "The prayers of Lord Brahmā glorifying the Supreme Lord Govinda — with translation and commentary by Śrīla Prabhupāda.", mm: "Devotional prayers of King Kulaśekhara expressing the purest sentiments of surrender to Lord Kṛṣṇa.", nbs: "The eternal principles of devotional service as taught by the great sage Nārada Muni.", kb: "The enchanting pastimes of Lord Śrī Kṛṣṇa in Vṛndāvana and Dvārakā — the Supreme Personality of Godhead in His original form.", tlc: "The sublime teachings of Lord Śrī Caitanya Mahāprabhu to Śrīla Sanātana Gosvāmī, Śrīla Rūpa Gosvāmī, and others.", ej: "A scientific presentation of the anti-material world and how the soul can travel there.", ek: "How to elevate consciousness from the material to the spiritual platform through the practice of Kṛṣṇa consciousness.", mg: "An introduction to the unmatched gift of Kṛṣṇa consciousness — the path to eternal happiness and self-realization.", bbd: "A concise exploration of the soul's journey through repeated birth and death, and the path to liberation.", owk: "Practical guidance on the journey toward Kṛṣṇa — developing love of God through devotional service.", rv: "The king of knowledge — understanding the supreme science of devotional service as taught in the Bhagavad-gītā.", poy: "The perfection of the yoga system — how bhakti-yoga surpasses all other forms of yoga and meditation.", pqpa: "Conversations between Śrīla Prabhupāda and Bob Cohen — answering life's most fundamental questions.", ttp: "The teachings of Lord Kapila to His mother Devahūti on the science of God realization and devotional service.", ochkm: "The transcendental vibration of the Hare Kṛṣṇa mahā-mantra — its meaning, potency, and practice.", hkh: "A practical handbook for understanding and practicing Kṛṣṇa consciousness in daily life.", lcl: "Morning walks with Śrīla Prabhupāda — challenging the fundamental assumptions of modern science.", lob: "Meditations on the beauty of nature as a reflection of the Supreme Lord's transcendental opulence.", tlk: "Lord Kapila's instructions on Sāṅkhya philosophy and the path of devotional service.", tqk: "The prayers and teachings of Queen Kuntī — exemplifying pure devotion in the face of adversity.", gg: "Bengali poetic renderings of the Bhagavad-gītā by Śrīla Prabhupāda.", mog: "Śrīla Prabhupāda's message of Godhead — presenting the Absolute Truth for the modern world.", cbhc: "Commentary on the Śrī Caitanya-bhāgavata — the pastimes of Lord Caitanya by Vṛndāvana dāsa Ṭhākura.", bk: "Bhagavāner Kathā — Bengali narration of the pastimes of the Supreme Lord.", pk: "Prabhupāda-Kṛpā — the mercy of Śrīla Prabhupāda preserved in his writings.", ssr: "Conversations and essays illuminating the timeless science of self-realization for the modern reader.", ds: "Śrīla Prabhupāda's critique of Western philosophy from Socrates to Sartre — in light of Vedic wisdom.", sva: "Sacred songs of the great Vaiṣṇava ācāryas — prayers and bhajanas with translations and purports.", va: "Śrīla Prabhupāda's commentary on the Vedānta-sūtra — the conclusion of all Vedic knowledge.", }; const CONTENT_TYPE_DESCRIPTIONS: Record = { letters: "Personal correspondence by Śrīla Prabhupāda — guiding disciples, Godbrothers, and well-wishers in the science of Kṛṣṇa consciousness.", essays: "Foundational essays on the science of Kṛṣṇa consciousness by His Divine Grace A.C. Bhaktivedanta Swami Prabhupāda.", glossary: "A comprehensive glossary of Sanskrit and Vedic terms from the books of Śrīla Prabhupāda.", disciples: "A directory of disciples initiated by Śrīla Prabhupāda between 1966 and 1977.", journals: "From the personal journals of Śrīla Prabhupāda — a window into the daily life and mission of the Founder-Ācārya of ISKCON.", magazines: "Back to Godhead — the magazine founded by Śrīla Prabhupāda in 1944 to present the timeless science of Kṛṣṇa consciousness.", "other-documents": "Writings and analyses by His Divine Grace A.C. Bhaktivedanta Swami Prabhupāda.", }; function getFallbackQuote(title: string, code?: string): string { // Book-specific description if (code && BOOK_DESCRIPTIONS[code]) { return BOOK_DESCRIPTIONS[code]; } // Content-type descriptions if (title.startsWith("Letter to")) { return CONTENT_TYPE_DESCRIPTIONS.letters; } for (const [key, desc] of Object.entries(CONTENT_TYPE_DESCRIPTIONS)) { if (title.toLowerCase().includes(key)) { return desc; } } // Generic fallback return SITE_DESCRIPTION; } // --------------------------------------------------------------------------- // Build the /api/og image URL // --------------------------------------------------------------------------- /** * Build a fully-qualified URL to the /api/og image endpoint. * * The URL is kept as short as possible — only non-empty params are included. * The quote is truncated to 260 chars to keep URL length reasonable. */ export function buildOgImageUrl(params: OgParams): string { const url = new URL("/api/og", SITE_URL); url.searchParams.set("title", params.title); if (params.detail) { url.searchParams.set("detail", params.detail); } if (params.quote) { const truncated = params.quote.length > 260 ? params.quote.slice(0, 257) + "..." : params.quote; url.searchParams.set("quote", truncated); } return url.toString(); } /** * Build OG metadata object for use in Next.js page `metadata` exports. * * Returns the `openGraph` and `twitter` fields to spread into `Metadata`. */ export function buildOgMeta(params: OgParams & { description?: string }) { const imageUrl = buildOgImageUrl(params); const description = params.description || params.quote || `${params.title}${params.detail ? ` — ${params.detail}` : ""}`; return { openGraph: { title: params.title, description, images: [ { url: imageUrl, width: 1200, height: 630, alt: params.title, }, ], siteName: "Prabhupāda Says", }, twitter: { card: "summary_large_image" as const, title: params.title, description, images: [imageUrl], }, }; } // --------------------------------------------------------------------------- // Helpers for common page types // --------------------------------------------------------------------------- /** OG metadata for a book page. */ export function ogForBook(code: string) { const title = BOOK_NAMES[code] || code.toUpperCase(); return buildOgMeta({ title, quote: getFallbackQuote(title, code), }); } /** OG metadata for a verse page. */ export function ogForVerse( code: string, parts: { division?: string; chapter: number; verse: number }, ) { const title = BOOK_NAMES[code] || code.toUpperCase(); let detail: string; if (code === "sb" && parts.division) { detail = `Canto ${parts.division}, Chapter ${parts.chapter}, Verse ${parts.verse}`; } else if (code === "cc" && parts.division) { const lila = CC_LILAS[parts.division] || parts.division; detail = `${lila}, Chapter ${parts.chapter}, Verse ${parts.verse}`; } else { detail = `Chapter ${parts.chapter}, Verse ${parts.verse}`; } return buildOgMeta({ title, detail, quote: getFallbackQuote(title, code), }); } /** OG metadata for a prose chapter. */ export function ogForChapter(code: string, chapterTitle: string) { const bookTitle = BOOK_NAMES[code] || code.toUpperCase(); return buildOgMeta({ title: bookTitle, detail: chapterTitle, quote: getFallbackQuote(bookTitle, code), }); } /** OG metadata for an essay. */ export function ogForEssay(title: string) { return buildOgMeta({ title, detail: "Essay by Śrīla Prabhupāda", quote: CONTENT_TYPE_DESCRIPTIONS.essays, }); } /** OG metadata for a letter. */ export function ogForLetter(recipient: string, meta?: string) { return buildOgMeta({ title: `Letter to ${recipient}`, detail: meta || undefined, quote: CONTENT_TYPE_DESCRIPTIONS.letters, }); } /** OG metadata for a journal entry. */ export function ogForJournal(journalTitle: string, entryTitle?: string) { return buildOgMeta({ title: journalTitle, detail: entryTitle || undefined, quote: CONTENT_TYPE_DESCRIPTIONS.journals, }); } /** OG metadata for a magazine issue. */ export function ogForMagazine(magazineTitle: string, issueTitle?: string) { return buildOgMeta({ title: magazineTitle, detail: issueTitle || undefined, quote: CONTENT_TYPE_DESCRIPTIONS.magazines, }); } /** OG metadata for an other document chapter. */ export function ogForOtherDocument(docTitle: string, chapterTitle?: string) { return buildOgMeta({ title: docTitle, detail: chapterTitle || undefined, quote: CONTENT_TYPE_DESCRIPTIONS["other-documents"], }); } /** OG metadata for the disciples directory. */ export function ogForDisciples() { return buildOgMeta({ title: "Disciples of Śrīla Prabhupāda", quote: CONTENT_TYPE_DESCRIPTIONS.disciples, }); } /** OG metadata for letters filtered by year. */ export function ogForLettersByYear(year: number, count: number) { return buildOgMeta({ title: `Letters from ${year}`, detail: `${count} ${count === 1 ? "letter" : "letters"}`, quote: `Śrīla Prabhupāda's correspondence from ${year} — personal letters guiding disciples and well-wishers in the science of Kṛṣṇa consciousness.`, }); } /** OG metadata for letters filtered by recipient. */ export function ogForLettersByRecipient(name: string, count: number) { return buildOgMeta({ title: `Letters to ${name}`, detail: `${count} ${count === 1 ? "letter" : "letters"}`, quote: `Śrīla Prabhupāda's personal correspondence to ${name} — instructions, blessings, and guidance in devotional service.`, }); } /** OG metadata for letters filtered by location. */ export function ogForLettersByLocation(location: string, count: number) { return buildOgMeta({ title: `Letters from ${location}`, detail: `${count} ${count === 1 ? "letter" : "letters"}`, quote: `Śrīla Prabhupāda's letters written from ${location} — chronicling the worldwide mission of the Hare Kṛṣṇa movement.`, }); } // --------------------------------------------------------------------------- // hl= query param → OG quote extraction // --------------------------------------------------------------------------- /** * Extract a display quote from the `?hl=` search param. * * When `paragraphs` are provided (from the database), the full matched text * is extracted by searching the content — giving the OG image the actual * selected quote, not just the prefix/suffix. * * When `paragraphs` are not provided, falls back to reconstructing a * readable snippet from prefix + suffix: "first few words ... last few words" * * Returns null if hl is missing or invalid. */ export function extractQuoteFromHl( searchParams: Record | URLSearchParams, paragraphs?: string[], ): string | null { const raw = searchParams instanceof URLSearchParams ? searchParams.get("hl") : typeof searchParams.hl === "string" ? searchParams.hl : null; if (!raw) return null; const fragment = decodeTextFragment(raw); if (!fragment) return null; // If paragraphs are available, extract the full matched text if (paragraphs && paragraphs.length > 0) { const fullMatch = extractMatchedText(fragment, paragraphs); if (fullMatch) { // Truncate at word boundary if too long for OG image if (fullMatch.length > 280) { const truncated = fullMatch.slice(0, 277); const lastSpace = truncated.lastIndexOf(" "); return ( (lastSpace > 200 ? truncated.slice(0, lastSpace) : truncated) + "..." ); } return fullMatch; } } // Fallback: reconstruct from prefix + suffix const { prefix, suffix } = fragment; if (!prefix) return null; if (suffix && suffix !== prefix) { return `${prefix} ... ${suffix}`; } return prefix; } /** Default OG metadata for the site. */ export function ogDefault() { return buildOgMeta({ title: "Prabhupāda Says", description: SITE_DESCRIPTION, quote: SITE_DESCRIPTION, }); }