From ba0a79cc491bcf4439f0654918c199568a8da549 Mon Sep 17 00:00:00 2001 From: boyska Date: Sun, 30 Jan 2022 14:01:38 +0100 Subject: [PATCH] jsdoc comments all over the place --- calendar.js | 54 ++++++++++++++++++++++++++++++++ jsdoc.conf.json | 2 ++ radiomanifest.js | 80 ++++++++++++++++++++++++++++++++++++++++++++++-- shows.js | 8 +++++ 4 files changed, 142 insertions(+), 2 deletions(-) diff --git a/calendar.js b/calendar.js index 7e1163d..7b9b6e9 100644 --- a/calendar.js +++ b/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} + * */ 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 + */ diff --git a/jsdoc.conf.json b/jsdoc.conf.json index 5a55bba..e063683 100644 --- a/jsdoc.conf.json +++ b/jsdoc.conf.json @@ -11,6 +11,8 @@ "search": true, "collapse": true, "typedefs": false, + "private": false, + "navLevel": 0, "removeQuotes": "none", "scripts": [] } diff --git a/radiomanifest.js b/radiomanifest.js index 5b6a6b5..b3d2b33 100644 --- a/radiomanifest.js +++ b/radiomanifest.js @@ -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} + */ 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 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} + */ 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} + */ 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() diff --git a/shows.js b/shows.js index 461a282..3f174bc 100644 --- a/shows.js +++ b/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} + */ function parseRadioShows(xml) { const doc = xml.cloneNode(true) const bookmarks = doc.evaluate('//bookmark', doc, showsNamespaceResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)