Index: channels/__init__.py ================================================================== --- channels/__init__.py +++ channels/__init__.py @@ -179,12 +179,12 @@ 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) - + uikit.add_menu([parent.channelmenuitems], self.meta["title"], lambda w: parent.channel_switch_by_name(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) Index: config.py ================================================================== --- config.py +++ config.py @@ -87,34 +87,52 @@ # some defaults def defaults(self): self.play = { - "audio/mpeg": "audacious ", # %u for url to .pls, %g for downloaded .m3u - "audio/ogg": "audacious ", - "audio/*": "audacious ", - "video/youtube": "totem $(youtube-dl -g %srv)", - "video/*": "vlc --one-instance %srv", - "url/http": "sensible-browser", + "audio/mpeg": self.find_player(), + "audio/ogg": self.find_player(), + "audio/*": self.find_player(), + "video/youtube": self.find_player(typ="video") + " $(youtube-dl -g %srv)", + "video/*": self.find_player(typ="video", default="vlc"), + "url/http": self.find_player(typ="browser"), } self.record = { - "audio/*": "xterm -e streamripper %srv", # -d /home/***USERNAME***/Musik - "video/youtube": "xterm -e \"youtube-dl %srv\"", + "audio/*": self.find_player(typ="xterm") + " -e streamripper %srv", # -d /home/***USERNAME***/Musik + "video/youtube": self.find_player(typ="xterm") + " -e \"youtube-dl %srv\"", } + # these presets are a temporary workaround, until `priority:` is checked before module loading self.plugins = { - "bookmarks": 1, # built-in plugin, cannot be disabled + # core plugins, cannot be disabled anyway + "bookmarks": 1, "search": 1, "streamedit": 1, "configwin": 1, + # standard channels "shoutcast": 1, "xiph": 1, - "modarchive": 0, # disable per default - "file": 0, # disable per default - "punkcast": 0, # disable per default + "myoggradio": 1, + "internet_radio": 1, + "surfmusik": 1, + "jamendo": 1, + "icast": 1, + "itunes": 1, + # disabled per default + "radiobrowser": 0, + "youtube": 0, + "modarchive": 0, + "live365": 0, + "radiotray": 0, + "timer": 0, "history": 0, - "basicch": 0, # ceased - "tv": 0, # ceased + "global_key": 0, + "useragentswitcher": 0, + # obsolete / removed + "file": 0, + "punkcast": 0, + "basicch": 0, + "tv": 0, } self.tmp = os.environ.get("TEMP", "/tmp") self.max_streams = "500" self.show_bookmarks = 1 self.show_favicons = 1 @@ -142,11 +160,22 @@ # plugin state if module and module not in conf.plugins: conf.plugins[module] = meta.get("priority") in ("core", "builtin", "default", "standard") - + # look at system binaries for standard audio players + def find_player(self, typ="audio", default="xdg-open"): + players = { + "audio": ["audacious %g", "audacious2", "exaile %p", "xmms2", "banshee", "amarok %g", "clementine", "qmmp", "quodlibet", "aqualung", "mp3blaster %g", "vlc --one-instance %srv", "totem"], + "video": ["parole", "umplayer", "xnoise", "gxine", "totem", "vlc --one-instance", "smplayer", "gnome-media-player", "xine", "bangarang"], + "browser": ["opera", "midori", "sensible-browser"], + "xterm": ["xfce4-terminal", "x-termina-emulator", "gnome-terminal", "xterm", "rxvt"], + } + for bin in players[typ]: + if find_executable(bin.split()[0]): + return bin + return default # http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html def xdg(self, path="/streamtuner2"): home = os.environ.get("HOME", self.tmp) config = os.environ.get("XDG_CONFIG_HOME", os.environ.get("APPDATA", home+"/.config")) @@ -272,12 +301,12 @@ # Should list plugins within zips as well as local paths ls = pkgutil.iter_modules([conf.share+"/channels", "channels"]) ls = [name for loader,name,ispkg in ls] - # resort with tab order - order = [module.strip() for module in conf.channel_order.lower().replace(".","_").replace("-","_").split(",")] + # resort according to configured channel tab order + order = re.findall("\w+", conf.channel_order.lower()) ls = [module for module in (order) if (module in ls)] + [module for module in (ls) if (module not in order)] return ls Index: st2.py ================================================================== --- st2.py +++ st2.py @@ -14,15 +14,14 @@ # { type: env, name: http_proxy, description: proxy for HTTP access } # { type: env, name: XDG_CONFIG_HOME, description: relocates user .config subdirectory } # category: sound # depends: pygtk | gi, threading, requests, pyquery, lxml # id: streamtuner2 -# pack: *.py, gtk*.xml, bin=/usr/bin/streamtuner2, channels/__init__.py, bundle/*.py, +# pack: *.py, gtk*.xml, bin, channels/__init__.py, bundle/*.py, CREDITS, help/index.page, # streamtuner2.desktop=/usr/share/applications/, README=/usr/share/doc/streamtuner2/, -# NEWS.gz=/usr/share/doc/streamtuner2/changelog.gz, help/streamtuner2.1=/usr/share/man/man1/, -# help/*page=/usr/share/doc/streamtuner2/help/, help/img/*=/usr/share/doc/streamtuner2/help/img/, -# logo.png=/usr/share/pixmaps/streamtuner2.png, +# help/streamtuner2.1=/usr/share/man/man1/, NEWS.gz=/usr/share/doc/streamtuner2/changelog.gz, +# logo.png=/usr/share/pixmaps/streamtuner2.png # architecture: all # # Streamtuner2 is a GUI for browsing internet radio directories, music # collections, and video services - grouped by genres or categories. # It runs your preferred audio player, and streamripper for recording. @@ -85,13 +84,11 @@ } meta = plugin_meta() # status variables - channel_names = ["bookmarks"] # order of channel notebook tabs current_channel = "bookmarks" # currently selected channel name (as index in self.channels{}) - # constructor def __init__(self): # Load stylesheet, instantiate GtkBuilder in self, menu and logo hooks @@ -230,38 +227,41 @@ def get_widget(self, name): if name in self.widgets: return self.widgets[name] else: return gtk.Builder.get_object(self, name) + - # returns the currently selected directory/channel object (remembered position) + # Returns the currently selected directory/channel object (remembered position) def channel(self): return self.channels[self.current_channel] - # returns the currently selected directory/channel object (from gtk) + # List of module titles for channel tabs + @property + def channel_names(self): + n = self.notebook_channels + return [n.get_menu_label_text(n.get_nth_page(i)) for i in xrange(0, n.get_n_pages())] + + # Returns the currently selected directory/channel object (from gtk) def current_channel_gtk(self): - i = self.notebook_channels.get_current_page() - try: return self.channel_names[i] - except: return "bookmarks" - - # Notebook tab clicked + return self.channel_names[self.notebook_channels.get_current_page()] + + + # Notebook tab has been clicked (receives numeric page_num), but *NOT* yet changed (visually). def channel_switch(self, notebook, page, page_num=0, *args): - - # can be called from channelmenu as well: - if type(page) == str: - self.current_channel = page - self.notebook_channels.set_current_page(self.channel_names.index(page)) - # notebook invocation: - else: #if type(page_num) == int: - self.current_channel = self.channel_names[page_num] + self.current_channel = notebook.get_menu_label_text(notebook.get_nth_page(page_num)) + __print__(dbg.UI, "main.channel_switch():", "set current_channel :=", self.current_channel) # if first selected, load current category - try: - __print__(dbg.PROC, "channel_switch: try .first_show", self.channel().module); - self.channel().first_show() - except: - __print__(dbg.INIT, "channel .first_show() initialization error") + __print__(dbg.STAT, "TRY", "main.channel_switch(): ", self.current_channel + ".first_show()") + try: self.channel().first_show() + except: __print__(dbg.INIT, ".first_show() initialization error") + + # Invoked from the menu instead, uses module name instead of numeric tab id + def channel_switch_by_name(self, name): + self.notebook_channels.set_current_page(self.channel_names.index(name)) + # Convert ListStore iter to row number def rowno(self): (model, iter) = self.model_iter() return model.get_path(iter)[0] @@ -412,11 +412,11 @@ # initialize plugin modules (pre-ordered) ls = module_list() for module in ls: gui_startup(4/20.0 + 13.5/20.0 * float(ls.index(module))/len(ls), "loading module "+module) - + # skip module if disabled if conf.plugins.get(module, 1) == False: __print__(dbg.STAT, "disabled plugin:", module) continue # or if it's a built-in (already imported) @@ -431,12 +431,10 @@ plugin_obj = plugin_class(parent=self) # add to .channels{} if issubclass(plugin_class, channels.GenericChannel): self.channels[module] = plugin_obj - if module not in self.channel_names: # skip (glade) built-in channels - self.channel_names.append(module) # or .features{} for other plugin types else: self.features[module] = plugin_obj except Exception as e: