-
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
|
||||
Copyright (C) 2013-2014 Team-XBMC
|
||||
Copyright (C) 2014-2019 Team Kodi
|
||||
|
||||
This file is part of service.xbmc.versioncheck
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
See LICENSES/GPL-3.0-or-later.txt for more information.
|
||||
|
||||
"""
|
||||
|
||||
__all__ = ['apt_daemon_handler', 'common', 'handler', 'json_interface', 'service',
|
||||
'shell_handler_apt', 'versions', 'viewer']
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,120 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
|
||||
Copyright (C) 2013-2014 Team-XBMC
|
||||
Copyright (C) 2014-2019 Team Kodi
|
||||
|
||||
This file is part of service.xbmc.versioncheck
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
See LICENSES/GPL-3.0-or-later.txt for more information.
|
||||
|
||||
"""
|
||||
|
||||
from .common import log
|
||||
from .handler import Handler
|
||||
|
||||
try:
|
||||
import apt
|
||||
from aptdaemon import client
|
||||
from aptdaemon import errors
|
||||
except ImportError:
|
||||
apt = None
|
||||
client = None
|
||||
errors = None
|
||||
log('ImportError: apt, aptdaemon')
|
||||
|
||||
|
||||
class AptDaemonHandler(Handler):
|
||||
""" Apt daemon handler
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Handler.__init__(self)
|
||||
self.apt_client = client.AptClient()
|
||||
|
||||
def _check_versions(self, package):
|
||||
""" Check apt package versions
|
||||
|
||||
:param package: package to check
|
||||
:type package: str
|
||||
:return: installed version, candidate version
|
||||
:rtype: str, str / False, False
|
||||
"""
|
||||
if self.update and not self._update_cache():
|
||||
return False, False
|
||||
try:
|
||||
trans = self.apt_client.upgrade_packages([package])
|
||||
# trans = self.apt_client.upgrade_packages('bla')
|
||||
trans.simulate(reply_handler=self._apt_trans_started,
|
||||
error_handler=self._apt_error_handler)
|
||||
pkg = trans.packages[4][0]
|
||||
if pkg == package:
|
||||
cache = apt.Cache()
|
||||
cache.open(None)
|
||||
cache.upgrade()
|
||||
if cache[pkg].installed:
|
||||
return cache[pkg].installed.version, cache[pkg].candidate.version
|
||||
|
||||
return False, False
|
||||
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
log('Exception while checking versions: %s' % error)
|
||||
return False, False
|
||||
|
||||
def _update_cache(self):
|
||||
""" Update apt client cache
|
||||
|
||||
:return: success of updating apt cache
|
||||
:rtype: bool
|
||||
"""
|
||||
try:
|
||||
return self.apt_client.update_cache(wait=True) == 'exit-success'
|
||||
except errors.NotAuthorizedError:
|
||||
log('You are not allowed to update the cache')
|
||||
return False
|
||||
|
||||
def upgrade_package(self, package):
|
||||
""" Upgrade apt package
|
||||
|
||||
:param package: package to upgrade
|
||||
:type package: str
|
||||
:return: success of apt package upgrade
|
||||
:rtype: bool
|
||||
"""
|
||||
try:
|
||||
log('Installing new version')
|
||||
if self.apt_client.upgrade_packages([package], wait=True) == 'exit-success':
|
||||
log('Upgrade successful')
|
||||
return True
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
log('Exception during upgrade: %s' % error)
|
||||
return False
|
||||
|
||||
def upgrade_system(self):
|
||||
""" Upgrade system
|
||||
|
||||
:return: success of system upgrade
|
||||
:rtype: bool
|
||||
"""
|
||||
try:
|
||||
log('Upgrading system')
|
||||
if self.apt_client.upgrade_system(wait=True) == 'exit-success':
|
||||
return True
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
log('Exception during system upgrade: %s' % error)
|
||||
return False
|
||||
|
||||
def _apt_trans_started(self):
|
||||
""" Apt transfer reply handler
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def _apt_error_handler(error):
|
||||
""" Apt transfer error handler
|
||||
|
||||
:param error: apt error message
|
||||
:type error: str
|
||||
"""
|
||||
log('Apt Error %s' % error)
|
||||
@@ -0,0 +1,283 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
|
||||
Copyright (C) 2013-2014 Team-XBMC
|
||||
Copyright (C) 2014-2019 Team Kodi
|
||||
|
||||
This file is part of service.xbmc.versioncheck
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
See LICENSES/GPL-3.0-or-later.txt for more information.
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
import xbmc # pylint: disable=import-error
|
||||
import xbmcaddon # pylint: disable=import-error
|
||||
import xbmcgui # pylint: disable=import-error
|
||||
import xbmcvfs # pylint: disable=import-error
|
||||
|
||||
try:
|
||||
xbmc.translatePath = xbmcvfs.translatePath
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
ADDON = xbmcaddon.Addon('service.xbmc.versioncheck')
|
||||
ADDON_VERSION = ADDON.getAddonInfo('version')
|
||||
ADDON_NAME = ADDON.getAddonInfo('name')
|
||||
if sys.version_info[0] >= 3:
|
||||
ADDON_PATH = ADDON.getAddonInfo('path')
|
||||
ADDON_PROFILE = xbmc.translatePath(ADDON.getAddonInfo('profile'))
|
||||
else:
|
||||
ADDON_PATH = ADDON.getAddonInfo('path').decode('utf-8')
|
||||
ADDON_PROFILE = xbmc.translatePath(ADDON.getAddonInfo('profile')).decode('utf-8')
|
||||
ICON = ADDON.getAddonInfo('icon')
|
||||
KODI_VERSION_MAJOR = int(xbmc.getInfoLabel('System.BuildVersion')[0:2])
|
||||
|
||||
MONITOR = xbmc.Monitor()
|
||||
|
||||
|
||||
# Fixes unicode problems
|
||||
def string_unicode(text, encoding='utf-8'):
|
||||
""" Python 2/3 -> unicode/str
|
||||
|
||||
:param text: text to convert
|
||||
:type text: unicode (py2) / str (py3) / bytes (py3)
|
||||
:param encoding: text encoding
|
||||
:type encoding: str
|
||||
:return: converted text
|
||||
:rtype: unicode (py2) / str (py3)
|
||||
"""
|
||||
try:
|
||||
if sys.version_info[0] >= 3:
|
||||
text = str(text)
|
||||
else:
|
||||
text = unicode(text, encoding) # pylint: disable=undefined-variable
|
||||
except: # pylint: disable=bare-except
|
||||
pass
|
||||
return text
|
||||
|
||||
|
||||
def normalize_string(text):
|
||||
""" Normalize string
|
||||
|
||||
:param text: text to normalize
|
||||
:type text: unicode (py2) / str (py3) / bytes (py3)
|
||||
:return: normalized text
|
||||
:rtype: unicode (py2) / str (py3)
|
||||
"""
|
||||
try:
|
||||
text = unicodedata.normalize('NFKD', string_unicode(text)).encode('ascii', 'ignore') # pylint: disable=undefined-variable
|
||||
except: # pylint: disable=bare-except
|
||||
pass
|
||||
return text
|
||||
|
||||
|
||||
def localise(string_id):
|
||||
""" Localise string id
|
||||
|
||||
:param string_id: id of the string to localise
|
||||
:type string_id: int
|
||||
:return: localised string
|
||||
:rtype: unicode (py2) / str (py3)
|
||||
"""
|
||||
string = normalize_string(ADDON.getLocalizedString(string_id))
|
||||
return string
|
||||
|
||||
|
||||
def log(txt):
|
||||
""" Log text at xbmc.LOGDEBUG level
|
||||
|
||||
:param txt: text to log
|
||||
:type txt: str / unicode / bytes (py3)
|
||||
"""
|
||||
if sys.version_info[0] >= 3:
|
||||
if isinstance(txt, bytes):
|
||||
txt = txt.decode('utf-8')
|
||||
message = '%s: %s' % (ADDON_NAME, txt)
|
||||
else:
|
||||
if isinstance(txt, str):
|
||||
txt = txt.decode('utf-8')
|
||||
message = (u'%s: %s' % (ADDON_NAME, txt)).encode('utf-8') # pylint: disable=redundant-u-string-prefix
|
||||
xbmc.log(msg=message, level=xbmc.LOGDEBUG)
|
||||
|
||||
|
||||
def notification(heading, message, icon=None, time=15000, sound=True):
|
||||
""" Create a notification
|
||||
|
||||
:param heading: notification heading
|
||||
:type heading: str
|
||||
:param message: notification message
|
||||
:type message: str
|
||||
:param icon: path and filename for the notification icon
|
||||
:type icon: str
|
||||
:param time: time to display notification
|
||||
:type time: int
|
||||
:param sound: is notification audible
|
||||
:type sound: bool
|
||||
"""
|
||||
if not icon:
|
||||
icon = ICON
|
||||
xbmcgui.Dialog().notification(heading, message, icon, time, sound)
|
||||
|
||||
|
||||
def get_password_from_user():
|
||||
""" Prompt user to input password
|
||||
|
||||
:return: password
|
||||
:rtype: str
|
||||
"""
|
||||
pwd = ''
|
||||
keyboard = xbmc.Keyboard('', ADDON_NAME + ': ' + localise(32022), True)
|
||||
keyboard.doModal()
|
||||
if keyboard.isConfirmed():
|
||||
pwd = keyboard.getText()
|
||||
return pwd
|
||||
|
||||
|
||||
def message_upgrade_success():
|
||||
""" Upgrade success notification
|
||||
"""
|
||||
notification(ADDON_NAME, localise(32013))
|
||||
|
||||
|
||||
def message_restart():
|
||||
""" Prompt user to restart Kodi
|
||||
"""
|
||||
if dialog_yes_no(32014):
|
||||
xbmc.executebuiltin('RestartApp')
|
||||
|
||||
|
||||
def dialog_yes_no(line1=0, line2=0):
|
||||
""" Prompt user with yes/no dialog
|
||||
|
||||
:param line1: string id for the first line of the dialog
|
||||
:type line1: int
|
||||
:param line2: string id for the second line of the dialog
|
||||
:type line2: int
|
||||
:return: users selection (yes / no)
|
||||
:rtype: bool
|
||||
"""
|
||||
return xbmcgui.Dialog().yesno(ADDON_NAME, '[CR]'.join([localise(line1), localise(line2)]))
|
||||
|
||||
|
||||
def linux_upgrade_message(msg):
|
||||
""" Prompt user with upgrade suggestion message
|
||||
|
||||
:param msg: string id for prompt message
|
||||
:type msg: int
|
||||
"""
|
||||
wait_for_end_of_video()
|
||||
|
||||
if ADDON.getSetting('lastnotified_version') < ADDON_VERSION:
|
||||
answer = xbmcgui.Dialog().ok(
|
||||
ADDON_NAME,
|
||||
'[CR]'.join([localise(msg), localise(32001), localise(32002)])
|
||||
)
|
||||
else:
|
||||
answer = False
|
||||
log('Already notified one time for upgrading.')
|
||||
|
||||
return answer
|
||||
|
||||
|
||||
def non_linux_upgrade_message(version_installed, version_available, version_stable, old_version):
|
||||
""" Prompt user with upgrade suggestion message
|
||||
|
||||
:param version_installed: currently installed version
|
||||
:type version_installed: dict
|
||||
:param version_available: available version
|
||||
:type version_available: dict
|
||||
:param version_stable: latest stable version
|
||||
:type version_stable: dict
|
||||
:param old_version: whether using an old version
|
||||
:type old_version: bool / 'stable'
|
||||
"""
|
||||
# shorten releasecandidate to rc
|
||||
if version_installed['tag'] == 'releasecandidate':
|
||||
version_installed['tag'] = 'rc'
|
||||
if version_available['tag'] == 'releasecandidate':
|
||||
version_available['tag'] = 'rc'
|
||||
# convert json-rpc result to strings for usage
|
||||
msg_current = '%i.%i %s%s' % (version_installed['major'],
|
||||
version_installed['minor'],
|
||||
version_installed['tag'],
|
||||
version_installed.get('tagversion', ''))
|
||||
msg_available = version_available['major'] + '.' + version_available['minor'] + ' ' + \
|
||||
version_available['tag'] + version_available.get('tagversion', '')
|
||||
msg_stable = version_stable['major'] + '.' + version_stable['minor'] + ' ' + \
|
||||
version_stable['tag'] + version_stable.get('tagversion', '')
|
||||
msg = localise(32034) % (msg_current, msg_available)
|
||||
|
||||
wait_for_end_of_video()
|
||||
|
||||
# hack: convert current version number to stable string
|
||||
# so users don't get notified again. remove in future
|
||||
if ADDON.getSetting('lastnotified_version') == '0.1.24':
|
||||
ADDON.setSetting('lastnotified_stable', msg_stable)
|
||||
|
||||
# Show different dialogs depending on if there's a newer stable available.
|
||||
# Also split them between xbmc and kodi notifications to reduce possible confusion.
|
||||
# People will find out once they visit the website.
|
||||
# For stable only notify once and when there's a newer stable available.
|
||||
# Ignore any add-on updates as those only count for != stable
|
||||
if old_version == 'stable' and ADDON.getSetting('lastnotified_stable') != msg_stable:
|
||||
xbmcgui.Dialog().ok(ADDON_NAME, '[CR]'.join([msg, localise(32032), localise(32033)]))
|
||||
ADDON.setSetting('lastnotified_stable', msg_stable)
|
||||
|
||||
elif old_version != 'stable' and ADDON.getSetting('lastnotified_version') != msg_available:
|
||||
xbmcgui.Dialog().ok(ADDON_NAME, '[CR]'.join([msg, localise(32035), localise(32033)]))
|
||||
|
||||
ADDON.setSetting('lastnotified_version', msg_available)
|
||||
|
||||
else:
|
||||
log('Already notified one time for upgrading.')
|
||||
|
||||
|
||||
def abort_requested():
|
||||
""" Kodi 13+ compatible xbmc.Monitor().abortRequested()
|
||||
|
||||
:return: whether abort requested
|
||||
:rtype: bool
|
||||
"""
|
||||
if KODI_VERSION_MAJOR > 13:
|
||||
return MONITOR.abortRequested()
|
||||
|
||||
return xbmc.abortRequested
|
||||
|
||||
|
||||
def wait_for_abort(seconds):
|
||||
""" Kodi 13+ compatible xbmc.Monitor().waitForAbort()
|
||||
|
||||
:param seconds: seconds to wait for abort
|
||||
:type seconds: int / float
|
||||
:return: whether abort was requested
|
||||
:rtype: bool
|
||||
"""
|
||||
if KODI_VERSION_MAJOR > 13:
|
||||
return MONITOR.waitForAbort(seconds)
|
||||
|
||||
for _ in range(0, seconds * 1000 / 200):
|
||||
if xbmc.abortRequested:
|
||||
return True
|
||||
xbmc.sleep(200)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def wait_for_end_of_video():
|
||||
""" Wait for video playback to end
|
||||
"""
|
||||
# Don't show notify while watching a video
|
||||
while xbmc.Player().isPlayingVideo() and not abort_requested():
|
||||
if wait_for_abort(1):
|
||||
# Abort was requested while waiting. We should exit
|
||||
break
|
||||
i = 0
|
||||
while i < 10 and not abort_requested():
|
||||
if wait_for_abort(1):
|
||||
# Abort was requested while waiting. We should exit
|
||||
break
|
||||
i += 1
|
||||
@@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
See LICENSES/Apache-2.0.txt for more information.
|
||||
|
||||
"""
|
||||
|
||||
__all__ = ['distro']
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,95 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
|
||||
Copyright (C) 2019 Team Kodi
|
||||
|
||||
This file is part of service.xbmc.versioncheck
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
See LICENSES/GPL-3.0-or-later.txt for more information.
|
||||
|
||||
"""
|
||||
|
||||
from .common import get_password_from_user
|
||||
from .common import log
|
||||
|
||||
|
||||
class Handler:
|
||||
""" Base handler class for apt_daemon_handler, and shell_handler_apt
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._pwd = ''
|
||||
self._update = True
|
||||
|
||||
@property
|
||||
def pwd(self):
|
||||
""" password property
|
||||
|
||||
:return: password
|
||||
:rtype: str
|
||||
"""
|
||||
return self._pwd
|
||||
|
||||
@pwd.setter
|
||||
def pwd(self, value):
|
||||
""" password setter
|
||||
|
||||
:param value: password
|
||||
:type value: str
|
||||
"""
|
||||
self._pwd = value
|
||||
|
||||
@property
|
||||
def update(self):
|
||||
""" update apt-cache property
|
||||
|
||||
:return: whether to update apt-cache or not when checking for upgrades
|
||||
:rtype: bool
|
||||
"""
|
||||
return self._update
|
||||
|
||||
@update.setter
|
||||
def update(self, value):
|
||||
""" update apt-cache setter
|
||||
|
||||
:param value: whether to update apt-cache or not when checking for upgrades
|
||||
:type value: bool
|
||||
"""
|
||||
self._update = value
|
||||
|
||||
def _check_versions(self, package):
|
||||
raise NotImplementedError
|
||||
|
||||
def check_upgrade_available(self, package):
|
||||
""" Check if package upgrade is available
|
||||
|
||||
:param package: package to check for upgrade availability
|
||||
:type package: str
|
||||
:return: whether an upgrade exists for the provided package
|
||||
:rtype: bool
|
||||
"""
|
||||
installed, candidate = self._check_versions(package)
|
||||
if installed and candidate:
|
||||
if installed != candidate:
|
||||
log('Version installed %s' % installed)
|
||||
log('Version available %s' % candidate)
|
||||
return True
|
||||
log('Already on newest version')
|
||||
return False
|
||||
|
||||
if not installed:
|
||||
log('No installed package found')
|
||||
|
||||
return False
|
||||
|
||||
def _get_password(self):
|
||||
""" Get password, ask user for password if not known
|
||||
|
||||
:return: password
|
||||
:rtype: str
|
||||
"""
|
||||
if not self.pwd:
|
||||
self.pwd = get_password_from_user()
|
||||
return self.pwd
|
||||
@@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
|
||||
Copyright (C) 2013-2014 Team-XBMC
|
||||
Copyright (C) 2014-2019 Team Kodi
|
||||
|
||||
This file is part of service.xbmc.versioncheck
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
See LICENSES/GPL-3.0-or-later.txt for more information.
|
||||
|
||||
"""
|
||||
|
||||
from contextlib import closing
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
import xbmc # pylint: disable=import-error
|
||||
import xbmcvfs # pylint: disable=import-error
|
||||
|
||||
from .common import ADDON_PATH
|
||||
|
||||
|
||||
def get_installed_version():
|
||||
""" Retrieve the currently installed version
|
||||
|
||||
:return: currently installed version
|
||||
:rtype: dict
|
||||
"""
|
||||
query = {
|
||||
"jsonrpc": "2.0",
|
||||
"method": "Application.GetProperties",
|
||||
"params": {
|
||||
"properties": ["version", "name"]
|
||||
},
|
||||
"id": 1
|
||||
}
|
||||
json_query = xbmc.executeJSONRPC(json.dumps(query))
|
||||
if sys.version_info[0] >= 3:
|
||||
json_query = str(json_query)
|
||||
else:
|
||||
json_query = unicode(json_query, 'utf-8', errors='ignore') # pylint: disable=undefined-variable
|
||||
json_query = json.loads(json_query)
|
||||
version_installed = []
|
||||
if 'result' in json_query and 'version' in json_query['result']:
|
||||
version_installed = json_query['result']['version']
|
||||
return version_installed
|
||||
|
||||
|
||||
def get_version_file_list():
|
||||
""" Retrieve version lists from supplied version file (resources/versions.txt)
|
||||
|
||||
:return: all provided versions
|
||||
:rtype: dict
|
||||
"""
|
||||
version_file = os.path.join(ADDON_PATH, 'resources/versions.txt')
|
||||
with closing(xbmcvfs.File(version_file)) as open_file:
|
||||
data = open_file.read()
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
version_query = str(data)
|
||||
else:
|
||||
version_query = unicode(data, 'utf-8', errors='ignore') # pylint: disable=undefined-variable
|
||||
version_query = json.loads(version_query)
|
||||
return version_query
|
||||
@@ -0,0 +1,174 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
|
||||
Copyright (C) 2013-2014 Team-XBMC
|
||||
Copyright (C) 2014-2019 Team Kodi
|
||||
|
||||
This file is part of service.xbmc.versioncheck
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
See LICENSES/GPL-3.0-or-later.txt for more information.
|
||||
|
||||
"""
|
||||
import json
|
||||
import platform
|
||||
import sys
|
||||
|
||||
import xbmc # pylint: disable=import-error
|
||||
import xbmcgui # pylint: disable=import-error
|
||||
|
||||
from .common import ADDON
|
||||
from .common import ADDON_NAME
|
||||
from .common import ADDON_VERSION
|
||||
from .common import dialog_yes_no
|
||||
from .common import localise
|
||||
from .common import log
|
||||
from .common import wait_for_abort
|
||||
from .common import message_restart
|
||||
from .common import message_upgrade_success
|
||||
from .common import linux_upgrade_message
|
||||
from .common import non_linux_upgrade_message
|
||||
from .json_interface import get_version_file_list
|
||||
from .json_interface import get_installed_version
|
||||
from .versions import compare_version
|
||||
|
||||
|
||||
DISTRIBUTION = ''
|
||||
|
||||
if sys.platform.startswith('linux'):
|
||||
if sys.version_info[0] == 3 and sys.version_info[1] >= 8:
|
||||
try:
|
||||
from .distro import distro
|
||||
|
||||
DISTRIBUTION = distro.linux_distribution(full_distribution_name=False)[0].lower()
|
||||
|
||||
except (AttributeError, ImportError):
|
||||
DISTRIBUTION = ''
|
||||
|
||||
else:
|
||||
DISTRIBUTION = platform.linux_distribution(full_distribution_name=0)[0].lower() # pylint: disable=no-member
|
||||
|
||||
if not DISTRIBUTION:
|
||||
DISTRIBUTION = platform.uname()[0].lower()
|
||||
|
||||
# webOS
|
||||
try:
|
||||
with open('/var/run/nyx/os_info.json', 'r') as os_info_file:
|
||||
json_info = json.load(os_info_file)
|
||||
if 'webos_name' in json_info.keys():
|
||||
DISTRIBUTION = 'webos'
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
|
||||
def _version_check():
|
||||
""" Check versions (non-linux)
|
||||
|
||||
:return: old, current, available, and stable versions
|
||||
:rtype: bool / 'stable', dict, dict, dict
|
||||
"""
|
||||
# retrieve version_lists from supplied version file
|
||||
version_list = get_version_file_list()
|
||||
# retrieve version installed
|
||||
version_installed = get_installed_version()
|
||||
# compare installed and available
|
||||
old_version, version_installed, version_available, version_stable = \
|
||||
compare_version(version_installed, version_list)
|
||||
return old_version, version_installed, version_available, version_stable
|
||||
|
||||
|
||||
def _version_check_linux(packages):
|
||||
""" Check package version on linux
|
||||
|
||||
:param packages: list of packages to check
|
||||
:type packages: list of str
|
||||
"""
|
||||
if DISTRIBUTION in ['ubuntu', 'debian', 'linuxmint']:
|
||||
try:
|
||||
# try aptdaemon first
|
||||
# pylint: disable=import-outside-toplevel
|
||||
from .apt_daemon_handler import AptDaemonHandler
|
||||
handler = AptDaemonHandler()
|
||||
except: # pylint: disable=bare-except
|
||||
# fallback to shell
|
||||
# since we need the user password, ask to check for new version first
|
||||
# pylint: disable=import-outside-toplevel
|
||||
from .shell_handler_apt import ShellHandlerApt
|
||||
handler = ShellHandlerApt(use_sudo=True)
|
||||
if dialog_yes_no(32015):
|
||||
pass
|
||||
elif dialog_yes_no(32009, 32010):
|
||||
log('disabling addon by user request')
|
||||
ADDON.setSetting('versioncheck_enable', 'false')
|
||||
return
|
||||
|
||||
if handler:
|
||||
if handler.check_upgrade_available(packages[0]):
|
||||
if linux_upgrade_message(32012):
|
||||
if ADDON.getSetting('upgrade_system') == 'false':
|
||||
result = handler.upgrade_package(packages[0])
|
||||
else:
|
||||
result = handler.upgrade_system()
|
||||
if result:
|
||||
message_upgrade_success()
|
||||
message_restart()
|
||||
else:
|
||||
log('Error during upgrade')
|
||||
return
|
||||
|
||||
log('No upgrade available')
|
||||
return
|
||||
|
||||
log('Error: no handler found')
|
||||
return
|
||||
|
||||
log('Unsupported platform %s' % DISTRIBUTION)
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def _check_cryptography():
|
||||
""" Check for cryptography package, and version
|
||||
|
||||
Python cryptography < 1.7 (still shipped with Ubuntu 16.04) has issues with
|
||||
pyOpenSSL integration, leading to all sorts of weird bugs - check here to save
|
||||
on some troubleshooting. This check may be removed in the future (when switching
|
||||
to Python3?)
|
||||
See https://github.com/pyca/pyopenssl/issues/542
|
||||
"""
|
||||
try:
|
||||
import cryptography # pylint: disable=import-outside-toplevel
|
||||
ver = cryptography.__version__
|
||||
except ImportError:
|
||||
# If the module is not found - no problem
|
||||
return
|
||||
|
||||
ver_parts = list(map(int, ver.split('.')))
|
||||
if len(ver_parts) < 2 or ver_parts[0] < 1 or (ver_parts[0] == 1 and ver_parts[1] < 7):
|
||||
log('Python cryptography module version %s is too old, at least version 1.7 needed' % ver)
|
||||
xbmcgui.Dialog().ok(
|
||||
ADDON_NAME,
|
||||
'[CR]'.join([localise(32040) % ver, localise(32041), localise(32042)])
|
||||
)
|
||||
|
||||
|
||||
def run():
|
||||
""" Service entry-point
|
||||
"""
|
||||
_check_cryptography()
|
||||
|
||||
if ADDON.getSetting('versioncheck_enable') == 'false':
|
||||
log('Disabled')
|
||||
else:
|
||||
log('Version %s started' % ADDON_VERSION)
|
||||
|
||||
if wait_for_abort(5):
|
||||
sys.exit(0)
|
||||
|
||||
if (DISTRIBUTION != 'webos' and xbmc.getCondVisibility('System.Platform.Linux') and
|
||||
ADDON.getSetting('upgrade_apt') == 'true'):
|
||||
_version_check_linux(['kodi'])
|
||||
else:
|
||||
old_version, version_installed, version_available, version_stable = _version_check()
|
||||
if old_version:
|
||||
non_linux_upgrade_message(version_installed, version_available, version_stable, old_version)
|
||||
@@ -0,0 +1,139 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
|
||||
Copyright (C) 2013-2014 Team-XBMC
|
||||
Copyright (C) 2014-2019 Team Kodi
|
||||
|
||||
This file is part of service.xbmc.versioncheck
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
See LICENSES/GPL-3.0-or-later.txt for more information.
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from .common import log
|
||||
from .handler import Handler
|
||||
|
||||
|
||||
try:
|
||||
from subprocess import check_output
|
||||
except ImportError:
|
||||
def check_output(*args, **kwargs):
|
||||
return
|
||||
log('ImportError: subprocess')
|
||||
|
||||
|
||||
class ShellHandlerApt(Handler):
|
||||
""" Apt shell handler
|
||||
"""
|
||||
|
||||
def __init__(self, use_sudo=False):
|
||||
Handler.__init__(self)
|
||||
self.sudo = use_sudo
|
||||
self._update = False
|
||||
installed, _ = self._check_versions('kodi')
|
||||
if not installed:
|
||||
# there is no package installed via repo, so we exit here
|
||||
log('No installed package found, exiting')
|
||||
sys.exit(0)
|
||||
self._update = True
|
||||
|
||||
def _check_versions(self, package):
|
||||
""" Check apt package versions
|
||||
|
||||
:param package: package to check
|
||||
:type package: str
|
||||
:return: installed version, candidate version
|
||||
:rtype: str, str / False, False
|
||||
"""
|
||||
_cmd = 'apt-cache policy ' + package
|
||||
|
||||
if self.update and not self._update_cache():
|
||||
return False, False
|
||||
|
||||
try:
|
||||
result = check_output([_cmd], shell=True)
|
||||
try:
|
||||
result = result.decode('utf-8')
|
||||
except (AttributeError, UnicodeDecodeError):
|
||||
pass
|
||||
result = result.split('\n')
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
log('ShellHandlerApt: exception while executing shell command %s: %s' % (_cmd, error))
|
||||
return False, False
|
||||
|
||||
if result[0].replace(':', '') == package:
|
||||
installed = result[1].split()[1]
|
||||
candidate = result[2].split()[1]
|
||||
if installed == '(none)':
|
||||
installed = False
|
||||
if candidate == '(none)':
|
||||
candidate = False
|
||||
return installed, candidate
|
||||
|
||||
log('ShellHandlerApt: error during version check')
|
||||
return False, False
|
||||
|
||||
def _update_cache(self):
|
||||
""" Update apt cache
|
||||
|
||||
:return: success of updating apt cache
|
||||
:rtype: bool
|
||||
"""
|
||||
_cmd = 'apt-get update'
|
||||
try:
|
||||
if self.sudo:
|
||||
_ = check_output('echo \'%s\' | sudo -S %s' %
|
||||
(self._get_password(), _cmd), shell=True)
|
||||
else:
|
||||
_ = check_output(_cmd.split())
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
log('Exception while executing shell command %s: %s' % (_cmd, error))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def upgrade_package(self, package):
|
||||
""" Upgrade apt package
|
||||
|
||||
:param package: package to upgrade
|
||||
:type package: str
|
||||
:return: success of apt package upgrade
|
||||
:rtype: bool
|
||||
"""
|
||||
_cmd = 'apt-get install -y ' + package
|
||||
try:
|
||||
if self.sudo:
|
||||
_ = check_output('echo \'%s\' | sudo -S %s' %
|
||||
(self._get_password(), _cmd), shell=True)
|
||||
else:
|
||||
_ = check_output(_cmd.split())
|
||||
log('Upgrade successful')
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
log('Exception while executing shell command %s: %s' % (_cmd, error))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def upgrade_system(self):
|
||||
""" Upgrade system
|
||||
|
||||
:return: success of system upgrade
|
||||
:rtype: bool
|
||||
"""
|
||||
_cmd = 'apt-get upgrade -y'
|
||||
try:
|
||||
log('Upgrading system')
|
||||
if self.sudo:
|
||||
_ = check_output('echo \'%s\' | sudo -S %s' %
|
||||
(self._get_password(), _cmd), shell=True)
|
||||
else:
|
||||
_ = check_output(_cmd.split())
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
log('Exception while executing shell command %s: %s' % (_cmd, error))
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -0,0 +1,216 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
|
||||
Copyright (C) 2013-2014 Team-XBMC
|
||||
Copyright (C) 2014-2019 Team Kodi
|
||||
|
||||
This file is part of service.xbmc.versioncheck
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
See LICENSES/GPL-3.0-or-later.txt for more information.
|
||||
|
||||
"""
|
||||
|
||||
from .common import log
|
||||
|
||||
|
||||
def compare_version(version_installed, version_list):
|
||||
""" Compare the installed version against the provided version list
|
||||
|
||||
:param version_installed: currently installed version
|
||||
:type version_installed: dict
|
||||
:param version_list: provided versions to compare against
|
||||
:type version_list: dict
|
||||
:return: old, current, available, and stable versions
|
||||
:rtype: bool / 'stable', dict, dict, dict
|
||||
"""
|
||||
# Create separate version lists
|
||||
version_list_stable = version_list['releases']['stable']
|
||||
version_list_rc = version_list['releases']['releasecandidate']
|
||||
version_list_beta = version_list['releases']['beta']
|
||||
version_list_alpha = version_list['releases']['alpha']
|
||||
# version_list_prealpha = version_list['releases']['prealpha']
|
||||
|
||||
stable_version = version_list_stable[0]
|
||||
rc_version = version_list_rc[0]
|
||||
beta_version = version_list_beta[0]
|
||||
alpha_version = version_list_alpha[0]
|
||||
|
||||
log('Version installed %s' % version_installed)
|
||||
|
||||
# Check to upgrade to newest available stable version
|
||||
# check on smaller major version. Smaller version than available always notify
|
||||
|
||||
# check for stable versions
|
||||
old_version, version_available = _check_for_stable_version(version_installed, stable_version)
|
||||
|
||||
if not old_version:
|
||||
# Already skipped a possible newer stable build. Let's continue with non stable builds.
|
||||
# Check also 'old version' hasn't been set to 'stable' or true by previous checks because
|
||||
# if so, those part need to be skipped
|
||||
old_version, version_available = _check_for_rc_version(version_installed,
|
||||
rc_version, beta_version)
|
||||
|
||||
if not old_version:
|
||||
# check for beta builds
|
||||
old_version, version_available = _check_for_beta_version(version_installed, beta_version)
|
||||
|
||||
if not old_version:
|
||||
# check for alpha builds
|
||||
old_version, version_available = _check_for_alpha_version(version_installed, alpha_version)
|
||||
|
||||
return old_version, version_installed, version_available, stable_version
|
||||
|
||||
|
||||
def _check_for_stable_version(version_installed, stable_version):
|
||||
""" Compare the installed version against the latest stable version
|
||||
|
||||
:param version_installed: currently installed version
|
||||
:type version_installed: dict
|
||||
:param stable_version: latest stable version
|
||||
:type stable_version: dict
|
||||
:return: whether using an old version, and available version if newer stable version available
|
||||
:rtype: bool / 'stable', dict
|
||||
"""
|
||||
# check if installed major version is smaller than available major stable
|
||||
# here we don't care if running non stable
|
||||
old_version = False
|
||||
version_available = {}
|
||||
|
||||
if version_installed['major'] < int(stable_version['major']):
|
||||
version_available = stable_version
|
||||
old_version = 'stable'
|
||||
log('Version available %s' % stable_version)
|
||||
log('You are running an older version')
|
||||
|
||||
# check if installed major version is equal than available major stable
|
||||
# however also check on minor version and still don't care about non stable
|
||||
elif version_installed['major'] == int(stable_version['major']):
|
||||
if version_installed['minor'] < int(stable_version['minor']):
|
||||
version_available = stable_version
|
||||
old_version = 'stable'
|
||||
log('Version available %s' % stable_version)
|
||||
log('You are running an older minor version')
|
||||
# check for <= minor !stable
|
||||
elif version_installed['tag'] != 'stable' and \
|
||||
version_installed['minor'] <= int(stable_version['minor']):
|
||||
version_available = stable_version
|
||||
old_version = True
|
||||
log('Version available %s' % stable_version)
|
||||
log('You are running an older non stable minor version')
|
||||
else:
|
||||
log('Version available %s' % stable_version)
|
||||
log('There is no newer stable available')
|
||||
|
||||
return old_version, version_available
|
||||
|
||||
|
||||
def _check_for_rc_version(version_installed, rc_version, beta_version):
|
||||
""" Compare the installed version against the latest RC version
|
||||
|
||||
:param version_installed: currently installed version
|
||||
:type version_installed: dict
|
||||
:param rc_version: latest rc version
|
||||
:type rc_version: dict
|
||||
:param beta_version: latest beta version
|
||||
:type beta_version: dict
|
||||
:return: whether using an old version, and available version if newer rc version available
|
||||
:rtype: bool, dict
|
||||
"""
|
||||
old_version = False
|
||||
version_available = {}
|
||||
# check for RC builds
|
||||
if version_installed['tag'] in ['releasecandidate']:
|
||||
# check if you are using a RC build lower than current available RC
|
||||
# then check if you are using a beta/alpha lower than current available RC
|
||||
# 14.0rc3 is newer than: 14.0rc1, 14.0b9, 14.0a15
|
||||
if version_installed['major'] <= int(rc_version['major']):
|
||||
if version_installed['minor'] <= int(rc_version['minor']):
|
||||
if version_installed.get('tagversion', '') < rc_version['tagversion']:
|
||||
version_available = rc_version
|
||||
old_version = True
|
||||
log('Version available %s' % rc_version)
|
||||
log('You are running an older RC version')
|
||||
# now check if installed !=rc
|
||||
elif version_installed['tag'] in ['beta', 'alpha', 'prealpha']:
|
||||
if version_installed['major'] <= int(rc_version['major']):
|
||||
if version_installed['minor'] <= int(beta_version['minor']):
|
||||
version_available = rc_version
|
||||
old_version = True
|
||||
log('Version available %s' % rc_version)
|
||||
log('You are running an older non RC version')
|
||||
|
||||
return old_version, version_available
|
||||
|
||||
|
||||
def _check_for_beta_version(version_installed, beta_version):
|
||||
""" Compare the installed version against the latest beta version
|
||||
|
||||
:param version_installed: currently installed version
|
||||
:type version_installed: dict
|
||||
:param beta_version: latest beta version
|
||||
:type beta_version: dict
|
||||
:return: whether using an old version, and available version if newer beta version available
|
||||
:rtype: bool, dict
|
||||
"""
|
||||
old_version = False
|
||||
version_available = {}
|
||||
# check for beta builds
|
||||
if not old_version and version_installed['tag'] == 'beta':
|
||||
# check if you are using a RC build lower than current available RC
|
||||
# then check if you are using a beta/alpha lower than current available RC
|
||||
# 14.0b3 is newer than: 14.0b1, 14.0a15
|
||||
if version_installed['major'] <= int(beta_version['major']):
|
||||
if version_installed['minor'] <= int(beta_version['minor']):
|
||||
if version_installed.get('tagversion', '') < beta_version['tagversion']:
|
||||
version_available = beta_version
|
||||
old_version = True
|
||||
log('Version available %s' % beta_version)
|
||||
log('You are running an older beta version')
|
||||
# now check if installed !=beta
|
||||
elif not old_version and version_installed['tag'] in ['alpha', 'prealpha']:
|
||||
if version_installed['major'] <= int(beta_version['major']):
|
||||
if version_installed['minor'] <= int(beta_version['minor']):
|
||||
version_available = beta_version
|
||||
old_version = True
|
||||
log('Version available %s' % beta_version)
|
||||
log('You are running an older non beta version')
|
||||
|
||||
return old_version, version_available
|
||||
|
||||
|
||||
def _check_for_alpha_version(version_installed, alpha_version):
|
||||
""" Compare the installed version against the latest alpha version
|
||||
|
||||
:param version_installed: currently installed version
|
||||
:type version_installed: dict
|
||||
:param alpha_version: latest alpha version
|
||||
:type alpha_version: dict
|
||||
:return: whether using an old version, and available version if newer alpha version available
|
||||
:rtype: bool, dict
|
||||
"""
|
||||
old_version = False
|
||||
version_available = {}
|
||||
# check for alpha builds and older
|
||||
if version_installed['tag'] == 'alpha':
|
||||
# check if you are using a RC build lower than current available RC
|
||||
# then check if you are using a beta/alpha lower than current available RC
|
||||
# 14.0a3 is newer than: 14.0a1 or pre-alpha
|
||||
if version_installed['major'] <= int(alpha_version['major']):
|
||||
if version_installed['minor'] <= int(alpha_version['minor']):
|
||||
if version_installed.get('tagversion', '') < alpha_version['tagversion']:
|
||||
version_available = alpha_version
|
||||
old_version = True
|
||||
log('Version available %s' % alpha_version)
|
||||
log('You are running an older alpha version')
|
||||
# now check if installed !=alpha
|
||||
elif version_installed['tag'] in ['prealpha']:
|
||||
if version_installed['major'] <= int(alpha_version['major']):
|
||||
if version_installed['minor'] <= int(alpha_version['minor']):
|
||||
version_available = alpha_version
|
||||
old_version = True
|
||||
log('Version available %s' % alpha_version)
|
||||
log('You are running an older non alpha version')
|
||||
|
||||
return old_version, version_available
|
||||
@@ -0,0 +1,159 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
|
||||
Copyright (C) 2011-2013 Martijn Kaijser
|
||||
Copyright (C) 2013-2014 Team-XBMC
|
||||
Copyright (C) 2014-2019 Team Kodi
|
||||
|
||||
This file is part of service.xbmc.versioncheck
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
See LICENSES/GPL-3.0-or-later.txt for more information.
|
||||
|
||||
"""
|
||||
|
||||
from contextlib import closing
|
||||
import os
|
||||
import sys
|
||||
|
||||
import xbmc # pylint: disable=import-error
|
||||
import xbmcaddon # pylint: disable=import-error
|
||||
import xbmcgui # pylint: disable=import-error
|
||||
import xbmcvfs # pylint: disable=import-error
|
||||
|
||||
_ADDON = xbmcaddon.Addon('service.xbmc.versioncheck')
|
||||
_ADDON_NAME = _ADDON.getAddonInfo('name')
|
||||
if sys.version_info[0] >= 3:
|
||||
_ADDON_PATH = _ADDON.getAddonInfo('path')
|
||||
else:
|
||||
_ADDON_PATH = _ADDON.getAddonInfo('path').decode('utf-8')
|
||||
_ICON = _ADDON.getAddonInfo('icon')
|
||||
|
||||
|
||||
class Viewer:
|
||||
""" Show user a text viewer (WINDOW_DIALOG_TEXT_VIEWER)
|
||||
Include the text file for the viewers body in the resources/ directory
|
||||
|
||||
usage:
|
||||
script_path = os.path.join(_ADDON_PATH, 'resources', 'lib', 'version_check', 'viewer.py')
|
||||
xbmc.executebuiltin('RunScript(%s,%s,%s)' % (script_path, 'Heading', 'notice.txt'))
|
||||
|
||||
:param heading: text viewer heading
|
||||
:type heading: str
|
||||
:param filename: filename to use for text viewers body
|
||||
:type filename: str
|
||||
"""
|
||||
WINDOW = 10147
|
||||
CONTROL_LABEL = 1
|
||||
CONTROL_TEXTBOX = 5
|
||||
|
||||
def __init__(self, heading, filename):
|
||||
self.heading = heading
|
||||
self.filename = filename
|
||||
# activate the text viewer window
|
||||
xbmc.executebuiltin('ActivateWindow(%d)' % (self.WINDOW,))
|
||||
# get window
|
||||
self.window = xbmcgui.Window(self.WINDOW)
|
||||
# give window time to initialize
|
||||
xbmc.sleep(100)
|
||||
# set controls
|
||||
self.set_controls()
|
||||
|
||||
def set_controls(self):
|
||||
""" Set the window controls
|
||||
"""
|
||||
# get text viewer body text
|
||||
text = self.get_text()
|
||||
# set heading
|
||||
self.window.getControl(self.CONTROL_LABEL).setLabel('%s : %s' % (_ADDON_NAME,
|
||||
self.heading,))
|
||||
# set text
|
||||
self.window.getControl(self.CONTROL_TEXTBOX).setText(text)
|
||||
xbmc.sleep(2000)
|
||||
|
||||
def get_text(self):
|
||||
""" Get the text viewers body text from self.filename
|
||||
|
||||
:return: contents of self.filename
|
||||
:rtype: str
|
||||
"""
|
||||
try:
|
||||
return self.read_file(self.filename)
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
xbmc.log(_ADDON_NAME + ': ' + str(error), xbmc.LOGERROR)
|
||||
return ''
|
||||
|
||||
@staticmethod
|
||||
def read_file(filename):
|
||||
""" Read the contents of the provided file, from
|
||||
os.path.join(_ADDON_PATH, 'resources', filename)
|
||||
|
||||
:param filename: name of file to read
|
||||
:type filename: str
|
||||
:return: contents of the provided file
|
||||
:rtype: str
|
||||
"""
|
||||
filename = os.path.join(_ADDON_PATH, 'resources', filename)
|
||||
with closing(xbmcvfs.File(filename)) as open_file:
|
||||
contents = open_file.read()
|
||||
return contents
|
||||
|
||||
|
||||
class WebBrowser:
|
||||
""" Display url using the default browser
|
||||
|
||||
usage:
|
||||
script_path = os.path.join(_ADDON_PATH, 'resources', 'lib', 'version_check', 'viewer.py')
|
||||
xbmc.executebuiltin('RunScript(%s,%s,%s)' % (script_path, 'webbrowser', 'https://kodi.tv/'))
|
||||
|
||||
:param url: url to open
|
||||
:type url: str
|
||||
"""
|
||||
|
||||
def __init__(self, url):
|
||||
self.url = url
|
||||
try:
|
||||
# notify user
|
||||
self.notification(_ADDON_NAME, self.url)
|
||||
xbmc.sleep(100)
|
||||
# launch url
|
||||
self.launch_url()
|
||||
except Exception as error: # pylint: disable=broad-except
|
||||
xbmc.log(_ADDON_NAME + ': ' + str(error), xbmc.LOGERROR)
|
||||
|
||||
@staticmethod
|
||||
def notification(heading, message, icon=None, time=15000, sound=True):
|
||||
""" Create a notification
|
||||
|
||||
:param heading: notification heading
|
||||
:type heading: str
|
||||
:param message: notification message
|
||||
:type message: str
|
||||
:param icon: path and filename for the notification icon
|
||||
:type icon: str
|
||||
:param time: time to display notification
|
||||
:type time: int
|
||||
:param sound: is notification audible
|
||||
:type sound: bool
|
||||
"""
|
||||
if not icon:
|
||||
icon = _ICON
|
||||
xbmcgui.Dialog().notification(heading, message, icon, time, sound)
|
||||
|
||||
def launch_url(self):
|
||||
""" Open self.url in the default web browser
|
||||
"""
|
||||
import webbrowser # pylint: disable=import-outside-toplevel
|
||||
webbrowser.open(self.url)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
if sys.argv[1] == 'webbrowser':
|
||||
WebBrowser(sys.argv[2])
|
||||
else:
|
||||
Viewer(sys.argv[1], sys.argv[2])
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
xbmc.log(_ADDON_NAME + ': ' + str(err), xbmc.LOGERROR)
|
||||
Reference in New Issue
Block a user