forked from boyska/radiomanifest.js
171 lines
3.6 KiB
JavaScript
171 lines
3.6 KiB
JavaScript
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<external:ICAL~Component>}
|
|
* */
|
|
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
|
|
*/
|