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

⌈⌋ ⎇ branch:  streamtuner2


Check-in [7411543862]

Overview
Comment:Use distinct /tmp/streamtuner2/ directory for temporary pls/m3u/xspf files (also for DND). And have action. module reuse them, based on numeric row{} hash.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 7411543862460859b371ce2d213b8761bcd84a51
User & Date: mario on 2015-04-23 19:31:48
Other Links: manifest | tags
Context
2015-04-24
00:00
Add conf.nothreads flag and --nt cmdline flag, to prevent Gtk3 idle update race conditions if need be. (Still flaky for initial startups.) check-in: ce0e9149db user: mario tags: trunk
2015-04-23
19:31
Use distinct /tmp/streamtuner2/ directory for temporary pls/m3u/xspf files (also for DND). And have action. module reuse them, based on numeric row{} hash. check-in: 7411543862 user: mario tags: trunk
19:04
Some more comments and log messages. check-in: 79ef6e5f2a user: mario tags: trunk
Changes

Modified action.py from [4f5c978225] to [c19df7fb22].

221
222
223
224
225
226
227





228
229
230
231
232
233
234
def convert_playlist(url, source, dest, local_file=True, row={}):
    urls = []
    log.PROC("convert_playlist(", url, source, dest, ")")

    # Leave alone if format matches, or if already "srv" URL, or if not http (local path, mms:/rtsp:)
    if source == dest or source in ("srv", "href") or not re.match("(https?|spdy)://", url):
        return [url]





    
    # Retrieve from URL
    (mime, cnt) = http_probe_get(url)
    
    # Leave streaming server as is
    if mime == "srv":
        cnt = ""







>
>
>
>
>







221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
def convert_playlist(url, source, dest, local_file=True, row={}):
    urls = []
    log.PROC("convert_playlist(", url, source, dest, ")")

    # Leave alone if format matches, or if already "srv" URL, or if not http (local path, mms:/rtsp:)
    if source == dest or source in ("srv", "href") or not re.match("(https?|spdy)://", url):
        return [url]

    # Reuse tempoary files?
    if local_file and conf.reuse_m3u and os.path.exists(tmp_fn(row, dest)):
        log.STAT("reuse temporary filename")
        return [tmp_fn(row, dest)]
    
    # Retrieve from URL
    (mime, cnt) = http_probe_get(url)
    
    # Leave streaming server as is
    if mime == "srv":
        cnt = ""
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
    if not urls:
        return [url]
    elif dest in ("srv", "href"):
        return urls

    # Otherwise convert to local file
    if local_file:
        fn, is_unique = tmp_fn(cnt, dest)
        with open(fn, "w") as f:
            log.DATA("exporting with format:", dest, " into filename:", fn)
            f.write( save_playlist(source="srv", multiply=True).export(urls, row, dest) )
        return [fn]
    else:
        return urls








|







258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
    if not urls:
        return [url]
    elif dest in ("srv", "href"):
        return urls

    # Otherwise convert to local file
    if local_file:
        fn = tmp_fn(row, dest)
        with open(fn, "w") as f:
            log.DATA("exporting with format:", dest, " into filename:", fn)
            f.write( save_playlist(source="srv", multiply=True).export(urls, row, dest) )
        return [fn]
    else:
        return urls

628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
    # .DESKTOP links
    def desktop(self, rows):
        row = rows[0]
        return "[Desktop Entry]\nVersion=1.0\nIcon=media-playback-start\nType=Link\nName={title}\nComment={playing}\nURL={url}\n".format(**row)



# Generate filename for temporary .m3u, if possible with unique id
def tmp_fn(pls, ext="m3u"):
    # use shoutcast unique stream id if available
    stream_id = re.search("http://.+?/.*?(\d+)", pls, re.M)
    stream_id = stream_id and stream_id.group(1) or "XXXXXX"
    try:
        channelname = main.current_channel
    except:
        channelname = "unknown"
    # return temp filename
    fn = "%s/streamtuner2.%s.%s.%s" % (str(conf.tmp), channelname, stream_id, ext)
    is_unique = len(stream_id) > 3 and stream_id != "XXXXXX"
    tmp_files.append(fn)
    return fn, is_unique

# Collect generated filenames
tmp_files = []

# Callback from main / after gtk_main_quit
def cleanup_tmp_files():
    if not int(conf.reuse_m3u):
        [os.remove(fn) for fn in set(tmp_files)]








|
|
|
|
<
<
|
<
|

|
<

|









633
634
635
636
637
638
639
640
641
642
643


644

645
646
647

648
649
650
651
652
653
654
655
656
657
658
    # .DESKTOP links
    def desktop(self, rows):
        row = rows[0]
        return "[Desktop Entry]\nVersion=1.0\nIcon=media-playback-start\nType=Link\nName={title}\nComment={playing}\nURL={url}\n".format(**row)



# Generate filename for temporary .pls/m3u, with unique id
def tmp_fn(row, ext="pls"):
    # use original url for generating a hash sum
    stream_url_hash = abs(hash(str(row)))


    try: channelname = main.current_channel

    except: channelname = "unknown"
    # return temp filename
    fn = "%s/%s.%s.%s" % (str(conf.tmp), channelname, stream_url_hash, ext)

    tmp_files.append(fn)
    return fn

# Collect generated filenames
tmp_files = []

# Callback from main / after gtk_main_quit
def cleanup_tmp_files():
    if not int(conf.reuse_m3u):
        [os.remove(fn) for fn in set(tmp_files)]

Modified channels/dnd.py from [badcdb5ec3] to [d216a79da8].

184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
            #buf = 'uris', urls
            buf = 'text', urls[0]
        # Text sources are assumed to understand the literal URL or expect a description block
        elif info >= 5:
            buf = 'text', "{url}\n# Title: {title}\n# Homepage: {homepage}\n\n".format(**r)
        # Create temporary PLS file, because "text/uri-list" is widely misunderstood and just implemented for file:// IRLs
        else:
            tmpfn = "{}/{}.{}".format(conf.tmp, re.sub("[^\w-]+", " ", r["title"]), conf.dnd_format)
            cnv.file(rows=[r], dest=conf.dnd_format, fn=tmpfn)
            buf = 'uris', ["file://{}".format(tmpfn)] if (info==4) else tmpfn

        # Keep in type request buffer
        self.buf[info] = buf
        return buf








|







184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
            #buf = 'uris', urls
            buf = 'text', urls[0]
        # Text sources are assumed to understand the literal URL or expect a description block
        elif info >= 5:
            buf = 'text', "{url}\n# Title: {title}\n# Homepage: {homepage}\n\n".format(**r)
        # Create temporary PLS file, because "text/uri-list" is widely misunderstood and just implemented for file:// IRLs
        else:
            tmpfn = "{}/{}.{}".format(conf.tmp, re.sub("[^\w-]+", " ", r["title"]).strip(), conf.dnd_format)
            cnv.file(rows=[r], dest=conf.dnd_format, fn=tmpfn)
            buf = 'uris', ["file://{}".format(tmpfn)] if (info==4) else tmpfn

        # Keep in type request buffer
        self.buf[info] = buf
        return buf

Modified config.py from [fe8ee86843] to [9fc88f2a2d].

83
84
85
86
87
88
89




90
91
92
93
94
95
96
                del last["share"]
            self.update(last)
            self.migrate()
        # store defaults in file
        else:
            self.save("settings")
            self.firstrun = 1




        
        # add argv
        self.args = self.init_args(argparse.ArgumentParser())
        self.apply_args(self.args)


    # some defaults







>
>
>
>







83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
                del last["share"]
            self.update(last)
            self.migrate()
        # store defaults in file
        else:
            self.save("settings")
            self.firstrun = 1

        # temporary files
        if not os.path.exists(self.tmp):
            os.mkdir(self.tmp)
        
        # add argv
        self.args = self.init_args(argparse.ArgumentParser())
        self.apply_args(self.args)


    # some defaults
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
        self.plugins = {
             # core plugins, cannot be disabled anyway
            "bookmarks": 1,
            "search": 1,
            "streamedit": 1,
            "configwin": 1,
        }
        self.tmp = os.environ.get("TEMP", "/tmp")
        self.max_streams = "500"
        self.show_bookmarks = 1
        self.show_favicons = 1
        self.load_favicon = 1
        self.heuristic_bookmark_update = 0
        self.retain_deleted = 0
        self.auto_save_appstate = 1







|







115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
        self.plugins = {
             # core plugins, cannot be disabled anyway
            "bookmarks": 1,
            "search": 1,
            "streamedit": 1,
            "configwin": 1,
        }
        self.tmp = os.environ.get("TEMP", "/tmp") + "/streamtuner2"
        self.max_streams = "500"
        self.show_bookmarks = 1
        self.show_favicons = 1
        self.load_favicon = 1
        self.heuristic_bookmark_update = 0
        self.retain_deleted = 0
        self.auto_save_appstate = 1
236
237
238
239
240
241
242


243
244
245
246
247
248
249

    # update old setting names
    def migrate(self):
        # 2.1.1
        if "audio/mp3" in self.play:
            self.play["audio/mpeg"] = self.play["audio/mp3"]
            del self.play["audio/mp3"]



         
    # check for existing filename in directory list
    def find_in_dirs(self, dirs, file):
        for d in dirs:
            if os.path.exists(d+"/"+file):
                return d+"/"+file







>
>







240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255

    # update old setting names
    def migrate(self):
        # 2.1.1
        if "audio/mp3" in self.play:
            self.play["audio/mpeg"] = self.play["audio/mp3"]
            del self.play["audio/mp3"]
        if self.tmp == "/tmp":
            self.tmp = "/tmp/streamtuner2"

         
    # check for existing filename in directory list
    def find_in_dirs(self, dirs, file):
        for d in dirs:
            if os.path.exists(d+"/"+file):
                return d+"/"+file