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 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 215 216 217 218 219 220 221 222 223 224 225
|
# encoding: UTF-8
# api: streamtuner2
# title: Gtk2 theme installer
# description: Shows themes in the bookmarks pane for installation
# type: feature
# category: ui
# version: 0.5
# priority: experimental
#
# Downloads a list of Gtk themes and presents it in the bookmarks
# tab under... »themes«. Double clicking will download and install
# a theme right away.
#
# Note that this is primarily meant for Windows, as it unpacks
# *.dll engines if found. Should work on BSD/Linux still, but would
# require setting up .gtkrc, and writeable module_path for engines.
# It only handles Gtk2 themes currently.
#
# Reuses the `conf.theme` setting from the `gtk_theme` plugin, and
# should work in conjunction to it.
#
# Using a repo.json-style format, which mirrors the station row{}
# layout however. Allows later integration with pluginmanager2...
# The bookmark/themes channel provides the nicer UI however.
#
# A theme.zip should contain a structure like:
# --------- ---------- ----- ----
# 62937 2016-12-12 16:39 librezlooks.dll
# 0 2016-12-12 16:40 Rezlooks-dark/
# 0 2016-12-03 20:58 Rezlooks-dark/gtk-2.0/
# 5332 2006-06-30 05:28 Rezlooks-dark/gtk-2.0/gtkrc
# --------- ---------- ----- ----
# With the dll in the root, and theme files in a named subdir.
# Zips are extracted into the config dir ../streamtuner2/themes/
# and copies left there even.
import os, shutil
import csv
import zipfile
import re
import json
import ahttp
from config import *
from config import pluginconf
import uikit
from compat2and3 import *
import action
import channels.favicon as fi
# register a key
class theme_installer(object):
# plugin info
module = "theme_installer"
meta = plugin_meta()
category = "themes"
theme_dir = conf.dir + "/themes/"
themes_url = "http://oha59.bplaced.net/streamtuner2/themes/" #"http://milki.include-once.org/streamtuner2/themes/"
themes_csv = "themes.json"
mime = "zip/gtk-theme"
parent = None
bm = None
# register
def __init__(self, parent):
if not parent:
return
if not uikit.ver == 2:
return
if not "theme" in conf:
conf.theme = "default"
if not os.path.exists(self.theme_dir):
os.mkdir(self.theme_dir)
self.parent = parent
self.bm = parent.bookmarks
# register hooks
action.handler[self.mime] = self.install_handler # zip/gtk-theme downloader
self.bm.add_category(self.category) # add subcategory
self.bm.category_plugins[self.category] = self # reloading theme list
parent.hooks["init"].append(self.apply_theme) # load gtk theme on start
# gtk.rc_parse() called on configwin.save and ST2 startup
def apply_theme(self, now=True):
if conf.theme == "default":
return
# look if theme exists
fn = "%s%s/%s" % (self.theme_dir, conf.theme, "gtk-2.0/gtkrc")
if not os.path.exists(fn):
return
log.GTK_THEME_FILE(fn)
# .GTKRC/Gtk2
uikit.gtk.rc_parse_string("module_path \"%s:%s\"\n" % (uikit.gtk.rc_get_module_dir(), self.theme_dir))
uikit.gtk.rc_parse(fn)
uikit.gtk.rc_reset_styles(uikit.gtk.settings_get_for_screen(uikit.gtk.gdk.screen_get_default()))
# download list of themes
def update_streams(self, cat):
r = []
data = ahttp.get(self.themes_url + self.themes_csv)
#-- repo.JSON
if re.match("^\s*\[\s*\{", data):
r = json.loads(data)
# can contain a literal rows-list + repo meta data
#-- themes.CSV
else:
for row in re.findall("^(?!#)\s*(.+?),\s*(.+?),\s*(.+?),\s*(.+?),\s*(.+)", data, re.M):
# prepare row
d = dict(
genre = "gtk2",
url = row[0],
img = row[1],
title = row[2],
playing = row[3],
homepage = row[4],
state = "gtk-zoom-fit",
format = self.mime,
listformat = "href"
)
# add
r.append(d)
# filter on depends: (such as "gtk => 2.0, win32")
r = [d for d in r if self.deps(d)]
# convert relative references
for d in r:
for field in ("url", "img", '$file'):
v = str(d.get(field))
if v and v.find("://") < 0:
d[field] = self.themes_url + v
d["title"] = "\n%s\n" % d.get("title", "-")
# predownload favicons
for d in r:
d["favicon"] = fi.row_to_fn(d)
if not os.path.exists(d["favicon"]):
fi.banner_localcopy(d["img"], d["favicon"], 64)
log.COPY( d["img"], d["favicon"] )
return r
# invoked by action. module when encounterin a zip/gtk-theme links
def install_handler(self, row, audioformat, source, assoc):
if not "url" in row:
return
# download
log.THEME_INSTALL(row["url"])
zip = self.theme_dir + os.path.basename(row["url"])
#if not os.path.exists(zip):
with open(zip, "wb") as f:
f.write(ahttp.get(row["url"], binary=True))
# extract
z = zipfile.ZipFile(zip)
z.extractall(self.theme_dir)
z.close()
os.remove(zip)
ls = z.namelist()
dll = [fn for fn in ls if re.search("\w+\.(dll|so)$", fn)]
base = [m.group(1) for fn in ls for m in [re.match("^([\w\s\-\.]+)/gtk-2.0/.+", fn)] if m]
# move *.dll / *.so
for gtk_dir in uikit.gtk.rc_get_module_dir().split(";" if conf.windows else ":"):
if os.path.exists(gtk_dir) and os.access(gtk_dir, os.W_OK):
for fn in dll:
if fn.find("/") > 0: # create lib/engines/.../ if given
try: os.makedirs(self.theme_dir + os.path.basename(fn))
except: pass # copy file
try:
if shutil.copy(self.theme_dir + fn, gtk_dir):
break
except Exception as e: #access denied - either 'file in use'
if not os.path.exists(gtk_dir + "/" + fn): # or missing file system rights
log.THEME_INSTALL("Copy Gtk theme engine error ", e)
self.parent.status('<span background="orange">⛔ Set theme unsuccessful. - Check access rights!</span>', timeout=22, markup=1)
self.clear_theme(ls, dll)
return
else:
if conf.windows:
log.THEME_INSTALL("Copy Gtk theme engine error, gtk_dir= " + gtk_dir)
self.parent.status('<span background="orange">⛔ Set theme unsuccessful. - Check ' + gtk_dir + '</span>', timeout=22, markup=1)
self.clear_theme(ls, dll)
return
# enable
if dll:
self.clear_dll(dll)
conf.theme = base[0]
self.apply_theme(True)
conf.save()
# delete theme files if application failed
def clear_theme(self, ls, dll):
for fn in ls:
try:
shutil.rmtree(self.theme_dir + fn)
except: pass # probably not found
if dll:
self.clear_dll(dll)
# delete theme engine dll
def clear_dll(self, dll):
for fn in dll:
os.remove(self.theme_dir + fn)
# instantiate pluginconf.dependency()
def deps(self, theme):
self.deps = pluginconf.dependency(add = dict(
gtk = uikit.gtk.gtk_version,
gtk2 = uikit.ver == 2,
gtk3 = uikit.ver == 3,
linux = "-1" if conf.windows else "3.0.0",
win32 = "6.1" if conf.windows else "-1"
)).depends
#log.HAVE(dict((k,d.have[k].get("version")) for k in d.have))
return self.deps(theme)
|