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

⌈⌋ ⎇ branch:  streamtuner2


Check-in [c2360471af]

Overview
Comment:updated SomeFM extraction and default stream list
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: c2360471af6a9a06c2bbb99bca5017b7b4b9a5df
User & Date: mario on 2020-05-11 19:11:25
Other Links: manifest | tags
Context
2020-05-11
19:11
Xiph simplified duplicate filter check-in: 77480a7e73 user: mario tags: trunk
19:11
updated SomeFM extraction and default stream list check-in: c2360471af user: mario tags: trunk
18:41
filtermusic: XML blob is gone, just web extraction now. check-in: eadb28d92c user: mario tags: trunk
Changes

Modified channels/somafm.py from [9c3d6cd3c5] to [10c266c14e].

1
2
3
4
5
6
7
8
9
10
11
12
# encoding: UTF-8
# api: streamtuner2
# title: SomaFM
# description: Alternative radio, entirely community sponsored and non-advertisey.
# version: 0.9
# type: channel
# category: radio
# url: http://somafm.com/
# config:
#   { name: somafm_bitrate, value: 64, type: select, select: "130=AAC → 128 kbit/s|64=AAC → 64 kbit/s|32=AAC → 32 kbit/s|0=MP3 → 128 kbit/s|56=MP3 → 56 kbit/s|24=MP3 → 24 kbit/s", description: "Most streams are accessible in different bitrates." }
# priority: extra
# extraction-method: static




|







1
2
3
4
5
6
7
8
9
10
11
12
# encoding: UTF-8
# api: streamtuner2
# title: SomaFM
# description: Alternative radio, entirely community sponsored and non-advertisey.
# version: 1.0
# type: channel
# category: radio
# url: http://somafm.com/
# config:
#   { name: somafm_bitrate, value: 64, type: select, select: "130=AAC → 128 kbit/s|64=AAC → 64 kbit/s|32=AAC → 32 kbit/s|0=MP3 → 128 kbit/s|56=MP3 → 56 kbit/s|24=MP3 → 24 kbit/s", description: "Most streams are accessible in different bitrates." }
# priority: extra
# extraction-method: static
38
39
40
41
42
43
44
45
46
47

48




49
50



51

52
53
54
55
56

57
58
59
60
61
62
63
64
65









66
67


68
69
70
71
72
73
74
75
76
77
78
79
80


81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
    # description
    has_search = False
    listformat = "pls"
    audioformat = "audio/aac"
    titles = dict(listeners=False, playing="Description")

    categories = ["listen", "support"]
    streams = {
    "listen": [
        {'genre': 'ambient', 'listeners': 2187, 'title': 'Drone Zone', 'url': 'http://somafm.com/dronezone64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/dronezone/', 'playing': 'Served best chilled, safe with most medications. Atmospheric textures with minimal beats.'},

        {'genre': 'alternative', 'listeners': 420, 'title': 'Indie Pop Rocks!', 'url': 'http://somafm.com/indiepop64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/indiepop/', 'playing': 'New and classic favorite indie pop tracks.'},




        {'genre': 'electronica', 'listeners': 380, 'title': 'Lush', 'url': 'http://somafm.com/lush64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/lush/', 'playing': 'Sensuous and mellow vocals, mostly female, with an electronic influence.'},
        {'genre': 'lounge', 'listeners': 377, 'title': 'Secret Agent', 'url': 'http://somafm.com/secretagent64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/secretagent/', 'playing': 'The soundtrack for your stylish, mysterious, dangerous life. For Spies and PIs too!'},



        {'genre': 'electronica', 'listeners': 375, 'title': 'Space Station Soma', 'url': 'http://somafm.com/spacestation64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/spacestation/', 'playing': 'Tune in, turn on, space out. Spaced-out ambient and mid-tempo electronica.'},

        {'genre': 'americana', 'listeners': 337, 'title': 'Boot Liquor', 'url': 'http://somafm.com/bootliquor64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/bootliquor/', 'playing': 'Americana Roots music for Cowhands, Cowpokes and Cowtippers'},
        {'genre': 'ambient', 'listeners': 165, 'title': 'Deep Space One', 'url': 'http://somafm.com/deepspaceone64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/deepspaceone/', 'playing': 'Deep ambient electronic, experimental and space music. A soundtrack for inner and outer space exploration.'},
        {'genre': 'electronica', 'listeners': 149, 'title': 'Beat Blender', 'url': 'http://somafm.com/beatblender64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/beatblender/', 'playing': 'A late night blend of deep-house and downtempo chill.'},
        {'genre': 'alternative', 'listeners': 148, 'title': 'PopTron', 'url': 'http://somafm.com/poptron64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/poptron/', 'playing': 'Electropop and indie dance rock with sparkle and pop.'},
        {'genre': 'world', 'listeners': 121, 'title': 'Suburbs of Goa', 'url': 'http://somafm.com/suburbsofgoa64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/suburbsofgoa/', 'playing': 'Desi-influenced Asian world beats and beyond.'},

        {'genre': 'lounge', 'listeners': 112, 'title': 'Illinois Street Lounge', 'url': 'http://somafm.com/illstreet64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/illstreet/', 'playing': 'Classic bachelor pad, playful exotica and vintage music of tomorrow.'},
        {'genre': 'jazz', 'listeners': 111, 'title': 'Sonic Universe', 'url': 'http://somafm.com/sonicuniverse64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/sonicuniverse/', 'playing': 'Transcending the world of jazz with eclectic, avant-garde takes on tradition.'},
        {'genre': 'alternative', 'listeners': 92, 'title': 'BAGeL Radio', 'url': 'http://somafm.com/bagel64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/bagel/', 'playing': 'What alternative rock radio should sound like.'},
        {'genre': 'electronica', 'listeners': 89, 'title': 'The Trip', 'url': 'http://somafm.com/thetrip64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/thetrip/', 'playing': 'Progressive house / trance. Tip top tunes.'},
        {'genre': 'electronica', 'listeners': 87, 'title': 'Dub Step Beyond', 'url': 'http://somafm.com/dubstep64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/dubstep/', 'playing': 'Dubstep, Dub and Deep Bass. May damage speakers at high volume.'},
        {'genre': 'electronica', 'listeners': 77, 'title': 'cliqhop idm', 'url': 'http://somafm.com/cliqhop64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/cliqhop/', 'playing': u"Blips'n'beeps backed mostly w/beats. Intelligent Dance Music."},
        {'genre': 'alternative', 'listeners': 51, 'title': 'Seven Inch Soul', 'url': 'http://somafm.com/7soul64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/7soul/', 'playing': 'Vintage soul tracks from the original 45 RPM vinyl.'},
        {'genre': 'eclectic', 'listeners': 38, 'title': 'Black Rock FM', 'url': 'http://somafm.com/brfm64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/brfm/', 'playing': 'From the Playa to the world, for the 2014 Burning Man festival.'},
        {'genre': 'eclectic', 'listeners': 30, 'title': 'Covers', 'url': 'http://somafm.com/covers64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/covers/', 'playing': u"Just covers. Songs you know by artists you don't. We've got you covered."},









        {'genre': 'experimental', 'listeners': 14, 'title': 'Earwaves', 'url': 'http://somafm.com/earwaves64.pls', 'bitrate': 64, 'homepage': 'http://somafm.com/earwaves/', 'playing': 'Spanning the history of electronic and experimental music from the early pioneers to the latest innovators.'}
    ],


    "support": [
        {'genre': 'faq', 'listeners': 6398, 'title': 'Commercial-free, listener supported radio station.', 'url': 'https://youtube.com/v/DAjSPgRPhzw', 'format': 'video/youtube', 'bitrate': 256, 'homepage': 'http://somafm.com/support/', 'playing': 'Unique among music stations, SomaFM depends on community donations to operate. PS: SomaFM Loves You!!'}
    ]}

    # All static
    def update_categories(self):
        pass

    # Just update entries with bitrate setting
    def update_streams(self, cat, search=None):
        if not cat in self.categories:
            return
        


        # Just reuse
        rows = self.streams[cat] # or self._real_parse()
        
        # Overwrite bitrate
        bitreal = int(conf.somafm_bitrate) or 128
        biturl = int(conf.somafm_bitrate) or ""
        for i,row in enumerate(rows):
            rows[i]["format"] = "audio/mp3" if bitreal in (128,56,24) else "audio/aac"
            rows[i]["bitrate"] = int(bitreal)
            rows[i]["url"] = re.sub("\d*\.pls$", "%s.pls" % biturl, row["url"])
        
        # Resend stream list
        return rows

    # Disabled at runtime.
    def _real_parse(self):
        html = ahttp.get("http://somafm.com/listen/")
        ls = re.findall(r"""
            Listeners:\s(\d+) .*?
            <!-- .*? \((\w+)\)\s*--> .*?
            <h3>(.+?)</h3> .*?
            <p.*?>(.+?)</p> .*?
            href="(http://somafm.com/.+?.pls)" .*?
        """, html, re.X|re.S)
        rows = [
           dict(genre=g, title=t, playing=p, url=u, listeners=int(l), bitrate=128, homepage=re.sub("\d*\.pls$", "/", u))
            for l,g,t,p,u in ls
        ]
        #log.DATA(rows)
        return rows
        







|
|
|
>
|
>
>
>
>
|
|
>
>
>
|
>
|
|
|
|
|
>
|
|
|
|
|
<
|
|
|
>
>
>
>
>
>
>
>
>
|
<
>
>












|
>
>
|
|
















|
|
|
|
|


|


|


38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

72
73
74
75
76
77
78
79
80
81
82
83
84

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
    # description
    has_search = False
    listformat = "pls"
    audioformat = "audio/aac"
    titles = dict(listeners=False, playing="Description")

    categories = ["listen", "support"]
    builtin = [
        {'genre': 'ambient/electronica', 'listeners': 3210, 'title': 'Groove Salad', 'url': 'http://somafm.com/groovesalad130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/groovesalad/', 'playing': 'A nicely chilled plate of ambient/downtempo beats and grooves.'},
        {'genre': 'ambient', 'listeners': 962, 'title': 'Drone Zone', 'url': 'http://somafm.com/dronezone130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/dronezone/', 'playing': 'Served best chilled, safe with most medications. Atmospheric textures with minimal beats.'},
        {'genre': 'electronica', 'listeners': 571, 'title': 'Space Station Soma', 'url': 'http://somafm.com/spacestation130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/spacestation/', 'playing': 'Tune in, turn on, space out. Spaced-out ambient and mid-tempo electronica.'},
        {'genre': 'alternative/rock', 'listeners': 548, 'title': 'Indie Pop Rocks!', 'url': 'http://somafm.com/indiepop130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/indiepop/', 'playing': 'New and classic favorite indie pop tracks.'},
        {'genre': 'ambient', 'listeners': 459, 'title': 'Deep Space One', 'url': 'http://somafm.com/deepspaceone130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/deepspaceone/', 'playing': 'Deep ambient electronic, experimental and space music. For inner and outer space exploration.'},
        {'genre': 'lounge', 'listeners': 445, 'title': 'Secret Agent', 'url': 'http://somafm.com/secretagent130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/secretagent/', 'playing': 'The soundtrack for your stylish, mysterious, dangerous life. For Spies and PIs too!'},
        {'genre': 'ambient/electronica', 'listeners': 415, 'title': 'Groove Salad Classic', 'url': 'http://somafm.com/gsclassic130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/gsclassic/', 'playing': 'The classic (early 2000s) version of a nicely chilled plate of ambient/downtempo beats and grooves.'},
        {'genre': 'alternative/electronica', 'listeners': 387, 'title': 'Underground 80s', 'url': 'http://somafm.com/u80s130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/u80s/', 'playing': 'Early 80s UK Synthpop and a bit of New Wave.'},
        {'genre': 'electronica', 'listeners': 366, 'title': 'Lush', 'url': 'http://somafm.com/lush130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/lush/', 'playing': 'Sensuous and mellow vocals, mostly female, with an electronic influence.'},
        {'genre': '70s/rock', 'listeners': 355, 'title': 'Left Coast 70s', 'url': 'http://somafm.com/seventies130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/seventies/', 'playing': 'Mellow album rock from the Seventies. Yacht not required.'},
        {'genre': 'electronica/specials', 'listeners': 246, 'title': 'DEF CON Radio', 'url': 'http://somafm.com/defcon130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/defcon/', 'playing': 'Music for Hacking. The DEF CON Year-Round Channel.'},
        {'genre': 'alternative/rock', 'listeners': 225, 'title': 'BAGeL Radio', 'url': 'http://somafm.com/bagel130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/bagel/', 'playing': 'What alternative rock radio should sound like. [explicit]'},
        {'genre': 'folk/alternative', 'listeners': 217, 'title': 'Folk Forward', 'url': 'http://somafm.com/folkfwd130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/folkfwd/', 'playing': 'Indie Folk, Alt-folk and the occasional folk classics.'},
        {'genre': 'electronica', 'listeners': 190, 'title': 'Beat Blender', 'url': 'http://somafm.com/beatblender130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/beatblender/', 'playing': 'A late night blend of deep-house and downtempo chill.'},
        {'genre': 'world', 'listeners': 156, 'title': 'Suburbs of Goa', 'url': 'http://somafm.com/suburbsofgoa130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/suburbsofgoa/', 'playing': 'Desi-influenced Asian world beats and beyond.'},
        {'genre': 'americana', 'listeners': 155, 'title': 'Boot Liquor', 'url': 'http://somafm.com/bootliquor130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/bootliquor/', 'playing': 'Americana Roots music for Cowhands, Cowpokes and Cowtippers'},
        {'genre': 'celtic/world', 'listeners': 155, 'title': 'ThistleRadio', 'url': 'http://somafm.com/thistle130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/thistle/', 'playing': 'Exploring music from Celtic roots and branches'},
        {'genre': 'electronica', 'listeners': 145, 'title': 'The Trip', 'url': 'http://somafm.com/thetrip130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/thetrip/', 'playing': 'Progressive house / trance. Tip top tunes.'},
        {'genre': 'alternative', 'listeners': 142, 'title': 'PopTron', 'url': 'http://somafm.com/poptron130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/poptron/', 'playing': 'Electropop and indie dance rock with sparkle and pop.'},
        {'genre': 'jazz', 'listeners': 130, 'title': 'Sonic Universe', 'url': 'http://somafm.com/sonicuniverse130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/sonicuniverse/', 'playing': 'Transcending the world of jazz with eclectic, avant-garde takes on tradition.'},
        {'genre': 'electronica/hiphop', 'listeners': 124, 'title': 'Fluid', 'url': 'http://somafm.com/fluid130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/fluid/', 'playing': 'Drown in the electronic sound of instrumental hiphop, future soul and liquid trap.'},
        {'genre': 'lounge', 'listeners': 108, 'title': 'Illinois Street Lounge', 'url': 'http://somafm.com/illstreet130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/illstreet/', 'playing': 'Classic bachelor pad, playful exotica and vintage music of tomorrow.'},
        {'genre': 'reggae', 'listeners': 84, 'title': 'Heavyweight Reggae', 'url': 'http://somafm.com/reggae130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/reggae/', 'playing': 'NEW! Reggae, Ska, Rocksteady classic and deep tracks.'},
        {'genre': 'electronica', 'listeners': 66, 'title': 'cliqhop idm', 'url': 'http://somafm.com/cliqhop130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/cliqhop/', 'playing': u"Blips'n'beeps backed mostly w/beats. Intelligent Dance Music."},
        {'genre': 'electronica/alternative', 'listeners': 64, 'title': 'Digitalis', 'url': 'http://somafm.com/digitalis130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/digitalis/', 'playing': 'Digitally affected analog rock to calm the agitated heart.'},
        {'genre': 'electronica', 'listeners': 64, 'title': 'Dub Step Beyond', 'url': 'http://somafm.com/dubstep130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/dubstep/', 'playing': 'Dubstep, Dub and Deep Bass. May damage speakers at high volume.'},

        {'genre': 'oldies', 'listeners': 58, 'title': 'Seven Inch Soul', 'url': 'http://somafm.com/7soul130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/7soul/', 'playing': 'Vintage soul tracks from the original 45 RPM vinyl.'},
        {'genre': 'eclectic', 'listeners': 47, 'title': 'Black Rock FM', 'url': 'http://somafm.com/brfm130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/brfm/', 'playing': 'From the Playa to the world, for the annual Burning Man festival.'},
        {'genre': 'eclectic', 'listeners': 42, 'title': 'Covers', 'url': 'http://somafm.com/covers130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/covers/', 'playing': u"Just covers. Songs you know by artists you don't. We've got you covered."},
        {'genre': 'ambient/electronica', 'listeners': 42, 'title': 'Mission Control', 'url': 'http://somafm.com/missioncontrol130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/missioncontrol/', 'playing': 'Celebrating NASA and Space Explorers everywhere.'},
        {'genre': 'metal', 'listeners': 32, 'title': 'Metal Detector', 'url': 'http://somafm.com/metal130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/metal/', 'playing': 'From black to doom, prog to sludge, thrash to post, stoner to crossover, punk to industrial.'},
        {'genre': 'ambient/news', 'listeners': 25, 'title': 'SF 10-33', 'url': 'http://somafm.com/sf1033130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/sf/', 'playing': 'Ambient music mixed with the sounds of San Francisco public safety radio traffic.'},
        {'genre': 'specials', 'listeners': 16, 'title': 'SomaFM Specials', 'url': 'http://somafm.com/specials130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/specials/', 'playing': 'Special, seasonal and experimental broadcasts'},
        {'genre': 'holiday', 'listeners': 10, 'title': 'Christmas Lounge', 'url': 'http://somafm.com/christmas130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/christmas/', 'playing': 'Chilled holiday grooves and classic winter lounge tracks. (Kid and Parent safe!)'},
        {'genre': 'holiday', 'listeners': 9, 'title': 'Christmas Rocks!', 'url': 'http://somafm.com/xmasrocks130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/xmasrocks/', 'playing': 'Have your self an indie/alternative holiday season!'},
        {'genre': 'specials', 'listeners': 9, 'title': 'SomaFM Live', 'url': 'http://somafm.com/live130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/live/', 'playing': 'Special Live Events and rebroadcasts of past live events'},
        {'genre': 'holiday', 'listeners': 7, 'title': u"Jolly Ol' Soul", 'url': 'http://somafm.com/jollysoul130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/jollysoul/', 'playing': 'Where we cut right to the soul of the season.'},
        {'genre': 'holiday', 'listeners': 4, 'title': 'Xmas in Frisko', 'url': 'http://somafm.com/xmasinfrisko130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/xmasinfrisko/', 'playing': u"SomaFM's wacky and eclectic holiday mix. Not for the easily offended."},
        {'genre': 'live/news', 'listeners': 3, 'title': 'SF Police Scanner', 'url': 'http://somafm.com/scanner130.pls', 'bitrate': 128, 'homepage': 'http://somafm.com/scanner/', 'playing': 'San Francisco Public Safety Scanner Feed'}

    ]
    streams = {
    "support": [
        {'genre': 'faq', 'listeners': 6398, 'title': 'Commercial-free, listener supported radio station.', 'url': 'https://youtube.com/v/DAjSPgRPhzw', 'format': 'video/youtube', 'bitrate': 256, 'homepage': 'http://somafm.com/support/', 'playing': 'Unique among music stations, SomaFM depends on community donations to operate. PS: SomaFM Loves You!!'}
    ]}

    # All static
    def update_categories(self):
        pass

    # Just update entries with bitrate setting
    def update_streams(self, cat, search=None):
        if not cat in self.categories:
            return
        elif cat == "listen":
            rows = self.builtin
           # or self._real_parse()
        else:
            rows = self.streams[cat]
        
        # Overwrite bitrate
        bitreal = int(conf.somafm_bitrate) or 128
        biturl = int(conf.somafm_bitrate) or ""
        for i,row in enumerate(rows):
            rows[i]["format"] = "audio/mp3" if bitreal in (128,56,24) else "audio/aac"
            rows[i]["bitrate"] = int(bitreal)
            rows[i]["url"] = re.sub("\d*\.pls$", "%s.pls" % biturl, row["url"])
        
        # Resend stream list
        return rows

    # Disabled at runtime.
    def _real_parse(self):
        html = ahttp.get("http://somafm.com/listen/")
        ls = re.findall(r"""
            Listeners: \s* (\d+) \s*--> \s*
            <!-- .*? \(([\w/]+)\) \s* --> .*?
            <h3>  (.+?)  </h3> \s+
            <p[^>]*> (.+?) </p> .*?
            href="(/\w+\.pls)"
        """, html, re.X|re.S)
        rows = [
           dict(genre=g, title=t, playing=p, url="http://somafm.com"+u, listeners=int(l), bitrate=128, homepage="http://somafm.com"+re.sub("\d*\.pls$", "/", u))
            for l,g,t,p,u in ls
        ]
        log.DATA(rows)
        return rows