This commit is contained in:
2025-10-25 13:21:06 +02:00
parent eb57506d39
commit 033ffb21f5
8388 changed files with 484789 additions and 16 deletions

View File

@@ -0,0 +1,14 @@
# -*- 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.
"""
__all__ = ['runner', 'version_check']

View File

@@ -0,0 +1,16 @@
# -*- 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 version_check import service # pylint: disable=import-error
service.run()

View File

@@ -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']

View File

@@ -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)

View File

@@ -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

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
"""
SPDX-License-Identifier: Apache-2.0
See LICENSES/Apache-2.0.txt for more information.
"""
__all__ = ['distro']

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)