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()
Beispiel #2
0
 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()
Beispiel #5
0
 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())
Beispiel #6
0
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
Beispiel #7
0
 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
Beispiel #8
0
 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()
Beispiel #13
0
    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()
Beispiel #14
0
 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
Beispiel #16
0
 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