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
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
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, is_unique = tmp_fn(cnt, dest)
        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
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 .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)
# 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)))
    stream_id = stream_id and stream_id.group(1) or "XXXXXX"
    try:
        channelname = main.current_channel
    try: channelname = main.current_channel
    except:
        channelname = "unknown"
    except: channelname = "unknown"
    # return temp filename
    fn = "%s/streamtuner2.%s.%s.%s" % (str(conf.tmp), channelname, stream_id, ext)
    fn = "%s/%s.%s.%s" % (str(conf.tmp), channelname, stream_url_hash, ext)
    is_unique = len(stream_id) > 3 and stream_id != "XXXXXX"
    tmp_files.append(fn)
    return fn, is_unique
    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
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)
            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
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
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")
        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
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