forked from boyska/radiomanifest.js
getNext
This commit is contained in:
parent
303819879d
commit
52cf3c5387
2 changed files with 149 additions and 25 deletions
145
calendar.js
145
calendar.js
|
@ -1,6 +1,11 @@
|
|||
const ICAL = require('ical.js')
|
||||
const shows = require('./shows.js')
|
||||
|
||||
function max(a, b) {
|
||||
if (a<b) return b
|
||||
return a
|
||||
}
|
||||
|
||||
/**
|
||||
* Represent a schedule (ie: a .ics file)
|
||||
*/
|
||||
|
@ -21,26 +26,9 @@ class RadioSchedule {
|
|||
}
|
||||
|
||||
/**
|
||||
* @returns {external:ICAL~Component} if nothing is going on right now, `null` is returned
|
||||
* @returns {RadioShow} tries to get a matching show, or create a new one
|
||||
*/
|
||||
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)
|
||||
|
||||
getShowByEvent(ev) {
|
||||
if (ev === null) return null
|
||||
if (this.radio !== undefined) {
|
||||
const showid = RadioSchedule.veventGetShowID(ev)
|
||||
|
@ -54,19 +42,59 @@ class RadioSchedule {
|
|||
}
|
||||
|
||||
/**
|
||||
* @todo To be implemented
|
||||
* @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)
|
||||
})
|
||||
ev_now.sort((e1, e2) => { return this.veventGetPriority(e1) - this.veventGetPriority(e2) })
|
||||
if(ev_now.length === 0)
|
||||
return null
|
||||
return ev_now[0]
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {RadioShow} if nothing is going on right now, `null` is returned
|
||||
*/
|
||||
getNowShow(now) {
|
||||
const ev = this.getNowEvent(now)
|
||||
return this.getShowByEvent(ev)
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {NextEvent} if there is none, `null` is returned
|
||||
*/
|
||||
getNextEvent(now) {
|
||||
// XXX
|
||||
var nowEvent = this.getNowEvent(now)
|
||||
let future_events = this.getEvents().filter((e) => { return e != nowEvent })
|
||||
.map((e) => {
|
||||
const vEvent = new ICAL.Event(e)
|
||||
return {event: e, time: getNext(vEvent, now)}
|
||||
})
|
||||
.filter((x) => { return x.time !== null && x.time !== undefined })
|
||||
// since ".sort()" is guaranteed to be stable, we can sort by priority, then by date, so that two events
|
||||
// starting at the same time will be sorted observing priority
|
||||
future_events.sort((x1, x2) => this.veventGetPriority(x1.event) - this.veventGetPriority(x2.event))
|
||||
future_events.sort((x, y) => x.time.toUnixTime() - y.time.toUnixTime())
|
||||
|
||||
if (future_events.length === 0) {
|
||||
return null
|
||||
}
|
||||
return future_events[0]
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo To be implemented
|
||||
* @returns {NextShow} if there is no next show, nothing will be returned
|
||||
* @returns {NextShow} if there is no next show, null will be returned
|
||||
*/
|
||||
getNextShow(now) {
|
||||
// XXX
|
||||
const next = this.getNextEvent(now)
|
||||
if (next === null) return null
|
||||
const ev = next.event
|
||||
const show = this.getShowByEvent(ev)
|
||||
return {show: show, time: next.time}
|
||||
}
|
||||
static veventGetSummary(vevent) {
|
||||
return vevent.getFirstProperty('summary').getFirstValue()
|
||||
|
@ -75,6 +103,24 @@ class RadioSchedule {
|
|||
return RadioSchedule.veventGetSummary(vevent) // XXX: X-Show-ID
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {integer} a normalized version of priority, easier to compare
|
||||
*/
|
||||
veventGetPriority(ev) {
|
||||
const prop = ev.getFirstProperty('priority')
|
||||
let prio;
|
||||
if (prop === null) {
|
||||
prio = null
|
||||
} else {
|
||||
prio = prop.getFirstValue()
|
||||
}
|
||||
if (prio === null || prio === 0) {
|
||||
prio = 100
|
||||
}
|
||||
return prio
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -85,7 +131,7 @@ function isNow(vEvent, now) {
|
|||
if (vEvent.isRecurring()) {
|
||||
return isNowRecurring(vEvent, now)
|
||||
}
|
||||
return (now < vEvent.endDate) && (now > event.startDate);
|
||||
return (now < vEvent.endDate) && (now > vEvent.startDate);
|
||||
}
|
||||
|
||||
|
||||
|
@ -105,6 +151,49 @@ function isNowRecurring(vEvent, now) {
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* @private
|
||||
* @param {external:ICAL~Component} vEvent a _recurring_ vEvent
|
||||
* @param {external:ICAL~Time} [now]
|
||||
* @return {external:ICAL~Time} first future occurrence of this event
|
||||
*/
|
||||
function getNext(vEvent, now) {
|
||||
if (now === undefined) {
|
||||
now = ICAL.Time.now()
|
||||
}
|
||||
if (vEvent.isRecurring()) {
|
||||
return getNextRecurring(vEvent, now)
|
||||
}
|
||||
if (vEvent.endDate > now) {
|
||||
const val = max(now, vEvent.startDate)
|
||||
return val
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/*
|
||||
* @private
|
||||
* @param {external:ICAL~Component} vEvent a _recurring_ vEvent
|
||||
* @param {external:ICAL~Time} now
|
||||
* @return {external:ICAL~Time} first future occurrence of this event
|
||||
*/
|
||||
function getNextRecurring(vEvent, now) {
|
||||
var expand = vEvent.iterator(vEvent.startDate)
|
||||
var next, next_end;
|
||||
|
||||
while ((next = expand.next())) {
|
||||
const start = next.clone()
|
||||
next_end = start.clone()
|
||||
next_end.addDuration(vEvent.duration)
|
||||
if (next_end <= now) {
|
||||
continue
|
||||
}
|
||||
return max(start, now)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
async function get(manifest) {
|
||||
if (manifest.scheduleURL) {
|
||||
let resp = null
|
||||
|
@ -151,6 +240,12 @@ module.exports = {
|
|||
* @property {external:ICAL~Time} time When it will start
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} NextEvent
|
||||
* @property {RadioShow} event 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
|
||||
|
|
|
@ -72,6 +72,35 @@ describe('examples/' + exampleName, () => {
|
|||
assert.equal(show.getFeed(), testFeed)
|
||||
})
|
||||
|
||||
it('monday at 7PM, "Entropia Massima" is next', async () => {
|
||||
const rm = await radiomanifest.get(url)
|
||||
const rs = rm.getSchedule()
|
||||
const now = new ICAL.Time({
|
||||
year: 2022,
|
||||
month: 1,
|
||||
day: 31,
|
||||
hour: 19,
|
||||
minute: 10,
|
||||
second: 0,
|
||||
isDate: false
|
||||
},
|
||||
);
|
||||
const vevent = rs.getNowEvent(now)
|
||||
assert.notEqual(vevent, null)
|
||||
const show = rs.getNowShow(now)
|
||||
assert.notEqual(show, null)
|
||||
assert.equal(show.getName(), 'Baraonda')
|
||||
|
||||
const next_event = rs.getNextEvent(now)
|
||||
assert.notEqual(next_event, null)
|
||||
assert.notEqual(next_event.event, null)
|
||||
const next_show = rs.getNextShow(now)
|
||||
assert.notEqual(next_show, null)
|
||||
assert.notEqual(next_show.show, null)
|
||||
assert.equal(next_show.show.getName(), testShowName)
|
||||
assert.equal(next_show.show.getFeed(), testFeed)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue