11# -*- coding: utf-8 -*-
22''' Implements the main InputStream Helper class '''
33from __future__ import absolute_import , division , unicode_literals
4-
54import os
6- import platform
7- import json
8- import subprocess
9- import shutil
10- from distutils .version import LooseVersion # pylint: disable=import-error,no-name-in-module
11- from datetime import datetime , timedelta
12-
13- try : # Python 3
14- from urllib .error import HTTPError
15- from urllib .request import build_opener , install_opener , ProxyHandler , urlopen
16- except ImportError : # Python 2
17- from urllib2 import build_opener , HTTPError , install_opener , ProxyHandler , urlopen
18-
195from inputstreamhelper import config
20-
21- import xbmc
22- from xbmcaddon import Addon
23- import xbmcvfs
246from .kodiutils import (browsesingle , execute_jsonrpc , get_addon_info , get_proxies , get_setting , kodi_to_ascii , localize , log ,
257 notification , ok_dialog , progress_dialog , set_setting , textviewer , translate_path , yesno_dialog )
26- from .unicodehelper import to_unicode
278
289# NOTE: Work around issue caused by platform still using os.popen()
2910# This helps to survive 'IOError: [Errno 10] No child processes'
@@ -40,9 +21,11 @@ def system_os():
4021
4122 # If it wasn't stored before, get the correct value
4223 if not hasattr (system_os , 'name' ):
43- if xbmc .getCondVisibility ('system.platform.android' ):
24+ from xbmc import getCondVisibility
25+ if getCondVisibility ('system.platform.android' ):
4426 system_os .name = 'Android'
4527 else :
28+ import platform
4629 system_os .name = platform .system ()
4730
4831 # Return the stored value
@@ -62,6 +45,7 @@ def __init__(self, protocol, drm=None):
6245 self .protocol = protocol
6346 self .drm = drm
6447
48+ import platform
6549 log ('Platform information: {platform}' , platform = platform .uname ())
6650
6751 if self .protocol not in config .INPUTSTREAM_PROTOCOLS :
@@ -76,7 +60,13 @@ def __init__(self, protocol, drm=None):
7660 self .drm = config .DRM_SCHEMES [drm ]
7761
7862 # Add proxy support to HTTP requests
79- install_opener (build_opener (ProxyHandler (get_proxies ())))
63+ proxies = get_proxies ()
64+ if proxies :
65+ try : # Python 3
66+ from urllib .request import build_opener , install_opener , ProxyHandler
67+ except ImportError : # Python 2
68+ from urllib2 import build_opener , install_opener , ProxyHandler
69+ install_opener (build_opener (ProxyHandler (proxies )))
8070
8171 def __repr__ (self ):
8272 ''' String representation of Helper class '''
@@ -91,31 +81,36 @@ def _diskspace(cls):
9181 @classmethod
9282 def _temp_path (cls ):
9383 ''' Return temporary path, usually ~/.kodi/userdata/addon_data/script.module.inputstreamhelper/temp '''
84+ from xbmcvfs import exists , mkdirs
9485 temp_path = translate_path (os .path .join (get_setting ('temp_path' , 'special://masterprofile/addon_data/script.module.inputstreamhelper' ), 'temp' ))
95- if not xbmcvfs . exists (temp_path ):
96- xbmcvfs . mkdirs (temp_path )
86+ if not exists (temp_path ):
87+ mkdirs (temp_path )
9788
9889 return temp_path
9990
10091 @classmethod
10192 def _mnt_path (cls ):
10293 ''' Return mount path, usually ~/.kodi/userdata/addon_data/script.module.inputstreamhelper/temp/mnt '''
94+ from xbmcvfs import exists , mkdir
10395 mnt_path = os .path .join (cls ._temp_path (), 'mnt' )
104- if not xbmcvfs . exists (mnt_path ):
105- xbmcvfs . mkdir (mnt_path )
96+ if not exists (mnt_path ):
97+ mkdir (mnt_path )
10698
10799 return mnt_path
108100
109101 @classmethod
110102 def _ia_cdm_path (cls ):
111103 ''' Return the specified CDM path for inputstream.adaptive, usually ~/.kodi/cdm '''
104+ from xbmcaddon import Addon
112105 try :
113106 addon = Addon ('inputstream.adaptive' )
114107 except RuntimeError :
115108 return None
109+
116110 cdm_path = translate_path (addon .getSetting ('DECRYPTERPATH' ))
117- if not xbmcvfs .exists (cdm_path ):
118- xbmcvfs .mkdir (cdm_path )
111+ from xbmcvfs import exists , mkdir
112+ if not exists (cdm_path ):
113+ mkdir (cdm_path )
119114
120115 return cdm_path
121116
@@ -129,6 +124,7 @@ def _widevine_config_path(cls):
129124 @classmethod
130125 def _load_widevine_config (cls ):
131126 ''' Load the widevine or recovery config in JSON format '''
127+ import json
132128 with open (cls ._widevine_config_path (), 'r' ) as config_file :
133129 return json .loads (config_file .read ())
134130
@@ -141,20 +137,23 @@ def _widevine_path(cls):
141137
142138 if cls ._ia_cdm_path ():
143139 widevine_path = os .path .join (cls ._ia_cdm_path (), widevine_cdm_filename )
144- if xbmcvfs .exists (widevine_path ):
140+ from xbmcvfs import exists
141+ if exists (widevine_path ):
145142 return widevine_path
146143
147144 return False
148145
149146 @classmethod
150147 def _kodi_version (cls ):
151148 ''' Return the current Kodi version '''
152- version = xbmc .getInfoLabel ('System.BuildVersion' )
149+ from xbmc import getInfoLabel
150+ version = getInfoLabel ('System.BuildVersion' )
153151 return version .split (' ' )[0 ]
154152
155153 @classmethod
156154 def _arch (cls ):
157155 """Map together and return the system architecture."""
156+ import platform
158157 arch = platform .machine ()
159158 if arch == 'aarch64' :
160159 import struct
@@ -189,6 +188,7 @@ def _sizeof_fmt(num, suffix='B'):
189188 def _cmd_exists (cmd ):
190189 """Check whether cmd exists on system."""
191190 # https://stackoverflow.com/questions/377017/test-if-executable-exists-in-python
191+ import subprocess
192192 return subprocess .call ('type ' + cmd , shell = True , stdout = subprocess .PIPE , stderr = subprocess .PIPE ) == 0
193193
194194 def _update_temp_path (self , new_temp_path ):
@@ -197,6 +197,7 @@ def _update_temp_path(self, new_temp_path):
197197
198198 set_setting ('temp_path' , new_temp_path )
199199 if old_temp_path != self ._temp_path ():
200+ import shutil
200201 shutil .move (old_temp_path , self ._temp_path ())
201202
202203 def _helper_disabled (self ):
@@ -226,11 +227,13 @@ def enable():
226227
227228 def _inputstream_version (self ):
228229 ''' Return the requested inputstream version '''
230+ from xbmcaddon import Addon
229231 try :
230232 addon = Addon (self .inputstream_addon )
231233 except RuntimeError :
232234 return None
233235
236+ from .unicodehelper import to_unicode
234237 return to_unicode (addon .getAddonInfo ('version' ))
235238
236239 @staticmethod
@@ -265,6 +268,7 @@ def _chromeos_offset(self, bin_path):
265268
266269 def _run_cmd (self , cmd , sudo = False , shell = False ):
267270 ''' Run subprocess command and return if it succeeds as a bool '''
271+ import subprocess
268272 output = ''
269273 success = False
270274 if sudo and os .getuid () != 0 and self ._cmd_exists ('sudo' ):
@@ -345,6 +349,13 @@ def _has_widevine(self):
345349 @staticmethod
346350 def _http_request (url ):
347351 ''' Perform an HTTP request and return request '''
352+
353+ try : # Python 3
354+ from urllib .error import HTTPError
355+ from urllib .request import urlopen
356+ except ImportError : # Python 2
357+ from urllib2 import HTTPError , urlopen
358+
348359 log ('Request URL: {url}' , url = url )
349360 filename = url .split ('/' )[- 1 ]
350361
@@ -442,6 +453,7 @@ def _supports_widevine(self):
442453 ok_dialog (localize (30004 ), localize (30011 , os = system_os ())) # Operating system not supported by Widevine
443454 return False
444455
456+ from distutils .version import LooseVersion # pylint: disable=import-error,no-name-in-module
445457 if LooseVersion (config .WIDEVINE_MINIMUM_KODI_VERSION [system_os ()]) > LooseVersion (self ._kodi_version ()):
446458 log ('Unsupported Kodi version for Widevine: {version}' , version = self ._kodi_version ())
447459 ok_dialog (localize (30004 ), localize (30010 , version = config .WIDEVINE_MINIMUM_KODI_VERSION [system_os ()])) # Kodi too old
@@ -480,6 +492,7 @@ def _select_best_chromeos_image(devices):
480492 continue
481493
482494 # Select the newest version
495+ from distutils .version import LooseVersion # pylint: disable=import-error,no-name-in-module
483496 if LooseVersion (device ['version' ]) > LooseVersion (best ['version' ]):
484497 log ('{device[hwid]} ({device[version]}) is newer than {best[hwid]} ({best[version]})' , # pylint: disable=invalid-format-index
485498 device = device ,
@@ -505,6 +518,7 @@ def _latest_widevine_version(self, eula=False):
505518 versions = self ._http_get (url )
506519 return versions .split ()[- 1 ]
507520
521+ from datetime import datetime
508522 import time
509523 set_setting ('last_update' , str (time .mktime (datetime .utcnow ().timetuple ())))
510524 if 'x86' in self ._arch ():
@@ -643,6 +657,7 @@ def _install_widevine_arm(self): # pylint: disable=too-many-statements
643657 progress .update (97 , line1 = localize (30050 )) # Finishing
644658 self ._cleanup ()
645659 if self ._has_widevine ():
660+ import json
646661 set_setting ('chromeos_version' , arm_device ['version' ])
647662 with open (self ._widevine_config_path (), 'w' ) as config_file :
648663 config_file .write (json .dumps (devices , indent = 4 ))
@@ -673,10 +688,11 @@ def install_widevine(self):
673688
674689 def remove_widevine (self ):
675690 """Removes Widevine CDM"""
691+ from xbmcvfs import delete , exists
676692 widevinecdm = self ._widevine_path ()
677- if widevinecdm and xbmcvfs . exists (widevinecdm ):
693+ if widevinecdm and exists (widevinecdm ):
678694 log ('Remove Widevine CDM at {path}' , path = widevinecdm )
679- xbmcvfs . delete (widevinecdm )
695+ delete (widevinecdm )
680696 notification (localize (30037 ), localize (30052 )) # Success! Widevine successfully removed.
681697 return True
682698 notification (localize (30004 ), localize (30053 )) # Error. Widevine CDM not found.
@@ -691,6 +707,7 @@ def _first_run():
691707 addon_version = get_addon_info ('version' )
692708
693709 # Compare versions
710+ from distutils .version import LooseVersion # pylint: disable=import-error,no-name-in-module
694711 if LooseVersion (addon_version ) > LooseVersion (settings_version ):
695712 # New version found, save addon_version to settings
696713 set_setting ('version' , addon_version )
@@ -702,6 +719,7 @@ def _update_widevine(self):
702719 """Prompts user to upgrade Widevine CDM when a newer version is available."""
703720 last_update = get_setting ('last_update' )
704721 if last_update and not self ._first_run ():
722+ from datetime import datetime , timedelta
705723 last_update_dt = datetime .fromtimestamp (float (get_setting ('last_update' )))
706724 if last_update_dt + timedelta (days = int (get_setting ('update_frequency' , '14' ))) >= datetime .utcnow ():
707725 log ('Widevine update check was made on {date}' , date = last_update_dt .isoformat ())
@@ -718,6 +736,7 @@ def _update_widevine(self):
718736 log ('Latest {component} version is {version}' , component = component , version = latest_version )
719737 log ('Current {component} version installed is {version}' , component = component , version = current_version )
720738
739+ from distutils .version import LooseVersion # pylint: disable=import-error,no-name-in-module
721740 if LooseVersion (latest_version ) > LooseVersion (current_version ):
722741 log ('There is an update available for {component}' , component = component )
723742 if yesno_dialog (localize (30040 ), localize (30033 ), nolabel = localize (30028 ), yeslabel = localize (30034 )):
@@ -749,6 +768,7 @@ def _widevine_eula(self):
749768
750769 def _extract_widevine_from_img (self ):
751770 ''' Extract the Widevine CDM binary from the mounted Chrome OS image '''
771+ import shutil
752772 for root , dirs , files in os .walk (str (self ._mnt_path ())): # pylint: disable=unused-variable
753773 if str ('libwidevinecdm.so' ) not in files :
754774 continue
@@ -854,6 +874,7 @@ def _unmount(self):
854874
855875 def _cleanup (self ):
856876 ''' Clean up function after Widevine CDM installation '''
877+ import shutil
857878 self ._unmount ()
858879 if self ._attached_loop_dev :
859880 cmd = ['losetup' , '-d' , self ._loop_dev ]
@@ -870,6 +891,7 @@ def _cleanup(self):
870891
871892 def _supports_hls (self ):
872893 """Return if HLS support is available in inputstream.adaptive."""
894+ from distutils .version import LooseVersion # pylint: disable=import-error,no-name-in-module
873895 if LooseVersion (self ._inputstream_version ()) >= LooseVersion (config .HLS_MINIMUM_IA_VERSION ):
874896 return True
875897
@@ -894,9 +916,11 @@ def _check_drm(self):
894916
895917 def _install_inputstream (self ):
896918 """Install inputstream addon."""
919+ from xbmc import executebuiltin
920+ from xbmcaddon import Addon
897921 try :
898922 # See if there's an installed repo that has it
899- xbmc . executebuiltin ('InstallAddon({})' .format (self .inputstream_addon ), wait = True )
923+ executebuiltin ('InstallAddon({})' .format (self .inputstream_addon ), wait = True )
900924
901925 # Check if InputStream add-on exists!
902926 Addon ('{}' .format (self .inputstream_addon ))
@@ -934,7 +958,6 @@ def check_inputstream(self):
934958
935959 def info_dialog (self ):
936960 """ Show an Info box with useful info e.g. for bug reports"""
937-
938961 text = localize (30800 ) + '\n ' # Kodi information
939962 text += ' - ' + localize (30801 , version = self ._kodi_version ()) + '\n '
940963 text += ' - ' + localize (30802 , platform = system_os (), arch = self ._arch ()) + '\n '
@@ -950,6 +973,7 @@ def info_dialog(self):
950973
951974 if system_os () != 'Android' :
952975 text += ' - ' + localize (30820 ) + '\n ' # Widevine information
976+ from datetime import datetime
953977 wv_updated = datetime .fromtimestamp (float (get_setting ('last_update' ))).strftime ("%Y-%m-%d %H:%M" ) if get_setting ('last_update' ) else 'Never'
954978 text += ' - ' + localize (30821 , version = self ._get_lib_version (self ._widevine_path ()), date = wv_updated ) + '\n '
955979 text += ' - ' + localize (30822 , path = self ._ia_cdm_path ()) + '\n '
@@ -961,5 +985,5 @@ def info_dialog(self):
961985
962986 text += localize (30830 , url = config .ISSUE_URL ) # Report issues
963987
964- log ('\n {info}' .format (info = kodi_to_ascii (text )), level = xbmc . LOGNOTICE )
988+ log ('\n {info}' .format (info = kodi_to_ascii (text )), level = 2 )
965989 textviewer (localize (30901 ), text )
0 commit comments