Skip to content

Commit

Permalink
Autoinstall Widevine updates
Browse files Browse the repository at this point in the history
This adds a feature to enable automatic installation of Widevine.
  • Loading branch information
dagwieers committed Sep 25, 2019
1 parent defc09a commit c7ac5a2
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 67 deletions.
132 changes: 67 additions & 65 deletions lib/inputstreamhelper/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@

import xbmc
from xbmcaddon import Addon
from xbmcgui import Dialog, DialogProgress
import xbmcvfs
from .kodiutils import execute_jsonrpc, get_addon_info, get_proxies, get_setting, localize, log, set_setting, translate_path
from .kodiutils import (browsesingle, execute_jsonrpc, get_addon_info, get_proxies, get_setting, localize,
log, notification, ok_dialog, progress_dialog, set_setting, translate_path, yesno_dialog)
from .unicodehelper import to_unicode

# NOTE: Work around issue caused by platform still using os.popen()
Expand Down Expand Up @@ -356,11 +356,11 @@ def _http_request(url):
raise HTTPError('HTTP %s Error for url: %s' % (req.getcode(), url), response=req)
break
except HTTPError:
Dialog().ok(localize(30004), localize(30013, filename=filename)) # Failed to retrieve file
ok_dialog(localize(30004), localize(30013, filename=filename)) # Failed to retrieve file
return None
except BadStatusLine:
if retry:
Dialog().ok(localize(30004), localize(30013, filename=filename)) # Failed to retrieve file
ok_dialog(localize(30004), localize(30013, filename=filename)) # Failed to retrieve file
return None
return req

Expand All @@ -387,8 +387,8 @@ def _http_download(self, url, message=None):

self._download_path = os.path.join(self._temp_path(), filename)
total_length = float(req.info().get('content-length'))
progress_dialog = DialogProgress()
progress_dialog.create(localize(30014), message) # Download in progress
progress = progress_dialog()
progress.create(localize(30014), message) # Download in progress

chunk_size = 32 * 1024
with open(self._download_path, 'wb') as image:
Expand All @@ -400,13 +400,13 @@ def _http_download(self, url, message=None):
image.write(chunk)
size += len(chunk)
percent = int(size * 100 / total_length)
if progress_dialog.iscanceled():
progress_dialog.close()
if progress.iscanceled():
progress.close()
req.close()
return False
progress_dialog.update(percent)
progress.update(percent)

progress_dialog.close()
progress.close()
return True

def _has_inputstream(self):
Expand Down Expand Up @@ -440,22 +440,22 @@ def _supports_widevine(self):
"""Checks if Widevine is supported on the architecture/operating system/Kodi version."""
if self._arch() not in config.WIDEVINE_SUPPORTED_ARCHS:
log('Unsupported Widevine architecture found: {arch}', arch=self._arch())
Dialog().ok(localize(30004), localize(30007)) # Widevine not available on this architecture
ok_dialog(localize(30004), localize(30007)) # Widevine not available on this architecture
return False

if system_os() not in config.WIDEVINE_SUPPORTED_OS:
log('Unsupported Widevine OS found: {os}', os=system_os())
Dialog().ok(localize(30004), localize(30011, os=system_os())) # Operating system not supported by Widevine
ok_dialog(localize(30004), localize(30011, os=system_os())) # Operating system not supported by Widevine
return False

if LooseVersion(config.WIDEVINE_MINIMUM_KODI_VERSION[system_os()]) > LooseVersion(self._kodi_version()):
log('Unsupported Kodi version for Widevine: {version}', version=self._kodi_version())
Dialog().ok(localize(30004), localize(30010, version=config.WIDEVINE_MINIMUM_KODI_VERSION[system_os()])) # Kodi too old
ok_dialog(localize(30004), localize(30010, version=config.WIDEVINE_MINIMUM_KODI_VERSION[system_os()])) # Kodi too old
return False

if 'WindowsApps' in translate_path('special://xbmcbin/'): # uwp is not supported
log('Unsupported UWP Kodi version detected.')
Dialog().ok(localize(30004), localize(30012)) # Windows Store Kodi falls short
ok_dialog(localize(30004), localize(30012)) # Windows Store Kodi falls short
return False

return True
Expand Down Expand Up @@ -522,7 +522,7 @@ def _latest_widevine_version(self, eula=False):
arm_device = self._select_best_chromeos_image(devices)
if arm_device is None:
log('We could not find an ARM device in recovery.conf')
Dialog().ok(localize(30004), localize(30005))
ok_dialog(localize(30004), localize(30005))
return ''
return arm_device['version']

Expand Down Expand Up @@ -555,12 +555,12 @@ def _install_widevine_x86(self):

downloaded = self._http_download(url)
if downloaded:
progress_dialog = DialogProgress()
progress_dialog.create(heading=localize(30043), line1=localize(30044)) # Extracting Widevine CDM
progress_dialog.update(94, line1=localize(30049)) # Installing Widevine CDM
progress = progress_dialog()
progress.create(heading=localize(30043), line1=localize(30044)) # Extracting Widevine CDM
progress.update(94, line1=localize(30049)) # Installing Widevine CDM
self._unzip(self._ia_cdm_path())

progress_dialog.update(97, line1=localize(30050)) # Finishing
progress.update(97, line1=localize(30050)) # Finishing
self._cleanup()
if not self._widevine_eula():
return False
Expand All @@ -571,13 +571,13 @@ def _install_widevine_x86(self):
os.rename(os.path.join(self._ia_cdm_path(), config.WIDEVINE_MANIFEST_FILE), self._widevine_config_path())
wv_check = self._check_widevine()
if wv_check:
progress_dialog.update(100, line1=localize(30051)) # Widevine CDM successfully installed.
Dialog().notification(localize(30037), localize(30051)) # Success! Widevine successfully installed.
progress_dialog.close()
progress.update(100, line1=localize(30051)) # Widevine CDM successfully installed.
notification(localize(30037), localize(30051)) # Success! Widevine successfully installed.
progress.close()
return wv_check

progress_dialog.close()
Dialog().ok(localize(30004), localize(30005)) # An error occurred
progress.close()
ok_dialog(localize(30004), localize(30005)) # An error occurred

return False

Expand All @@ -588,52 +588,54 @@ def _install_widevine_arm(self): # pylint: disable=too-many-statements
arm_device = self._select_best_chromeos_image(devices)
if arm_device is None:
log('We could not find an ARM device in recovery.conf')
Dialog().ok(localize(30004), localize(30005))
ok_dialog(localize(30004), localize(30005))
return ''
required_diskspace = int(arm_device['filesize']) + int(arm_device['zipfilesize'])
if Dialog().yesno(localize(30001), # Due to distributing issues, this takes a long time
localize(30006, diskspace=self._sizeof_fmt(required_diskspace))) and self._widevine_eula():
if yesno_dialog(localize(30001), # Due to distributing issues, this takes a long time
localize(30006, diskspace=self._sizeof_fmt(required_diskspace)),
autoanswer=True) and self._widevine_eula():
if system_os() != 'Linux':
Dialog().ok(localize(30004), localize(30019, os=system_os()))
ok_dialog(localize(30004), localize(30019, os=system_os()))
return False

while required_diskspace >= self._diskspace():
if Dialog().yesno(localize(30004), localize(30055)): # Not enough space, alternative path?
self._update_temp_path(Dialog().browseSingle(3, localize(30909), 'files')) # Temporary path
if yesno_dialog(localize(30004), localize(30055)): # Not enough space, alternative path?
self._update_temp_path(browsesingle(3, localize(30909), 'files')) # Temporary path
continue

Dialog().ok(localize(30004), # Not enough free disk space
localize(30018, diskspace=self._sizeof_fmt(required_diskspace)))
ok_dialog(localize(30004), # Not enough free disk space
localize(30018, diskspace=self._sizeof_fmt(required_diskspace)))
return False

if not self._cmd_exists('fdisk') and not self._cmd_exists('parted'):
Dialog().ok(localize(30004), localize(30020, command1='fdisk', command2='parted')) # Commands are missing
ok_dialog(localize(30004), localize(30020, command1='fdisk', command2='parted')) # Commands are missing
return False

if not self._cmd_exists('mount'):
Dialog().ok(localize(30004), localize(30021, command='mount')) # Mount command is missing
ok_dialog(localize(30004), localize(30021, command='mount')) # Mount command is missing
return False

if not self._cmd_exists('losetup'):
Dialog().ok(localize(30004), localize(30021, command='losetup')) # Losetup command is missing
ok_dialog(localize(30004), localize(30021, command='losetup')) # Losetup command is missing
return False

if os.getuid() != 0: # ask for permissions to run cmds as root
if not Dialog().yesno(localize(30001), localize(30030, cmds=', '.join(root_cmds)), yeslabel=localize(30027), nolabel=localize(30028)):
return False
if os.getuid() != 0 and not yesno_dialog(localize(30001), # Ask for permission to run cmds as root
localize(30030, cmds=', '.join(root_cmds)),
nolabel=localize(30028), yeslabel=localize(30027), autoanswer=True):
return False

# Clean up any remaining mounts
self._unmount()

url = arm_device['url']
downloaded = self._http_download(url, message=localize(30022)) # Downloading the recovery image
if downloaded:
progress_dialog = DialogProgress()
progress_dialog.create(heading=localize(30043), line1=localize(30044)) # Extracting Widevine CDM
progress = progress_dialog()
progress.create(heading=localize(30043), line1=localize(30044)) # Extracting Widevine CDM
bin_filename = url.split('/')[-1].replace('.zip', '')
bin_path = os.path.join(self._temp_path(), bin_filename)

progress_dialog.update(5, line1=localize(30045), line2=localize(30046), line3=localize(30047)) # Uncompressing image
progress.update(5, line1=localize(30045), line2=localize(30046), line3=localize(30047)) # Uncompressing image
success = [
self._unzip(self._temp_path(), bin_filename),
self._check_loop(),
Expand All @@ -642,27 +644,27 @@ def _install_widevine_arm(self): # pylint: disable=too-many-statements
self._mnt_loop_dev(),
]
if all(success):
progress_dialog.update(91, line1=localize(30048)) # Extracting Widevine CDM
progress.update(91, line1=localize(30048)) # Extracting Widevine CDM
self._extract_widevine_from_img()
progress_dialog.update(94, line1=localize(30049)) # Installing Widevine CDM
progress_dialog.update(97, line1=localize(30050)) # Finishing
progress.update(94, line1=localize(30049)) # Installing Widevine CDM
progress.update(97, line1=localize(30050)) # Finishing
self._cleanup()
if self._has_widevine():
set_setting('chromeos_version', arm_device['version'])
with open(self._widevine_config_path(), 'w') as config_file:
config_file.write(json.dumps(devices, indent=4))
wv_check = self._check_widevine()
if wv_check:
progress_dialog.update(100, line1=localize(30051)) # Widevine CDM successfully installed.
Dialog().notification(localize(30037), localize(30051)) # Success! Widevine CDM successfully installed.
progress_dialog.close()
progress.update(100, line1=localize(30051)) # Widevine CDM successfully installed.
notification(localize(30037), localize(30051)) # Success! Widevine CDM successfully installed.
progress.close()
return wv_check
else:
progress_dialog.update(100, line1=localize(30050)) # Finishing
progress.update(100, line1=localize(30050)) # Finishing
self._cleanup()

progress_dialog.close()
Dialog().ok(localize(30004), localize(30005)) # An error occurred
progress.close()
ok_dialog(localize(30004), localize(30005)) # An error occurred

return False

Expand All @@ -682,9 +684,9 @@ def remove_widevine(self):
if widevinecdm and xbmcvfs.exists(widevinecdm):
log('Remove Widevine CDM at {path}', path=widevinecdm)
xbmcvfs.delete(widevinecdm)
Dialog().notification(localize(30037), localize(30052)) # Success! Widevine successfully removed.
notification(localize(30037), localize(30052)) # Success! Widevine successfully removed.
return True
Dialog().notification(localize(30004), localize(30053)) # Error. Widevine CDM not found.
notification(localize(30004), localize(30053)) # Error. Widevine CDM not found.
return False

@staticmethod
Expand Down Expand Up @@ -725,7 +727,7 @@ def _update_widevine(self):

if LooseVersion(latest_version) > LooseVersion(current_version):
log('There is an update available for {component}', component=component)
if Dialog().yesno(localize(30040), localize(30033), yeslabel=localize(30034), nolabel=localize(30028)):
if yesno_dialog(localize(30040), localize(30033), nolabel=localize(30028), yeslabel=localize(30034), autoanswer=True):
self.install_widevine()
else:
log('User declined to update {component}.', component=component)
Expand All @@ -750,7 +752,7 @@ def _widevine_eula(self):
with archive.open(config.WIDEVINE_LICENSE_FILE) as file_obj:
eula = file_obj.read().decode().strip().replace('\n', ' ')

return Dialog().yesno(localize(30026), eula, yeslabel=localize(30027), nolabel=localize(30028)) # Widevine CDM EULA
return yesno_dialog(localize(30026), eula, nolabel=localize(30028), yeslabel=localize(30027), autoanswer=True) # Widevine CDM EULA

def _extract_widevine_from_img(self):
''' Extract the Widevine CDM binary from the mounted Chrome OS image '''
Expand Down Expand Up @@ -799,7 +801,7 @@ def _missing_widevine_libs(self):
import struct
if struct.calcsize('P') * 8 == 64:
log('ARM64 ldd check failed. User needs 32-bit userspace.')
Dialog().ok(localize(30004), localize(30039)) # Widevine not available on ARM64
ok_dialog(localize(30004), localize(30039)) # Widevine not available on ARM64

log('Failed to check for missing Widevine libraries.')
return None
Expand All @@ -811,18 +813,18 @@ def _check_widevine(self):

if not os.path.exists(self._widevine_config_path()):
log('Widevine or recovery config is missing. Reinstall is required.')
Dialog().ok(localize(30001), localize(30031)) # An update of Widevine is required
ok_dialog(localize(30001), localize(30031)) # An update of Widevine is required
return self.install_widevine()

if 'x86' in self._arch(): # check that widevine arch matches system arch
wv_config = self._load_widevine_config()
if config.WIDEVINE_ARCH_MAP_X86[self._arch()] != wv_config['arch']:
log('Widevine/system arch mismatch. Reinstall is required.')
Dialog().ok(localize(30001), localize(30031)) # An update of Widevine is required
ok_dialog(localize(30001), localize(30031)) # An update of Widevine is required
return self.install_widevine()

if self._missing_widevine_libs():
Dialog().ok(localize(30004), localize(30032, libs=', '.join(self._missing_widevine_libs()))) # Missing libraries
ok_dialog(localize(30004), localize(30032, libs=', '.join(self._missing_widevine_libs()))) # Missing libraries
return False

self._update_widevine()
Expand Down Expand Up @@ -866,7 +868,7 @@ def _cleanup(self):
if unattach_output['success']:
self._loop_dev = False
if self._modprobe_loop:
Dialog().notification(localize(30035), localize(30036)) # Unload by hand in CLI
notification(localize(30035), localize(30036)) # Unload by hand in CLI
if not self._has_widevine():
shutil.rmtree(self._ia_cdm_path())

Expand All @@ -892,7 +894,7 @@ def _check_drm(self):
if self._has_widevine():
return self._check_widevine()

if Dialog().yesno(localize(30041), localize(30002), yeslabel=localize(30038), nolabel=localize(30028)): # Widevine required
if yesno_dialog(localize(30041), localize(30002), nolabel=localize(30028), yeslabel=localize(30038), autoanswer=True): # Widevine required
return self.install_widevine()

return False
Expand Down Expand Up @@ -921,18 +923,18 @@ def check_inputstream(self):
if not self._has_inputstream():
# Try to install InputStream add-on
if not self._install_inputstream():
Dialog().ok(localize(30004), localize(30008, addon=self.inputstream_addon)) # inputstream is missing on system
ok_dialog(localize(30004), localize(30008, addon=self.inputstream_addon)) # inputstream is missing on system
return False
elif not self._inputstream_enabled():
ret = Dialog().yesno(localize(30001), localize(30009, addon=self.inputstream_addon)) # inputstream is disabled
ret = yesno_dialog(localize(30001), localize(30009, addon=self.inputstream_addon)) # inputstream is disabled
if ret:
self._enable_inputstream()
return False
log('{addon} {version} is installed and enabled.', addon=self.inputstream_addon, version=self._inputstream_version())

if self.protocol == 'hls' and not self._supports_hls():
Dialog().ok(localize(30004), # HLS Minimum version is needed
localize(30017, addon=self.inputstream_addon, version=config.HLS_MINIMUM_IA_VERSION))
ok_dialog(localize(30004), # HLS Minimum version is needed
localize(30017, addon=self.inputstream_addon, version=config.HLS_MINIMUM_IA_VERSION))
return False

return self._check_drm()
Expand Down Expand Up @@ -963,4 +965,4 @@ def info_dialog(self):
+ "\n - ".join(wv_info) + "\n\n"
+ localize(30830, url=config.ISSUE_URL))

Dialog().textviewer(localize(30901), text)
textviewer(localize(30901), text)
Loading

0 comments on commit c7ac5a2

Please sign in to comment.