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

⌈⌋ ⎇ branch:  streamtuner2


Check-in [7c085d54f9]

Overview
Comment:Add windows media guide radio lists (ASX). Fixed action module to extract with case-insensitve matches for that playlist format. (It's not really XML after all.)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 7c085d54f9f21a9e0955e9cb0da6dfd3de894196
User & Date: mario on 2015-05-07 01:17:42
Other Links: manifest | tags
Context
2015-05-07
01:20
Add description: line. check-in: 749946913c user: mario tags: trunk
01:17
Add windows media guide radio lists (ASX). Fixed action module to extract with case-insensitve matches for that playlist format. (It's not really XML after all.) check-in: 7c085d54f9 user: mario tags: trunk
01:16
Another radio station directory: listenlive.eu check-in: b0e9e031d9 user: mario tags: trunk
Changes

Modified action.py from [d816e37503] to [02cf432a5f].

95
96
97
98
99
100
101
102

103
104
105
106
107
108
109
95
96
97
98
99
100
101

102
103
104
105
106
107
108
109







-
+







)

# Playlist format content probing (assert type)
playlist_content_map = [
   ("pls",  r""" (?i)\[playlist\].*NumberOfEntries """),
   ("xspf", r""" <\?xml .* <playlist .* ((?i)http://xspf\.org)/ns/0/ """),
   ("m3u",  r""" ^ \s* \#(EXT)?M3U """),
   ("asx" , r""" <asx\b """),
   ("asx" , r""" (?i) <asx\b """),
   ("smil", r""" <smil[^>]*> .* <seq> """),
   ("html", r""" (?i)<(audio|video)\b[^>]+\bsrc\s*=\s*["']?https?:// """),
   ("wpl",  r""" <\?wpl \s+ version="1\.0" \s* \?> """),
   ("b4s",  r""" <WinampXML> """),   # http://gonze.com/playlists/playlist-format-survey.html
   ("jspf", r""" ^ \s* \{ \s* "playlist": \s* \{ """),
   ("asf",  r""" ^ \[Reference\] .*? ^Ref\d+= """),
   ("url",  r""" ^ \[InternetShortcut\] .*? ^URL= """),
362
363
364
365
366
367
368
369

370
371
372
373
374
375
376
362
363
364
365
366
367
368

369
370
371
372
373
374
375
376







-
+







        # regex scheme
        rules = self.extr_urls[fmt]
        rows = []
        fields = [name for name in ("url", "title", "homepage", "genre", "playing") if rules.get(name)]

        # Block-wise processing
        if rules.get("split"):
            for part_src in re.split(rules["split"], self.src, re.X):
            for part_src in re.split(rules["split"], self.src, 0, re.X):
                row = {}
                for name in fields:
                    val = self.field(name, rules, part_src)
                    if val and val[0]:
                        row[name] = val[0]
                if row.get("url"):
                    rows.append(row)
435
436
437
438
439
440
441
442
443
444



445
446
447
448
449
450
451
435
436
437
438
439
440
441



442
443
444
445
446
447
448
449
450
451







-
-
-
+
+
+







            url   = r"(?x) <location> (\w+://[^<>\s]+) </location> ",
            title = r"(?x) <title> ([^<>]+) ",
            homepage = r"(?x) <info> ([^<>]+) ",
            playing  = r"(?x) <annotation> ([^<>]+) ",
            unesc = "xml",
        ),
        "asx": dict(
            split = r" (?x) <entry[^>]*> ",
            url   = r" (?x) <ref \b[^>]+\b href \s*=\s* [\'\"] (\w+://[^\s\"\']+) [\'\"] ",
            title = r"(?x) <title> ([^<>]+) ",
            split = r" (?ix) <entry[^>]*> ",
            url   = r" (?ix) <ref \b[^>]+\b href \s*=\s* [\'\"] (\w+://[^\s\"\']+) [\'\"] ",
            title = r" (?ix) <title> ([^<>]+) ",
            unesc = "xml",
        ),
        "smil": dict(
            url   = r" (?x) <(?:audio|video|media)\b [^>]+ \b src \s*=\s* [^\"\']? \s* (\w+://[^\"\'\s\>]+) ",
            unesc = "xml",
        ),
        "jspf": dict(

Added contrib/windowsmedia.py version [eb5ea6c6b8].

























































































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
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# encoding: UTF-8
# api: streamtuner2
# title: WindowsMedia
# description: 
# url: http://windowsmedia.com/
# version: 0.3
# type: channel
# category: radio
# config:
#   { name: windowsmedia_culture, type: select, value: en-gb, select: "en-gb|de-de|da-dk|cs-cz|es-es|fr-fr|it-it|nl-nl|pl-pl|tr-tr|pt-pt|pt-br|en-us", description: "Country/language preference (for localized ads:?)" }
# png:
#   iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAk1BMVEWwMjIAAQACAAoSAwQ2CQcAH5VqERZsESgNK489KFwA
#   MrMAN6UQQwAXNpEAPakAQJ8AQ44RO7S2JQ8ASr6vLwqPOgoZYgCCSgvPPgVCbAF9XwzMTRrqUAHNY0VemADjbTiShjOYhiJR
#   oApJqgRerwlUtgBjwgDAsQDdqQB02AnowgP+soDfywDs2RL25Qr4727/8rdsT1F2AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgF
#   HUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQffBQcANDDneHDIAAAAlUlEQVQY02VPWxKCMAzcKmIF5GGrVkVAKA+x
#   Ve9/OhUK0xmTj2Q3yU4WAPnmL6Y69rDQwJB/bDOLpYNDEFgc352betYh4EIcK5lVgVHnIk/3kCdp5g7PHykv742sp43LFS4y
#   lMMNgbvebJl3U6ptjSxFFDPop+7NF5RFcVK8X6qfPIS+n0BrNf9FQ98retXZDlfoxuYDwYsJfXHQg0AAAAAASUVORK5CYII=
# png-orig: https://openclipart.org/detail/176727/windows-bug
# priority: extra
# status: unsupported
#
# Well, this one is Windows-specific, so naturally uses
# horrible formats WAX ( ASX ) for playlists. Still can
# be parsed by action module, but possibly falling back
# onto raw extraction etc.
#
# Only fetches the first page for each category anyway.
# And there's no specific category extraction, so stuck
# on the UK entries.
#
# Most entries are lower bitrates, 32 to 64 kbit/s MP3.


import re
from config import *
from channels import *
import ahttp


# Yay, windows playlists.
class windowsmedia (ChannelPlugin):


    # control flags
    has_search = False
    listformat = "wax"
    audioformat = "audio/mpeg"
    titles = dict(listeners=False, bitrate=False, playing="Location")
    _web = "http://www.windowsmedia.com/RadioUI/Home.aspx?g={}&culture=en-gb"
    base = "http://www.windowsmedia.com/RadioUI/getstationsforgenre.aspx?g={}&offset=0&culture={}"
    _url = "http://www.windowsmedia.com/RadioTunerAPI/Service.asmx/playStation?stationID={}&dialupDetected=true&useHighBandwidth=false&locale={}"

    categories = ["80s", "Adult Hits", "Adult Rock", "Alternative Rock",
    "Americana + Roots", "Big Band", "Blues", "Christian Hits", "Classic R&B",
    "Classic Rock", "Classical", "Comedy", "Country", "Dance + Electronica",
    "Holiday", "Indie", "International", "Jazz", "Latin", "Metal", "Miscellaneous",
    "New Age", "News + Talk", "Oldies", "Public Radio", "Rap + Hip Hop", "Reggae",
    "Religious", "Rock", "Smooth Jazz", "Soft Rock", "Soundtracks + Musicals",
    "Sports", "Top 40", "Urban/Modern R&B"]


    # static
    def update_categories(self):
        pass


    # Fetch entries
    def update_streams(self, cat, search=None):

        ucat = re.sub("\W+", "", cat.lower())
        html = ahttp.get(self.base.format(ucat, conf.windowsmedia_culture))

        r = []
        ls = re.findall("""
            stationid="([a-f0-9-]+)"  \s+
            onclick="Listen\('[\w-]+',\s*'(.+?)',\s*'(.+?)',
        """, html, re.X|re.S)
        for id, title, homepage in ls:
            r.append(dict(
                id = id,
                title = unhtml(title),
                homepage = homepage,
                url = self._url.format(id, conf.windowsmedia_culture),
                bitrate = 32,
            ))
        return r