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

⌈⌋ ⎇ branch:  streamtuner2


Check-in [08a43fb795]

Overview
Comment:Support .QTL output format
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 08a43fb795240f30a0a59b2e6ed531833182f7c0
User & Date: mario on 2016-11-20 16:09:18
Other Links: manifest | tags
Context
2016-11-20
16:43
More detailed Win installer scripts, as provided by Oliver. Plus *.ico file for packaging. check-in: 3670ccbb4b user: mario tags: trunk
16:09
Support .QTL output format check-in: 08a43fb795 user: mario tags: trunk
16:05
new plugin: liveradio.ie check-in: 979614a6d9 user: mario tags: trunk
Changes

Modified action.py from [e425666e38] to [c3fda330b1].

102
103
104
105
106
107
108

109
110
111
112
113
114
115
   ("xspf", r""" <\?xml .* <playlist .* ((?i)http://xspf\.org)/ns/0/ """),
   ("m3u",  r""" ^ \s* \#(EXT)?M3U """),
   ("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= """),
("desktop", r""" ^ \[Desktop Entry\] .*? ^Link= """),
   ("json", r""" "url": \s* "\w+:\\?/\\?/ """),
   ("jamj", r""" "audio": \s* "\w+:\\?/\\?/ """),
   ("gvp",  r""" ^gvp_version:1\.\d+$ """),







>







102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
   ("xspf", r""" <\?xml .* <playlist .* ((?i)http://xspf\.org)/ns/0/ """),
   ("m3u",  r""" ^ \s* \#(EXT)?M3U """),
   ("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
   ("qtl",  r""" <?quicktime\d+type="application/x-quicktime-media-link"\d*?> """),
   ("jspf", r""" ^ \s* \{ \s* "playlist": \s* \{ """),
   ("asf",  r""" ^ \[Reference\] .*? ^Ref\d+= """),
   ("url",  r""" ^ \[InternetShortcut\] .*? ^URL= """),
("desktop", r""" ^ \[Desktop Entry\] .*? ^Link= """),
   ("json", r""" "url": \s* "\w+:\\?/\\?/ """),
   ("jamj", r""" "audio": \s* "\w+:\\?/\\?/ """),
   ("gvp",  r""" ^gvp_version:1\.\d+$ """),
143
144
145
146
147
148
149
150


151
152
153
154
155
156
157
        log.EXEC(cmd)
        os.system(cmd)
    except:
        log.ERR("Command not found:", cmd)

# Open help browser, chm, or streamtuner2 pages
def help(*args):
    for path in [p for p in ("./help", "/usr/share/doc/streamtuner2/help") if os.path.exists(p)]:


        if conf.windows:
            return run(("%s/help.chm" % path).replace("/", '\\'))
        else:
            return run("yelp %s" % path)
    return browser("http://fossil.include-once.org/streamtuner2/doc/tip/help/html/index.html")

# Invokes player/recorder for stream url and format







|
>
>







144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
        log.EXEC(cmd)
        os.system(cmd)
    except:
        log.ERR("Command not found:", cmd)

# Open help browser, chm, or streamtuner2 pages
def help(*args):
    for path in ("./help", "/usr/share/doc/streamtuner2/help", "./usr/share/doc/streamtuner2"):
        if not os.path.exists(path):
            continue
        if conf.windows:
            return run(("%s/help.chm" % path).replace("/", '\\'))
        else:
            return run("yelp %s" % path)
    return browser("http://fossil.include-once.org/streamtuner2/doc/tip/help/html/index.html")

# Invokes player/recorder for stream url and format
532
533
534
535
536
537
538




539
540
541
542
543
544
545
            genre = r" (?x) \"(?:genre|keywords|category)\" \s*:\s* \"([^\"]+)\" ",
            unesc = "json",
        ),
        "asf": dict(
            url   = r" (?m) ^ \s*Ref\d+ = (\w+://[^\s]+) ",
            unesc = "xml",
        ),




        "url": dict(
            url   = r"(?m) ^URL=(\w+://.+)",
        ),
        "desktop": dict(
            url   = r"(?m) ^URL=(\w+://.+)",
            title = r"(?m) ^Name=(.+)",
            genre = r"(?m) ^Categories=(.+)",







>
>
>
>







535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
            genre = r" (?x) \"(?:genre|keywords|category)\" \s*:\s* \"([^\"]+)\" ",
            unesc = "json",
        ),
        "asf": dict(
            url   = r" (?m) ^ \s*Ref\d+ = (\w+://[^\s]+) ",
            unesc = "xml",
        ),
        "qtl": dict(
            url   = r" <embed\s+src=[\"\']([^\"\']+)[\"\']\s*/>",
            unesc = "xml",
        ),
        "url": dict(
            url   = r"(?m) ^URL=(\w+://.+)",
        ),
        "desktop": dict(
            url   = r"(?m) ^URL=(\w+://.+)",
            title = r"(?m) ^Name=(.+)",
            genre = r"(?m) ^Categories=(.+)",
738
739
740
741
742
743
744






745
746
747
748
749
750
751
    def asx(self, rows):
        txt = """<asx version="3.0">\n"""
        for row in rows:
            txt += """\t<entry>\n\t\t<title>%s</title>\n\t\t<ref href="%s"/>\n\t</entry>\n""" % (xmlentities(row["title"]), xmlentities(row["url"]))
        txt += """</asx>\n"""
        return txt








    # SMIL
    def smil(self, rows):
        txt = """<smil>\n<head>\n\t<meta name="title" content="%s"/>\n</head>\n<body>\n\t<seq>\n""" % (rows[0]["title"])
        for row in rows:
            if row.get("url"):
                txt += """\t\t<{} src="{}"/>\n""".format(row.get("format", "audio").split("/")[0], row["url"])







>
>
>
>
>
>







745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
    def asx(self, rows):
        txt = """<asx version="3.0">\n"""
        for row in rows:
            txt += """\t<entry>\n\t\t<title>%s</title>\n\t\t<ref href="%s"/>\n\t</entry>\n""" % (xmlentities(row["title"]), xmlentities(row["url"]))
        txt += """</asx>\n"""
        return txt


    # QTL
    def qtl(self, rows):
        return """<?xml version="1.0"?>\n<?quicktime type="application/x-quicktime-media-link"?>\n"""\
            + "<embed src=\"%s\" />\n" % xmlentities(rows[0]["url"])


    # SMIL
    def smil(self, rows):
        txt = """<smil>\n<head>\n\t<meta name="title" content="%s"/>\n</head>\n<body>\n\t<seq>\n""" % (rows[0]["title"])
        for row in rows:
            if row.get("url"):
                txt += """\t\t<{} src="{}"/>\n""".format(row.get("format", "audio").split("/")[0], row["url"])

Modified st2.py from [8350cc9006] to [05596eca4a].

338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
    # Save stream to file (.m3u)
    def save_as(self, widget):
        row = self.row()
        default_fn = row["title"] + ".m3u"
        fn = uikit.save_file("Save Stream", None, default_fn, [(".m3u","*m3u"),(".pls","*pls"),(".xspf","*xspf"),(".jspf","*jspf"),(".smil","*smil"),(".asx","*asx"),("all files","*")])
        if fn:
            source = row.get("listformat", self.channel().listformat)
            dest = (re.findall("\.(m3u|pls|xspf|jspf|json|smil|asx|wpl)8?$", fn) or ["pls"])[0]
            action.save_playlist(source=source, multiply=True).file(rows=[row], fn=fn, dest=dest)
        pass

    # Save current stream URL into clipboard
    def menu_copy(self, w):
        gtk.clipboard_get().set_text(self.selected("url"))








|







338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
    # Save stream to file (.m3u)
    def save_as(self, widget):
        row = self.row()
        default_fn = row["title"] + ".m3u"
        fn = uikit.save_file("Save Stream", None, default_fn, [(".m3u","*m3u"),(".pls","*pls"),(".xspf","*xspf"),(".jspf","*jspf"),(".smil","*smil"),(".asx","*asx"),("all files","*")])
        if fn:
            source = row.get("listformat", self.channel().listformat)
            dest = (re.findall("\.(m3u|pls|xspf|jspf|json|smil|asx|wpl|qtl)8?$", fn) or ["pls"])[0]
            action.save_playlist(source=source, multiply=True).file(rows=[row], fn=fn, dest=dest)
        pass

    # Save current stream URL into clipboard
    def menu_copy(self, w):
        gtk.clipboard_get().set_text(self.selected("url"))