On our program schedule, we wanted dedicated buttons next to each show title to “play last week’s show”. Came up with some .js that accepts a day and time (24hr format) arguement, and uses that to start an audio player at the most recent (past) occurrence of that day/time. In case this is useful to anyone else:
.js (place at the END of the page):
<script>
const STATION_ID = "XCSB";
const STATION_TZ = "America/New_York";
function calculateRecentTimestamp(dayName, timeStr) {
const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
const targetDay = days.indexOf(dayName);
const [hrs, mins] = timeStr.split(':').map(Number);
const now = new Date();
const stationNow = new Date(now.toLocaleString("en-US", {
timeZone: STATION_TZ
}));
let target = new Date(stationNow);
target.setHours(hrs, mins, 0, 0);
let diff = stationNow.getDay() - targetDay;
if (diff < 0 || (diff === 0 && stationNow < target)) {
diff += 7;
}
target.setDate(target.getDate() - diff);
return target.toISOString().replace(/[:\-]/g, '').split('.')[0] + 'Z';
}
function playArchive(name, day, time) {
const timestamp = calculateRecentTimestamp(day, time);
// Updated URL format from arkPlayer.js: STATION-TIMESTAMP/index.m3u8
const streamUrl = `https://ark3.spinitron.com/ark2/${STATION_ID}-${timestamp}/index.m3u8`;
const audio = document.getElementById('main-audio');
const bar = document.getElementById('player-bar');
const label = document.getElementById('now-playing-label');
label.innerText = `NOW PLAYING: ${name} (Broadcast: ${day} @ ${time} EST)`;
bar.style.display = 'block';
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(streamUrl);
hls.attachMedia(audio);
hls.on(Hls.Events.MANIFEST_PARSED, () => audio.play());
} else if (audio.canPlayType('application/vnd.apple.mpegurl')) {
audio.src = streamUrl;
audio.play();
}
}
</script>
(our schedule was in an HTML table, this is the styling for that table, and for the player widget floated to the bottom) CSS:
<style>
.table_programgrid {
overflow: auto;
width: 100%;
}
.table_programgrid table {
border: 1px solid #dededf;
width: 100%;
table-layout: auto;
border-collapse: collapse;
text-align: left;
}
.table_programgrid th {
border: 1px solid #dededf;
background-color: #66ccff;
color: #000000;
padding: 5px;
}
.table_programgrid td {
border: 1px solid #dededf;
padding: 5px;
}
.table_programgrid tr:nth-child(even) td {
background-color: #eeeeee;
}
.table_programgrid tr:nth-child(odd) td {
background-color: #dddddd;
}
.day-header {
font-weight: bold;
background-color: #444 !important;
color: #0033cc !important;
}
.spacer {
font-weight: bold;
background-color: #fff !important;
color: #fff !important;
}
/* Persistent Player Bar */
#player-bar {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
background: #222;
color: white;
padding: 15px;
display: none;
box-shadow: 0 -5px 15px rgba(0, 0, 0, 0.3);
z-index: 1000;
}
.player-inner {
max-width: 960px;
margin: 0 auto;
}
audio {
width: 100%;
height: 35px;
/* filter: invert(100%) hue-rotate(180deg); */
}
#now-playing-label {
font-size: 12px;
color: #aaa;
margin-bottom: 5px;
}
.listenbutton {
cursor: pointer;
color: #0066cc;
text-decoration: underline;
font-weight: bold;
}
</style>
and then each table cell with the show info and the play button:
<tr>
<td>9:00 AM</td>
<td>Waking the Intonarumori</td>
<td>bbob</td>
<td><span class="listenbutton" onclick="playArchive('Waking the Intonarumori', 'Monday', '09:00')">Listen</span></td>
</tr>