1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
-
+
-
-
+
+
-
+
+
-
-
+
+
-
+
-
-
+
+
+
|
#
# api: streamtuner2
# title: Xiph.org
# description: Xiph/ICEcast radio directory
# version: 0.1
# version: 0.2
#
#
# Xiph.org maintains the Ogg streaming standard and Vorbis audio compression
# format, amongst others. The ICEcast server is an alternative to SHOUTcast.
# But it turns out, that Xiph lists only MP3 streams, no OGG. And the directory
# is less encompassing than Shoutcast.
#
# It meanwhile provides a JSOL dump, which is faster to download and process.
# So we'll use that over the older yp.xml. (Sadly it also doesn't output
#
# homepage URLs, listeners, etc.)
#
#
# streamtuner2 modules
from config import conf
from mygtk import mygtk
import ahttp as http
from channels import *
from config import __print__, dbg
import json
# python modules
import re
from xml.sax.saxutils import unescape as entity_decode, escape as xmlentities
import xml.dom.minidom
#from xml.sax.saxutils import unescape as entity_decode, escape as xmlentities
#import xml.dom.minidom
# I wonder what that is for ---------------------------------------
class xiph (ChannelPlugin):
# desc
api = "streamtuner2"
module = "xiph"
title = "Xiph.org"
version = 0.1
version = 0.2
homepage = "http://dir.xiph.org/"
base_url = "http://dir.xiph.org/"
yp = "yp.xml"
base_url = "http://api.dir.xiph.org/"
json = "experimental/full"
old_yp = "yp.xml"
listformat = "url/http"
config = [
{"name":"xiph_min_bitrate", "value":64, "type":"int", "description":"minimum bitrate, filter anything below", "category":"filter"}
]
# content
categories = ["all", [], ]
|
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
-
+
+
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
|
def update_streams(self, cat, search=""):
# there is actually just a single category to download,
# all else are virtual
if (cat == "all"):
#-- get data
yp = http.get(self.base_url + self.yp)
data = http.get(self.base_url + self.json)
data = re.sub('":NaN,', '":0,', data) # the output is JSOL, not valid JSON
data = re.sub('[\\\\]"(?!,)', '\'', data) # and some invalid string escaping
data = re.sub('[\\\\]{1}', '', data) # and all other backslashes to be safe
data = data.decode("latin-1")
#-- extract
l = []
__print__( dbg.DATA, "xml.dom.minidom parses yp.xml" )
for entry in xml.dom.minidom.parseString(yp).getElementsByTagName("entry"):
bitrate = self.bitrate(self.x(entry, "bitrate"))
__print__( dbg.DATA, "processing dir.xiph.org/experimental/full JSON" )
for e in json.loads(data):
bitrate = e["bitrate"]
if conf.xiph_min_bitrate and bitrate and bitrate >= int(conf.xiph_min_bitrate):
l.append({
"title": str(self.x(entry, "server_name")),
"url": str(self.x(entry, "listen_url")),
"format": self.mime_fmt(str(self.x(entry, "server_type"))[6:]),
"bitrate": bitrate,
"channels": str(self.x(entry, "channels")),
"samplerate": str(self.x(entry, "samplerate")),
"genre": str(self.x(entry, "genre")),
"playing": str(self.x(entry, "current_song")),
"title": e["stream_name"],
"url": e["listen_url"],
"format": "audio/mpeg",
"bitrate": int(e["bitrate"]),
"channels": e["channels"],
"samplerate": e["samplerate"],
"genre": e["genre"],
"playing": e["current_song"],
"listeners": 0,
"max": 0, # this information is in the html view, but not in the yp.xml (seems pretty static, we might as well make it a built-in list)
"max": 0,
"homepage": "",
})
# filter out a single subtree
else:
rx = re.compile(self.filter.get(cat.lower(), cat.lower()))
l = []
|