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

⌈⌋ ⎇ branch:  streamtuner2


Check-in [3c07d74bb6]

Overview
Comment:Less indentation, update pack: list.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 3c07d74bb63a08dbdd464a010c68861df243db79
User & Date: mario on 2015-03-29 14:41:25
Other Links: manifest | tags
Context
2015-03-29
14:43
Rename `mygtk` to `uikit`. Move AuxiliaryWindow and About dialog from main. check-in: b9cadd6925 user: mario tags: trunk
14:41
Less indentation, update pack: list. check-in: 3c07d74bb6 user: mario tags: trunk
03:09
Moved all config[] lists into plugin meta data fields (JSOL style retained). Add channel homepages as plugin url: field. check-in: d58eeed475 user: mario tags: trunk
Changes

Modified channels/__init__.py from [14b4644be4] to [01af7f840e].

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
#
# encoding: UTF-8
# api: streamtuner2
# type: base
# category: ui
# title: Channel plugins
# description: Base implementation for channels and feature plugins
# version: 1.1
# license: public domain
# author: mario
# url: http://fossil.include-once.org/streamtuner2/
# pack:
#   file.py  global_key.py  history.py  icast.py  internet_radio.py 
#   itunes.py  jamendo.py  links.py  live365.py  modarchive.py 
#   myoggradio.py  punkcast.py  shoutcast.py  surfmusik.py  tunein.py 

#   timer.py  xiph.py  youtube.py  radiotray.py  *.png
# priority: core
# config: -

#
#
# Just exports GenericChannel and ChannelPlugin.
# GenericChannel implements the basic GUI functions and defines
# the default channel data structure. It implements base and
# fallback logic for all other channel implementations.
#
# Built-in channels derive directly from generic. Additional
# channels don't have a pre-defined Notebook tab in the glade
# file. They derive from the ChannelPlugins class instead, which
# adds the required gtk Widgets manually.
#
# Makes module scanning available.  Checks for conf.share, so
# should pick up /usr/share/streamtuner2/channels/*.py plugins
# as well as local ./channels/*.* - Needs rework for in-zip
# searching.
#

import gtk
from mygtk import mygtk
from config import *
import ahttp as http
import action
import favicon
import os.path
import xml.sax.saxutils
import re












|
|
|
>
|
<

>



















|







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
#
# encoding: UTF-8
# api: streamtuner2
# type: base
# category: ui
# title: Channel plugins
# description: Base implementation for channels and feature plugins
# version: 1.1
# license: public domain
# author: mario
# url: http://fossil.include-once.org/streamtuner2/
# pack:
#    bookmarks.py  configwin.py  global_key.py  history.py
#    icast.py  internet_radio.py  itunes.py  jamendo.py links.py  live365.py
#    modarchive.py  myoggradio.py  punkcast.py  radiotray.py  search.py
#    shoutcast.py  streamedit.py  surfmusik.py  timer.py  tunein.py  xiph.py
#    youtube.py  *.png

# config: -
# priority: core
#
#
# Just exports GenericChannel and ChannelPlugin.
# GenericChannel implements the basic GUI functions and defines
# the default channel data structure. It implements base and
# fallback logic for all other channel implementations.
#
# Built-in channels derive directly from generic. Additional
# channels don't have a pre-defined Notebook tab in the glade
# file. They derive from the ChannelPlugins class instead, which
# adds the required gtk Widgets manually.
#
# Makes module scanning available.  Checks for conf.share, so
# should pick up /usr/share/streamtuner2/channels/*.py plugins
# as well as local ./channels/*.* - Needs rework for in-zip
# searching.
#

import gtk
from uikit import uikit
from config import *
import ahttp as http
import action
import favicon
import os.path
import xml.sax.saxutils
import re
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
            # save reference to main window/glade API
            self.parent = parent
            self.gtk_list = parent.get_widget(self.module+"_list")
            self.gtk_cat = parent.get_widget(self.module+"_cat")
            
            # category tree
            self.display_categories()
            #mygtk.tree(self.gtk_cat, self.categories, title="Category", icon=gtk.STOCK_OPEN);
            
            # update column names
            for field,title in list(self.titles.items()):
                self.update_datamap(field, title=title)
            
            # prepare stream list
            if (not self.rowmap):
                for row in self.datamap:
                    for x in range(2, len(row)):
                        self.rowmap.append(row[x][0])

            # load default category
            if (self.current):
                self.load(self.current)
            else:
                mygtk.columns(self.gtk_list, self.datamap, [])
                
            # add to main menu
            mygtk.add_menu(parent.channelmenuitems, self.meta["title"], lambda w: parent.channel_switch(w, self.module) or 1)
            
            
        # make private copy of .datamap and modify field (title= only ATM)
        def update_datamap(self, search="name", title=None):
            if self.datamap == GenericChannel.datamap:
                self.datamap = copy.deepcopy(self.datamap)
            for i,row in enumerate(self.datamap):







|















|


|







190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
            # save reference to main window/glade API
            self.parent = parent
            self.gtk_list = parent.get_widget(self.module+"_list")
            self.gtk_cat = parent.get_widget(self.module+"_cat")
            
            # category tree
            self.display_categories()
        #uikit.tree(self.gtk_cat, self.categories, title="Category", icon=gtk.STOCK_OPEN);
            
            # update column names
            for field,title in list(self.titles.items()):
                self.update_datamap(field, title=title)
            
            # prepare stream list
            if (not self.rowmap):
                for row in self.datamap:
                    for x in range(2, len(row)):
                        self.rowmap.append(row[x][0])

            # load default category
            if (self.current):
                self.load(self.current)
            else:
            uikit.columns(self.gtk_list, self.datamap, [])
                
            # add to main menu
        uikit.add_menu(parent.channelmenuitems, self.meta["title"], lambda w: parent.channel_switch(w, self.module) or 1)
            
            
        # make private copy of .datamap and modify field (title= only ATM)
        def update_datamap(self, search="name", title=None):
            if self.datamap == GenericChannel.datamap:
                self.datamap = copy.deepcopy(self.datamap)
            for i,row in enumerate(self.datamap):
271
272
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
                    
            # assign to treeview model
            #self.streams[self.default] = []
            #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] = \
            mygtk.do(lambda:mygtk.columns(self.gtk_list, self.datamap, self.prepare(self.streams[category])))

            # set pointer
            self.current = category
            self.parent.status("")
            self.parent.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
        def update_streams_partially_done(self, entries):
            mygtk.do(lambda: mygtk.columns(self.gtk_list, self.datamap, entries))

            
        # finds differences in new/old streamlist, marks deleted with flag
        def deleted_streams(self, new, old):
            diff = []
            new = [row.get("url","http://example.com/") for row in new]
            for row in old:







|














|







272
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
301
                    
            # assign to treeview model
            #self.streams[self.default] = []
            #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.parent.status("")
            self.parent.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
        def update_streams_partially_done(self, entries):
        uikit.do(lambda: uikit.columns(self.gtk_list, self.datamap, entries))

            
        # finds differences in new/old streamlist, marks deleted with flag
        def deleted_streams(self, new, old):
            diff = []
            new = [row.get("url","http://example.com/") for row in new]
            for row in old:
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
            self.update_categories()
            if self.categories:
                conf.save("cache/categories_"+self.module, self.categories)
            if self.catmap:
                conf.save("cache/catmap_" + self.module, self.catmap);

            # display outside of this non-main thread            
            mygtk.do(self.display_categories)

        # insert content into gtk category list
        def display_categories(self):
        
            # remove any existing columns
            if self.gtk_cat:
                [self.gtk_cat.remove_column(c) for c in self.gtk_cat.get_columns()]
            # rebuild gtk.TreeView
            mygtk.tree(self.gtk_cat, self.categories, title="Category", icon=gtk.STOCK_OPEN);

            # if it's a short list of categories, there's probably subfolders
            if len(self.categories) < 20:
                self.gtk_cat.expand_all()
                
            # select any first element
            self.gtk_cat.get_selection().select_path("0") #set_cursor







|








|







425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
            self.update_categories()
            if self.categories:
                conf.save("cache/categories_"+self.module, self.categories)
            if self.catmap:
                conf.save("cache/catmap_" + self.module, self.catmap);

            # display outside of this non-main thread            
        uikit.do(self.display_categories)

        # insert content into gtk category list
        def display_categories(self):
        
            # remove any existing columns
            if self.gtk_cat:
                [self.gtk_cat.remove_column(c) for c in self.gtk_cat.get_columns()]
            # rebuild gtk.TreeView
        uikit.tree(self.gtk_cat, self.categories, title="Category", icon=gtk.STOCK_OPEN);

            # if it's a short list of categories, there's probably subfolders
            if len(self.categories) < 20:
                self.gtk_cat.expand_all()
                
            # select any first element
            self.gtk_cat.get_selection().select_path("0") #set_cursor
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551


# channel plugin without glade-pre-defined notebook tab
#
class ChannelPlugin(GenericChannel):

        module = "abstract"
        title = "New Tab"
        version = 0.1


        def gui(self, parent):
        
            # name id
            module = self.module

            if parent:
                # two panes
                vbox = gtk.HPaned()
                vbox.show()
                # category treeview
                sw1 = gtk.ScrolledWindow()
                sw1.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
                sw1.set_property("width_request", 150)







<
<

<

|
<
|
<
<







529
530
531
532
533
534
535


536

537
538

539


540
541
542
543
544
545
546


# channel plugin without glade-pre-defined notebook tab
#
class ChannelPlugin(GenericChannel):

        module = "abstract"




        def gui(self, parent):
        if parent:

            module = self.__class__.__name__


                # two panes
                vbox = gtk.HPaned()
                vbox.show()
                # category treeview
                sw1 = gtk.ScrolledWindow()
                sw1.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
                sw1.set_property("width_request", 150)