Index: channels/configwin.py
==================================================================
--- channels/configwin.py
+++ channels/configwin.py
@@ -87,24 +87,27 @@
if row[0] and row[1]:
config[key][row[0]] = row[1]
__print__(dbg.CONF, "config save", prefix+key, val)
- # Generic Gtk callback to update ListStore when entries get edited
+ # Generic Gtk callback to update ListStore when entries get edited.
+ # (The main signal_connect() dict prepares individual lambda funcs
+ # for each ListStore column id.)
def list_edit(self, liststore, path, column, new_text):
liststore[path][column] = new_text
liststore[path][3] = self.app_bin_check(new_text)
- # The signal_connect() dict actually prepares individual lambda functions
- # to bind the correct ListStore and column id.
# return OK or CANCEL depending on availability of app
def app_bin_check(self, v):
m = re.search("(?![$(`])\S+", v)
- if m and m.group(0) and find_executable(m.group(0)):
- return gtk.STOCK_MEDIA_PLAY
+ if m and m.group(0):
+ if find_executable(m.group(0)):
+ return gtk.STOCK_MEDIA_PLAY
+ else:
+ return gtk.STOCK_CANCEL
else:
- return gtk.STOCK_CANCEL
+ return gtk.STOCK_NEW
# iterate over channel and feature plugins
def add_plugins(self):
@@ -115,10 +118,12 @@
# elif name in self.features:
# ls[name] = self.features[name].meta
# else:
# ls[name] = plugin_meta(module=name)
for name,meta in sorted(ls.items(), key=lambda e: e[1]["type"]+e[1]["title"].lower(), reverse=False):
+ if not name in conf.plugins:
+ conf.plugins[name] = False
self.add_plg(name, meta)
# add configuration setting definitions from plugins
plugin_text = "%s (%s/%s) %s\n%s"
Index: config.py
==================================================================
--- config.py
+++ config.py
@@ -99,41 +99,17 @@
}
self.record = {
"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
+ # Presets are redundant now. On first startup the `priority:` field of each plugin is checked.
self.plugins = {
# core plugins, cannot be disabled anyway
"bookmarks": 1,
"search": 1,
"streamedit": 1,
"configwin": 1,
- # standard channels
- "shoutcast": 1,
- "xiph": 1,
- "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,
- "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
@@ -157,11 +133,12 @@
if ("name" in opt) and ("value" in opt) and (opt["name"] not in vars(self)):
self.__dict__[opt["name"]] = opt["value"]
# plugin state
if module and module not in conf.plugins:
- conf.plugins[module] = meta.get("priority") in ("core", "builtin", "default", "standard")
+ conf.plugins[module] = meta.get("priority") in ("core", "builtin", "always", "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"],
@@ -171,10 +148,11 @@
}
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"))
@@ -244,10 +222,11 @@
if type(value) == dict:
self[key].update(value)
else:
self[key] = value
# descends into sub-dicts instead of wiping them with subkeys
+
# update old setting names
def migrate(self):
# 2.1.1
if "audio/mp3" in self.play:
@@ -258,10 +237,11 @@
# 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
+
# standard user account storage in ~/.netrc or less standard but contemporarily in ~/.config/netrc
def netrc(self, varhosts=("shoutcast.com")):
global netrc
if not netrc:
@@ -280,13 +260,13 @@
# Retrieve content from install path or pyzip archive (alias for pkgutil.get_data)
#
-def get_data(fn, decode=False, z=False):
+def get_data(fn, decode=False, z=False, file_base="config"):
try:
- bin = pkgutil.get_data("config", fn)
+ bin = pkgutil.get_data(file_base, fn)
if z:
bin = zlib.decompress(bin)
if decode:
return bin.decode("utf-8")
else:
@@ -296,14 +276,14 @@
# Search through ./channels/ and get module basenames.
# (Reordering channel tabs is now done by uikit.apply_state.)
#
-def module_list():
+def module_list(plugin_base="channels"):
# Should list plugins within zips as well as local paths
- ls = pkgutil.iter_modules([conf.share+"/channels", "channels"])
+ ls = pkgutil.iter_modules([plugin_base, conf.share+"/"+plugin_base, conf.dir+"/plugins"])
return [name for loader,name,ispkg in ls]
# Plugin meta data extraction
@@ -313,16 +293,16 @@
# · fn= to read from literal files, out of a .pyzip package
# · src= to extract from pre-read script code
# · module= utilizes pkgutil to read
# · frame= automatically extract comment header from caller
#
-def plugin_meta(fn=None, src=None, module=None, frame=1):
+def plugin_meta(fn=None, src=None, module=None, frame=1, plugin_base="channels"):
# try via pkgutil first
if module:
fn = module
- src = pkgutil.get_data("channels", fn+".py")
+ src = pkgutil.get_data(plugin_base, fn+".py")
# get source directly from caller
elif not src and not fn:
module = inspect.getmodule(sys._getframe(frame))
fn = inspect.getsourcefile(module)
Index: st2.py
==================================================================
--- st2.py
+++ st2.py
@@ -15,13 +15,12 @@
# { type: env, name: XDG_CONFIG_HOME, description: relocates user .config subdirectory }
# category: sound
# depends: pygtk | gi, threading, requests, pyquery, lxml
# id: streamtuner2
# pack: *.py, gtk3.xml.zlib, bin, channels/__init__.py, bundle/*.py, CREDITS, help/index.page,
-# streamtuner2.desktop=/usr/share/applications/, README=/usr/share/doc/streamtuner2/,
-# help/streamtuner2.1=/usr/share/man/man1/, NEWS.gz=/usr/share/doc/streamtuner2/changelog.gz,
-# logo.png=/usr/share/pixmaps/streamtuner2.png
+# streamtuner2.desktop, README, 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.
@@ -403,37 +402,41 @@
# load plugins from /usr/share/streamtuner2/channels/
def load_plugin_channels(self):
# 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)
+ for name in ls:
+ gui_startup(4/20.0 + 13.5/20.0 * float(ls.index(name))/len(ls), "loading module "+name)
+ # load defaults on first startup
+ if not name in conf.plugins:
+ conf.add_plugin_defaults(plugin_meta(module=name), name)
+
# skip module if disabled
- if conf.plugins.get(module, 1) == False:
- __print__(dbg.STAT, "disabled plugin:", module)
+ if conf.plugins.get(name, 1) == False:
+ __print__(dbg.STAT, "disabled plugin:", name)
continue
# or if it's a built-in (already imported)
- elif module in self.features or module in self.channels:
+ elif name in self.features or name in self.channels:
continue
# load plugin
try:
- plugin = __import__("channels."+module, globals(), None, [""])
+ plugin = __import__("channels."+name, globals(), None, [""])
#print [name for name,c in inspect.getmembers(plugin) if inspect.isclass(c)]
- plugin_class = plugin.__dict__[module]
+ plugin_class = plugin.__dict__[name]
plugin_obj = plugin_class(parent=self)
# add to .channels{}
if issubclass(plugin_class, channels.GenericChannel):
- self.channels[module] = plugin_obj
+ self.channels[name] = plugin_obj
# or .features{} for other plugin types
else:
- self.features[module] = plugin_obj
+ self.features[name] = plugin_obj
except Exception as e:
- __print__(dbg.INIT, "load_plugin_channels: error initializing:", module, ", exception:")
+ __print__(dbg.INIT, "load_plugin_channels: error initializing:", name, ", exception:")
traceback.print_exc()
# store window/widget states (sizes, selections, etc.)
def app_state(self, widget):