apply prettier
This commit is contained in:
parent
dd96ce7b2a
commit
4e19b6304e
18 changed files with 902 additions and 852 deletions
1
.prettierignore
Normal file
1
.prettierignore
Normal file
|
@ -0,0 +1 @@
|
|||
libs/
|
|
@ -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`
|
||||
|
|
172
calendar.js
172
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<b) return b
|
||||
return a
|
||||
if (a < b) return b;
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represent a schedule (ie: a .ics file)
|
||||
*/
|
||||
class RadioSchedule {
|
||||
constructor (calendar, radio) {
|
||||
this.calendar = calendar // ICAL.Calendar
|
||||
this.radio = radio // radiomanifest.Radio
|
||||
constructor(calendar, radio) {
|
||||
this.calendar = calendar; // ICAL.Calendar
|
||||
this.radio = radio; // radiomanifest.Radio
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,136 +21,138 @@ class RadioSchedule {
|
|||
* @returns {Array<external:ICAL~Component>}
|
||||
* */
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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"],
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
566
radiomanifest.js
566
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<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,
|
||||
);
|
||||
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<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
|
||||
)
|
||||
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 <streaming> 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<string>}
|
||||
*/
|
||||
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<string>}
|
||||
*/
|
||||
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<string>}
|
||||
*/
|
||||
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<string>}
|
||||
*/
|
||||
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,
|
||||
},
|
||||
};
|
||||
|
|
132
shows.js
132
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<RadioShow>}
|
||||
*/
|
||||
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,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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", () => {});
|
||||
});
|
||||
});
|
||||
|
|
92
ui.js
92
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();
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue