Replace Spotify embeds with branded custom audio player backed by RSS

- Add /api/episode-audio route returning MP3 URLs from Anchor RSS feed
- Replace Spotify URL text inputs in admin with RSS episode dropdowns
- New EpisodeAudioPlayer component with podcast art, show name, progress
  bar, and Spotify icon link — full and compact sizes
- Backward-compatible: legacy Spotify embed URLs still render as iframes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nmemmert
2026-06-17 13:23:19 -04:00
parent 3d8f02d915
commit a1d5889711
6 changed files with 393 additions and 25 deletions
+172
View File
@@ -2702,6 +2702,178 @@
margin: 0 0 2.5rem;
}
/* Custom audio player */
.episode-audio-player {
background: var(--brand-black-2, #0f0f0c);
border: 1px solid rgba(201, 168, 76, 0.2);
border-radius: 10px;
padding: 1rem 1.1rem;
}
.episode-audio-player__inner {
display: flex;
align-items: center;
gap: 14px;
}
.episode-audio-player__art {
width: 56px;
height: 56px;
border-radius: 6px;
object-fit: cover;
flex-shrink: 0;
}
.episode-audio-player__body {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 4px;
}
.episode-audio-player__meta {
display: flex;
align-items: center;
justify-content: space-between;
}
.episode-audio-player__show-name {
font-size: 0.7rem;
color: var(--brand-gold, #c9a84c);
font-family: var(--brand-font-body, Georgia, serif);
letter-spacing: 0.02em;
}
.episode-audio-player__spotify-link {
color: rgba(176, 164, 140, 0.5);
display: flex;
align-items: center;
transition: color 0.15s;
flex-shrink: 0;
}
.episode-audio-player__spotify-link:hover {
color: #1DB954;
}
.episode-audio-player__spotify-link svg {
width: 16px;
height: 16px;
}
.episode-audio-player__title {
font-size: 0.88rem;
color: var(--brand-warm-white, #f0ead8);
margin: 0;
font-family: var(--brand-font-heading, Georgia, serif);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.episode-audio-player__controls {
display: flex;
align-items: center;
gap: 10px;
margin-top: 2px;
}
.episode-audio-player__play {
width: 34px;
height: 34px;
border-radius: 50%;
background: var(--brand-gold, #c9a84c);
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
color: var(--brand-black, #0a0a08);
transition: background 0.15s;
}
.episode-audio-player__play:hover {
background: var(--brand-gold-light, #e0c070);
}
.episode-audio-player__play svg {
width: 14px;
height: 14px;
}
.episode-audio-player__track {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
min-width: 0;
}
.episode-audio-player__bar {
position: relative;
height: 3px;
background: rgba(42, 37, 24, 0.9);
border-radius: 2px;
cursor: pointer;
outline: none;
}
.episode-audio-player__bar:focus-visible {
box-shadow: 0 0 0 2px var(--brand-gold, #c9a84c);
}
.episode-audio-player__fill {
height: 100%;
background: var(--brand-gold, #c9a84c);
border-radius: 2px;
pointer-events: none;
}
.episode-audio-player__thumb {
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--brand-gold-light, #e0c070);
pointer-events: none;
}
.episode-audio-player__times {
display: flex;
justify-content: space-between;
font-size: 0.68rem;
color: var(--brand-muted, #b0a48c);
font-family: var(--brand-font-body, Georgia, serif);
}
/* Compact variant (study sections) */
.episode-audio-player--compact .episode-audio-player__art {
width: 42px;
height: 42px;
}
.episode-audio-player--compact .episode-audio-player__play {
width: 28px;
height: 28px;
}
.episode-audio-player--compact .episode-audio-player__play svg {
width: 11px;
height: 11px;
}
.episode-audio-player--compact .episode-audio-player__bar {
height: 2px;
}
.episode-audio-player--compact .episode-audio-player__title {
font-size: 0.8rem;
}
.episode-detail-show-notes,
.episode-detail-questions {
margin-top: 2.5rem;