Skip to content

Commit

Permalink
quicktools update
Browse files Browse the repository at this point in the history
  • Loading branch information
jurialmunkey committed May 29, 2022
1 parent 42d0f87 commit c9056dd
Show file tree
Hide file tree
Showing 13 changed files with 461 additions and 304 deletions.
3 changes: 2 additions & 1 deletion addon.xml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<addon id="script.skinvariables"
version="0.2.8"
version="1.0.0"
name="Skin Variables"
provider-name="jurialmunkey">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
</requires>
<extension point="xbmc.python.script" library="script.py" />
<extension point="xbmc.python.pluginsource" library="plugin.py" />
<extension point="xbmc.addon.metadata">
<summary lang="en_GB">Skin Variables helps skinners to construct variables and expressions for multiple containers and listitems using a template</summary>
<summary lang="fr_FR">Skin Variables aide les skinners à créer des variables et des expressions pour plusieurs conteneurs et listes à l'aide d'un modèle</summary>
Expand Down
8 changes: 8 additions & 0 deletions plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
# Module: default
# Author: jurialmunkey
# License: GPL v.3 https://www.gnu.org/copyleft/gpl.html
from resources.lib.plugin import Plugin

if __name__ == '__main__':
Plugin().run()
53 changes: 53 additions & 0 deletions resources/lib/fileutils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
# Module: default
# Author: jurialmunkey
# License: GPL v.3 https://www.gnu.org/copyleft/gpl.html
import xbmc
import xbmcvfs


def make_hash(content):
import hashlib
return hashlib.md5(content.encode('utf-8')).hexdigest()


def check_hash(hashname, hashvalue=None):
last_version = xbmc.getInfoLabel('Skin.String({})'.format(hashname))
if not last_version:
return hashvalue
if hashvalue != last_version:
return hashvalue


def load_filecontent(filename=None):
try:
vfs_file = xbmcvfs.File(filename)
content = vfs_file.read()
finally:
vfs_file.close()
return content


def write_file(filepath=None, content=None):
if not filepath:
return
f = xbmcvfs.File(filepath, 'w')
f.write(content)
f.close()


def write_skinfile(filename=None, folders=None, content=None, hashvalue=None, hashname=None, reloadskin=True, checksum=None):
if not filename or not folders or not content:
return

for folder in folders:
write_file(filepath='special://skin/{}/{}'.format(folder, filename), content=content)

if hashvalue and hashname:
xbmc.executebuiltin('Skin.SetString({},{})'.format(hashname, hashvalue))

if checksum:
xbmc.executebuiltin('Skin.SetString({},{})'.format(checksum, make_hash(content)))

if reloadskin:
xbmc.executebuiltin('ReloadSkin()')
61 changes: 61 additions & 0 deletions resources/lib/jsonrpc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
# Module: default
# Author: jurialmunkey
# License: GPL v.3 https://www.gnu.org/copyleft/gpl.html
import json
import xbmc
import xbmcgui
from resources.lib.kodiutils import kodi_log


def get_jsonrpc(method=None, params=None):
if not method:
return {}
query = {
"jsonrpc": "2.0",
"method": method,
"id": 1}
if params:
query["params"] = params
try:
jrpc = xbmc.executeJSONRPC(json.dumps(query))
response = json.loads(jrpc)
except Exception as exc:
kodi_log(u'SkinVariables - JSONRPC Error:\n{}'.format(exc), 1)
response = {}
return response


PLAYERSTREAMS = {
'audio': {'key': 'audiostreams', 'cur': 'currentaudiostream'},
'subtitle': {'key': 'subtitles', 'cur': 'currentsubtitle'}
}


def get_player_streams(stream_type):
def make_item(i):
label = i.get("language", "UND")
label2 = i.get("name", "")
path = f'plugin://script.skinquicktools/?info=set_player_streams&stream_index={i.get("index")}&stream_type={stream_type}'
infoproperties = {f'{k}': f'{v}' for k, v in i.items() if v}
if cur_strm == i.get('index'):
infoproperties['iscurrent'] = 'true'
infoproperties['isfolder'] = 'false'
listitem = xbmcgui.ListItem(label=label, label2=label2, path=path, offscreen=True)
listitem.setProperties(infoproperties)
return listitem

ps_def = PLAYERSTREAMS.get(stream_type)
if not ps_def:
return []

method = "Player.GetProperties"
params = {"playerid": 1, "properties": [ps_def['key'], ps_def['cur']]}
response = get_jsonrpc(method, params) or {}
response = response.get('result', {})
all_strm = response.get(ps_def['key']) or []
if not all_strm:
return []

cur_strm = response.get(ps_def['cur'], {}).get('index', 0)
return [make_item(i) for i in all_strm if i]
81 changes: 81 additions & 0 deletions resources/lib/kodiutils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
# Module: default
# Author: jurialmunkey
# License: GPL v.3 https://www.gnu.org/copyleft/gpl.html
import xbmc
import xbmcgui
import xbmcaddon
from contextlib import contextmanager
from urllib.parse import unquote_plus


ADDON = xbmcaddon.Addon('script.skinvariables')
ADDONLOGNAME = '[script.skinvariables]\n'


def kodi_log(value, level=0):
try:
if isinstance(value, list):
value = ''.join(map(str, value))
if isinstance(value, bytes):
value = value.decode('utf-8')
logvalue = u'{0}{1}'.format(ADDONLOGNAME, value)
if level == 1:
xbmc.log(logvalue, level=xbmc.LOGINFO)
else:
xbmc.log(logvalue, level=xbmc.LOGDEBUG)
except Exception as exc:
xbmc.log(u'Logging Error: {}'.format(exc), level=xbmc.LOGINFO)


def parse_paramstring(paramstring):
""" helper to assist to standardise urllib parsing """
params = dict()
paramstring = paramstring.replace('&amp;', '&') # Just in case xml string
for param in paramstring.split('&'):
if '=' not in param:
continue
k, v = param.split('=')
params[unquote_plus(k)] = unquote_plus(v)
return params


def try_int(string, base=None, fallback=0):
'''helper to parse int from string without erroring on empty or misformed string'''
try:
return int(string, base) if base else int(string)
except Exception:
return fallback


@contextmanager
def isactive_winprop(name, value='True', windowid=10000):
xbmcgui.Window(windowid).setProperty(name, value)
try:
yield
finally:
xbmcgui.Window(windowid).clearProperty(name)


def del_empty_keys(d, values=[]):
my_dict = d.copy()
for k, v in d.items():
if not v or v in values:
del my_dict[k]
return my_dict


def merge_dicts(org, upd, skipempty=False):
source = org.copy()
for k, v in upd.items():
if not k:
continue
if skipempty and not v:
continue
if isinstance(v, dict):
if not isinstance(source.get(k), dict):
source[k] = {}
source[k] = merge_dicts(source.get(k), v, skipempty=skipempty)
continue
source[k] = v
return source
31 changes: 31 additions & 0 deletions resources/lib/method.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Module: default
# Author: jurialmunkey
# License: GPL v.3 https://www.gnu.org/copyleft/gpl.html
import xbmc
import time
import xbmcgui
from resources.lib.jsonrpc import get_jsonrpc
from resources.lib.kodiutils import try_int


def set_player_subtitle(set_player_subtitle, reload_property='UID', **kwargs):
method = "Player.SetSubtitle"
params = {"playerid": 1, "subtitle": try_int(set_player_subtitle), "enable": True}
get_jsonrpc(method, params)
xbmc.executebuiltin(f'SetProperty({reload_property},{time.time()})')


def set_player_audiostream(set_player_audiostream, reload_property='UID', **kwargs):
method = "Player.SetAudioStream"
params = {"playerid": 1, "stream": try_int(set_player_audiostream)}
get_jsonrpc(method, params)
xbmc.executebuiltin(f'SetProperty({reload_property},{time.time()})')


def set_editcontrol(set_editcontrol, text, window_id=None, setfocus=None, setfocus_wait='00:00', **kwargs):
window_id = int(window_id or xbmcgui.getCurrentWindowId())
window_id += 10000 if window_id < 10000 else 0
my_window = xbmcgui.Window(window_id)
my_window.getControl(int(set_editcontrol)).setText(text)
xbmc.executebuiltin(f'AlarmClock(Refocus,SetFocus({setfocus}),{setfocus_wait},silent)') if setfocus else None
52 changes: 52 additions & 0 deletions resources/lib/plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
# Module: default
# Author: jurialmunkey
# License: GPL v.3 https://www.gnu.org/copyleft/gpl.html
import sys
import xbmcplugin
from resources.lib.kodiutils import parse_paramstring
from resources.lib.jsonrpc import get_player_streams
from resources.lib.method import set_player_subtitle, set_player_audiostream


class Plugin(object):
def __init__(self):
self.handle = int(sys.argv[1])
self.paramstring = sys.argv[2][1:]
self.params = parse_paramstring(self.paramstring)
self.routes = {
'get_player_streams': self.get_player_streams,
'set_player_streams': self.set_player_streams
}

def add_items(self, items, update_listing=False, plugin_category='', container_content=''):
for i in items:
xbmcplugin.addDirectoryItem(handle=self.handle, **i)
xbmcplugin.setPluginCategory(self.handle, plugin_category) # Container.PluginCategory
xbmcplugin.setContent(self.handle, container_content) # Container.Content
xbmcplugin.endOfDirectory(self.handle, updateListing=update_listing)

def set_player_streams(self, stream_type=None, stream_index=None, **kwargs):
if not stream_type or stream_index is None:
return
if stream_type == 'audio':
set_player_audiostream(stream_index)
return
if stream_type == 'subtitle':
set_player_subtitle(stream_index)
return

def get_player_streams(self, stream_type=None, **kwargs):
if not stream_type:
return
items = [
{'url': li.getPath(), 'listitem': li, 'isFolder': li.getProperty('isfolder').lower() == 'true'}
for li in get_player_streams(stream_type) if li]
self.add_items(items)

def run(self):
if not self.params:
return
if self.params.get('info') not in self.routes:
return
self.routes[self.params['info']](**self.params)
64 changes: 41 additions & 23 deletions resources/lib/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,54 @@
# Author: jurialmunkey
# License: GPL v.3 https://www.gnu.org/copyleft/gpl.html
import sys
from resources.lib.skinvariables import SkinVariables
from resources.lib.viewtypes import ViewTypes
from importlib import import_module


def importmodule(module_name, import_attr=None):
module = import_module(module_name)
if not import_attr:
return module
return getattr(module, import_attr)


class Script(object):
def __init__(self):
def map_args(arg):
if '=' in arg:
key, value = arg.split('=', 1)
value = value.strip('\'').strip('"') if value else None
return (key, value)
return (arg, True)

self.params = {}
for arg in sys.argv[1:]:
k, v = map_args(arg)
self.params[k] = v

# lambda **kwargs: importmodule('resources.lib.script.method', 'split_value')(**kwargs),
routing_table = {
'set_player_subtitle':
lambda **kwargs: importmodule('resources.lib.method', 'set_player_subtitle')(**kwargs),
'set_player_audiostream':
lambda **kwargs: importmodule('resources.lib.method', 'set_player_audiostream')(**kwargs),
'set_editcontrol':
lambda **kwargs: importmodule('resources.lib.method', 'set_editcontrol')(**kwargs),
}

def get_params(self):
for arg in sys.argv:
if arg == 'script.py':
pass
elif '=' in arg:
arg_split = arg.split('=', 1)
if arg_split[0] and arg_split[1]:
key, value = arg_split
self.params.setdefault(key, value)
else:
self.params.setdefault(arg, True)
def run(self):
if not self.params:
return
routes_available, params_given = set(self.routing_table.keys()), set(self.params.keys())
try:
route_taken = set.intersection(routes_available, params_given).pop()
except KeyError:
return self.router()
return self.routing_table[route_taken](**self.params)

def router(self):
if self.params.get('action') == 'buildviews':
ViewTypes().update_xml(
skinfolder=self.params.get('folder'),
force=self.params.get('force'),
configure=self.params.get('configure'),
contentid=self.params.get('contentid'),
viewid=self.params.get('viewid'),
pluginname=self.params.get('pluginname'))
from resources.lib.viewtypes import ViewTypes
return ViewTypes().update_xml(skinfolder=self.params.get('folder'), **self.params)
else:
SkinVariables().update_xml(
skinfolder=self.params.get('folder'),
force=self.params.get('force'))
from resources.lib.skinvariables import SkinVariables
return SkinVariables().update_xml(skinfolder=self.params.get('folder'), **self.params)
Loading

0 comments on commit c9056dd

Please sign in to comment.