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

⌈⌋ branch:  streamtuner2


Check-in [dd605f1352]

Overview
Comment:Remove some obsolete comments/code snippets.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: dd605f1352910c7ca4e069fd2f987a914f76ca17
User & Date: mario on 2015-04-20 16:21:54
Other Links: manifest | tags
Context
2015-04-20
16:22
Introduce log.ERR() etc. instead of __print__(dbg.XY...) workaround (was meant for Py3 only). check-in: 256b1e5833 user: mario tags: trunk
16:21
Remove some obsolete comments/code snippets. check-in: dd605f1352 user: mario tags: trunk
2015-04-19
22:17
Implement in-application row copying per JSON (info=51, mime=json/vnd.streamtuner2.station). Fixed set_text() bug by using set("STRING",..) atom instead. check-in: 38812e4bbf user: mario tags: trunk
Changes

Modified channels/__init__.py from [b9ead90d77] to [f6644128e5].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# encoding: UTF-8
# api: streamtuner2
# type: class
# category: ui
# title: Channel plugins
# description: Base implementation for channels and feature plugins
# version: 1.3
# license: public domain
# author: mario
# url: http://fossil.include-once.org/streamtuner2/
# pack:
#    bookmarks.py configwin.py streamedit.py history.py search.py links.py 
#    icast.py internet_radio.py itunes.py jamendo.py live365.py global_key.py
#    modarchive.py myoggradio.py punkcast.py radiobrowser.py radiotray.py
#    shoutcast.py surfmusik.py timer.py tunein.py xiph.py youtube.py
#    exportcat.py useragentswitcher.py somafm.py
# config: -
# priority: core
#
# GenericChannel implements the basic GUI functions and defines
# the default channel data structure. It implements fallback logic
# for all other channel implementations. Only `bookmarks` uses it
# directly.






|








|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# encoding: UTF-8
# api: streamtuner2
# type: class
# category: ui
# title: Channel plugins
# description: Base implementation for channels and feature plugins
# version: 1.5
# license: public domain
# author: mario
# url: http://fossil.include-once.org/streamtuner2/
# pack:
#    bookmarks.py configwin.py streamedit.py history.py search.py links.py 
#    icast.py internet_radio.py itunes.py jamendo.py live365.py global_key.py
#    modarchive.py myoggradio.py punkcast.py radiobrowser.py radiotray.py
#    shoutcast.py surfmusik.py timer.py tunein.py xiph.py youtube.py
#    exportcat.py useragentswitcher.py somafm.py dnd.py
# config: -
# priority: core
#
# GenericChannel implements the basic GUI functions and defines
# the default channel data structure. It implements fallback logic
# for all other channel implementations. Only `bookmarks` uses it
# directly.
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
    #-- keep track of currently selected genre/category
    __current = None
    @property
    def current(self):
        return self.__current
    @current.setter
    def current(self, newcat):
        print "{}.current:={} ← from {}".format(self.module, newcat, [inspect.stack()[x][3] for x in range(1,4)])
        self.__current = newcat
        return self.__current


    #--------------------------- initialization --------------------------------









|







100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
    #-- keep track of currently selected genre/category
    __current = None
    @property
    def current(self):
        return self.__current
    @current.setter
    def current(self, newcat):
        __print__(dbg.PROC, "{}.current:={} ← from {}".format(self.module, newcat, [inspect.stack()[x][3] for x in range(1,4)]))
        self.__current = newcat
        return self.__current


    #--------------------------- initialization --------------------------------


252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
            self.status(-0.1)
            if category == "empty":
                new_streams = self.empty_stub
            else:
                new_streams = self.update_streams(category)
  
            if new_streams:

                # check and modify entry;
                # assert that title and url are present
                modified = []
                for row in new_streams:
                    if len(set(["", None]) & set([row.get("title"), row.get("url")])):
                        continue
                    try:







<







252
253
254
255
256
257
258

259
260
261
262
263
264
265
            self.status(-0.1)
            if category == "empty":
                new_streams = self.empty_stub
            else:
                new_streams = self.update_streams(category)
  
            if new_streams:

                # check and modify entry;
                # assert that title and url are present
                modified = []
                for row in new_streams:
                    if len(set(["", None]) & set([row.get("title"), row.get("url")])):
                        continue
                    try:
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
                   self.streams[category] = new_streams + self.deleted_streams(new_streams, self.streams.get(category,[]))
                else:
                   self.streams[category] = new_streams
  
                # save in cache
                self.save()
  
                # invalidate gtk list cache
                #if (self.liststore.has_key(category)):
                #    del self.liststore[category]
  
            else:
                # parse error
                self.status("Category parsed empty.")
                self.streams[category] = self.nothing_found
                __print__(dbg.INFO, "Oooops, parser returned nothing for category " + category)
                
        # assign to treeview model
        #if (self.liststore.has_key(category)):  # was already loded before
        #    self.gtk_list.set_model(self.liststore[category])
        #else:   # currently list is new, had not been converted to gtk array before
        #    self.liststore[category] = \
        uikit.do(lambda:uikit.columns(self.gtk_list, self.datamap, self.prepare(self.streams[category])))

        # set pointer
        self.current = category
        self.status("")
        self.status(1.0)
        pass
        
    # store current streams data
    def save(self):
        conf.save("cache/" + self.module, self.streams, gz=1)


    # called occasionally while retrieving and parsing







<
<
<
<







<
<
<
<






|







273
274
275
276
277
278
279




280
281
282
283
284
285
286




287
288
289
290
291
292
293
294
295
296
297
298
299
300
                   self.streams[category] = new_streams + self.deleted_streams(new_streams, self.streams.get(category,[]))
                else:
                   self.streams[category] = new_streams
  
                # save in cache
                self.save()
  




            else:
                # parse error
                self.status("Category parsed empty.")
                self.streams[category] = self.nothing_found
                __print__(dbg.INFO, "Oooops, parser returned nothing for category " + category)
                
        # assign to treeview model




        uikit.do(lambda:uikit.columns(self.gtk_list, self.datamap, self.prepare(self.streams[category])))

        # set pointer
        self.current = category
        self.status("")
        self.status(1.0)

        
    # store current streams data
    def save(self):
        conf.save("cache/" + self.module, self.streams, gz=1)


    # called occasionally while retrieving and parsing
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
    
    # prepare data for display
    #
    #  - favourite icon
    #  - or deleted icon
    #
    def prepare(self, streams):
        #__print__(dbg.PROC, "prepare", streams)

        for i,row in enumerate(streams):
                                        # oh my, at least it's working
                                        # at start the bookmarks module isn't fully registered at instantiation in parent.channels{} - might want to do that step by step rather
                                        # then display() is called too early to take effect - load() & co should actually be postponed to when a notebook tab gets selected first
                                        # => might be fixed now, 1.9.8
            # state icon: bookmark star
            if (conf.show_bookmarks and "bookmarks" in self.parent.channels and self.parent.bookmarks.is_in(streams[i].get("url", "file:///tmp/none"))):
                streams[i]["favourite"] = 1
            
            # state icon: INFO or DELETE
            if (not row.get("state")):
                if row.get("favourite"):
                    streams[i]["state"] = gtk.STOCK_ABOUT
                if conf.retain_deleted and row.get("deleted"):
                    streams[i]["state"] = gtk.STOCK_DELETE
                  
            # guess homepage url  
            #self.postprocess(row)
            
            # favicons?
            if conf.show_favicons:
            
                # entry provides its own image
                if "img" in row:
                    favicon_url = row["img"]
                    streams[i]["favicon"] = favicon.localcopy(favicon_url)
                
                # get actual homepage favicon.png
                elif "homepage" in row:
                    homepage_url = row.get("homepage")
                    # check for availability of PNG file, inject local icons/ filename
                    if homepage_url and favicon.available(homepage_url):
                        streams[i]["favicon"] = favicon.file(homepage_url)
            
        return streams


    # data preparations directly after reload
    #
    # - drop shoutcast homepage links
    # - or find homepage name in title
    #
    def postprocess(self, row):

        # remove non-homepages from shoutcast
        if row.get("homepage") and row["homepage"].find("//yp.shoutcast.")>0:
            row["homepage"] = ""
            
        # deduce homepage URLs from title
        # by looking for www.xyz.com domain names
        if not row.get("homepage"):
            url = self.rx_www_url.search(row.get("title", ""))
            if url:
                url = url.group(0).lower().replace(" ", "")
                url = (url if url.find("www.") == 0 else "www."+url)
                row["homepage"] = http.fix_url(url)
        
        return row

        

    # reload current stream from web directory
    def reload(self):
        self.load(self.current, force=1)







<
<

<
<
<
<










<
<
<



<











<









<
<
<
<
<








<







315
316
317
318
319
320
321


322




323
324
325
326
327
328
329
330
331
332



333
334
335

336
337
338
339
340
341
342
343
344
345
346

347
348
349
350
351
352
353
354
355





356
357
358
359
360
361
362
363

364
365
366
367
368
369
370
    
    # prepare data for display
    #
    #  - favourite icon
    #  - or deleted icon
    #
    def prepare(self, streams):


        for i,row in enumerate(streams):




            # state icon: bookmark star
            if (conf.show_bookmarks and "bookmarks" in self.parent.channels and self.parent.bookmarks.is_in(streams[i].get("url", "file:///tmp/none"))):
                streams[i]["favourite"] = 1
            
            # state icon: INFO or DELETE
            if (not row.get("state")):
                if row.get("favourite"):
                    streams[i]["state"] = gtk.STOCK_ABOUT
                if conf.retain_deleted and row.get("deleted"):
                    streams[i]["state"] = gtk.STOCK_DELETE



            
            # favicons?
            if conf.show_favicons:

                # entry provides its own image
                if "img" in row:
                    favicon_url = row["img"]
                    streams[i]["favicon"] = favicon.localcopy(favicon_url)
                
                # get actual homepage favicon.png
                elif "homepage" in row:
                    homepage_url = row.get("homepage")
                    # check for availability of PNG file, inject local icons/ filename
                    if homepage_url and favicon.available(homepage_url):
                        streams[i]["favicon"] = favicon.file(homepage_url)

        return streams


    # data preparations directly after reload
    #
    # - drop shoutcast homepage links
    # - or find homepage name in title
    #
    def postprocess(self, row):





        # deduce homepage URLs from title
        # by looking for www.xyz.com domain names
        if not row.get("homepage"):
            url = self.rx_www_url.search(row.get("title", ""))
            if url:
                url = url.group(0).lower().replace(" ", "")
                url = (url if url.find("www.") == 0 else "www."+url)
                row["homepage"] = http.fix_url(url)

        return row

        

    # reload current stream from web directory
    def reload(self):
        self.load(self.current, force=1)