Check-in [0479db10f9]
Overview
| Comment: | Make plugin list prettier. Implement vbox redrawing on [save]. Activation seems to work okay for channel plugins, and main already had enough safeguards to make st2.load_plugin_channels() work as is. |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA1: |
0479db10f9a285b9d1ad28f3b716b0de |
| User & Date: | mario on 2015-05-03 17:36:14 |
| Other Links: | manifest | tags |
Context
|
2015-05-03
| ||
| 20:23 | Disable iCast plugin for now. check-in: 9d1b6cc7f5 user: mario tags: trunk | |
| 17:36 | Make plugin list prettier. Implement vbox redrawing on [save]. Activation seems to work okay for channel plugins, and main already had enough safeguards to make st2.load_plugin_channels() work as is. check-in: 0479db10f9 user: mario tags: trunk | |
| 17:35 | Make short help notices for player/recording configuration smaller. check-in: 55a1d1e516 user: mario tags: trunk | |
Changes
Modified channels/pluginmanager2.py from [ca1e100e6f] to [d1c55d3594].
1 2 3 4 | # encoding: UTF-8 # api: streamtuner2 # title: User Plugin Manager Ⅱ # description: Downloads new plugins, or updates them. | | | > | | > > | 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 |
# encoding: UTF-8
# api: streamtuner2
# title: User Plugin Manager Ⅱ
# description: Downloads new plugins, or updates them.
# version: 0.2
# type: hook
# category: config
# depends: uikit, config, pluginconf
# config:
# { name: plugin_repos, type: text, value: "http://fossil.include-once.org/repo.json/streamtuner2/contrib/*.py, http://fossil.include-once.org/repo.json/streamtuner2/channels/*.py", description: "Plugin repository JSON source references." }
# { name: plugin_auto, type: boolean, value: 1, description: Apply plugin activation/disabling without restart. }
# 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 channel
# plugins, often without restarting streamtuner2.
#
# Actually rather trivial. The Gtk interface building just
# makes this handler look complicated.
import imp
import config
import pkgutil
from channels import __path__ as channels__path__
import os
|
| ︙ | ︙ | |||
50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# 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()
| > | > > < < | 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 |
# 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)
parent.hooks["config_save"].append(self.clean_config_vboxen)
# 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 user config dir "~/.config/streamtuner2/plugins" for module loading
sys.path.insert(0, conf.dir)
# Let channels.* package load modules from two directories
channels__path__.insert(0, conf.plugin_dir)
# Craft new config dialog notebook tab
def add_config_tab(self, *w):
if self.vbox:
return
|
| ︙ | ︙ | |||
108 109 110 111 112 113 114 |
b.connect("clicked", cb)
return b
# Add plugin list
def refresh(self, *w):
| | | | < | | | | | | | > | > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | 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 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 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 |
b.connect("clicked", cb)
return b
# Add plugin list
def refresh(self, *w):
# Fetch repository JSON list
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 placeholders in vbox
vbox = self.vbox
for c in vbox.get_children()[3:]:
vbox.remove(c)
# Query existing plugins
have = {name: plugin_meta(module=name) for name in module_list()}
# Attach available downloads
for newpl in meta:
id = newpl.get("$name")
if id.find("__") == 0: # skip __init__.py
continue
if have.get(id):
if have[id]["version"] >= newpl.get("version"):
continue;
self.add_plugin(newpl)
# Readd some filler labels
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))
p = self.update_p(p)
text = "<b>$title</b>, "\
"<small>version:</small> <span weight='bold' color='orange'>$version</span>, "\
"<small>type: <i><span color='#559'>$type</span></i> "\
"category: <i><span color='blue'>$category</span></i></small>\n"\
"<span variant='smallcaps' color='#333'>$description</span>\n"\
"<span size='small' color='#532' weight='ultralight'>$extras, <a href='view-source:$file'>view src</a></span>"
self.add_(b, safe_format(text, **p), markup=1)
# Add placeholder fields
def update_p(self, p):
extras = ["{}: <b>{}</b>".format(n, p[n]) for n in ("status", "support", "author", "depends") if p.get(n)]
p["extras"] = " ".join(["💁"] + extras)
p["file"] = p["$file"]
for field in ("version", "title", "description", "type", "category"):
p.setdefault(field, "-")
return p
# 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))
# Empty out [channels] and [feature] tab in configdialog, so it rereads them
def clean_config_vboxen(self, *w):
self.parent.configwin.first_open = 1
for vbox in [self.parent.plugin_options, self.parent.feature_options]:
for c in vbox.get_children()[1:]:
vbox.remove(c)
# Activate/deactivate changed plugins
def activate_plugins(self, *w):
if not conf.plugin_auto:
return
p = self.parent
for name,act in conf.plugins.items():
# disable channel plugin
if not act and name in p.channels:
p.notebook_channels.remove_page(p.channel_names.index(name))
del p.channels[name]
# feature plugins usually have to many hooks
if not act and name in p.features:
log.WARN("Cannot disable feature plugin '{}'.".format(name))
p.status("Disabling feature plugins requires a restart.")
# just let main load any new plugins
p.load_plugin_channels()
# Alternative to .format(), with keys possibly being absent
from string import Template
def safe_format(str, **kwargs):
return Template(str).safe_substitute(**kwargs)
|