Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save RayHollister/f0bb913174afefc2f672d3da01d244e7 to your computer and use it in GitHub Desktop.
Save RayHollister/f0bb913174afefc2f672d3da01d244e7 to your computer and use it in GitHub Desktop.
This JavaScript fetches and formats the weekly broadcast schedule of a specific NPR radio show from their API. It groups same times on sequential days and displays the schedule on the radio show's webpage. The script runs automatically on page load, if no schedule is already present.
<script>
const daysOfWeek = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
async function getShowSchedule() {
let scheduleDiv = document.querySelector(".RadioShowPage-mediaSchedule");
if (scheduleDiv) return;
const programName = document.querySelector(".RadioShowPage-headline").textContent.trim();
try {
const today = new Date();
const nextWeek = new Date();
nextWeek.setDate(today.getDate() + 6);
const formatDate = (date) => {
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
};
const todayStr = formatDate(today);
const nextWeekStr = formatDate(nextWeek);
// caching
// const response = await fetch(`https://api.composer.nprstations.org/v1/widget/{ucs}/week?date=${todayStr},${nextWeekStr}&format=json`);
// cache buster
const response = await fetch(`https://api.composer.nprstations.org/v1/widget/{ucs}/week?date=${todayStr},${nextWeekStr}&format=json&nocache=${new Date().getTime()}`);
const data = await response.json();
const showSchedules = data.onThisWeek.filter(show => show.program.name === programName);
if (!showSchedules.length) {
console.error('Invalid API response', data);
return;
}
const timeDayMap = {};
showSchedules.forEach(show => {
const showDate = new Date(show.start_utc);
let time = formatStartTime(show.start_time);
let day;
if (time === "12 a.m.") {
time = "at midnight";
day = shiftDay(showDate);
} else if (time === "12 p.m.") {
time = "at noon";
day = showDate.toLocaleDateString('en-US', { weekday: 'long' });
} else {
day = showDate.toLocaleDateString('en-US', { weekday: 'long' });
}
if (!timeDayMap[time]) {
timeDayMap[time] = [day];
} else {
timeDayMap[time].push(day);
}
});
const formattedScheduleTimesArr = combineTimesForDays(timeDayMap);
let formattedScheduleTimes = "";
if (formattedScheduleTimesArr.length > 1) {
formattedScheduleTimes = formattedScheduleTimesArr.slice(0, -1).join(", ") + " and " + formattedScheduleTimesArr.slice(-1);
} else {
formattedScheduleTimes = formattedScheduleTimesArr[0];
}
scheduleDiv = document.createElement('div');
scheduleDiv.className = "RadioShowPage-mediaSchedule";
const radioTopDiv = document.querySelector('.RadioShowPage-top');
radioTopDiv.insertAdjacentElement('afterend', scheduleDiv);
// Capitalize first character
formattedScheduleTimes = formattedScheduleTimes.charAt(0).toUpperCase() + formattedScheduleTimes.slice(1);
scheduleDiv.textContent = formattedScheduleTimes;
} catch (error) {
console.error("Error: ", error);
}
}
function combineSequentialDays(days) {
let combinedDays = "";
let sequenceStart = days[0];
let previousDay = days[0];
for (let i = 1; i < days.length; i++) {
const day = days[i];
const dayIndex = daysOfWeek.indexOf(day);
const previousDayIndex = daysOfWeek.indexOf(previousDay);
if ((dayIndex !== (previousDayIndex + 1) % 7) && (dayIndex !== previousDayIndex)) {
if (sequenceStart === previousDay) {
combinedDays += `${sequenceStart}, `;
} else if ((daysOfWeek.indexOf(sequenceStart) + 4) % 7 === daysOfWeek.indexOf(previousDay)) {
combinedDays += "weekdays, ";
} else {
combinedDays += `${sequenceStart} through ${previousDay}, `;
}
sequenceStart = day;
}
previousDay = day;
}
if (sequenceStart === previousDay) {
combinedDays += `${sequenceStart}`;
} else if ((daysOfWeek.indexOf(sequenceStart) + 4) % 7 === daysOfWeek.indexOf(previousDay)) {
combinedDays += "weekdays";
} else {
combinedDays += `${sequenceStart} through ${previousDay}`;
}
// Replace the last comma with 'and' if there are only two items
if (combinedDays.split(', ').length == 2) {
combinedDays = combinedDays.replace(', ', ' and ');
}
return combinedDays;
}
function formatStartTime(startTime) {
if (!startTime) {
console.error('Invalid start time:', startTime);
return '';
}
const timeParts = startTime.split(":");
let hours = parseInt(timeParts[0]);
const minutes = timeParts[1];
let period = "a.m.";
if (hours >= 12) {
period = "p.m.";
if (hours > 12) {
hours -= 12;
}
}
if (hours === 0) {
hours = 12;
}
let formattedTime = `${hours}`;
if (minutes !== '00') {
formattedTime += `:${minutes}`;
}
formattedTime += ` ${period}`;
return formattedTime;
}
function shiftDay(date) {
date.setDate(date.getDate() - 1);
return date.toLocaleDateString('en-US', { weekday: 'long' });
}
function combineTimesForDays(timeDayMap) {
const dayTimeMap = {};
for (let time in timeDayMap) {
const days = timeDayMap[time].sort((a, b) => daysOfWeek.indexOf(a) - daysOfWeek.indexOf(b));
const dayString = combineSequentialDays(days);
if (!dayTimeMap[dayString]) {
dayTimeMap[dayString] = [time];
} else {
dayTimeMap[dayString].push(time);
}
}
let formattedScheduleTimesArr = [];
for (let days in dayTimeMap) {
const times = dayTimeMap[days];
const timeString = times.join(" and ");
formattedScheduleTimesArr.push({days: days, timeString: timeString});
}
// Sort the array
formattedScheduleTimesArr.sort((a, b) => {
const timeA = parseInt(a.timeString.split(":")[0]);
const timeB = parseInt(b.timeString.split(":")[0]);
return timeA - timeB;
});
// Convert back to string format
return formattedScheduleTimesArr.map(item => `${item.days} ${item.timeString}`);
}
getShowSchedule();
</script>
@RayHollister
Copy link
Author

rev 5. Changed 'Monday through Friday' to 'Weekdays'

@RayHollister
Copy link
Author

RayHollister commented Jul 21, 2023

Rev 6. Changed 12 p.m. and 12 a.m. to noon and midnight respectively. Changed the day to the previous day if midnight. (Stupid AP Style!!)

@RayHollister
Copy link
Author

Rev 7 added a cache buster to the JSON

@RayHollister
Copy link
Author

Fixed weekdays

@RayHollister
Copy link
Author

Fixed capitalization on weekdays!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment