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) |