def register_internal_apps(kodi_interface): """Register the internal DIAL applications based on the installed Kodi add-ons""" # The DIAL apps will be loaded from "resources/lib/apps" sub-folders directory_base = file_ops.join_folders_paths(G.ADDON_DATA_PATH, 'resources/lib/apps') dirs, _ = file_ops.list_dir(directory_base) MUTEX.acquire() for dir_name in dirs: try: if dir_name.startswith('_') or dir_name == 'dial_app_test': continue class_file_path = file_ops.join_folders_paths( directory_base, dir_name, dir_name + '.py') if not file_ops.file_exists(class_file_path): LOG.error( 'register_internal_apps: missing module file {}.py on {} folder', dir_name, dir_name) continue # Load the internal module loaded_module = importlib.import_module('resources.lib.apps.' + dir_name + '.' + dir_name) app_class = getattr(loaded_module, 'DialApp', None) if app_class is None: LOGGER.error( 'register_internal_apps: "DialApp" class not found in {}.py file', dir_name) continue register_app(app_class, kodi_interface) except Exception: LOGGER.error( 'register_internal_apps: could not import the DIAL app from {} folder', dir_name) import traceback LOG.error(traceback.format_exc()) MUTEX.release()
def initialize_connection(self): """Checks database existence and performs first connection tests""" try: LOG.debug('Trying connection to the database {}', self.db_filename) self.conn = sql.connect(self.db_file_path, check_same_thread=False) cur = self.conn.cursor() cur.execute(str('SELECT SQLITE_VERSION()')) LOG.debug('Database connection {} was successful (SQLite ver. {})', self.db_filename, cur.fetchone()[0]) cur.row_factory = lambda cursor, row: row[0] cur.execute( str('SELECT name FROM sqlite_master WHERE type=\'table\' ' 'AND name NOT LIKE \'sqlite_%\'')) list_tables = cur.fetchall() if not list_tables: # If no tables exist create a new one self.conn.close() db_create_sqlite.create_database(self.db_file_path, self.db_filename) except sql.Error as exc: LOG.error('SQLite error {}:', exc.args[0]) raise DBSQLiteConnectionError from exc finally: if self.conn: self.conn.close()
def reporthook(num_blocks, block_size, file_size, dlg, start_time, filename): try: percent = min(num_blocks * block_size * 100 / file_size, 100) currently_downloaded = float(num_blocks) * block_size / (1024 * 1024) kbps_speed = num_blocks * block_size / (time.time() - start_time) eta = 0 if kbps_speed > 0: eta = (file_size - num_blocks * block_size) / kbps_speed if eta < 0: eta = 0 kbps_speed = kbps_speed / 1024 eta = divmod(eta, 60) file_size_mb = float(file_size) / (1024 * 1024) status = '{:.02f} MB of {:.02f} MB '.format(currently_downloaded, file_size_mb) status += '{} {:.02f} Kb/s '.format(get_local_string(30501), kbps_speed) status += '{} {:02d}:{:02d}'.format(get_local_string(30502), int(eta[0]), int(eta[1])) dlg.update( int(percent), '{}[CR]{}[CR]{}'.format(get_local_string(30500), filename, status)) except Exception as exc: LOG.error('[download_file] reporthook raised an error: {}', exc) dlg.update(100) if dlg.iscanceled(): raise InterruptedError
def register_apps(kodi_interface): """Register DIAL applications based on the Kodi add-ons that support Cast feature""" # We find out which add-ons has "script.appcast" optional dependency, # then for each add-on we try import the "DialApp" class included in the path "resources/lib/dial_app_test/" addons = kodi_ops.json_rpc( 'Addons.GetAddons', { 'type': 'unknown', 'properties': ['name', 'dependencies', 'enabled', 'path'] }) MUTEX.acquire() # # Clear previous sys modules added # for name in list(sys.modules.keys()): # if name.startswith('dial_app_'): # del sys.modules[name] for addon in addons['addons']: if not addon['enabled']: continue if any(dep['addonid'] == 'script.appcast' for dep in addon['dependencies']): # Try add the DIAL app included in the add-on try: name = addon['addonid'].split('.')[-1] package = 'dial_app_' + name module_path_folder1 = file_ops.join_folders_paths( addon['path'], package, package + '.py') module_path_folder2 = file_ops.join_folders_paths( addon['path'], 'resources/lib/' + package, package + '.py') # Check if the add-on has the package file if file_ops.file_exists(module_path_folder1): module_path = module_path_folder1 elif file_ops.file_exists(module_path_folder2): module_path = module_path_folder2 else: LOGGER.error( 'register_apps: missing module file {}.py on {} add-on', package, addon['addonid']) continue # Load the external module (and allow it's own relative imports) spec = importlib.util.spec_from_file_location( package, module_path, submodule_search_locations=[]) module = importlib.util.module_from_spec(spec) sys.modules[module.__name__] = module spec.loader.exec_module(module) # Get the "DialApp" class from the loaded module app_class = getattr(module, 'DialApp', None) if app_class is None: LOGGER.error( 'register_apps: "DialApp" class not found in {}.py file of {} add-on', package, addon['addonid']) continue register_app(app_class, kodi_interface) except Exception: LOGGER.error( 'register_apps: could not import the DIAL app from {}', addon['addonid']) import traceback LOG.error(traceback.format_exc()) MUTEX.release()
def run(self): """Main loop. Runs until xbmc.Monitor requests abort""" try: self.start_services() monitor = xbmc.Monitor() while not monitor.abortRequested(): monitor.waitForAbort(1) self.shutdown() except Exception: # pylint: disable=broad-except import traceback LOG.error(traceback.format_exc())
def _perform_ipc_return_call_instance(instance, func, data): try: result = _call_with_instance(instance, func, data) except Exception as exc: # pylint: disable=broad-except LOG.error('IPC callback raised exception: {exc}', exc=exc) import traceback LOG.error(traceback.format_exc()) result = ipc_convert_exc_to_json(exc) AddonSignals.returnCall(signal=func.__name__, source_id=IPC_ADDON_ID, data=result) return result
def _execute_notify(app, callback_name, data): try: method = getattr(app, callback_name) method(data) return True except Exception: # pylint: disable=broad-except LOG.error( 'The app {} has raised the following error on {} callback:', app.DIAL_APP_NAME, callback_name) import traceback LOG.error(traceback.format_exc()) return False
def init_servers(self): """Initialize the servers""" try: self.dial_srv_instance = dial_server.DialTCPServer((self.HOST_ADDRESS, G.DIAL_SERVER_PORT)) self.dial_srv_instance.allow_reuse_address = True self.dial_srv_thread = threading.Thread(target=self.dial_srv_instance.serve_forever) self.ssdp_udp_srv_instance = ssdp_server.SSDPUDPServer() self.ssdp_udp_srv_instance.allow_reuse_address = True self.ssdp_udp_srv_thread = threading.Thread(target=self.ssdp_udp_srv_instance.serve_forever) return True except Exception: # pylint: disable=broad-except import traceback LOG.error(traceback.format_exc()) return False
def convert_to_string(value): if value is None: return None data_type = type(value) if data_type == str: return value converter = None if data_type in (int, float, bool, tuple, datetime.datetime): converter = _conv_standard_to_string if data_type in (list, dict, OrderedDict): converter = _conv_json_to_string if not converter: LOG.error( 'convert_to_string: Data type {} not mapped'.format(data_type)) raise DataTypeNotMapped return converter(value)
def convert_from_string(value, to_data_type): if value is None: return None if to_data_type in (str, int, float): return to_data_type(value) if to_data_type in (bool, list, tuple): return literal_eval(value) converter = None if to_data_type == dict: converter = _conv_string_to_json if to_data_type == datetime.datetime: converter = _conv_string_to_datetime if not converter: LOG.error('convert_from_string: Data type {} not mapped'.format( to_data_type)) raise DataTypeNotMapped return converter(value)
def download_file(url, dest_path, filename): start_time = time.time() dlg = xbmcgui.DialogProgress() dlg.create(G.ADDON_ID, get_local_string(30499)) try: urlretrieve( url.rstrip('/'), dest_path, lambda num_blocks, block_size, file_size: reporthook( num_blocks, block_size, file_size, dlg, start_time, filename)) return True except InterruptedError: LOG.error('Download interrupted by user') except Exception as exc: LOG.error('Download failed due to an error: {}', exc) raise Exception('Download failed') from exc finally: dlg.close() return False
def run(argv): # Initialize globals right away to avoid stale values from the last addon invocation. # Otherwise Kodi's reuseLanguageInvoker will cause some really quirky behavior! # PR: https://github.com/xbmc/xbmc/pull/13814 G.init_globals(argv) LOG.info('Started (Version {})'.format(G.VERSION_RAW)) LOG.info('URL is {}'.format(G.URL)) success = False try: pathitems = [part for part in G.REQUEST_PATH.split('/') if part] success = route(pathitems) except Exception as exc: import traceback LOG.error(traceback.format_exc()) kodi_ops.dlg_ok( 'AutoUpdateKodi', kodi_ops.get_local_string(30700).format('[{}] {}'.format( exc.__class__.__name__, exc))) if not success: from xbmcplugin import endOfDirectory endOfDirectory(handle=G.PLUGIN_HANDLE, succeeded=False) LOG.log_time_trace()
def wrapper(*args, **kwargs): if args[0].is_mysql_database: # If database is mysql pass to next decorator return func(*args, **kwargs) conn = None try: if not args[0].is_connected: args[0].mutex.acquire() args[0].conn = sql.connect( args[0].db_file_path, isolation_level=CONN_ISOLATION_LEVEL) args[0].is_connected = True conn = args[0].conn return func(*args, **kwargs) except sql.Error as exc: LOG.error('SQLite error {}:', exc.args[0]) raise DBSQLiteConnectionError from exc finally: if conn: args[0].is_connected = False conn.close() args[0].mutex.release()
def _executemany_non_query(self, query, params, cursor=None): try: if cursor is None: cursor = self.get_cursor() cursor.executemany(query, params) except sql.Error as exc: LOG.error('SQLite error {}:', exc.args[0]) raise DBSQLiteError from exc except ValueError: LOG.error('Value {}', str(params)) LOG.error('Value type {}', type(params)) raise
def _get_linux_uuid(): # pylint: disable=broad-except import subprocess uuid_value = None try: uuid_value = subprocess.check_output(['cat', '/var/lib/dbus/machine-id']).decode('utf-8') except Exception as exc: import traceback LOG.error('_get_linux_uuid first attempt returned: {}', exc) LOG.error(traceback.format_exc()) if not uuid_value: try: # Fedora linux uuid_value = subprocess.check_output(['cat', '/etc/machine-id']).decode('utf-8') except Exception as exc: LOG.error('_get_linux_uuid second attempt returned: {}', exc) return uuid_value
def _execute_non_query(self, query, params=None, cursor=None, **kwargs): """ Execute a query without returning a value :param query: sql query :param params: tuple of values :param cursor: a cursor, if None get a instance of standard cursor """ try: if cursor is None: cursor = self.get_cursor() if params is not None: cursor.execute(query, params) else: cursor.execute(query) except sql.Error as exc: LOG.error('SQLite error {}:', exc.args[0]) raise DBSQLiteError from exc except ValueError: LOG.error('Value {}', str(params)) LOG.error('Value type {}', type(params)) raise