Internet radio browser GUI for music/video streams from various directory services.

βŒˆβŒ‹ βŽ‡ branch:  streamtuner2


Changes On Branch a1c4d0960a024d6e

Changes In Branch xiphjson Through [a1c4d0960a] Excluding Merge-Ins

This is equivalent to a diff from b9af78503d to a1c4d0960a

2014-05-04
02:15
Utilize caching interface on IO for Xiph.orgΒ΄s JSON API check-in: 4e7851c986 user: mario tags: xiphjson
2014-04-28
00:19
Adapted DMOZ retrieval to changed HTML check-in: a44d5a6e74 user: mario tags: trunk
2014-04-27
23:33
Experimental Xiph.org JSON API (doesn't work) check-in: a1c4d0960a user: mario tags: xiphjson
22:48
Fully replace ahttp.ajax with ahttp.get() wrapper check-in: b9af78503d user: mario tags: trunk
22:19
Python3 support back into trunk check-in: 9ecea4fb26 user: mario tags: trunk

Modified channels/xiph.py from [04e8c2b58d] to [59dd45edfc].

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
#
# api: streamtuner2
# title: Xiph.org
# description: Xiph/ICEcast radio directory
# version: 0.1
#
#
# 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.
#


#
#
#



# streamtuner2 modules
from config import conf
from mygtk import mygtk
import ahttp as http
from channels import *
from config import __print__, dbg


# python modules
import re
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
        homepage = "http://dir.xiph.org/"
        base_url = "http://dir.xiph.org/"

        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", [],           ]




|




<
<

>
>
|











>



|
|











|

|
>
|







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.2
#
#
# Xiph.org maintains the Ogg streaming standard and Vorbis audio compression
# format, amongst others. The ICEcast server is an alternative to 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



          
# I wonder what that is for                                             ---------------------------------------
class xiph (ChannelPlugin):

        # desc
        api = "streamtuner2"
        module = "xiph"
        title = "Xiph.org"
        version = 0.2
        homepage = "http://dir.xiph.org/"
        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
        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)




                
                #-- 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"))
                    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")),
                        "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)
                        "homepage": "",
                      })
                
            # filter out a single subtree
            else:
                rx = re.compile(self.filter.get(cat.lower(), cat.lower()))
                l = []







|
>
>
>
>



|
|
|


|
|
|
|
|
|
|
|

|







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
                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, "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": 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,
                        "homepage": "",
                      })
                
            # filter out a single subtree
            else:
                rx = re.compile(self.filter.get(cat.lower(), cat.lower()))
                l = []