|
@@ -0,0 +1,121 @@
|
|
|
+function getStreaminfoUrl(siteurl) {
|
|
|
+ return siteurl + '/streaminfo.json'; // XXX: improve this logic
|
|
|
+}
|
|
|
+function getManifestUrl(siteurl) {
|
|
|
+ return siteurl + '/radiomanifest.xml'; // XXX: improve this logic
|
|
|
+}
|
|
|
+function parseRadioManifest(xml) {
|
|
|
+ var res = xml.evaluate('/radio-manifest/streaming/source', xml)
|
|
|
+ var sources = []
|
|
|
+ while(true) {
|
|
|
+ var src = res.iterateNext()
|
|
|
+ if(src === null) break;
|
|
|
+ if(!src.hasAttribute("priority")) {
|
|
|
+ src.setAttribute("priority", "0")
|
|
|
+ } else if(parseInt(src.getAttribute("priority"), 10) < 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ sources.push(src)
|
|
|
+ }
|
|
|
+ sources.sort(function cmp(a,b) {
|
|
|
+ return parseInt(a.getAttribute("priority", 10)) < parseInt(b.getAttribute("priority", 10));
|
|
|
+ })
|
|
|
+
|
|
|
+ res = xml.evaluate('/radio-manifest/schedule', xml)
|
|
|
+ var scheduleEl = res.iterateNext()
|
|
|
+ var schedule = null
|
|
|
+ if(scheduleEl !== null) {
|
|
|
+ schedule = scheduleEl.getAttribute("url")
|
|
|
+ }
|
|
|
+
|
|
|
+ res = xml.evaluate('/radio-manifest/shows', xml)
|
|
|
+ var showsEl = res.iterateNext()
|
|
|
+ var shows = null
|
|
|
+ if(showsEl !== null) {
|
|
|
+ shows = showsEl.getAttribute("src")
|
|
|
+ }
|
|
|
+
|
|
|
+ var manifest = new Radio(sources, schedule, shows)
|
|
|
+ return manifest
|
|
|
+}
|
|
|
+
|
|
|
+function Radio(sources, schedule, shows) {
|
|
|
+ this.streaming = new RadioStreaming(sources)
|
|
|
+ this.schedule = schedule
|
|
|
+ this.shows = shows
|
|
|
+ this.name = ""
|
|
|
+}
|
|
|
+
|
|
|
+Radio.prototype.getStreaming = function() {
|
|
|
+ return this.streaming
|
|
|
+}
|
|
|
+
|
|
|
+Radio.prototype.setName = function(name) {
|
|
|
+ this.name = name
|
|
|
+}
|
|
|
+
|
|
|
+function RadioStreaming(sources) {
|
|
|
+ this.sources = sources
|
|
|
+}
|
|
|
+
|
|
|
+RadioStreaming.prototype.getOptions = function() {
|
|
|
+ return this.sources.map(function(x) {
|
|
|
+ return x.getAttribute('name')
|
|
|
+ })
|
|
|
+}
|
|
|
+RadioStreaming.prototype.getSource = function (name) {
|
|
|
+ if(name === undefined) {
|
|
|
+
|
|
|
+ }
|
|
|
+ var s = this.sources.find(function(x) {
|
|
|
+ return x.getAttribute('name') === name
|
|
|
+ })
|
|
|
+ if(s === undefined) return s
|
|
|
+ return s.getAttribute('src')
|
|
|
+}
|
|
|
+
|
|
|
+async function get(siteurl, options) {
|
|
|
+ let resp = await fetch(getManifestUrl(siteurl));
|
|
|
+ let text = await resp.text()
|
|
|
+
|
|
|
+ var parser = new DOMParser();
|
|
|
+ var dom = parser.parseFromString(text, 'text/xml')
|
|
|
+ var manifest = parseRadioManifest(dom)
|
|
|
+
|
|
|
+ resp = null
|
|
|
+ try {
|
|
|
+ resp = await fetch(getStreaminfoUrl(siteurl));
|
|
|
+ text = await resp.text()
|
|
|
+
|
|
|
+ var data = JSON.parse(text)
|
|
|
+ var name = data["icy-name"]
|
|
|
+ if(name !== undefined) {
|
|
|
+ manifest.setName(name)
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ if(e instanceof TypeError && e.message.startsWith('NetworkError')) {
|
|
|
+ // the fetch has failed
|
|
|
+ true
|
|
|
+ } else if(e instanceof SyntaxError && e.message.startsWith('JSON.parse')) {
|
|
|
+ true
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ console.error('Error', e)
|
|
|
+ throw e
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // XXX: in base alle options fai fetch anche di altra roba
|
|
|
+ return manifest
|
|
|
+}
|
|
|
+
|
|
|
+function parseM3U(body) {
|
|
|
+ body.split("\n").filter((e) => {
|
|
|
+ if(e.startsWith("#")) {
|
|
|
+ return false
|
|
|
+ } else {
|
|
|
+ try { new URL(e); return true }
|
|
|
+ catch {return false}
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|