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

⌈⌋ ⎇ branch:  streamtuner2


xiph.py at [4e7851c986]

File channels/xiph.py artifact b29fdbb5ef part of check-in 4e7851c986


#
# 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_url = "http://api.include-once.org/xiph/cache.php"
        listformat = "url/http"
        config = [
           {"name":"xiph_min_bitrate", "value":64, "type":"int", "description":"minimum bitrate, filter anything below", "category":"filter"}
        ]

        # content
        categories = ["all", [],           ]
        current = ""
        default = "all"
        empty = None
        
        
        # prepare category names
        def __init__(self, parent=None):
            
            self.categories = ["all"]
            self.filter = {}
            for main in self.genres:
                if (type(main) == str):
                    id = main.split("|")
                    self.categories.append(id[0].title())
                    self.filter[id[0]] = main
                else:
                    l = []
                    for sub in main:
                        id = sub.split("|")
                        l.append(id[0].title())
                        self.filter[id[0]] = sub
                    self.categories.append(l)
            
            # GUI
            ChannelPlugin.__init__(self, parent)


        # just counts genre tokens, does not automatically create a category tree from it
        def update_categories(self):
            g = {}
            for row in self.streams["all"]:
                for t in row["genre"].split():
                    if g.has_key(t):
                        g[t] += 1
                    else:
                        g[t] = 0
            g = [ [v[1],v[0]] for v in g.items() ]
            g.sort()
            g.reverse()
            for row in g:
                pass
                __print__( dbg.DATA, '        "' + row[1] + '", #' + str(row[0]) )



        # downloads stream list from xiph.org for given category
        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.json_url)
                __print__(dbg.DATA, data)
                
                #-- 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": e["type"],
                        "bitrate": int(e["bitrate"]),
                        "genre": e["genre"],
                        "playing": e["current_song"],
                        "listeners": 0,
                        "max": 0,
                        "homepage": (e["homepage"] if ("homepage" in e) else ""),
                      })
                
            # filter out a single subtree
            else:
                rx = re.compile(self.filter.get(cat.lower(), cat.lower()))
                l = []
                for i,row in enumerate(self.streams["all"]):
                    if rx.search(row["genre"]):
                        l.append(row)
            
            # send back the list 
            return l




        genres = [
            "scanner", #442
            "rock", #305
            [
              "metal|heavy", #36
            ],
            "various", #286
            [
              "mixed", #96
            ],
            "pop", #221
            [
              "top40|top|40|top 40", #32
              "charts|hits", #20+4
              "80s", #68
              "90s", #20
              "disco", #17
              "remixes", #10
            ],
            "electronic|electro", #33
            [
              "dance", #161
              "house", #106
              "trance", #82
              "techno", #72
              "chillout", #16
              "lounge", #12
            ],
            "alternative", #68
            [
              "college", #32
              "student", #20
              "progressive", #20
            ],
            "classical|classic", #58+20
            "live", #57
            "jazz", #42
            [
              "blues", #19
            ],
            "talk|spoken|speak", #41
            [
              "news", #39
              "public", #12
              "info", #5
            ],
            "world|international", #25
            [
              "latin", #34
              "reggae", #12
              "indie", #12
              "folk", #9
              "schlager", #14
              "jungle", #13
              "country", #7
              "russian", #6
            ],
            "hip hop|hip|hop", #34
            [
               "oldschool", #10
               "rap",
            ],
            "ambient", #34
            "adult", #33
           ## "music", #32
            "oldies", #31
            [
              "60s", #2
              "70s", #17
            ],
            "religious", #4
            [
              "christian|bible", #14
            ],
            "rnb|r&b", #12
            [
              "soul", #11
              "funk", #24
              "urban", #11
            ],
            "other", #25
            [
              "deep", #14
              "soft", #12
              "minimal", #12
              "eclectic", #12
              "drum", #12
              "bass", #12
              "experimental", #11
              "hard", #10
              "funky", #10
              "downtempo", #10
              "slow", #9
              "break", #9
              "electronica", #8
              "dub", #8
              "retro", #7
              "punk", #7
              "psychedelic", #7
              "goa", #7
              "freeform", #7
              "c64", #7
              "breaks", #7
              "anime", #7
              "variety", #6
              "psytrance", #6
              "island", #6
              "downbeat", #6
              "underground", #5
              "newage", #5
              "gothic", #5
              "dnb", #5
              "club", #5
              "acid", #5
              "video", #4
              "trip", #4
              "pure", #4
              "industrial", #4
              "groove", #4
              "gospel", #4
              "gadanie", #4
              "french", #4
              "dark", #4
              "chill", #4
              "age", #4
              "wave", #3
              "vocal", #3
              "tech", #3
              "studio", #3
              "relax", #3
              "rave", #3
              "hardcore", #3
              "breakbeat", #3
              "avantgarde", #3
              "swing", #2
              "soundtrack", #2
              "salsa", #2
              "italian", #2
              "independant", #2
              "groovy", #2
              "european", #2
              "darkwave", #2
            ],
        ]
"""
[

{"tag_name":"various","tag_usage":785},
{"tag_name":"rock","tag_usage":691},
{"tag_name":"radio","tag_usage":589},
{"tag_name":"pop","tag_usage":578},
{"tag_name":"dance","tag_usage":370},
{"tag_name":"anime","tag_usage":339},
{"tag_name":"jpop","tag_usage":301},
{"tag_name":"jrock","tag_usage":299},
{"tag_name":"jhiphop","tag_usage":291},
{"tag_name":"jrap","tag_usage":291},
{"tag_name":"hits","tag_usage":246},
{"tag_name":"alternative","tag_usage":198},
{"tag_name":"house","tag_usage":197},
{"tag_name":"christian","tag_usage":190},
{"tag_name":"talk","tag_usage":186},
{"tag_name":"music","tag_usage":178},
{"tag_name":"misc","tag_usage":154},
{"tag_name":"jazz","tag_usage":127},
{"tag_name":"electro","tag_usage":119},
{"tag_name":"techno","tag_usage":113},
{"tag_name":"top40","tag_usage":110},
{"tag_name":"trance","tag_usage":110},
{"tag_name":"electronic","tag_usage":110},
{"tag_name":"oldies","tag_usage":104},
{"tag_name":"news","tag_usage":102},
{"tag_name":"80s","tag_usage":101},
{"tag_name":"la","tag_usage":99},
{"tag_name":"musica","tag_usage":92},
{"tag_name":"lounge","tag_usage":87},
{"tag_name":"metal","tag_usage":84},
{"tag_name":"hip","tag_usage":80},
{"tag_name":"country","tag_usage":80},
{"tag_name":"mixed","tag_usage":78},
{"tag_name":"rap","tag_usage":78},
{"tag_name":"classic","tag_usage":77},
{"tag_name":"indie","tag_usage":75},
{"tag_name":"hop","tag_usage":73},
{"tag_name":"promodj","tag_usage":71},
{"tag_name":"eclectic","tag_usage":65},
{"tag_name":"gospel","tag_usage":62},
{"tag_name":"ambient","tag_usage":60},
{"tag_name":"adult","tag_usage":58},
{"tag_name":"top","tag_usage":58},
{"tag_name":"disco","tag_usage":54},
{"tag_name":"live","tag_usage":54},
{"tag_name":"reggae","tag_usage":54},
{"tag_name":"musique","tag_usage":53},
{"tag_name":"classical","tag_usage":53},
{"tag_name":"college","tag_usage":53},
{"tag_name":"blues","tag_usage":51},
{"tag_name":"the","tag_usage":50},
{"tag_name":"world","tag_usage":50},
{"tag_name":"salsa","tag_usage":46},
{"tag_name":"contemporary","tag_usage":46},
{"tag_name":"folk","tag_usage":45},
{"tag_name":"and","tag_usage":45},
{"tag_name":"punk","tag_usage":44},
{"tag_name":"40","tag_usage":44},
{"tag_name":"soul","tag_usage":44},
{"tag_name":"hardcore","tag_usage":44},
{"tag_name":"funk","tag_usage":43},
{"tag_name":"urban","tag_usage":42},
{"tag_name":"club","tag_usage":41},
{"tag_name":"chillout","tag_usage":39},
{"tag_name":"90s","tag_usage":39},
{"tag_name":"unspecified","tag_usage":38},
{"tag_name":"dubstep","tag_usage":37},
{"tag_name":"hit","tag_usage":37},
{"tag_name":"webradio","tag_usage":36},
{"tag_name":"new","tag_usage":36},
{"tag_name":"latin","tag_usage":35},
{"tag_name":"game","tag_usage":35},
{"tag_name":"deep","tag_usage":34},
{"tag_name":"70s","tag_usage":33},
{"tag_name":"b","tag_usage":33},
{"tag_name":"international","tag_usage":33},
{"tag_name":"community","tag_usage":32},
{"tag_name":"progressive","tag_usage":32},
{"tag_name":"fun","tag_usage":31},
{"tag_name":"video","tag_usage":31},
{"tag_name":"hiphop","tag_usage":30},
{"tag_name":"musica","tag_usage":30},
{"tag_name":"electronica","tag_usage":30},
{"tag_name":"r","tag_usage":29},
{"tag_name":"bass","tag_usage":28},
{"tag_name":"jungle","tag_usage":27},
{"tag_name":"sports","tag_usage":27},
{"tag_name":"smooth","tag_usage":26},
{"tag_name":"scanner","tag_usage":26},
{"tag_name":"mix","tag_usage":26},
{"tag_name":"best","tag_usage":26},
{"tag_name":"60s","tag_usage":25},
{"tag_name":"soulful","tag_usage":25},
{"tag_name":"drum","tag_usage":25},
{"tag_name":"religious","tag_usage":25},
{"tag_name":"ska","tag_usage":25},
{"tag_name":"garage","tag_usage":24},
{"tag_name":"downtempo","tag_usage":24},
{"tag_name":"retro","tag_usage":23},
{"tag_name":"minimal","tag_usage":23},
]
"""