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

⌈⌋ branch:  streamtuner2


Check-in [215234ddad]

Overview
Comment:Dead-end update for radiolist.net channel (only titles+homepages now).
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | trunk
Files: files | file ages | folders
SHA1:215234ddad5ad5cd814f9009d9aa86e8f626002b
User & Date: mario on 2019-07-16 07:48:37
Other Links: manifest | tags
Context
2019-07-16
07:48
Dead-end update for radiolist.net channel (only titles+homepages now). Leaf check-in: 215234ddad user: mario tags: trunk
07:47
fix for runtime error (dictionary changed size during iteration) on plugin alias: detection check-in: daf3f873bd user: mario tags: trunk
Changes

Modified contrib/radiolist.py from [1e251d9dec] to [eff6434142].

     1      1   # encoding: UTF-8
     2      2   # api: streamtuner2
     3      3   # title: radiolist.net
     4      4   # description: Station list by continent+country
     5      5   # url: http://radiolist.net/
     6         -# version: 0.4
            6  +# version: 0.5
     7      7   # type: channel
     8      8   # category: radio
     9         -# priority: extra
            9  +# priority: obsolete
    10     10   # png:
    11     11   #   iVBORw0KGgoAAAANSUhEUgAAABgAAAAYBAMAAAASWSDLAAAAFVBMVEVKb61qibyDnMegs9S6yeDV4O37/vyx66abAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMB
    12     12   #   AJqcGAAAAAd0SU1FB+ECDBAgLJqgZW4AAADoSURBVBjTNdBNj4MgEAbgqdLeZdo9C5NwFmo5Y7Wedauc1y/+/09YdLskkDwJmZl3IOxnON4A8frQhdc/7mG2cv3gx29X
    13     13   #   rdUfZuVHQ3JHEzZ7GSuNXxFV/FYYwryO6MOiZqEdnQPUC/fsXZaMuxa6MFfOVYN7kIWpHZClyJGLFjbbC617KaRUEJ4r4fU7IqNYrW5f2kgU5gZInG6MZ086eejcyIvO
    14     14   #   1KwoLayoJjqnuWO5giW8msxVmBQXD5PttSlRm8TG2fDNZS3rRO/opeSCMnPa82xSmNgkfRxJ5yZxlPrPDmLu+7GqX4lERq4G0UEyAAAAAElFTkSuQmCC
    15     15   # extraction-method: regex
    16     16   #
    17     17   # Radio station list grouped by continents and countries.
    18         -# Some categories return no results, because web players are
    19         -# filtered out.
           18  +#
           19  +# NO LONGER LISTS STREAMING URLS (~ 2019), thus isn't interesting
           20  +# enough anymore for extended support.
    20     21   
    21     22   
    22     23   import re
    23     24   import action
    24     25   import ahttp
    25     26   from config import *
    26     27   from channels import *
    27     28   
    28     29   
    29     30   # radiolist.net
    30     31   #
    31         -# · Groups stations by continents and countries. Where Europe seems to be the
    32         -#   main category (empty "" path), while U.S. is labeled "/world", and Canada
    33         -#   and Asia etc. again a subpath "/world/canada" even. The .catmap{} assigns
    34         -#   paths to titles.
           32  +# · Groups stations by continents and countries.
           33  +#
           34  +# · Only fetches titles/homepages henceforth.
    35     35   #
    36         -# · Playlist formats vary wildly. Therefore this module comes with a guessing
    37         -#   method (super crude) of its own.
    38         -#
    39         -# · The audio-format-from-URL guessing should be generalized out of here perhaps.
    40         -#
    41         -# · Each station is in a <tr>…</tr> block. Invidual regexps are used for field
    42         -#   extraction afterwards (instead of a block match).
    43         -#
    44         -# · Entries may contain more than one streaming url. Each accompanied by a
    45         -#   bitrate. → Therefore the .best_url() sorting method.
    46         -#
    47         -# · Later versions might of course use multi-urls again…
    48         -#
    49         -class radiolist (ChannelPlugin, action.heuristic_funcs):
           36  +class radiolist (ChannelPlugin):
    50     37   
    51     38       # module attributes
    52         -    listformat = "pls"
           39  +    listformat = "href"
    53     40       has_search = False
    54         -    categories = ["Europe", "America", "Canada", "Oceania", "Asia"]
    55         -    catmap = {"Europe":"", "America":"world", "Canada":"world/canada", "Oceania":"world/oceania", "Asia":"world/asia"}
           41  +    categories = ["Europe", "America", "Canada", "Australia"]
           42  +    catmap = {"Albania": "albania", "America": "us", "Andorra": "andorra", "Australia": "au", "Austria": "austria", "Belarus": "belarus", "Belgium": "belgium", "Bulgaria": "bulgaria", "Canada": "can", "Croatia": "croatia", "Denmark": "denmark", "Estonia": "estonia", "Europe": "", "Finland": "finland", "France": "france", "Germany": "germany", "Greece": "greece", "Hungary": "hungary", "Iceland": "iceland", "Ireland": "ireland", "Italy": "italy", "Latvia": "latvia", "Liechtenstein": "liechtenstein", "Lithuania": "lithuania", "Luxembourg": "luxembourg", "Macedonia": "macedonia", "Malta": "malta", "Moldova": "moldova", "Monaco": "monaco", "Montenegro": "montenegro", "Netherlands": "netherlands", "New Zealand": "nz", "Norway": "norway", "Poland": "poland", "Portugal": "portugal", "Romania": "romania", "Russia": "russia", "Serbia": "serbia", "Slovakia": "slovakia", "Slovenia": "slovenia", "South America": "sa", "Spain": "spain", "Sweden": "sweden", "Switzerland": "switzerland", "Ukraine": "ukraine"}
    56     43       titles = dict( genre="Genre", title="Station", playing="Location", bitrate="Bitrate", listeners=False )
    57     44   
    58     45       # just a static list for now
    59     46       def update_categories(self):
    60         -        self.catmap = {"Europe":"", "America":"world", "Canada":"world/canada", "Oceania":"world/oceania", "Asia":"world/asia"}
           47  +        self.catmap = {"Europe":"", "America":"us", "Canada":"ca", "Australia":"au", "New Zealand":"nz", "South America":"sa"}
    61     48           c = []#
    62     49           rx_links = re.compile(r"""
    63         -            <td(?:\sstyle="height:\s30px;")?><a\s+href="(?:http://www.radiolist.net)?/([\w/.-]+)">([\w\s-]+)</a>[^<]*</td>
    64         -        """, re.X)
    65         -        for title in ["Europe", "America", "Canada", "Oceania", "Asia"]:
           50  +            <li \s+ id="item[\d-]+"> \s+ <!--[^>]+-->
           51  +            \s+ <a\s+href="(?:https?://radiolist.net)?/((?:\w{2,3}/)?\w+)"
           52  +            .+? <h3[^>]*>\s*([\w\s-]+?)\s*<
           53  +        """, re.X|re.S)
           54  +        for title in self.catmap.keys():
    66     55               c.append(title)
    67     56               html = ahttp.get("http://www.radiolist.net/" + self.catmap[title])
    68     57               sub = []
    69     58               for p,t in re.findall(rx_links, html):
           59  +                log.I(p,t)
    70     60                   if t in ["Terms", "About Us", "Donation", "United States"]:
    71     61                       continue
    72     62                   sub.append(t)
    73     63                   self.catmap[t] = p
    74     64               c.append(sorted(sub))
    75     65           self.categories = c
           66  +        
           67  +    # extraction rules
           68  +    recipe = {
           69  +        "block": """<li\s+id="item-\d+-\d+">(.+?)</li>""",
           70  +        "split": None,
           71  +        "fields": {
           72  +            "title": 'data-item-title="(.+?)"',
           73  +            "url": 'data-item-link="(http.+?)"',
           74  +            "homepage": 'data-item-link="(.+?)"',
           75  +            "favicon": '<img[^>]+src="(.+?)"',
           76  +            "description": '<p\sclass="ca-sub">(.+?)</p>'
           77  +        }
           78  +    }
    76     79   
    77     80       # extract stream urls
    78     81       def update_streams(self, cat):
    79         -        rx_title = re.compile('<a\s+href="([^">]+)"[^>]+target="_blank"[^>]*>(.+?)</a>', re.I)
    80         -        rx_urls = re.compile('<a href="([^">]+)">(\d+)(?: Kbps)*</a>', re.I)
    81         -        rx_genre = re.compile('<td[^>]+>(\w*[^<>]*)</td>\s*<td[^>]+>(\w+[^<>]+)</td>\s*$', re.I)
    82     82           entries = []
    83     83           html = ahttp.get("http://radiolist.net/" + self.catmap[cat])
    84         -        for block in re.findall("<tr>(.+?)</tr>", html, re.S):
    85         -            ut = re.findall(rx_title, block)  # homepage+title
    86         -            uu = re.findall(rx_urls, block)   # urls+bitrates
    87         -            lg = re.findall(rx_genre, block)  # location+genre
    88         -            #print ut, uu, lg
    89         -            if ut and uu and lg:
    90         -                url, br = self.best_url(uu)
    91         -                entries.append(dict(
    92         -                    homepage = ut[0][0],
    93         -                    title = unhtml(ut[0][1]),
    94         -                    url = url,
    95         -                    bitrate = br,
    96         -                    format = self.mime_guess(url, "audio/mpeg"),
    97         -                    listformat = self.list_guess(url),
    98         -                    playing = lg[0][0],
    99         -                    genre = lg[0][1]
   100         -                ))
           84  +        for block in re.findall(self.recipe["block"], html, re.S):
           85  +            log.HTML(block)
           86  +            e = {"genre":"-", "playing":cat, "format":"text/html"}
           87  +            for id,rx in self.recipe["fields"].iteritems():
           88  +                uu = re.findall(rx, block)
           89  +                log.RX(id,rx,uu)
           90  +                if uu:
           91  +                    e[id] = unhtml(uu[0])
           92  +            if "url" in e and "title" in e:
           93  +                entries.append(e)
   101     94           # done    
   102     95           [log.DATA(e) for e in entries]
   103     96           return entries
   104     97   
   105         -    # pick highest rated URL from [(url,bitrate),…] tuples
   106         -    def best_url(self, urls):
   107         -        r = dict([(u, to_int(b)) for u,b in urls])  # {url: bitrate, …}
   108         -        best = sorted(r, key=r.get, reverse=True)
   109         -        return best[0], r[best[0]]
   110         -
   111     98