From 5bfe52c2356d763995513372e7d580d4eff8783e Mon Sep 17 00:00:00 2001 From: boyska Date: Sun, 30 Jan 2022 02:23:11 +0100 Subject: [PATCH] support schedule --- calendar.js | 117 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + radiomanifest.js | 23 ++++++++-- 3 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 calendar.js diff --git a/calendar.js b/calendar.js new file mode 100644 index 0000000..7e1163d --- /dev/null +++ b/calendar.js @@ -0,0 +1,117 @@ +const ICAL = require('ical.js') +const shows = require('./shows.js') + +class RadioSchedule { + constructor (calendar, radio) { + this.calendar = calendar // ICAL.Calendar + this.radio = radio // radiomanifest.Radio + } + + getEvents() { + return this.calendar.getAllSubcomponents('vevent'); + + } + 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] + } + + 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)) + } + + getNextEvent(now) { + // XXX + } + + 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 + } + } + } +} + +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, +} diff --git a/package.json b/package.json index 8991da3..dd58442 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "webpack-cli": "^4.9.1" }, "dependencies": { + "ical.js": "^1.5.0", "isomorphic-unfetch": "^3.1.0" } } diff --git a/radiomanifest.js b/radiomanifest.js index 94058a2..c5f3a08 100644 --- a/radiomanifest.js +++ b/radiomanifest.js @@ -1,5 +1,6 @@ const fetch = require('isomorphic-unfetch') const shows = require('./shows.js') +const calendar = require('./calendar.js') function getStreaminfoUrl (siteurl) { return siteurl + '/streaminfo.json' // XXX: improve this logic @@ -15,13 +16,14 @@ function getAttribute(el, attr, default_value) { } class Radio { - constructor (sources, schedule, showsURL, feed) { + constructor (sources, scheduleURL, showsURL, feed) { this.streaming = new RadioStreaming(sources) - this.schedule = schedule + this.scheduleURL = scheduleURL this.showsURL = showsURL this.feed = feed this.name = '' this.shows = [] + this.schedule = null } getStreaming () { @@ -36,10 +38,12 @@ class Radio { return this.shows } getShowByName (showName) { + if (this.shows === undefined) return null return this.shows.find(s => s.name === showName) } getSchedule () { + return this.schedule } getShowAtTime () { @@ -62,9 +66,9 @@ class Radio { res = doc.evaluate('/radio-manifest/schedule', doc) const scheduleEl = res.iterateNext() - let schedule = null + let scheduleURL = null if (scheduleEl !== null) { - schedule = scheduleEl.getAttribute('src') + scheduleURL = scheduleEl.getAttribute('src') } res = xml.evaluate('/radio-manifest/shows', xml) @@ -81,7 +85,7 @@ class Radio { feed = feedEl.getAttribute('src') } - const manifest = new Radio(sources, schedule, showsURL, feed) + const manifest = new Radio(sources, scheduleURL, showsURL, feed) return manifest } } @@ -150,6 +154,15 @@ async function get (siteurl, options) { console.error("Error while fetching shows file", e) } + try { + manifest.schedule = await calendar.get(manifest) + if (manifest.schedule !== undefined) + manifest.schedule.radio = manifest + } catch (e) { + console.error("Error while fetching shows file", e) + } + + resp = null try { resp = await fetch(getStreaminfoUrl(siteurl))