Skip to content

Commit

Permalink
Add our own kodiutils library
Browse files Browse the repository at this point in the history
This PR includes:
- Introduce a kodiutils library
- Import some library to blocks where they are needed
- Import only the stuff we need from libraries
- Add proxy-support testing
  • Loading branch information
dagwieers committed Sep 17, 2019
1 parent ea25ede commit ea821b7
Show file tree
Hide file tree
Showing 19 changed files with 364 additions and 250 deletions.
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ script:
- pylint lib/ test/
#- kodi-addon-checker . --branch=krypton
#- kodi-addon-checker . --branch=leia
- coverage run default.py
- coverage run -a default.py
- proxy.py &
- coverage run -m unittest discover
- pkill -ef proxy.py

after_success:
- codecov
11 changes: 8 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ zip_dir = $(name)/

blue = \e[1;34m
white = \e[1;37m
reset = \e[0m
reset = \e[0;39m

.PHONY: test

Expand All @@ -43,9 +43,14 @@ addon: clean

unit: clean
@echo -e "$(white)=$(blue) Starting unit tests$(reset)"
python default.py
-pkill -ef proxy.py
proxy.py &
python -m unittest discover
# coverage run -m unittest discover
pkill -ef proxy.py

run:
@echo -e "$(white)=$(blue) Run CLI$(reset)"
python default.py

zip: clean
@echo -e "$(white)=$(blue) Building new package$(reset)"
Expand Down
311 changes: 100 additions & 211 deletions lib/inputstreamhelper/__init__.py

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion lib/inputstreamhelper/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
''' This is the actual InputStream Helper API script '''

from __future__ import absolute_import, division, unicode_literals
from inputstreamhelper import ADDON, Helper, log
from inputstreamhelper import Helper
from .kodiutils import ADDON, log


def run(params):
Expand Down
29 changes: 20 additions & 9 deletions lib/inputstreamhelper/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,26 +80,37 @@

# Last updated: 2019-08-20 (version 12239.67.0)
CHROMEOS_RECOVERY_ARM_HWIDS = [
'BOB',
'WHITETIP',
'SKATE',
'SPRING',
'SNOW',
'ELM',
'HANA',
# 'ARKHAM',
'BIG',
'BLAZE',
'RELM',
'BOB',
# 'DAISY',
'DRUWL',
'DUMO',
'SCARLET',
'ELM',
'EXPRESSO',
'FIEVEL',
'HANA',
'JAQ',
'JERRY',
'KEVIN',
'KITTY',
'MICKEY',
'MIGHTY',
'MINNIE',
'PHASER',
'PHASER360',
'PI',
'PIT',
'RELM',
'SCARLET',
'SKATE',
'SNOW',
'SPEEDY',
'SPRING',
'TIGER',
# 'WHIRLWIND',
'WHITETIP',
]

CHROMEOS_BLOCK_SIZE = 512
Expand Down
129 changes: 129 additions & 0 deletions lib/inputstreamhelper/kodiutils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

''' Implements Kodi Helper functions '''
from __future__ import absolute_import, division, unicode_literals
import xbmc
from xbmcgui import Dialog
from xbmcaddon import Addon
from.unicodehelper import from_unicode, to_unicode

ADDON = Addon('script.module.inputstreamhelper')


class SafeDict(dict):
''' A safe dictionary implementation that does not break down on missing keys '''
def __missing__(self, key):
''' Replace missing keys with the original placeholder '''
return '{' + key + '}'


def has_socks():
''' Test if socks is installed, and remember this information '''

# If it wasn't stored before, check if socks is installed
if not hasattr(has_socks, 'installed'):
try:
import socks # noqa: F401; pylint: disable=unused-variable,unused-import
has_socks.installed = True
except ImportError:
has_socks.installed = False
return None # Detect if this is the first run

# Return the stored value
return has_socks.installed


def localize(string_id, **kwargs):
''' Return the translated string from the .po language files, optionally translating variables '''
if kwargs:
import string
return string.Formatter().vformat(ADDON.getLocalizedString(string_id), (), SafeDict(**kwargs))

return ADDON.getLocalizedString(string_id)


def get_setting(setting_id, default=None):
''' Get an add-on setting '''
value = to_unicode(ADDON.getSetting(setting_id))
if value == '' and default is not None:
return default
return value


def set_setting(setting_id, setting_value):
''' Set an add-on setting '''
return ADDON.setSetting(setting_id, setting_value)


def get_global_setting(setting):
''' Get a Kodi setting '''
result = execute_jsonrpc(dict(jsonrpc='2.0', id=1, method='Settings.GetSettingValue', params=dict(setting='%s' % setting)))
return result.get('result', dict()).get('value')


def get_proxies():
''' Return a usable proxies dictionary from Kodi proxy settings '''
usehttpproxy = get_global_setting('network.usehttpproxy')
if usehttpproxy is not True:
return None

try:
httpproxytype = int(get_global_setting('network.httpproxytype'))
except ValueError:
httpproxytype = 0

socks_supported = has_socks()
if httpproxytype != 0 and not socks_supported:
# Only open the dialog the first time (to avoid multiple popups)
if socks_supported is None:
Dialog().ok('', localize(30042)) # Requires PySocks
return None

proxy_types = ['http', 'socks4', 'socks4a', 'socks5', 'socks5h']
if 0 <= httpproxytype < 5:
httpproxyscheme = proxy_types[httpproxytype]
else:
httpproxyscheme = 'http'

httpproxyserver = get_global_setting('network.httpproxyserver')
httpproxyport = get_global_setting('network.httpproxyport')
httpproxyusername = get_global_setting('network.httpproxyusername')
httpproxypassword = get_global_setting('network.httpproxypassword')

if httpproxyserver and httpproxyport and httpproxyusername and httpproxypassword:
proxy_address = '%s://%s:%s@%s:%s' % (httpproxyscheme, httpproxyusername, httpproxypassword, httpproxyserver, httpproxyport)
elif httpproxyserver and httpproxyport and httpproxyusername:
proxy_address = '%s://%s@%s:%s' % (httpproxyscheme, httpproxyusername, httpproxyserver, httpproxyport)
elif httpproxyserver and httpproxyport:
proxy_address = '%s://%s:%s' % (httpproxyscheme, httpproxyserver, httpproxyport)
elif httpproxyserver:
proxy_address = '%s://%s' % (httpproxyscheme, httpproxyserver)
else:
return None

return dict(http=proxy_address, https=proxy_address)


def get_userdata_path():
''' Return the profile's userdata path '''
return to_unicode(xbmc.translatePath(ADDON.getAddonInfo('profile')))


def get_addon_info(key):
''' Return addon information '''
return to_unicode(ADDON.getAddonInfo(key))


def execute_jsonrpc(payload):
''' Kodi JSON-RPC request. Return the response in a dictionary. '''
import json
log('jsonrpc payload: {payload}', payload=payload)
response = xbmc.executeJSONRPC(json.dumps(payload))
log('jsonrpc response: {response}', response=response)
return json.loads(response)


def log(msg, **kwargs):
''' InputStream Helper log method '''
xbmc.log(msg=from_unicode('[{addon}]: {msg}'.format(addon=get_addon_info('id'), msg=msg.format(**kwargs))), level=xbmc.LOGDEBUG)
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
codecov
kodi-addon-checker
polib
proxy.py
pylint
tox-travis
3 changes: 3 additions & 0 deletions test/test_api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2019, Dag Wieers (@dagwieers) <[email protected]>
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

# pylint: disable=invalid-name,missing-docstring

from __future__ import absolute_import, division, print_function, unicode_literals
Expand Down
3 changes: 3 additions & 0 deletions test/test_ishelper_android_arm.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2019, Dag Wieers (@dagwieers) <[email protected]>
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

# pylint: disable=duplicate-code,invalid-name,missing-docstring,protected-access

from __future__ import absolute_import, division, print_function, unicode_literals
Expand Down
3 changes: 3 additions & 0 deletions test/test_ishelper_linux_arm.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2019, Dag Wieers (@dagwieers) <[email protected]>
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

# pylint: disable=duplicate-code,invalid-name,missing-docstring,protected-access

from __future__ import absolute_import, division, print_function, unicode_literals
Expand Down
3 changes: 3 additions & 0 deletions test/test_ishelper_linux_x64.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2019, Dag Wieers (@dagwieers) <[email protected]>
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

# pylint: disable=duplicate-code,invalid-name,missing-docstring,protected-access

from __future__ import absolute_import, division, print_function, unicode_literals
Expand Down
3 changes: 3 additions & 0 deletions test/test_ishelper_macos_x64.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2019, Dag Wieers (@dagwieers) <[email protected]>
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

# pylint: disable=duplicate-code,invalid-name,missing-docstring,protected-access

from __future__ import absolute_import, division, print_function, unicode_literals
Expand Down
3 changes: 3 additions & 0 deletions test/test_ishelper_windows_x64.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2019, Dag Wieers (@dagwieers) <[email protected]>
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

# pylint: disable=duplicate-code,invalid-name,missing-docstring,protected-access

from __future__ import absolute_import, division, print_function, unicode_literals
Expand Down
59 changes: 59 additions & 0 deletions test/test_proxy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2019, Dag Wieers (@dagwieers) <[email protected]>
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

# pylint: disable=duplicate-code,invalid-name,missing-docstring,protected-access

from __future__ import absolute_import, division, print_function, unicode_literals
import unittest
import platform
import inputstreamhelper

xbmc = __import__('xbmc')
xbmcaddon = __import__('xbmcaddon')
xbmcgui = __import__('xbmcgui')
xbmcvfs = __import__('xbmcvfs')

xbmc.GLOBAL_SETTINGS['network.usehttpproxy'] = True
xbmc.GLOBAL_SETTINGS['network.httpproxytype'] = 0
xbmc.GLOBAL_SETTINGS['network.httpproxyserver'] = '127.0.0.1'
xbmc.GLOBAL_SETTINGS['network.httpproxyport'] = '8899'


class LinuxProxyTests(unittest.TestCase):

def test_check_inputstream_mpd(self):
inputstreamhelper.system_os = lambda: 'Linux'
platform.machine = lambda: 'x86_64'
is_helper = inputstreamhelper.Helper('mpd', drm='com.widevine.alpha')
is_helper.remove_widevine()
is_installed = is_helper.check_inputstream()
self.assertTrue(is_installed, True)

def test_check_inputstream_hls_again(self):
inputstreamhelper.system_os = lambda: 'Linux'
platform.machine = lambda: 'AMD64'
platform.architecture = lambda: ['64bit', '']
is_helper = inputstreamhelper.Helper('hls', drm='com.widevine.alpha')
is_installed = is_helper.check_inputstream()
self.assertTrue(is_installed, True)

def test_check_inputstream_rtmp(self):
inputstreamhelper.system_os = lambda: 'Linux'
platform.machine = lambda: 'x86_64'
is_helper = inputstreamhelper.Helper('rtmp')
is_installed = is_helper.check_inputstream()
self.assertTrue(is_installed, True)

def test_check_inputstream_disabled(self):
inputstreamhelper.system_os = lambda: 'Linux'
platform.machine = lambda: 'x86_64'
is_helper = inputstreamhelper.Helper('mpd', drm='com.widevine.alpha')
is_helper.disable()
is_installed = is_helper.check_inputstream()
is_helper.enable()
self.assertTrue(is_installed, True)


if __name__ == '__main__':
unittest.main()
10 changes: 5 additions & 5 deletions test/userdata/addon_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
},
"inputstream.rtmp": {},
"script.module.inputstreamhelper": {
"disabled": "false",
"last_update": "",
"temp_dir": "special://masterprofile/addon_data/script.module.inputstreamhelper",
"version": "",
"update_frequency": "14"
"disabled": "false",
"last_update": "",
"temp_path": "special://masterprofile/addon_data/script.module.inputstreamhelper",
"update_frequency": "14",
"version": ""
}
}
8 changes: 7 additions & 1 deletion test/userdata/global_settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
{
"locale.language": "resource.language.en_gb"
"locale.language": "resource.language.en_gb",
"network.usehttpproxy": false,
"network.httpproxytype": 0,
"network.httpproxyserver": "http://proxy.server/",
"network.httpproxyport": "8899",
"network.httpproxyusername": "",
"network.httpproxypassword": ""
}
4 changes: 4 additions & 0 deletions test/xbmc.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ def executeJSONRPC(jsonrpccommand):
if command.get('params', {}).get('addonid') == 'script.module.inputstreamhelper':
return json.dumps(dict(id=1, jsonrpc='2.0', result=dict(addon=dict(enabled='true', version='0.3.5'))))
return json.dumps(dict(id=1, jsonrpc='2.0', result=dict(addon=dict(enabled='true', version='1.2.3'))))
if command.get('method') == 'Textures.GetTextures':
return json.dumps(dict(id=1, jsonrpc='2.0', result=dict(textures=[dict(cachedurl="", imagehash="", lasthashcheck="", textureid=4837, url="")])))
if command.get('method') == 'Textures.RemoveTexture':
return json.dumps(dict(id=1, jsonrpc='2.0', result="OK"))
log("executeJSONRPC does not implement method '{method}'".format(**command), 'Error')
return json.dumps(dict(error=dict(code=-1, message='Not implemented'), id=1, jsonrpc='2.0'))

Expand Down
11 changes: 0 additions & 11 deletions test/xbmcextra.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,17 +110,6 @@ def addon_settings():
print("Error: Cannot use 'test/userdata/addon_settings.json' : %s" % e)
settings = {}

# Read credentials from credentials.json
try:
with open('test/userdata/credentials.json') as f:
settings.update(json.load(f))
except (IOError, OSError) as e:
if 'VRTNU_USERNAME' in os.environ and 'VRTNU_PASSWORD' in os.environ:
print('Using credentials from the environment variables VRTNU_USERNAME and VRTNU_PASSWORD')
settings['username'] = os.environ.get('VRTNU_USERNAME')
settings['password'] = os.environ.get('VRTNU_PASSWORD')
else:
print("Error: Cannot use 'test/userdata/credentials.json' : %s" % e)
return settings


Expand Down
Loading

0 comments on commit ea821b7

Please sign in to comment.