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 |