No episodes found.
';
return;
}
const episodes = Array.from(items).map((item, i) => {
const title = item.querySelector('title')?.textContent || 'Untitled';
const description = item.querySelector('description')?.textContent || '';
const enclosure = item.querySelector('enclosure');
const audioUrl = enclosure?.getAttribute('url') || '';
const pubDate = item.querySelector('pubDate')?.textContent || '';
const duration = item.getElementsByTagNameNS('http://www.itunes.com/dtds/podcast-1.0.dtd', 'duration')[0]?.textContent || '';
const episodeNum = item.getElementsByTagNameNS('http://www.itunes.com/dtds/podcast-1.0.dtd', 'episode')[0]?.textContent || '';
const link = item.querySelector('link')?.textContent || '';
return { title, description, audioUrl, pubDate, duration, episodeNum, link };
});
renderEpisodes(episodes);
}
function renderEpisodes(episodes) {
episodesList.innerHTML = '';
episodes.forEach((ep) => {
const card = document.createElement('div');
card.className = 'episode-card';
const epLabel = ep.episodeNum ? `Ep ${ep.episodeNum}` : '';
const dateStr = ep.pubDate ? formatDate(ep.pubDate) : '';
const durStr = parseDuration(ep.duration);
const metaParts = [epLabel, dateStr, durStr].filter(Boolean).join(' · ');
card.innerHTML = `
${metaParts}
${ep.title}
${truncate(ep.description, 150)}
`;
const btn = card.querySelector('.episode-play-btn');
btn.addEventListener('click', () => playEpisode(ep.audioUrl, ep.title, card, btn));
episodesList.appendChild(card);
});
}
function playEpisode(url, title, card, btn) {
if (!url) return;
// If clicking the same episode that's playing, toggle play/pause
if (audio.src === url || audio.src === encodeURI(url)) {
if (audio.paused) {
audio.play();
} else {
audio.pause();
}
return;
}
// Reset previous card button icon
if (currentEpisodeCard) {
const prevBtn = currentEpisodeCard.querySelector('.episode-play-btn');
if (prevBtn) {
prevBtn.innerHTML = playSVG;
prevBtn.classList.remove('playing');
}
}
currentEpisodeCard = card;
audio.src = url;
audio.play();
playerTitle.textContent = title;
stickyPlayer.classList.add('active');
}
// Sync UI with audio state
audio.addEventListener('play', () => {
updatePlayIcons(true);
});
audio.addEventListener('pause', () => {
updatePlayIcons(false);
});
audio.addEventListener('timeupdate', () => {
if (audio.duration) {
const pct = (audio.currentTime / audio.duration) * 100;
playerProgressFill.style.width = pct + '%';
playerTime.textContent = `${formatTime(audio.currentTime)} / ${formatTime(audio.duration)}`;
}
});
audio.addEventListener('ended', () => {
updatePlayIcons(false);
});
function updatePlayIcons(playing) {
// Sticky player icons
const iconPlay = playerPlayBtn.querySelector('.icon-play');
const iconPause = playerPlayBtn.querySelector('.icon-pause');
if (iconPlay) iconPlay.style.display = playing ? 'none' : 'block';
if (iconPause) iconPause.style.display = playing ? 'block' : 'none';
// Episode card icon
if (currentEpisodeCard) {
const btn = currentEpisodeCard.querySelector('.episode-play-btn');
if (btn) {
btn.innerHTML = playing ? pauseSVG : playSVG;
btn.classList.toggle('playing', playing);
}
}
}
// Sticky player play/pause button
playerPlayBtn.addEventListener('click', () => {
if (audio.src) {
if (audio.paused) audio.play();
else audio.pause();
}
});
// Progress bar seeking
playerProgress.addEventListener('click', (e) => {
if (audio.duration) {
const rect = playerProgress.getBoundingClientRect();
const pct = (e.clientX - rect.left) / rect.width;
audio.currentTime = pct * audio.duration;
}
});
// Init
fetchEpisodes();