Check-in [8e8a9dd020]
Overview
| Comment: | Add user plugin manager / download tool. Not very well integrated nor pretty. Still requires a restart, needs more hooks into config dialog. (Clean up previous plugin vboxes after installation. Auto-activation required to reimplement init loader. And channels.__path__ setup isn't yet injectable, because no plugin `order:` is honored by main/init, and pluginmanager2 would run too late for overrides.) |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA1: |
8e8a9dd020171d0b4b7bf13876c4c000 |
| User & Date: | mario on 2015-05-03 14:13:21 |
| Other Links: | manifest | tags |
Context
|
2015-05-03
| ||
| 14:29 | Catch invalid image files when creating pixbuf in uikit.columns() check-in: 61c264fdcb user: mario tags: trunk | |
| 14:13 | Add user plugin manager / download tool. Not very well integrated nor pretty. Still requires a restart, needs more hooks into config dialog. (Clean up previous plugin vboxes after installation. Auto-activation required to reimplement init loader. And channels.__path__ setup isn't yet injectable, because no plugin `order:` is honored by main/init, and pluginmanager2 would run too late for overrides.) check-in: 8e8a9dd020 user: mario tags: trunk | |
| 14:10 | Detect all command names in player config, so that both `xterm` and `streamripper` are probed for existence. Use dict comprehension for plugin_meta lookup. Remove wrap_entry in favour of generic uikit.wrap(). check-in: 91f8502978 user: mario tags: trunk | |
Changes
Added channels/pluginmanager2.py version [ca1e100e6f].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 47 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 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 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 162 163 164 165 |
# encoding: UTF-8
# api: streamtuner2
# title: User Plugin Manager â…¡
# description: Downloads new plugins, or updates them.
# version: 0.1
# type: hook
# category: config
# depends: uikit, config, pluginconf
# config:
# { name: plugin_repos, type: text, value: "http://fossil.include-once.org/plugins.php/streamtuner2/contrib/*.py, http://fossil.include-once.org/plugins.php/streamtuner2/channels/*.py", description: "Plugin sources (common-repo.json)" }
# priority: extra
# support: experimental
#
# Scans for new plugins from the repository server, using
# a common-repo.json list. Compares new against installed
# plugins, and permits to update or download new ones.
#
# User plugins go into ~/.config/streamtuner2/channels/
# and will be picked up in favour of system-installed ones.
#
# Further enables direct activation of existing plugins
# without restarting streamtuner2.
#
import imp
import config
import pkgutil
from channels import __path__ as channels__path__
import os
from config import *
from uikit import *
import ahttp
import json
import compat2and3
# Plugin manager
class pluginmanager2(object):
module = "pluginmanager2"
meta = plugin_meta()
parent = None
vbox = None
# Hook up
def __init__(self, parent):
# main references
self.parent = parent
conf.add_plugin_defaults(self.meta, self.module)
# config dialog
parent.hooks["config_load"].append(self.add_config_tab)
parent.hooks["config_save"].append(self.activate_plugins)
# prepare user plugin directory
conf.plugin_dir = conf.dir + "/plugins"
if not os.path.exists(conf.plugin_dir):
os.mkdir(conf.plugin_dir)
open(conf.plugin_dir + "/__init__.py", "w").close()
# register config dir for module loading
sys.path.insert(0, conf.dir)
channels__path__.insert(0, conf.plugin_dir)
# config.plugin_base.append("plugins")
# = pkgutil.extend_path(config.__path__, config.__name__)
# Craft new config dialog notebook tab
def add_config_tab(self, *w):
if self.vbox:
return
# Notebook tab = label, content = vbox in scrolledwindow
w = self.parent.config_notebook
self.vbox = gtk.VBox(True, 5)
vp = gtk.Viewport()
vp.add(self.vbox)
sw = gtk.ScrolledWindow()
sw.add(vp) # ScrolledWindow → Viewport → VBox
# label
label = gtk.EventBox()
label.add(gtk.Label(" 📦 Add "))
label.show_all()
sw.show_all()
# add page
tab = w.insert_page_menu(sw, label, label, -1)
# Prepare some text
self.add_(uikit.label("\n<b><big>Install or update plugins</big></b>", size=520, markup=1))
self.add_(uikit.label("You can update existing plugins, or install new contrib/ channels. User plugins reside in ~/.config/streamtuner2/plugins/ and can even be modified there (such as setting a custom # color: entry).\n", size=520, markup=1))
self.add_(self.button("Refresh", stock="gtk-refresh", cb=self.refresh), "Show available plugins from repository\nhttp://fossil.include-once.org/streamtuner2/")
self.add_(gtk.image_new_from_stock("gtk-info", gtk.ICON_SIZE_LARGE_TOOLBAR), "While plugins are generally compatible across releases, newer versions may also require to update the streamtuner2 core setup.\n Please note that plugin installation is rather experimental. It still requires a restart of ST2 to activate them.")
for i in range(1,10):
self.add_(uikit.label(""))
# Append to vbox
def add_(self, w, label=None, markup=0):
w = uikit.wrap(w=w, label=label, align=10, label_size=400, label_markup=1)
self.vbox.add(w)
# Create button, connect click signal
def button(self, label, stock=None, cb=None):
b = gtk.Button(label, stock=stock)
b.connect("clicked", cb)
return b
# Add plugin list
def refresh(self, *w):
# fetch plugins
meta = []
for url in re.split("[\s,]+", conf.plugin_repos.strip()):
if re.match("https?://", url):
d = ahttp.get(url, encoding='utf-8') or []
meta += json.loads(d)
self.parent.status()
# clean up vbox
vbox = self.vbox
for i,c in enumerate(vbox.get_children()):
if i>=3:
vbox.remove(c)
# query existing plugins
have = {name: plugin_meta(module=name) for name in module_list()}
# add plugins
for p in meta:
id = p.get("$name")
if id.find("__") == 0: # skip __init__.py
continue
if have.get(id):
if p.get("version") == have[id]["version"]:
continue;
self.add_plugin(p)
# some filler
for i in range(1,3):
self.add_(uikit.label(""))
# Entry for plugin list
def add_plugin(self, p):
b = self.button("Install", stock="gtk-save", cb=lambda *w:self.install(p))
text = "<b>{title}</b> <small>{version}</small>\n<small>{description}</small>\n{type}/{category}".format(**p)
self.add_(b, text, markup=1)
# Download a plugin
def install(self, p):
src = ahttp.get(p["$file"], encoding="utf-8")
with open("{}/{$name}.py".format(conf.plugin_dir, **p), "w") as f:
f.write(src)
self.parent.status("Plugin '{$name}.py' installed.".format(**p))
# Activate/deactivate changed plugins
def activate_plugins(self, *w):
pass
|