const ICAL = require('ical.js') const shows = require('./shows.js') /** * Represent a schedule (ie: a .ics file) */ class RadioSchedule { constructor (calendar, radio) { this.calendar = calendar // ICAL.Calendar this.radio = radio // radiomanifest.Radio } /** * Get a list of all known {@link vEvent}s * * @returns {Array} * */ getEvents() { return this.calendar.getAllSubcomponents('vevent'); } /** * @returns {external:ICAL~Component} if nothing is going on right now, `null` is returned */ getNowEvent(now) { var ev_now = this.getEvents().filter(function(vevent) { const ev = new ICAL.Event(vevent) return isNow(ev, now) }) if(ev_now.length === 0) return null if(ev_now.length > 1) console.log("More than one event right now?!") return ev_now[0] } /** * @returns {RadioShow} if nothing is going on right now, `null` is returned */ getNowShow(now) { var ev = this.getNowEvent(now) if (ev === null) return null if (this.radio !== undefined) { const showid = RadioSchedule.veventGetShowID(ev) var show = this.radio.getShowByName(showid) if (show === null || show === undefined) { return new shows.RadioShow(RadioSchedule.veventGetSummary(ev)) } return show; } return new shows.RadioShow(RadioSchedule.veventGetSummary(ev)) } /** * @todo To be implemented */ getNextEvent(now) { // XXX } /** * @todo To be implemented * @returns {NextShow} if there is no next show, nothing will be returned */ getNextShow(now) { // XXX } static veventGetSummary(vevent) { return vevent.getFirstProperty('summary').getFirstValue() } static veventGetShowID(vevent) { return RadioSchedule.veventGetSummary(vevent) // XXX: X-Show-ID } } function isNow(vEvent, now) { if (now === undefined) { now = ICAL.Time.now() } if (vEvent.isRecurring()) { return isNowRecurring(vEvent, now) } return (now < vEvent.endDate) && (now > event.startDate); } function isNowRecurring(vEvent, now) { var expand = vEvent.iterator(vEvent.startDate) var next, next_end; while ((next = expand.next())) { next_end = next.clone() next_end.addDuration(vEvent.duration) if (next_end > now) { break; } } return (now < next_end && now > next); } async function get(manifest) { if (manifest.scheduleURL) { let resp = null try { resp = await fetch(manifest.scheduleURL) } catch (e) { true } if (resp !== null) { try { text = await resp.text() return parse(text) } catch (e) { console.error('Error while parsing schedule', e) throw e } } } } /** * Parse ICAL and get a RadioSchedule * * @param {string} text The text, in ICS format * @returns {RadioSchedule} */ function parse(text) { var jcalData = ICAL.parse(text); var vcalendar = new ICAL.Component(jcalData); return new RadioSchedule(vcalendar) } module.exports = { get: get, parse: parse, RadioSchedule: RadioSchedule, } /** * @typedef {Object} NextShow * @property {RadioShow} show The next show scheduled * @property {external:ICAL~Time} time When it will start */ /** * @external ICAL * @see https://mozilla-comm.github.io/ical.js/api/index.html */ /** * @class Component * @memberof external:ICAL * @inner * @see https://mozilla-comm.github.io/ical.js/api/ICAL.Component.html */ /** * @class Time * @memberof external:ICAL * @inner * @see https://mozilla-comm.github.io/ical.js/api/ICAL.Time.html */