diff --git a/publish_episode.py b/publish_episode.py
index f053adf..15fd1f5 100755
--- a/publish_episode.py
+++ b/publish_episode.py
@@ -974,34 +974,6 @@ def sync_episode_media_to_bunny(episode_id: int, already_uploaded: set):
Path(tmp_path).unlink(missing_ok=True)
-def add_episode_to_sitemap(slug: str):
- """Add episode transcript page to sitemap.xml."""
- sitemap_path = Path(__file__).parent / "website" / "sitemap.xml"
- if not sitemap_path.exists():
- return
-
- url = f"https://lukeattheroost.com/episode.html?slug={slug}"
- content = sitemap_path.read_text()
-
- if url in content:
- print(f" Episode already in sitemap")
- return
-
- today = datetime.now().strftime("%Y-%m-%d")
- new_entry = f"""
No episode specified. Go back to episodes.
'; -} else { - loadEpisode(slug); -} - -async function loadEpisode(slug) { - try { - const res = await fetch(FEED_URL); - const xml = await res.text(); - const parser = new DOMParser(); - const doc = parser.parseFromString(xml, 'text/xml'); - const items = doc.querySelectorAll('item'); - - let episode = null; - for (const item of items) { - const link = item.querySelector('link')?.textContent || ''; - const itemSlug = link.split('/episodes/').pop()?.replace(/\/$/, ''); - if (itemSlug === slug) { - episode = { - title: item.querySelector('title')?.textContent || 'Untitled', - description: item.querySelector('description')?.textContent || '', - audioUrl: item.querySelector('enclosure')?.getAttribute('url') || '', - pubDate: item.querySelector('pubDate')?.textContent || '', - duration: item.getElementsByTagNameNS('http://www.itunes.com/dtds/podcast-1.0.dtd', 'duration')[0]?.textContent || '', - episodeNum: item.getElementsByTagNameNS('http://www.itunes.com/dtds/podcast-1.0.dtd', 'episode')[0]?.textContent || '', - }; - break; - } - } - - if (!episode) { - document.getElementById('ep-title').textContent = 'Episode not found'; - document.getElementById('transcript-body').innerHTML = 'Could not find this episode. Go back to episodes.
'; - return; - } - - // Populate header - const metaParts = [ - episode.episodeNum ? `Episode ${episode.episodeNum}` : '', - episode.pubDate ? formatDate(episode.pubDate) : '', - parseDuration(episode.duration), - ].filter(Boolean).join(' \u00b7 '); - - document.getElementById('ep-meta').textContent = metaParts; - document.getElementById('ep-title').textContent = episode.title; - document.getElementById('ep-desc').textContent = stripHtml(episode.description || ''); - - // Update page meta - document.title = `${episode.title} — Luke at the Roost`; - document.getElementById('page-description')?.setAttribute('content', `Full transcript of ${episode.title} from Luke at the Roost.`); - document.getElementById('og-title')?.setAttribute('content', episode.title); - document.getElementById('og-description')?.setAttribute('content', stripHtml(episode.description).slice(0, 200)); - const canonicalUrl = `https://lukeattheroost.com/episode.html?slug=${slug}`; - document.getElementById('page-canonical')?.setAttribute('href', canonicalUrl); - document.getElementById('og-url')?.setAttribute('content', canonicalUrl); - document.getElementById('tw-title')?.setAttribute('content', episode.title); - document.getElementById('tw-description')?.setAttribute('content', stripHtml(episode.description).slice(0, 200)); - - // Update JSON-LD structured data - const jsonLd = document.getElementById('episode-jsonld'); - if (jsonLd) { - const ld = JSON.parse(jsonLd.textContent); - ld.name = episode.title; - ld.url = canonicalUrl; - ld.description = stripHtml(episode.description).slice(0, 300); - if (episode.pubDate) ld.datePublished = new Date(episode.pubDate).toISOString().split('T')[0]; - if (episode.episodeNum) ld.episodeNumber = parseInt(episode.episodeNum, 10); - if (episode.audioUrl) { - ld.associatedMedia = { - "@type": "MediaObject", - "contentUrl": episode.audioUrl - }; - } - jsonLd.textContent = JSON.stringify(ld); - } - - // Play button - if (episode.audioUrl) { - const playBtn = document.getElementById('ep-play-btn'); - playBtn.style.display = 'inline-flex'; - playBtn.addEventListener('click', () => { - audio.src = episode.audioUrl; - audio.play(); - playerTitle.textContent = episode.title; - stickyPlayer.classList.add('active'); - }); - } - } catch (e) { - document.getElementById('ep-title').textContent = 'Error loading episode'; - } - - // Fetch transcript - try { - const txRes = await fetch(`/transcripts/${slug}.txt`); - if (!txRes.ok) throw new Error('Not found'); - const text = await txRes.text(); - const paragraphs = text.split(/\n\n+/).filter(Boolean); - const html = paragraphs.map(p => { - const escaped = escapeHtml(p); - const labeled = escaped.replace(/^([A-Z][A-Z\s'\-]+?):\s*/, '$1: '); - return `${labeled.replace(/\n/g, '
')}
Transcript not yet available for this episode.
'; - } -} diff --git a/website/llms.txt b/website/llms.txt index 007076f..4e15437 100644 --- a/website/llms.txt +++ b/website/llms.txt @@ -48,7 +48,7 @@ The show is entirely custom-built: callers are generated in real-time using larg - **How It Works**: https://lukeattheroost.com/how-it-works — Technical deep dive into the AI caller generation, recording, and production pipeline - **Clips**: https://lukeattheroost.com/clips — Best moments from the show as video clips - **Stats**: https://lukeattheroost.com/stats — Download numbers, reviews, YouTube metrics -- **Episode transcripts**: https://lukeattheroost.com/episode.html?slug=EPISODE-SLUG — Full text transcripts of every episode +- **Episode transcripts**: https://lukeattheroost.com/episode/EPISODE-SLUG/ — Full text transcripts of every episode ## Community @@ -121,16 +121,16 @@ Each episode (~30-60 minutes) follows a loose late-night radio format: ## Recent Episodes Episodes are published daily. Each has a full transcript available at: -https://lukeattheroost.com/episode.html?slug=EPISODE-SLUG +https://lukeattheroost.com/episode/EPISODE-SLUG/ Episode transcript URLs follow the pattern: episode-N-title-slug Recent episodes include: -- Episode 37: "Secrets, Lies, and Coffee Runs" — https://lukeattheroost.com/episode.html?slug=episode-37-secrets-lies-and-coffee-runs -- Episode 36: "Late Night Confessions and Unexpected Moments" — https://lukeattheroost.com/episode.html?slug=episode-36-late-night-confessions-and-unexpected-moments -- Episode 35: "Midnight Confessions and Unexpected Revelations" — https://lukeattheroost.com/episode.html?slug=episode-35-midnight-confessions-and-unexpected-revelations -- Episode 34: "Hidden Rooms, Potlucks, and Mysterious Notes" — https://lukeattheroost.com/episode.html?slug=episode-34-hidden-rooms-potlucks-and-mysterious-notes -- Episode 33: "Late Night Confessions and Cosmic Comedies" — https://lukeattheroost.com/episode.html?slug=episode-33-late-night-confessions-and-cosmic-comedies +- Episode 37: "Secrets, Lies, and Coffee Runs" — https://lukeattheroost.com/episode/episode-37-secrets-lies-and-coffee-runs/ +- Episode 36: "Late Night Confessions and Unexpected Moments" — https://lukeattheroost.com/episode/episode-36-late-night-confessions-and-unexpected-moments/ +- Episode 35: "Midnight Confessions and Unexpected Revelations" — https://lukeattheroost.com/episode/episode-35-midnight-confessions-and-unexpected-revelations/ +- Episode 34: "Hidden Rooms, Potlucks, and Mysterious Notes" — https://lukeattheroost.com/episode/episode-34-hidden-rooms-potlucks-and-mysterious-notes/ +- Episode 33: "Late Night Confessions and Cosmic Comedies" — https://lukeattheroost.com/episode/episode-33-late-night-confessions-and-cosmic-comedies/ ## Clip Highlights