forked from boyska/radiomanifest.js
jsdoc comments all over the place
This commit is contained in:
parent
eadb0fa7b6
commit
ba0a79cc49
4 changed files with 142 additions and 2 deletions
54
calendar.js
54
calendar.js
|
@ -1,16 +1,28 @@
|
|||
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)
|
||||
|
@ -23,6 +35,9 @@ class RadioSchedule {
|
|||
return ev_now[0]
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {RadioShow} if nothing is going on right now, `null` is returned
|
||||
*/
|
||||
getNowShow(now) {
|
||||
var ev = this.getNowEvent(now)
|
||||
|
||||
|
@ -38,10 +53,18 @@ class RadioSchedule {
|
|||
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
|
||||
}
|
||||
|
@ -102,6 +125,12 @@ async function get(manifest) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
@ -115,3 +144,28 @@ module.exports = {
|
|||
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
|
||||
*/
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
"search": true,
|
||||
"collapse": true,
|
||||
"typedefs": false,
|
||||
"private": false,
|
||||
"navLevel": 0,
|
||||
"removeQuotes": "none",
|
||||
"scripts": []
|
||||
}
|
||||
|
|
|
@ -15,7 +15,17 @@ function getAttribute(el, attr, default_value) {
|
|||
return default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents everything we know about a radio. This includes, but is not limited to, radiomanifest.xml.
|
||||
*/
|
||||
class Radio {
|
||||
|
||||
/**
|
||||
* @param {Array} [sources] optional
|
||||
* @param {string} [scheduleURL]
|
||||
* @param {string} [showsURL]
|
||||
* @param {string} [feed]
|
||||
*/
|
||||
constructor (sources, scheduleURL, showsURL, feed) {
|
||||
this.streaming = new RadioStreaming(sources)
|
||||
this.scheduleURL = scheduleURL
|
||||
|
@ -26,31 +36,64 @@ class Radio {
|
|||
this.schedule = null
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {RadioStreaming}
|
||||
*/
|
||||
getStreaming () {
|
||||
return this.streaming
|
||||
}
|
||||
|
||||
/**
|
||||
* Change radio name
|
||||
*/
|
||||
setName (name) {
|
||||
this.name = name
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Array<RadioShow>}
|
||||
*/
|
||||
getShows() {
|
||||
return this.shows
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {RadioShow} The lookup is exact and case-sensitive. If no such show can be found, `null`
|
||||
* is returned.
|
||||
*/
|
||||
getShowByName (showName) {
|
||||
if (this.shows === undefined) return null
|
||||
return this.shows.find(s => s.name === showName)
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {RadioSchedule} If no schedule is present, `null` is returned.
|
||||
*/
|
||||
getSchedule () {
|
||||
return this.schedule
|
||||
}
|
||||
|
||||
/**
|
||||
* Find if a show is running at the given moment. If there's none, `null` is returned.
|
||||
* If possible, a complete {@link RadioShow} including full informations (from shows.xml) is returned.
|
||||
* If, instead, we know from the `schedule` that there must be a show, but have no additional detail, a
|
||||
* {@link RadioShow} object will be created on the fly.
|
||||
*
|
||||
* @param {ICAL.Time} [now] If omitted, the current time is used.
|
||||
* @returns {RadioShow} If we don't know what's going on at the given moment, `null` is returned.
|
||||
*/
|
||||
getShowAtTime (now) {
|
||||
if (this.schedule === undefined || this.schedule === null) return null
|
||||
return this.getSchedule().getNowShow(now)
|
||||
}
|
||||
|
||||
/**
|
||||
* This static method can create a Radio object from a valid radiomanifest.xml file
|
||||
*
|
||||
* @param xml An already parsed xml block
|
||||
* @returns {Radio}
|
||||
*/
|
||||
static fromDOM (xml) {
|
||||
const doc = xml.cloneNode(true)
|
||||
let res = doc.evaluate('/radio-manifest/streaming/source', doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
|
||||
|
@ -92,6 +135,10 @@ class Radio {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the streaming capabilities of a radio.
|
||||
* This has probably been derived from the <streaming> block in radiomanifest.xml
|
||||
*/
|
||||
class RadioStreaming {
|
||||
constructor (sources) {
|
||||
this.sources = sources.sort(
|
||||
|
@ -99,22 +146,34 @@ class RadioStreaming {
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of possible options that are provided to the user
|
||||
* @returns {Array<string>}
|
||||
*/
|
||||
getOptions() {
|
||||
return this.sources.map(function (x) {
|
||||
return x.getAttribute('name')
|
||||
})
|
||||
}
|
||||
// this is private
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getPriority(element) {
|
||||
return parseInt(getAttribute(element, 'priority', '1'))
|
||||
}
|
||||
// this is private
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getTopPrioritySources() {
|
||||
var topPriority = this.getPriority(this.sources[0])
|
||||
return this.sources.filter(
|
||||
(src) => parseInt(src.getAttribute('priority'), 10) === topPriority
|
||||
)
|
||||
}
|
||||
/**
|
||||
* @return {string} url of the source. Note that this is much probably a playlist, in M3U format
|
||||
*/
|
||||
getSource(name) {
|
||||
if (name === undefined) {
|
||||
return this.getTopPrioritySources()[0]
|
||||
|
@ -126,6 +185,11 @@ class RadioStreaming {
|
|||
return s.getAttribute('src')
|
||||
}
|
||||
|
||||
/**
|
||||
* This is your go-to function whenever you need a list of URLs to play.
|
||||
* They will be picked honoring priorities, and expanding the playlist source
|
||||
* @return {Array<string>}
|
||||
*/
|
||||
async pickURLs() {
|
||||
var allSources = this.getTopPrioritySources()
|
||||
var allAudios = []
|
||||
|
@ -136,12 +200,24 @@ class RadioStreaming {
|
|||
}
|
||||
return allAudios
|
||||
}
|
||||
|
||||
/**
|
||||
* Just like {@link RadioStreaming#pickURLs}, but get a single URL
|
||||
* @return {string}
|
||||
*/
|
||||
async pickURL() {
|
||||
var allAudios = await this.pickURLs()
|
||||
return allAudios[0]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create everything you need - **you should start from here**
|
||||
*
|
||||
* @param {string} siteurl URL of website you want to load
|
||||
* @param {Object} options options. Currenly unused
|
||||
* @return {Radio}
|
||||
*/
|
||||
async function get (siteurl, options) {
|
||||
let resp = await fetch(getManifestUrl(siteurl))
|
||||
let text = await resp.text()
|
||||
|
|
8
shows.js
8
shows.js
|
@ -1,3 +1,7 @@
|
|||
/**
|
||||
* Represents a single show. This show could be defined in the shows.xml file, or could be inferred from the
|
||||
* schedule.
|
||||
*/
|
||||
class RadioShow {
|
||||
constructor(name, description, website, feed, schedule, radio_calendar) {
|
||||
this.name = name
|
||||
|
@ -23,6 +27,10 @@ class RadioShow {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @return {Array<RadioShow>}
|
||||
*/
|
||||
function parseRadioShows(xml) {
|
||||
const doc = xml.cloneNode(true)
|
||||
const bookmarks = doc.evaluate('//bookmark', doc, showsNamespaceResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
|
||||
|
|
Loading…
Reference in a new issue