1
0
Fork 0

jsdoc comments all over the place

This commit is contained in:
boyska 2022-01-30 14:01:38 +01:00
parent eadb0fa7b6
commit ba0a79cc49
4 changed files with 142 additions and 2 deletions

View file

@ -1,16 +1,28 @@
const ICAL = require('ical.js') const ICAL = require('ical.js')
const shows = require('./shows.js') const shows = require('./shows.js')
/**
* Represent a schedule (ie: a .ics file)
*/
class RadioSchedule { class RadioSchedule {
constructor (calendar, radio) { constructor (calendar, radio) {
this.calendar = calendar // ICAL.Calendar this.calendar = calendar // ICAL.Calendar
this.radio = radio // radiomanifest.Radio this.radio = radio // radiomanifest.Radio
} }
/**
* Get a list of all known {@link vEvent}s
*
* @returns {Array<external:ICAL~Component>}
* */
getEvents() { getEvents() {
return this.calendar.getAllSubcomponents('vevent'); return this.calendar.getAllSubcomponents('vevent');
} }
/**
* @returns {external:ICAL~Component} if nothing is going on right now, `null` is returned
*/
getNowEvent(now) { getNowEvent(now) {
var ev_now = this.getEvents().filter(function(vevent) { var ev_now = this.getEvents().filter(function(vevent) {
const ev = new ICAL.Event(vevent) const ev = new ICAL.Event(vevent)
@ -23,6 +35,9 @@ class RadioSchedule {
return ev_now[0] return ev_now[0]
} }
/**
* @returns {RadioShow} if nothing is going on right now, `null` is returned
*/
getNowShow(now) { getNowShow(now) {
var ev = this.getNowEvent(now) var ev = this.getNowEvent(now)
@ -38,10 +53,18 @@ class RadioSchedule {
return new shows.RadioShow(RadioSchedule.veventGetSummary(ev)) return new shows.RadioShow(RadioSchedule.veventGetSummary(ev))
} }
/**
* @todo To be implemented
*/
getNextEvent(now) { getNextEvent(now) {
// XXX // XXX
} }
/**
* @todo To be implemented
* @returns {NextShow} if there is no next show, nothing will be returned
*/
getNextShow(now) { getNextShow(now) {
// XXX // 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) { function parse(text) {
var jcalData = ICAL.parse(text); var jcalData = ICAL.parse(text);
var vcalendar = new ICAL.Component(jcalData); var vcalendar = new ICAL.Component(jcalData);
@ -115,3 +144,28 @@ module.exports = {
parse: parse, parse: parse,
RadioSchedule: RadioSchedule, 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
*/

View file

@ -11,6 +11,8 @@
"search": true, "search": true,
"collapse": true, "collapse": true,
"typedefs": false, "typedefs": false,
"private": false,
"navLevel": 0,
"removeQuotes": "none", "removeQuotes": "none",
"scripts": [] "scripts": []
} }

View file

@ -15,7 +15,17 @@ function getAttribute(el, attr, default_value) {
return default_value; return default_value;
} }
/**
* Represents everything we know about a radio. This includes, but is not limited to, radiomanifest.xml.
*/
class Radio { class Radio {
/**
* @param {Array} [sources] optional
* @param {string} [scheduleURL]
* @param {string} [showsURL]
* @param {string} [feed]
*/
constructor (sources, scheduleURL, showsURL, feed) { constructor (sources, scheduleURL, showsURL, feed) {
this.streaming = new RadioStreaming(sources) this.streaming = new RadioStreaming(sources)
this.scheduleURL = scheduleURL this.scheduleURL = scheduleURL
@ -26,31 +36,64 @@ class Radio {
this.schedule = null this.schedule = null
} }
/**
* @returns {RadioStreaming}
*/
getStreaming () { getStreaming () {
return this.streaming return this.streaming
} }
/**
* Change radio name
*/
setName (name) { setName (name) {
this.name = name this.name = name
} }
/**
*
* @returns {Array<RadioShow>}
*/
getShows() { getShows() {
return this.shows return this.shows
} }
/**
* @returns {RadioShow} The lookup is exact and case-sensitive. If no such show can be found, `null`
* is returned.
*/
getShowByName (showName) { getShowByName (showName) {
if (this.shows === undefined) return null if (this.shows === undefined) return null
return this.shows.find(s => s.name === showName) return this.shows.find(s => s.name === showName)
} }
/**
* @returns {RadioSchedule} If no schedule is present, `null` is returned.
*/
getSchedule () { getSchedule () {
return this.schedule 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) { getShowAtTime (now) {
if (this.schedule === undefined || this.schedule === null) return null if (this.schedule === undefined || this.schedule === null) return null
return this.getSchedule().getNowShow(now) 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) { static fromDOM (xml) {
const doc = xml.cloneNode(true) const doc = xml.cloneNode(true)
let res = doc.evaluate('/radio-manifest/streaming/source', doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null) 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 { class RadioStreaming {
constructor (sources) { constructor (sources) {
this.sources = sources.sort( 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() { getOptions() {
return this.sources.map(function (x) { return this.sources.map(function (x) {
return x.getAttribute('name') return x.getAttribute('name')
}) })
} }
// this is private
/**
* @private
*/
getPriority(element) { getPriority(element) {
return parseInt(getAttribute(element, 'priority', '1')) return parseInt(getAttribute(element, 'priority', '1'))
} }
// this is private /**
* @private
*/
getTopPrioritySources() { getTopPrioritySources() {
var topPriority = this.getPriority(this.sources[0]) var topPriority = this.getPriority(this.sources[0])
return this.sources.filter( return this.sources.filter(
(src) => parseInt(src.getAttribute('priority'), 10) === topPriority (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) { getSource(name) {
if (name === undefined) { if (name === undefined) {
return this.getTopPrioritySources()[0] return this.getTopPrioritySources()[0]
@ -126,6 +185,11 @@ class RadioStreaming {
return s.getAttribute('src') 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() { async pickURLs() {
var allSources = this.getTopPrioritySources() var allSources = this.getTopPrioritySources()
var allAudios = [] var allAudios = []
@ -136,12 +200,24 @@ class RadioStreaming {
} }
return allAudios return allAudios
} }
/**
* Just like {@link RadioStreaming#pickURLs}, but get a single URL
* @return {string}
*/
async pickURL() { async pickURL() {
var allAudios = await this.pickURLs() var allAudios = await this.pickURLs()
return allAudios[0] 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) { async function get (siteurl, options) {
let resp = await fetch(getManifestUrl(siteurl)) let resp = await fetch(getManifestUrl(siteurl))
let text = await resp.text() let text = await resp.text()

View file

@ -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 { class RadioShow {
constructor(name, description, website, feed, schedule, radio_calendar) { constructor(name, description, website, feed, schedule, radio_calendar) {
this.name = name this.name = name
@ -23,6 +27,10 @@ class RadioShow {
} }
/**
* @private
* @return {Array<RadioShow>}
*/
function parseRadioShows(xml) { function parseRadioShows(xml) {
const doc = xml.cloneNode(true) const doc = xml.cloneNode(true)
const bookmarks = doc.evaluate('//bookmark', doc, showsNamespaceResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null) const bookmarks = doc.evaluate('//bookmark', doc, showsNamespaceResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)