diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..2fd4c3b --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +libs/ diff --git a/README.md b/README.md index c0abed1..1b7f669 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,5 @@ npm run test ## Supported spec - - [radiomanifest](https://radiomanifest.degenerazione.xyz/) - - [stream-meta v2](https://www.stream-meta.info/version_2_files.html): name of the radio is extracted from `/streaminfo.json` - +- [radiomanifest](https://radiomanifest.degenerazione.xyz/) +- [stream-meta v2](https://www.stream-meta.info/version_2_files.html): name of the radio is extracted from `/streaminfo.json` diff --git a/calendar.js b/calendar.js index 6e8f438..c23f3f1 100644 --- a/calendar.js +++ b/calendar.js @@ -1,18 +1,18 @@ -import ICAL from 'ical.js' -import shows from './shows' +import ICAL from "ical.js"; +import shows from "./shows"; function max(a, b) { - if (a} * */ getEvents() { - return this.calendar.getAllSubcomponents('vevent'); - + return this.calendar.getAllSubcomponents("vevent"); } /** * @returns {RadioShow} tries to get a matching show, or create a new one */ getShowByEvent(ev) { - if (ev === null) return null + if (ev === null) return null; if (this.radio !== undefined) { - const showid = RadioSchedule.veventGetShowID(ev) - var show = this.radio.getShowByName(showid) + const showid = RadioSchedule.veventGetShowID(ev); + var show = this.radio.getShowByName(showid); if (show === null || show === undefined) { - return new shows.RadioShow(RadioSchedule.veventGetSummary(ev)) + return new shows.RadioShow(RadioSchedule.veventGetSummary(ev)); } return show; } - return new shows.RadioShow(RadioSchedule.veventGetSummary(ev)) + return new shows.RadioShow(RadioSchedule.veventGetSummary(ev)); } /** * @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] + 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) + const ev = this.getNowEvent(now); + return this.getShowByEvent(ev); } /** * @returns {NextEvent} if there is none, `null` is returned */ getNextEvent(now) { - 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)} + var nowEvent = this.getNowEvent(now); + let future_events = this.getEvents() + .filter((e) => { + return e != nowEvent; }) - .filter((x) => { return x.time !== null && x.time !== undefined }) + .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()) + 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 null; } - return future_events[0] + return future_events[0]; } - /** * @returns {NextShow} if there is no next show, null will be returned */ getNextShow(now) { - 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} + 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() + return vevent.getFirstProperty("summary").getFirstValue(); } static veventGetShowID(vevent) { - return RadioSchedule.veventGetSummary(vevent) // XXX: X-Show-ID + 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') + const prop = ev.getFirstProperty("priority"); let prio; if (prop === null) { - prio = null + prio = null; } else { - prio = prop.getFirstValue() + prio = prop.getFirstValue(); } if (prio === null || prio === 0) { - prio = 100 + prio = 100; } - return prio + return prio; } - - } - function isNow(vEvent, now) { - if (now === undefined) { - now = ICAL.Time.now() - } - if (vEvent.isRecurring()) { - return isNowRecurring(vEvent, now) - } - return (now < vEvent.endDate) && (now > vEvent.startDate); + if (now === undefined) { + now = ICAL.Time.now(); + } + if (vEvent.isRecurring()) { + return isNowRecurring(vEvent, now); + } + return now < vEvent.endDate && now > vEvent.startDate; } - function isNowRecurring(vEvent, now) { - var expand = vEvent.iterator(vEvent.startDate) + var expand = vEvent.iterator(vEvent.startDate); var next, next_end; while ((next = expand.next())) { - next_end = next.clone() - next_end.addDuration(vEvent.duration) + next_end = next.clone(); + next_end.addDuration(vEvent.duration); if (next_end > now) { break; } } - return (now < next_end && now > next); + return now < next_end && now > next; } - /* * @private * @param {external:ICAL~Component} vEvent a _recurring_ vEvent @@ -159,16 +161,16 @@ function isNowRecurring(vEvent, now) { */ function getNext(vEvent, now) { if (now === undefined) { - now = ICAL.Time.now() + now = ICAL.Time.now(); } if (vEvent.isRecurring()) { - return getNextRecurring(vEvent, now) + return getNextRecurring(vEvent, now); } if (vEvent.endDate > now) { - const val = max(now, vEvent.startDate) - return val + const val = max(now, vEvent.startDate); + return val; } - return null + return null; } /* @@ -178,37 +180,36 @@ function getNext(vEvent, now) { * @return {external:ICAL~Time} first future occurrence of this event */ function getNextRecurring(vEvent, now) { - var expand = vEvent.iterator(vEvent.startDate) + 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) + const start = next.clone(); + next_end = start.clone(); + next_end.addDuration(vEvent.duration); if (next_end <= now) { - continue + continue; } - return max(start, now) + return max(start, now); } - return null + return null; } - async function get(manifest) { if (manifest.scheduleURL) { - let resp = null + let resp = null; try { - resp = await fetch(manifest.scheduleURL) + resp = await fetch(manifest.scheduleURL); } catch (e) { - true + true; } if (resp !== null) { try { - const text = await resp.text() - return parse(text) + const text = await resp.text(); + return parse(text); } catch (e) { - console.error('Error while parsing schedule', e) - throw e + console.error("Error while parsing schedule", e); + throw e; } } } @@ -224,15 +225,14 @@ function parse(text) { var jcalData = ICAL.parse(text); var vcalendar = new ICAL.Component(jcalData); - return new RadioSchedule(vcalendar) + return new RadioSchedule(vcalendar); } - export default { get: get, parse: parse, RadioSchedule: RadioSchedule, -} +}; /** * @typedef {Object} NextShow diff --git a/doc/home.md b/doc/home.md index 56ec63a..93e5889 100644 --- a/doc/home.md +++ b/doc/home.md @@ -2,5 +2,6 @@ Radiomanifest is a specification to make webradios express their offer in a stan library aiming at making the development of expressive webapps a breeze. You are strongly invited to start from: - - {@tutorial quickstart} tutorial - - {@link get} this is the first function you'll ever call, in 99% of user scenarios + +- {@tutorial quickstart} tutorial +- {@link get} this is the first function you'll ever call, in 99% of user scenarios diff --git a/doc/tutorials/quickstart.md b/doc/tutorials/quickstart.md index 2a86f3b..a36c0e4 100644 --- a/doc/tutorials/quickstart.md +++ b/doc/tutorials/quickstart.md @@ -1,9 +1,9 @@ Using radiomanifest is pretty simple. In an ideal usecase, you can easily do this: ```javascript -const radiomanifest = require('radiomanifest') -const radio = radiomanifest.get('http://myradio.com/') -console.log(radio.getName()) +const radiomanifest = require("radiomanifest"); +const radio = radiomanifest.get("http://myradio.com/"); +console.log(radio.getName()); ``` Now we have `radio`, a {@link Radio} object, which can be seen as the "center" of our data. From here, we can @@ -11,13 +11,13 @@ get more data. ## Streaming -The first thing we could want to do is just to *play* the radio. Let's use the {@link RadioStreaming#pickURLs +The first thing we could want to do is just to _play_ the radio. Let's use the {@link RadioStreaming#pickURLs RadioStreaming.pickURLs} method then ```javascript -var streaming = radio.getStreaming() -var urls = await streaming.pickURLs() -console.log(urls) +var streaming = radio.getStreaming(); +var urls = await streaming.pickURLs(); +console.log(urls); ``` and here we go! This is a list of URLs that the radio is indicating to us as their preferred ones. Why not a @@ -30,17 +30,18 @@ Our {@link Radio} keeps track of those, and for each show we can have useful met for more details. ```javascript -var shows = radio.getShows() -console.log(shows.map(s => s.getName())) +var shows = radio.getShows(); +console.log(shows.map((s) => s.getName())); ``` + ## Schedule ```javascript -const show = radio.getShowAtTime() +const show = radio.getShowAtTime(); if (show !== null) { - console.log(show.getName()) + console.log(show.getName()); } else { - console.log("Nothing special going on right now, sorry") + console.log("Nothing special going on right now, sorry"); } ``` @@ -51,4 +52,4 @@ now and {@link RadioSchedule#getNextShow what will go on next} ## Conclusions I hope this tutorial helped you get your feet wet. Hopefully, using radiomanifest you'd be able to create -great webapps that work on *any* webradio (well, any webradio that supports radiomanifest, at least). +great webapps that work on _any_ webradio (well, any webradio that supports radiomanifest, at least). diff --git a/karma.config.js b/karma.config.js index 53da838..77528cc 100644 --- a/karma.config.js +++ b/karma.config.js @@ -1,34 +1,30 @@ module.exports = function (config) { config.set({ - frameworks: ['mocha'], + frameworks: ["mocha"], // plugins: ['karma-webpack', 'karma-mocha', 'karma-chai-as-promised'], webpack: { // karma watches the test entry points // Do NOT specify the entry option // webpack watches dependencies - // webpack configuration }, preprocessors: { - 'test/**/*.js': ['webpack'], - 'radiomanifest.js': ['webpack'] + "test/**/*.js": ["webpack"], + "radiomanifest.js": ["webpack"], }, - files: [ - 'radiomanifest.js', - 'test/**/*.js' - ], - reporters: ['progress'], + files: ["radiomanifest.js", "test/**/*.js"], + reporters: ["progress"], port: 9876, // karma web server port colors: true, logLevel: config.LOG_INFO, - browsers: ['ChromeHeadless', 'FirefoxHeadless'], + browsers: ["ChromeHeadless", "FirefoxHeadless"], autoWatch: false, concurrency: Infinity, customLaunchers: { FirefoxHeadless: { - base: 'Firefox', - flags: ['-headless'] - } - } - }) -} + base: "Firefox", + flags: ["-headless"], + }, + }, + }); +}; diff --git a/radiomanifest.js b/radiomanifest.js index 125c954..66378cc 100644 --- a/radiomanifest.js +++ b/radiomanifest.js @@ -1,174 +1,174 @@ -import fetch from 'isomorphic-unfetch' -import shows from './shows.js' -import calendar from './calendar.js' +import fetch from "isomorphic-unfetch"; +import shows from "./shows.js"; +import calendar from "./calendar.js"; function getStreaminfoUrl(siteurl) { - return siteurl + "/streaminfo.json" // XXX: improve this logic + return siteurl + "/streaminfo.json"; // XXX: improve this logic } function getManifestUrl(siteurl) { - return siteurl + "/radiomanifest.xml" // XXX: improve this logic + return siteurl + "/radiomanifest.xml"; // XXX: improve this logic } function getAttribute(el, attr, default_value) { - if (el.hasAttribute(attr)) return el.getAttribute(attr) - return default_value + if (el.hasAttribute(attr)) return el.getAttribute(attr); + 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 - this.showsURL = showsURL - this.feed = feed - this.name = "" - this.description = "" - this.logo = null - this.shows = [] - this.schedule = null + /** + * @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; + this.showsURL = showsURL; + this.feed = feed; + this.name = ""; + this.description = ""; + this.logo = null; + this.shows = []; + this.schedule = null; + } + + /** + * @returns {RadioStreaming} + */ + getStreaming() { + return this.streaming; + } + + setName(name) { + this.name = name; + } + + /** + * The radio name, as inferred by stream-meta + * + * @returns {string} + */ + getName() { + return this.name; + } + + setDescription(desc) { + this.description = desc; + } + + /** + * The description of the radio, as inferred by stream-meta + * + * @returns {string} + */ + getDescription() { + return this.description; + } + + setLogo(logo) { + this.logo = logo; + } + + /** + * @returns {string} the URL of the logo, or `null` if not found + */ + getLogo() { + return this.logo; + } + + /** + * + * @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, + ); + const sources = []; + for (let i = 0; i < res.snapshotLength; i++) { + const src = res.snapshotItem(i); + + if (!src.hasAttribute("priority")) { + src.setAttribute("priority", "0"); + } else if (parseInt(src.getAttribute("priority"), 10) < 0) { + continue; + } + sources.push(src); } - /** - * @returns {RadioStreaming} - */ - getStreaming() { - return this.streaming + res = doc.evaluate("/radio-manifest/schedule", doc); + const scheduleEl = res.iterateNext(); + let scheduleURL = null; + if (scheduleEl !== null) { + scheduleURL = scheduleEl.getAttribute("src"); } - setName(name) { - this.name = name + res = xml.evaluate("/radio-manifest/shows", xml); + const showsEl = res.iterateNext(); + let showsURL = null; + if (showsEl !== null) { + showsURL = showsEl.getAttribute("src"); } - /** - * The radio name, as inferred by stream-meta - * - * @returns {string} - */ - getName() { - return this.name + res = xml.evaluate("/radio-manifest/feed", xml); + const feedEl = res.iterateNext(); + let feed = null; + if (feedEl !== null) { + feed = feedEl.getAttribute("src"); } - setDescription(desc) { - this.description = desc - } - - /** - * The description of the radio, as inferred by stream-meta - * - * @returns {string} - */ - getDescription() { - return this.description - } - - setLogo(logo) { - this.logo = logo - } - - /** - * @returns {string} the URL of the logo, or `null` if not found - */ - getLogo() { - return this.logo - } - - /** - * - * @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 - ) - const sources = [] - for (let i = 0; i < res.snapshotLength; i++) { - const src = res.snapshotItem(i) - - if (!src.hasAttribute("priority")) { - src.setAttribute("priority", "0") - } else if (parseInt(src.getAttribute("priority"), 10) < 0) { - continue - } - sources.push(src) - } - - res = doc.evaluate("/radio-manifest/schedule", doc) - const scheduleEl = res.iterateNext() - let scheduleURL = null - if (scheduleEl !== null) { - scheduleURL = scheduleEl.getAttribute("src") - } - - res = xml.evaluate("/radio-manifest/shows", xml) - const showsEl = res.iterateNext() - let showsURL = null - if (showsEl !== null) { - showsURL = showsEl.getAttribute("src") - } - - res = xml.evaluate("/radio-manifest/feed", xml) - const feedEl = res.iterateNext() - let feed = null - if (feedEl !== null) { - feed = feedEl.getAttribute("src") - } - - const manifest = new Radio(sources, scheduleURL, showsURL, feed) - return manifest - } + const manifest = new Radio(sources, scheduleURL, showsURL, feed); + return manifest; + } } /** @@ -176,71 +176,75 @@ class Radio { * This has probably been derived from the block in radiomanifest.xml */ class RadioStreaming { - constructor(sources) { - this.sources = sources.sort((a, b) => this.getPriority(a) < this.getPriority(a)) - } + constructor(sources) { + this.sources = sources.sort( + (a, b) => this.getPriority(a) < this.getPriority(a), + ); + } - /** - * 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") - }) - } + /** + * 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"); + }); + } - /** - * @private - */ - getPriority(element) { - return parseInt(getAttribute(element, "priority", "1")) - } - /** - * @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] - } - const s = this.sources.find(function (x) { - return x.getAttribute("name") === name - }) - if (s === undefined) return s - return s.getAttribute("src") + /** + * @private + */ + getPriority(element) { + return parseInt(getAttribute(element, "priority", "1")); + } + /** + * @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]; } + const s = this.sources.find(function (x) { + return x.getAttribute("name") === name; + }); + if (s === undefined) return s; + 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 = [] - for (let src of allSources) { - let url = src.getAttribute("src") - let resp = await fetch(url) - allAudios.unshift(...parseM3U(await resp.text())) - } - return allAudios + /** + * 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 = []; + for (let src of allSources) { + let url = src.getAttribute("src"); + let resp = await fetch(url); + allAudios.unshift(...parseM3U(await resp.text())); } + return allAudios; + } - /** - * Just like {@link RadioStreaming#pickURLs}, but get a single URL - * @return {string} - */ - async pickURL() { - var allAudios = await this.pickURLs() - return allAudios[0] - } + /** + * Just like {@link RadioStreaming#pickURLs}, but get a single URL + * @return {string} + */ + async pickURL() { + var allAudios = await this.pickURLs(); + return allAudios[0]; + } } /** @@ -251,87 +255,87 @@ class RadioStreaming { * @return {Radio} */ async function get(siteurl, options) { - let resp = await fetch(getManifestUrl(siteurl)) - let text = await resp.text() + let resp = await fetch(getManifestUrl(siteurl)); + let text = await resp.text(); - const parser = new DOMParser() - const dom = parser.parseFromString(text, "text/xml") - const manifest = Radio.fromDOM(dom) + const parser = new DOMParser(); + const dom = parser.parseFromString(text, "text/xml"); + const manifest = Radio.fromDOM(dom); + try { + manifest.shows = await shows.get(manifest); + } catch (e) { + 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)); + } catch (e) { + true; + } + if (resp !== null) { try { - manifest.shows = await shows.get(manifest) + text = await resp.text(); + + const data = JSON.parse(text); + const name = data["icy-name"]; + if (name !== undefined) { + manifest.setName(name); + } + const desc = data["icy-description"]; + if (desc !== undefined) { + manifest.setDescription(desc); + } + + const logo = data["icy-logo"]; + if (desc !== undefined) { + manifest.setLogo(logo); + } } catch (e) { - console.error("Error while fetching shows file", e) + if (e instanceof SyntaxError) { + true; + } else { + console.error("Error", e); + throw 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)) - } catch (e) { - true - } - if (resp !== null) { - try { - text = await resp.text() - - const data = JSON.parse(text) - const name = data["icy-name"] - if (name !== undefined) { - manifest.setName(name) - } - const desc = data["icy-description"] - if (desc !== undefined) { - manifest.setDescription(desc) - } - - const logo = data["icy-logo"] - if (desc !== undefined) { - manifest.setLogo(logo) - } - } catch (e) { - if (e instanceof SyntaxError) { - true - } else { - console.error("Error", e) - throw e - } - } - } - - return manifest + return manifest; } function parseM3U(body) { - return body.split("\n").filter(line => { - if (line.startsWith("#")) { - return false - } else { - try { - new URL(line) - return true - } catch { - return false - } - } - }) + return body.split("\n").filter((line) => { + if (line.startsWith("#")) { + return false; + } else { + try { + new URL(line); + return true; + } catch { + return false; + } + } + }); } export default { - get: get, - objs: { - Radio: Radio, - RadioStreaming: RadioStreaming - }, - parsers: { - M3U: parseM3U, - radioManifest: Radio.fromDOM, - shows: shows.parse - } -} + get: get, + objs: { + Radio: Radio, + RadioStreaming: RadioStreaming, + }, + parsers: { + M3U: parseM3U, + radioManifest: Radio.fromDOM, + shows: shows.parse, + }, +}; diff --git a/shows.js b/shows.js index c2a24df..c4fc96f 100644 --- a/shows.js +++ b/shows.js @@ -3,30 +3,28 @@ * schedule. */ - class RadioShow { constructor(name, description, website, feed, schedule, radio_calendar) { - this.name = name - this.description = description - this.website = website - this.feed = feed - this.schedule = schedule - this.radio_calendar = radio_calendar + this.name = name; + this.description = description; + this.website = website; + this.feed = feed; + this.schedule = schedule; + this.radio_calendar = radio_calendar; } getName() { - return this.name + return this.name; } getWebsite() { - return this.website + return this.website; } getFeed() { - return this.feed + return this.feed; } getSchedule() { - return this.schedule + return this.schedule; } - } /** @@ -34,51 +32,98 @@ class RadioShow { * @return {Array} */ function parseRadioShows(xml) { - const doc = xml.cloneNode(true) - const bookmarks = doc.evaluate('//bookmark', doc, showsNamespaceResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null) - const shows = [] + const doc = xml.cloneNode(true); + const bookmarks = doc.evaluate( + "//bookmark", + doc, + showsNamespaceResolver, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, + null, + ); + const shows = []; for (let i = 0; i < bookmarks.snapshotLength; i++) { - const bm = bookmarks.snapshotItem(i) + const bm = bookmarks.snapshotItem(i); - let name = doc.evaluate('./info/metadata/show:name', bm, showsNamespaceResolver, XPathResult.STRING_TYPE).stringValue - if (name === '') { - name = doc.evaluate('./title', bm, showsNamespaceResolver, XPathResult.STRING_TYPE).stringValue + let name = doc.evaluate( + "./info/metadata/show:name", + bm, + showsNamespaceResolver, + XPathResult.STRING_TYPE, + ).stringValue; + if (name === "") { + name = doc.evaluate( + "./title", + bm, + showsNamespaceResolver, + XPathResult.STRING_TYPE, + ).stringValue; } - let website = doc.evaluate('./info/metadata/show:website', bm, showsNamespaceResolver, XPathResult.STRING_TYPE).stringValue - if (website === '') { - website = bm.getAttribute('href') + let website = doc.evaluate( + "./info/metadata/show:website", + bm, + showsNamespaceResolver, + XPathResult.STRING_TYPE, + ).stringValue; + if (website === "") { + website = bm.getAttribute("href"); } - const feed = doc.evaluate('./info/metadata/show:feed', bm, showsNamespaceResolver, XPathResult.STRING_TYPE).stringValue - const schedule = doc.evaluate('./info/metadata/show:schedule', bm, showsNamespaceResolver, XPathResult.STRING_TYPE).stringValue - let description = doc.evaluate('./info/metadata/show:description', bm, showsNamespaceResolver, XPathResult.STRING_TYPE).stringValue - if (description === '') { - description = doc.evaluate('./desc', bm, showsNamespaceResolver, XPathResult.STRING_TYPE).stringValue + const feed = doc.evaluate( + "./info/metadata/show:feed", + bm, + showsNamespaceResolver, + XPathResult.STRING_TYPE, + ).stringValue; + const schedule = doc.evaluate( + "./info/metadata/show:schedule", + bm, + showsNamespaceResolver, + XPathResult.STRING_TYPE, + ).stringValue; + let description = doc.evaluate( + "./info/metadata/show:description", + bm, + showsNamespaceResolver, + XPathResult.STRING_TYPE, + ).stringValue; + if (description === "") { + description = doc.evaluate( + "./desc", + bm, + showsNamespaceResolver, + XPathResult.STRING_TYPE, + ).stringValue; } - const show = new RadioShow(name, description || null, website || null, feed || null, schedule || null) - shows.push(show) + const show = new RadioShow( + name, + description || null, + website || null, + feed || null, + schedule || null, + ); + shows.push(show); } - return shows + return shows; } async function getShows(manifest) { if (manifest.showsURL) { - let resp = null + let resp = null; try { - resp = await fetch(manifest.showsURL) + resp = await fetch(manifest.showsURL); } catch (e) { - true + true; } if (resp !== null) { try { - const text = await resp.text() - const parser = new DOMParser() - const showsDom = parser.parseFromString(text, 'text/xml') - return parseRadioShows(showsDom) + const text = await resp.text(); + const parser = new DOMParser(); + const showsDom = parser.parseFromString(text, "text/xml"); + return parseRadioShows(showsDom); } catch (e) { - console.error('Error while parsing shows file', e) - throw e + console.error("Error while parsing shows file", e); + throw e; } } } @@ -86,14 +131,13 @@ async function getShows(manifest) { function showsNamespaceResolver(prefix) { const prefixes = { - show: 'https://radiomanifest.degenerazione.xyz/shows/', - } - return prefixes[prefix] || null + show: "https://radiomanifest.degenerazione.xyz/shows/", + }; + return prefixes[prefix] || null; } - export default { get: getShows, parse: parseRadioShows, RadioShow: RadioShow, -} +}; diff --git a/test/404.test.js b/test/404.test.js index 1a4d504..f2726e4 100644 --- a/test/404.test.js +++ b/test/404.test.js @@ -1,17 +1,17 @@ -const radiomanifest = require('../radiomanifest.js') -const chai = require('chai') -chai.use(require('chai-as-promised')) -const assert = chai.assert +const radiomanifest = require("../radiomanifest.js"); +const chai = require("chai"); +chai.use(require("chai-as-promised")); +const assert = chai.assert; -const expect = chai.expect -const testName = 'idontexist' -const url = 'https://example.org/radiomanifest/examples/' + testName + '/' +const expect = chai.expect; +const testName = "idontexist"; +const url = "https://example.org/radiomanifest/examples/" + testName + "/"; -describe('radiomanifest.js supports example ' + testName, () => { - describe('Get empty radiomanifest', () => { - it('should throw', () => { - const p = radiomanifest.get(url) - expect(p).to.be.rejected - }) - }) -}) +describe("radiomanifest.js supports example " + testName, () => { + describe("Get empty radiomanifest", () => { + it("should throw", () => { + const p = radiomanifest.get(url); + expect(p).to.be.rejected; + }); + }); +}); diff --git a/test/example-empty.test.js b/test/example-empty.test.js index 95f0ccb..1e6aa5c 100644 --- a/test/example-empty.test.js +++ b/test/example-empty.test.js @@ -1,26 +1,29 @@ -const radiomanifest = require('../radiomanifest.js') -const chai = require('chai') -chai.use(require('chai-as-promised')) -const assert = chai.assert +const radiomanifest = require("../radiomanifest.js"); +const chai = require("chai"); +chai.use(require("chai-as-promised")); +const assert = chai.assert; -const expect = chai.expect -const tests = ['empty', 'empty-no-streaminfo', 'empty-invalid-streaminfo'] +const expect = chai.expect; +const tests = ["empty", "empty-no-streaminfo", "empty-invalid-streaminfo"]; for (const exampleName of tests) { - let url = 'https://radiomanifest.degenerazione.xyz/v0.2/examples/' + exampleName + '/' + let url = + "https://radiomanifest.degenerazione.xyz/v0.2/examples/" + + exampleName + + "/"; - describe('examples/' + exampleName, () => { - describe('Get radiomanifest', () => { - it('should return a Promise', () => { - const p = radiomanifest.get(url) - expect(p instanceof Promise).to.be.eql(true) - }) - }) + describe("examples/" + exampleName, () => { + describe("Get radiomanifest", () => { + it("should return a Promise", () => { + const p = radiomanifest.get(url); + expect(p instanceof Promise).to.be.eql(true); + }); + }); - describe('streaming', () => { - it('shoud return no streaming option', async () => { - const p = await radiomanifest.get(url) - expect(p.getStreaming().getOptions().length).to.be.equal(0) - }) - }) - }) + describe("streaming", () => { + it("shoud return no streaming option", async () => { + const p = await radiomanifest.get(url); + expect(p.getStreaming().getOptions().length).to.be.equal(0); + }); + }); + }); } diff --git a/test/example-full-ondarossa.test.js b/test/example-full-ondarossa.test.js index 7734c43..e964b3b 100644 --- a/test/example-full-ondarossa.test.js +++ b/test/example-full-ondarossa.test.js @@ -1,107 +1,104 @@ -const ICAL = require('ical.js') +const ICAL = require("ical.js"); -const radiomanifest = require('../radiomanifest.js') -const chai = require('chai') -chai.use(require('chai-as-promised')) -const assert = chai.assert +const radiomanifest = require("../radiomanifest.js"); +const chai = require("chai"); +chai.use(require("chai-as-promised")); +const assert = chai.assert; -const exampleName = 'full-ondarossa' -const expect = chai.expect -const url = 'https://radiomanifest.degenerazione.xyz/v0.2/examples/' + exampleName + '/' +const exampleName = "full-ondarossa"; +const expect = chai.expect; +const url = + "https://radiomanifest.degenerazione.xyz/v0.2/examples/" + exampleName + "/"; -const testShowName = 'Entropia Massima' -const testWebsite = "http://www.ondarossa.info/trx/entropia-massima" -const testFeed = 'http://www.ondarossa.info/podcast/by-trx-id/10497/podcast.xml' +const testShowName = "Entropia Massima"; +const testWebsite = "http://www.ondarossa.info/trx/entropia-massima"; +const testFeed = + "http://www.ondarossa.info/podcast/by-trx-id/10497/podcast.xml"; -describe('examples/' + exampleName, () => { - describe('shows', () => { - it('shoud find many shows', async () => { - const rm = await radiomanifest.get(url) - assert.isAbove(rm.getShows().length, 1) - }) +describe("examples/" + exampleName, () => { + describe("shows", () => { + it("shoud find many shows", async () => { + const rm = await radiomanifest.get(url); + assert.isAbove(rm.getShows().length, 1); + }); it('one of which is called "Entropia Massima"', async () => { - const rm = await radiomanifest.get(url) - const show = rm.getShowByName(testShowName) - assert.equal(show.getName(), testShowName) - assert.equal(show.getWebsite(), testWebsite) - assert.equal(show.getSchedule(), null) - }) - }) + const rm = await radiomanifest.get(url); + const show = rm.getShowByName(testShowName); + assert.equal(show.getName(), testShowName); + assert.equal(show.getWebsite(), testWebsite); + assert.equal(show.getSchedule(), null); + }); + }); - describe('schedule', () => { - it('should find many event', async () => { - const rm = await radiomanifest.get(url) - assert.isAbove(rm.getSchedule().getEvents().length, 1) - }) + describe("schedule", () => { + it("should find many event", async () => { + const rm = await radiomanifest.get(url); + assert.isAbove(rm.getSchedule().getEvents().length, 1); + }); - it('At 1AM, nothing is going on', async () => { - const rm = await radiomanifest.get(url) - const rs = rm.getSchedule() + it("At 1AM, nothing is going on", async () => { + const rm = await radiomanifest.get(url); + const rs = rm.getSchedule(); const now = new ICAL.Time({ - year: 2022, - month: 1, - day: 31, - hour: 1, - minute: 0, - second: 0, - isDate: false - }); - const ev = rs.getNowEvent(now) - assert.equal(ev, null) - }) + year: 2022, + month: 1, + day: 31, + hour: 1, + minute: 0, + second: 0, + isDate: false, + }); + const ev = rs.getNowEvent(now); + assert.equal(ev, null); + }); it('monday at 8PM, "Entropia Massima" is going on', async () => { - const rm = await radiomanifest.get(url) - const rs = rm.getSchedule() + const rm = await radiomanifest.get(url); + const rs = rm.getSchedule(); const now = new ICAL.Time({ - year: 2022, - month: 1, - day: 31, - hour: 20, - 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(), testShowName) + year: 2022, + month: 1, + day: 31, + hour: 20, + 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(), testShowName); - assert.equal(show.getFeed(), testFeed) - }) + 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 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) - }) - - }) - -}) + 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); + }); + }); +}); diff --git a/test/example-full-spore.test.js b/test/example-full-spore.test.js index 2f5c607..0d6b09e 100644 --- a/test/example-full-spore.test.js +++ b/test/example-full-spore.test.js @@ -1,79 +1,75 @@ -const ICAL = require('ical.js') +const ICAL = require("ical.js"); -const radiomanifest = require('../radiomanifest.js') -const chai = require('chai') -chai.use(require('chai-as-promised')) -const assert = chai.assert +const radiomanifest = require("../radiomanifest.js"); +const chai = require("chai"); +chai.use(require("chai-as-promised")); +const assert = chai.assert; -const exampleName = 'full-spore' -const expect = chai.expect -const url = 'https://radiomanifest.degenerazione.xyz/v0.2/examples/' + exampleName + '/' +const exampleName = "full-spore"; +const expect = chai.expect; +const url = + "https://radiomanifest.degenerazione.xyz/v0.2/examples/" + exampleName + "/"; -const testShowName = 'scaricomerci' -const testNextShowName = 'nastrone notte' +const testShowName = "scaricomerci"; +const testNextShowName = "nastrone notte"; -describe('examples/' + exampleName, () => { - describe('schedule.getNow', () => { - it('observes priority correctly', async () => { +describe("examples/" + exampleName, () => { + describe("schedule.getNow", () => { + it("observes priority correctly", async () => { // tuesday, half past midnight - const rm = await radiomanifest.get(url) - const rs = rm.getSchedule() - const now = new ICAL.Time({ - year: 2022, - month: 1, - day: 30, - hour: 2, - minute: 20, - second: 0, - isDate: false + const rm = await radiomanifest.get(url); + const rs = rm.getSchedule(); + const now = new ICAL.Time( + { + year: 2022, + month: 1, + day: 30, + hour: 2, + minute: 20, + second: 0, + isDate: false, }, - ICAL.Timezone.utcTimezone + ICAL.Timezone.utcTimezone, ); - const vevent = rs.getNowEvent(now) - assert.notEqual(vevent, null) - const show = rs.getNowShow(now) - assert.notEqual(show, null) - assert.equal(show.getName(), testShowName) - }) - }) - describe('schedule.getNext', () => { - - it('getNext observes priority correctly', async () => { + const vevent = rs.getNowEvent(now); + assert.notEqual(vevent, null); + const show = rs.getNowShow(now); + assert.notEqual(show, null); + assert.equal(show.getName(), testShowName); + }); + }); + describe("schedule.getNext", () => { + it("getNext observes priority correctly", async () => { // tuesday, half past midnight - const rm = await radiomanifest.get(url) - const rs = rm.getSchedule() + const rm = await radiomanifest.get(url); + const rs = rm.getSchedule(); const now = new ICAL.Time({ - year: 2022, - month: 2, - day: 2, - hour: 2, - minute: 20, - 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(), testShowName) - - const next_event = rs.getNextEvent(now) - assert.notEqual(next_event, null) - assert.isObject(next_event.event) - assert.isObject(next_event.time) - const next_show = rs.getNextShow(now) - assert.isObject(next_show) - assert.isObject(next_show.show) - assert.equal(next_show.show.getName(), testNextShowName) - - const time = next_event.time.toJSDate() - assert.equal(time.getHours(), 2) - assert.equal(time.getMinutes(), 58) - }) - - }) - -}) + year: 2022, + month: 2, + day: 2, + hour: 2, + minute: 20, + 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(), testShowName); + const next_event = rs.getNextEvent(now); + assert.notEqual(next_event, null); + assert.isObject(next_event.event); + assert.isObject(next_event.time); + const next_show = rs.getNextShow(now); + assert.isObject(next_show); + assert.isObject(next_show.show); + assert.equal(next_show.show.getName(), testNextShowName); + const time = next_event.time.toJSDate(); + assert.equal(time.getHours(), 2); + assert.equal(time.getMinutes(), 58); + }); + }); +}); diff --git a/test/example-onlyics.test.js b/test/example-onlyics.test.js index 83a4cbc..ff74dbb 100644 --- a/test/example-onlyics.test.js +++ b/test/example-onlyics.test.js @@ -1,60 +1,59 @@ -const ICAL = require('ical.js') +const ICAL = require("ical.js"); -const radiomanifest = require('../radiomanifest.js') -const chai = require('chai') -chai.use(require('chai-as-promised')) -const assert = chai.assert +const radiomanifest = require("../radiomanifest.js"); +const chai = require("chai"); +chai.use(require("chai-as-promised")); +const assert = chai.assert; -const exampleName = 'onlyics' -const expect = chai.expect -const url = 'https://radiomanifest.degenerazione.xyz/v0.2/examples/' + exampleName + '/' +const exampleName = "onlyics"; +const expect = chai.expect; +const url = + "https://radiomanifest.degenerazione.xyz/v0.2/examples/" + exampleName + "/"; -describe('examples/' + exampleName, () => { - describe('schedule', () => { - it('should find one event', async () => { - const rm = await radiomanifest.get(url) - assert.equal(rm.getSchedule().getEvents().length, 1) - }) - it('with a specific name', async () => { - const rm = await radiomanifest.get(url) - const ev = rm.getSchedule().getEvents()[0] - const summary = ev.getFirstProperty('summary').getFirstValue() - assert.equal(summary, 'JavaScript show') - }) +describe("examples/" + exampleName, () => { + describe("schedule", () => { + it("should find one event", async () => { + const rm = await radiomanifest.get(url); + assert.equal(rm.getSchedule().getEvents().length, 1); + }); + it("with a specific name", async () => { + const rm = await radiomanifest.get(url); + const ev = rm.getSchedule().getEvents()[0]; + const summary = ev.getFirstProperty("summary").getFirstValue(); + assert.equal(summary, "JavaScript show"); + }); - it('happens every monday at 6AM', async () => { - const rm = await radiomanifest.get(url) - const rs = rm.getSchedule() + it("happens every monday at 6AM", async () => { + const rm = await radiomanifest.get(url); + const rs = rm.getSchedule(); const now = new ICAL.Time({ - year: 2022, - month: 1, - day: 3, - hour: 6, - minute: 30, - second: 0, - isDate: false - }); - const show = rs.getNowShow(now) - assert.notEqual(show, null) - assert.equal(show.getName(), 'JavaScript show') - }) + year: 2022, + month: 1, + day: 3, + hour: 6, + minute: 30, + second: 0, + isDate: false, + }); + const show = rs.getNowShow(now); + assert.notEqual(show, null); + assert.equal(show.getName(), "JavaScript show"); + }); - it('doesnt happen at any other time than 6AM', async () => { - const rm = await radiomanifest.get(url) - const rs = rm.getSchedule() + it("doesnt happen at any other time than 6AM", async () => { + const rm = await radiomanifest.get(url); + const rs = rm.getSchedule(); const now = new ICAL.Time({ - year: 2022, - month: 1, - day: 3, - hour: 9, - minute: 0, - second: 0, - isDate: false - }); - const ev = rs.getNowEvent(now) - assert.equal(ev, null) - }) - }) -}) - - + year: 2022, + month: 1, + day: 3, + hour: 9, + minute: 0, + second: 0, + isDate: false, + }); + const ev = rs.getNowEvent(now); + assert.equal(ev, null); + }); + }); +}); diff --git a/test/example-source404.test.js b/test/example-source404.test.js index f138e5b..de58707 100644 --- a/test/example-source404.test.js +++ b/test/example-source404.test.js @@ -1,23 +1,27 @@ -const radiomanifest = require('../radiomanifest.js') -const chai = require('chai') -chai.use(require('chai-as-promised')) -const assert = chai.assert +const radiomanifest = require("../radiomanifest.js"); +const chai = require("chai"); +chai.use(require("chai-as-promised")); +const assert = chai.assert; -const exampleName = 'source404' -const expect = chai.expect -const url = 'https://radiomanifest.degenerazione.xyz/v0.2/examples/' + exampleName + '/' +const exampleName = "source404"; +const expect = chai.expect; +const url = + "https://radiomanifest.degenerazione.xyz/v0.2/examples/" + exampleName + "/"; -describe('examples/' + exampleName, () => { - describe('streaming', () => { - it('shoud return one streaming option', async () => { - const p = await radiomanifest.get(url) - assert.equal(p.getStreaming().getOptions().length, 1) - }) - it('... whose url is stream.m3u', async () => { - const p = await radiomanifest.get(url) - assert.equal(p.getStreaming().getOptions()[0], 'try to find me') - const name = p.getStreaming().getOptions()[0] - assert.equal(p.getStreaming().getSource(name), 'https://www.radioexample.org/stream.m3u') - }) - }) -}) +describe("examples/" + exampleName, () => { + describe("streaming", () => { + it("shoud return one streaming option", async () => { + const p = await radiomanifest.get(url); + assert.equal(p.getStreaming().getOptions().length, 1); + }); + it("... whose url is stream.m3u", async () => { + const p = await radiomanifest.get(url); + assert.equal(p.getStreaming().getOptions()[0], "try to find me"); + const name = p.getStreaming().getOptions()[0]; + assert.equal( + p.getStreaming().getSource(name), + "https://www.radioexample.org/stream.m3u", + ); + }); + }); +}); diff --git a/test/parser-M3U.test.js b/test/parser-M3U.test.js index 7823165..1f6bf54 100644 --- a/test/parser-M3U.test.js +++ b/test/parser-M3U.test.js @@ -1,53 +1,52 @@ -const parseM3U = require('../radiomanifest.js').parsers.M3U -const chai = require('chai') -chai.use(require('chai-as-promised')) -const assert = chai.assert +const parseM3U = require("../radiomanifest.js").parsers.M3U; +const chai = require("chai"); +chai.use(require("chai-as-promised")); +const assert = chai.assert; -const expect = chai.expect - -describe('parseM3U parses basic M3U', () => { - describe('empty M3U', () => { - it('should return empty list', () => { - var r = parseM3U("") - assert.equal(r.length, 0) - }) - it('should discard empty lines', () => { - var r = parseM3U("\n\n\n") - assert.equal(r.length, 0) - }) - }) - describe('just some lines', () => { - it('should return appropriate list', () => { - var r = parseM3U("http://foo") - assert.equal(r.length, 1) - assert.equal(r[0], "http://foo") - }) - it('should work with longer list', () => { - var r = parseM3U("http://foo\nhttp://baz\nhttp://bar\n") - assert.equal(r.length, 3) - assert.equal(r[0], "http://foo") - assert.equal(r[1], "http://baz") - assert.equal(r[2], "http://bar") - }) - it('should discard empty lines', () => { - var r = parseM3U("http://foo\n\nhttp://baz\nhttp://bar\n\n\n") - assert.equal(r.length, 3) - assert.equal(r[0], "http://foo") - assert.equal(r[1], "http://baz") - assert.equal(r[2], "http://bar") - }) - }) - describe('comments', () => { - it('comments should be ignored', () => { - var r = parseM3U("http://foo\n#asd") - assert.equal(r.length, 1) - assert.equal(r[0], "http://foo") - }) - it('mid-line hash is not a comment', () => { - var r = parseM3U("http://foo#asd") - assert.equal(r.length, 1) - assert.equal(r[0], "http://foo#asd") - }) - }) -}) +const expect = chai.expect; +describe("parseM3U parses basic M3U", () => { + describe("empty M3U", () => { + it("should return empty list", () => { + var r = parseM3U(""); + assert.equal(r.length, 0); + }); + it("should discard empty lines", () => { + var r = parseM3U("\n\n\n"); + assert.equal(r.length, 0); + }); + }); + describe("just some lines", () => { + it("should return appropriate list", () => { + var r = parseM3U("http://foo"); + assert.equal(r.length, 1); + assert.equal(r[0], "http://foo"); + }); + it("should work with longer list", () => { + var r = parseM3U("http://foo\nhttp://baz\nhttp://bar\n"); + assert.equal(r.length, 3); + assert.equal(r[0], "http://foo"); + assert.equal(r[1], "http://baz"); + assert.equal(r[2], "http://bar"); + }); + it("should discard empty lines", () => { + var r = parseM3U("http://foo\n\nhttp://baz\nhttp://bar\n\n\n"); + assert.equal(r.length, 3); + assert.equal(r[0], "http://foo"); + assert.equal(r[1], "http://baz"); + assert.equal(r[2], "http://bar"); + }); + }); + describe("comments", () => { + it("comments should be ignored", () => { + var r = parseM3U("http://foo\n#asd"); + assert.equal(r.length, 1); + assert.equal(r[0], "http://foo"); + }); + it("mid-line hash is not a comment", () => { + var r = parseM3U("http://foo#asd"); + assert.equal(r.length, 1); + assert.equal(r[0], "http://foo#asd"); + }); + }); +}); diff --git a/test/radiomanifest.test.js b/test/radiomanifest.test.js index 0d940dd..9b9f6a2 100644 --- a/test/radiomanifest.test.js +++ b/test/radiomanifest.test.js @@ -1,25 +1,23 @@ -const radiomanifest = require('../radiomanifest.js') -const chai = require('chai') -chai.use(require('chai-as-promised')) +const radiomanifest = require("../radiomanifest.js"); +const chai = require("chai"); +chai.use(require("chai-as-promised")); -const expect = chai.expect +const expect = chai.expect; -describe('radiomanifest.js', () => { - describe('Get a radiomanifest', () => { - it('should return a Promise', () => { - const p = radiomanifest.get('http://example.com/') - expect(p instanceof Promise).to.be.eql(true) - }) +describe("radiomanifest.js", () => { + describe("Get a radiomanifest", () => { + it("should return a Promise", () => { + const p = radiomanifest.get("http://example.com/"); + expect(p instanceof Promise).to.be.eql(true); + }); - it('should reject on invalid URL', () => { - const p = radiomanifest.get('http://example.com/') - expect(p).to.eventually.be.rejected - }) - }) + it("should reject on invalid URL", () => { + const p = radiomanifest.get("http://example.com/"); + expect(p).to.eventually.be.rejected; + }); + }); - describe('streaming', () => { - it('shoud return a valid streaming URL', () => { - - }) - }) -}) + describe("streaming", () => { + it("shoud return a valid streaming URL", () => {}); + }); +}); diff --git a/ui.js b/ui.js index 0410ab9..1d55ae4 100644 --- a/ui.js +++ b/ui.js @@ -1,55 +1,65 @@ // const radiomanifest = require('radiomanifest.js') function updateNow(radio) { - var box = document.querySelector('#now-info') - const show = radio.getSchedule().getNowShow() + var box = document.querySelector("#now-info"); + const show = radio.getSchedule().getNowShow(); try { - var showText = show.getName() + '\nfeed: ' + show.getWebsite() + var showText = show.getName() + "\nfeed: " + show.getWebsite(); } catch (e) { - var showText = String(show) + var showText = String(show); } - var text = radio.getName() + ' - ' + radio.getDescription() + ' -- ' + showText - box.textContent = text + var text = + radio.getName() + " - " + radio.getDescription() + " -- " + showText; + box.textContent = text; } -async function fai () { - const radio = await radiomanifest.get('https://radiomanifest.degenerazione.xyz/v0.2/examples/full-ondarossa') - console.log('radio?', radio) - const s = radio.getStreaming() - console.log(s.sources) - console.log(s.getOptions()) - console.log(s.getSource(s.getOptions()[0])) - console.log(s.getSource()) +async function fai() { + const radio = await radiomanifest.get( + "https://radiomanifest.degenerazione.xyz/v0.2/examples/full-ondarossa", + ); + console.log("radio?", radio); + const s = radio.getStreaming(); + console.log(s.sources); + console.log(s.getOptions()); + console.log(s.getSource(s.getOptions()[0])); + console.log(s.getSource()); - var audioEl = document.querySelector('#player audio') - var urls = await s.pickURLs() - console.log('audios', urls) - urls.forEach( function(url) { - var srcEl = document.createElement("source") - srcEl.setAttribute('src', url) - console.log('src', srcEl, url) - audioEl.appendChild(srcEl) - }) + var audioEl = document.querySelector("#player audio"); + var urls = await s.pickURLs(); + console.log("audios", urls); + urls.forEach(function (url) { + var srcEl = document.createElement("source"); + srcEl.setAttribute("src", url); + console.log("src", srcEl, url); + audioEl.appendChild(srcEl); + }); - const showList = document.querySelector('#shows > ul') + const showList = document.querySelector("#shows > ul"); for (const show of radio.getShows()) { - const item = document.createElement('li') - const link = document.createElement('a') - link.dataset['show'] = show.getName() - link.textContent = show.getName() - link.setAttribute('href', show.getWebsite()) - item.appendChild(link) - showList.appendChild(item) + const item = document.createElement("li"); + const link = document.createElement("a"); + link.dataset["show"] = show.getName(); + link.textContent = show.getName(); + link.setAttribute("href", show.getWebsite()); + item.appendChild(link); + showList.appendChild(item); } - showList.addEventListener('mouseenter', function (evt) { - if (evt.target.dataset['show'] === undefined) - return; - const info = document.querySelector('#show-info') - info.textContent = radio.getShowByName(evt.target.dataset['show']).getFeed() - }, true) + showList.addEventListener( + "mouseenter", + function (evt) { + if (evt.target.dataset["show"] === undefined) return; + const info = document.querySelector("#show-info"); + info.textContent = radio + .getShowByName(evt.target.dataset["show"]) + .getFeed(); + }, + true, + ); - updateNow(radio) - console.log(radio.getSchedule()) - setInterval(function() { updateNow(radio) }, 2000) + updateNow(radio); + console.log(radio.getSchedule()); + setInterval(function () { + updateNow(radio); + }, 2000); } -fai() +fai(); diff --git a/webpack.config.js b/webpack.config.js index e75ce1e..773fa4e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,27 +1,25 @@ -const path = require('path') +const path = require("path"); const variants = [ - { name: 'web', libtype: 'amd', target: 'web' }, - { name: 'node', libtype: 'amd', target: 'node' }, - { name: 'oldstyle', libtype: 'umd', target: 'web' } -] -module.exports = variants.map( - variant => { - const obj = { - name: variant.name, - mode: 'production', - entry: './radiomanifest.js', - target: [variant.target], - output: { - path: path.resolve(__dirname, 'dist'), - filename: 'radiomanifest-' + variant.name + '.bundle.js', - clean: false, - library: { - name: 'radiomanifest', - type: variant.libtype - } - } - } - return obj - } -) + { name: "web", libtype: "amd", target: "web" }, + { name: "node", libtype: "amd", target: "node" }, + { name: "oldstyle", libtype: "umd", target: "web" }, +]; +module.exports = variants.map((variant) => { + const obj = { + name: variant.name, + mode: "production", + entry: "./radiomanifest.js", + target: [variant.target], + output: { + path: path.resolve(__dirname, "dist"), + filename: "radiomanifest-" + variant.name + ".bundle.js", + clean: false, + library: { + name: "radiomanifest", + type: variant.libtype, + }, + }, + }; + return obj; +});