245 lines
5.7 KiB
JavaScript
245 lines
5.7 KiB
JavaScript
const raws = {};
|
|
let hideConfirmed = true;
|
|
const icsUrls = [
|
|
{
|
|
url: '/sonarr-hs-ics',
|
|
name: 'hidden-sea',
|
|
},
|
|
// {
|
|
// url: '/sonarr-cb-ics',
|
|
// name: 'cold-badlands',
|
|
// },
|
|
// {
|
|
// url: '/sonarr-fv-ics',
|
|
// name: 'fancy-valley',
|
|
// },
|
|
{
|
|
url: '/sonarr-ag-ics',
|
|
name: 'ancient-grove',
|
|
},
|
|
{
|
|
url: '/sonarr-bs-ics',
|
|
name: 'bold-silence',
|
|
},
|
|
{
|
|
url: '/sonarr-fd-ics',
|
|
name: 'frosty-darkness',
|
|
},
|
|
{
|
|
url: '/sonarr-rs-ics',
|
|
name: 'royal-stream [4K]',
|
|
},
|
|
];
|
|
|
|
async function fetchIcs(url, name, shouldForce)
|
|
{
|
|
let icalRaw = raws[name] || '';
|
|
|
|
if (!raws[name] || shouldForce) {
|
|
const response = await fetch(url);
|
|
icalRaw = await response.text();
|
|
raws[name] = icalRaw;
|
|
}
|
|
|
|
const parse = ICAL.parse(icalRaw.trim());
|
|
const component = new ICAL.Component(parse);
|
|
|
|
// console.log(component);
|
|
|
|
const eventComps = component.getAllSubcomponents('vevent');
|
|
const events = eventComps.map((item) => {
|
|
if (item.getFirstPropertyValue('class') === 'PRIVATE') {
|
|
return null;
|
|
}
|
|
|
|
let title = item.getFirstPropertyValue('summary');
|
|
const status = item.getFirstPropertyValue('status');
|
|
const start = item.getFirstPropertyValue('dtstart').toJSDate();
|
|
const end = item.getFirstPropertyValue('dtend').toJSDate();
|
|
const publisher = item.getFirstPropertyValue('categories');
|
|
|
|
let isAdded = false;
|
|
if (status.toLowerCase() === 'confirmed') {
|
|
// Status toggled to only show upcoming/missing.
|
|
if (hideConfirmed) {
|
|
return null;
|
|
}
|
|
|
|
isAdded = true;
|
|
title = `✅ ${title}`;
|
|
}
|
|
|
|
const now = Date.now();
|
|
|
|
if (!isAdded)
|
|
{
|
|
/**
|
|
* Start date in the past
|
|
*/
|
|
if (now > start.getTime()) {
|
|
title = `🟠 ${title}`;
|
|
}
|
|
else {
|
|
title = `⏰ ${title}`;
|
|
}
|
|
}
|
|
|
|
// if (status.toLowerCase() !== 'confirmed' && status !== 'tentative') {
|
|
// console.log(status);
|
|
// }
|
|
|
|
title = `${title} [${publisher}]`;
|
|
|
|
return {
|
|
title: title,
|
|
start,
|
|
end,
|
|
display: 'list-item',
|
|
classNames: [name],
|
|
extendedProps: {
|
|
hostname: name,
|
|
}
|
|
};
|
|
});
|
|
|
|
return events;
|
|
}
|
|
|
|
let keyHandlersAdded = false;
|
|
function keyHandlers(calendar)
|
|
{
|
|
if (keyHandlersAdded) {
|
|
return;
|
|
}
|
|
|
|
window.addEventListener('keydown', function(ev) {
|
|
const key = ev.key;
|
|
|
|
if (!key) {
|
|
return;
|
|
}
|
|
|
|
switch (key) {
|
|
case 'ArrowLeft':
|
|
calendar.prev();
|
|
break;
|
|
|
|
case 'ArrowRight':
|
|
calendar.next();
|
|
break;
|
|
}
|
|
});
|
|
|
|
keyHandlersAdded = true;
|
|
}
|
|
|
|
function scrollToCalendarDay()
|
|
{
|
|
const date = new Date();
|
|
const today = date.toISOString().split('T')[0];
|
|
|
|
const dateRow = document.querySelector(`[data-date="${today}"]`);
|
|
|
|
if (!dateRow) {
|
|
console.log('Could not find date row', today);
|
|
return;
|
|
}
|
|
|
|
dateRow.scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'start',
|
|
});
|
|
}
|
|
|
|
const showAllText = 'Show all tracked episodes';
|
|
const showTrackedText = 'Show only missing/upcoming episodes';
|
|
const toggleButton = {
|
|
text: (hideConfirmed ? showAllText : showTrackedText),
|
|
click: async function() {
|
|
if (!hideConfirmed) {
|
|
toggleButton.text = showAllText;
|
|
}
|
|
else {
|
|
toggleButton.text = showTrackedText;
|
|
}
|
|
|
|
hideConfirmed = !hideConfirmed;
|
|
|
|
await loadSonarrCalendar();
|
|
},
|
|
};
|
|
|
|
const refreshButton = {
|
|
text: 'Refresh calendar',
|
|
click: function() {
|
|
loadSonarrCalendar(true);
|
|
},
|
|
};
|
|
|
|
async function loadSonarrCalendar(shouldForce = false)
|
|
{
|
|
const calendarElement = document.querySelector('#sonarr-calendar');
|
|
let events = [];
|
|
|
|
let promises = [];
|
|
for (const list of icsUrls)
|
|
{
|
|
const { url, name } = list;
|
|
promises.push(fetchIcs(url, name, shouldForce));
|
|
}
|
|
|
|
events = await Promise.all(promises);
|
|
events = events.flat();
|
|
events = events.filter(x => x !== null);
|
|
|
|
const calendar = new FullCalendar.Calendar(calendarElement, {
|
|
// initialView: 'dayGridMonth',
|
|
customButtons: {
|
|
toggleButton,
|
|
refreshButton,
|
|
},
|
|
themeSystem: 'bootstrap5',
|
|
initialView: 'listWeek',
|
|
initialDate: new Date(),
|
|
headerToolbar: {
|
|
left: 'prev,next today',
|
|
center: 'title',
|
|
// right: 'dayGridMonth,dayGridWeek,dayGridDay,listWeek',
|
|
right: 'toggleButton refreshButton',
|
|
},
|
|
events: events,
|
|
eventDidMount: function(info) {
|
|
const hostname = info.event.extendedProps.hostname;
|
|
info.el.querySelector('.fc-list-event-title').innerHTML += `<span class="float-end">${hostname}</span>`;
|
|
}
|
|
});
|
|
|
|
const message = document.querySelector('#status-message');
|
|
try {
|
|
calendar.render();
|
|
|
|
if (message) {
|
|
message.textContent = '';
|
|
}
|
|
|
|
keyHandlers(calendar);
|
|
}
|
|
catch (err) {
|
|
console.error(err);
|
|
if (message) {
|
|
message.textContent = 'An error occurred loading schedule...';
|
|
}
|
|
}
|
|
}
|
|
|
|
window.addEventListener('DOMContentLoaded', function() {
|
|
loadSonarrCalendar();
|
|
|
|
this.setInterval(
|
|
function() {
|
|
loadSonarrCalendar(true);
|
|
},
|
|
60 * 1000
|
|
);
|
|
});
|