Check-in [e663445700]
Overview
Comment: | Shorten first_show(). Plugins don't initialize on startup anymore, but again on first channel tab selection. Use first entry from categories as current, if none is set. Remove some obsolete code. Display now matches on fresh installations as well as with existing cache/state. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
e6634457005a4cebb2b78ef2511c589f |
User & Date: | mario on 2015-04-11 14:12:57 |
Other Links: | manifest | tags |
Context
2015-04-11
| ||
16:08 | Fix bookmarks tab swtich on search completion. check-in: 98f8aea21d user: mario tags: trunk | |
14:12 | Shorten first_show(). Plugins don't initialize on startup anymore, but again on first channel tab selection. Use first entry from categories as current, if none is set. Remove some obsolete code. Display now matches on fresh installations as well as with existing cache/state. check-in: e663445700 user: mario tags: trunk | |
14:10 | Slim down first_show() in main.channel_switch(). check-in: 97a832d8f2 user: mario tags: trunk | |
Changes
Modified channels/__init__.py from [08c3ab8bab] to [f719a01f46].
1 2 | # encoding: UTF-8 # api: streamtuner2 | | | < < | | > < | | | | < < < < < | 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 | # 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 # 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. # # All other plugins don't have a pre-defined Notebook tab in the # GtkBuilder description. They derive from ChannelPlugins therefore, # which constructs and registers the required gtk widgets manually. import gtk from uikit import uikit from config import * import ahttp as http import action import favicon |
︙ | ︙ | |||
55 56 57 58 59 60 61 62 63 64 65 | # generic channel module --------------------------------------- class GenericChannel(object): # desc meta = { "config": [] } homepage = "http://fossil.include-once.org/streamtuner2/" base_url = "" listformat = "pls" audioformat = "audio/mpeg" # fallback value | > < | < | < | | | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | # generic channel module --------------------------------------- class GenericChannel(object): # desc meta = { "config": [] } config = [] homepage = "http://fossil.include-once.org/streamtuner2/" base_url = "" listformat = "pls" audioformat = "audio/mpeg" # fallback value has_search = False # categories categories = ["empty", ] catmap = {} current = None shown = None # last selected entry in stream list, also indicator if notebook tab has been selected once / stream list of current category been displayed yet # gui + data streams = {} # Station list dict, associates each genre to a list of stream rows gtk_list = None # Gtk widget for station treeview gtk_cat = None # Gtk widget for category columns # mapping of stream{} data into gtk treeview/treestore representation datamap = [ # coltitle width [ datasrc key, type, renderer, attrs ] [cellrenderer2], ... ["", 20, ["state", str, "pixbuf", {}], ], ["Genre", 65, ['genre', str, "t", {}], ], ["Station Title",275,["title", str, "text", {"strikethrough":11, "cell-background":12, "cell-background-set":13}], ["favicon", gtk.gdk.Pixbuf, "pixbuf", {}], ], |
︙ | ︙ | |||
99 100 101 102 103 104 105 106 107 108 109 110 111 112 | ] rowmap = [] # [state,genre,title,...] field enumeration still needed separately titles = {} # for easier adapting of column titles in datamap # for empty grouping / categories placeholder = [dict(genre="./.", title="Subcategory placeholder", playing="./.", url="none:", listeners=0, bitrate=0, homepage="", state="gtk-folder")] empty_stub = [dict(genre="./.", title="No categories found (HTTP error)", playing="Try Channel→Reload Categories later..", url="none:", listeners=0, bitrate=0, homepage="", state="gtk-stop")] # regex rx_www_url = re.compile("""(www(\.\w+[\w-]+){2,}|(\w+[\w-]+[ ]?\.)+(com|FM|net|org|de|PL|fr|uk))""", re.I) #--------------------------- initialization -------------------------------- | > | 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | ] rowmap = [] # [state,genre,title,...] field enumeration still needed separately titles = {} # for easier adapting of column titles in datamap # for empty grouping / categories placeholder = [dict(genre="./.", title="Subcategory placeholder", playing="./.", url="none:", listeners=0, bitrate=0, homepage="", state="gtk-folder")] empty_stub = [dict(genre="./.", title="No categories found (HTTP error)", playing="Try Channel→Reload Categories later..", url="none:", listeners=0, bitrate=0, homepage="", state="gtk-stop")] nothing_found = [dict(genre="./.", title="No contents found on directory server", playing="Notice", listeners=0, bitrate=0, state="gtk-info")] # regex rx_www_url = re.compile("""(www(\.\w+[\w-]+){2,}|(\w+[\w-]+[ ]?\.)+(com|FM|net|org|de|PL|fr|uk))""", re.I) #--------------------------- initialization -------------------------------- |
︙ | ︙ | |||
122 123 124 125 126 127 128 | self.meta = plugin_meta(src = inspect.getcomments(inspect.getmodule(self))) self.config = self.meta.get("config", []) self.title = self.meta.get("title", self.module) # add default options values to config.conf.* dict conf.add_plugin_defaults(self.meta, self.module) | < < < | | > > > < < < < < < | | | | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | self.meta = plugin_meta(src = inspect.getcomments(inspect.getmodule(self))) self.config = self.meta.get("config", []) self.title = self.meta.get("title", self.module) # add default options values to config.conf.* dict conf.add_plugin_defaults(self.meta, self.module) # Only if streamtuner2 is run in graphical mode if (parent): self.cache() self.gui(parent) # Stub for ST2 main window / dispatcher else: self.parent = stub_parent(None) # initialize Gtk widgets / data objects def gui(self, parent): # 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() # 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]) # Initialize stations TreeView uikit.columns(self.gtk_list, self.datamap, []) # add to main menu uikit.add_menu([parent.channelmenuitems], self.meta["title"], lambda w: parent.channel_switch_by_name(self.module) or 1) # Statusbar stub (defers to parent/main window, if in GUI mode) def status(self, *v): if self.parent: self.parent.status(*v) |
︙ | ︙ | |||
245 246 247 248 249 250 251 | # load data, # update treeview content def load(self, category, force=False): # get data from cache or download if (force or not category in self.streams): __print__(dbg.PROC, "load", "update_streams") | | | | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | # load data, # update treeview content def load(self, category, force=False): # get data from cache or download if (force or not category in self.streams): __print__(dbg.PROC, "load", "update_streams") self.status("Updating streams...") self.status(-0.1) if category == "empty": new_streams = self.empty_stub else: new_streams = self.update_streams(category) if new_streams: |
︙ | ︙ | |||
281 282 283 284 285 286 287 | # invalidate gtk list cache #if (self.liststore.has_key(category)): # del self.liststore[category] else: # parse error | | | < | | | 267 268 269 270 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 | # 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) |
︙ | ︙ | |||
404 405 406 407 408 409 410 | if self.current == category: self.reload() # display .current category, once notebook/channel tab is first opened def first_show(self): | > | > | | | | | < | | | | | | < < < | | < > | > | > > | | < | > | | | | | | > > > > > > > > > > | 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 | if self.current == category: self.reload() # display .current category, once notebook/channel tab is first opened def first_show(self): # Already processed if (self.shown == 55555): return __print__(dbg.PROC, self.module, "→ first_show()", ", current=", self.current, ", categories=", len(self.categories)) # if category tree is empty, initialize it if not self.categories: __print__(dbg.PROC, self.module, "→ first_show() → reload_categories()"); try: self.reload_categories() except: __print__(dbg.ERR, "HTTP error or extraction failure.") self.categories = ["empty"] self.display_categories() # Select first category self.current = self.str_from_struct(self.categories) or None __print__(dbg.STAT, self.module, "→ first_show(); use first category as current =", self.current) try: self.load(self.current) except: pass # put selection/cursor on last position __print__(dbg.STAT, self.module+".first_show()", "select last known category treelist position =", self.shown) try: self.gtk_list.get_selection().select_path(self.shown) except: pass # Invoke only once self.shown = 55555 # Retrieve first list value, or key from dict (-- used to get first category on init) def str_from_struct(self, d): if isinstance(d, (str)): return d elif isinstance(d, (dict)): return self.str_from_struct(d.keys()) or self.str_from_struct(d.values()) elif isinstance(d, (list, tuple)): return d[0] if len(d) else None # update categories, save, and display def reload_categories(self): # get data and save self.update_categories() |
︙ | ︙ | |||
528 529 530 531 532 533 534 | "ogg":"ogg", "ogm":"ogg", "xiph":"ogg", "vorbis":"ogg", "vnd.xiph.vorbis":"ogg", "mp3":"mpeg", "mp":"mpeg", "mp2":"mpeg", "mpc":"mpeg", "mps":"mpeg", "aac+":"aac", "aacp":"aac", "realaudio":"x-pn-realaudio", "real":"x-pn-realaudio", "ra":"x-pn-realaudio", "ram":"x-pn-realaudio", "rm":"x-pn-realaudio", # yes, we do video "flv":"video/flv", "mp4":"video/mp4", } | | | 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 | "ogg":"ogg", "ogm":"ogg", "xiph":"ogg", "vorbis":"ogg", "vnd.xiph.vorbis":"ogg", "mp3":"mpeg", "mp":"mpeg", "mp2":"mpeg", "mpc":"mpeg", "mps":"mpeg", "aac+":"aac", "aacp":"aac", "realaudio":"x-pn-realaudio", "real":"x-pn-realaudio", "ra":"x-pn-realaudio", "ram":"x-pn-realaudio", "rm":"x-pn-realaudio", # yes, we do video "flv":"video/flv", "mp4":"video/mp4", } #map.update(action.listfmt_t) # list type formats (.m3u .pls and .xspf) if map.get(s): s = map[s] # add prefix: if s.find("/") < 1: s = "audio/" + s # return s |
︙ | ︙ |