class NetflixService: """Netflix addon service""" HOST_ADDRESS = '127.0.0.1' def __init__(self): self.library_updater = None self.nf_server_instance = None self.nf_server_thread = None def init_servers(self): """Initialize the HTTP server""" try: # Import modules here to intercept possible missing libraries on linux systems from resources.lib.services.http_server import NFThreadedTCPServer self.nf_server_instance = NFThreadedTCPServer( (self.HOST_ADDRESS, select_port('NF_SERVER'))) self.nf_server_instance.allow_reuse_address = True self.nf_server_thread = threading.Thread( target=self.nf_server_instance.serve_forever) return True except Exception as exc: # pylint: disable=broad-except LOG.error( 'Background services do not start due to the following error') import traceback LOG.error(traceback.format_exc()) if isinstance(exc, gaierror): message = ( 'Something is wrong in your network localhost configuration.\r\n' f'It is possible that the hostname {self.HOST_ADDRESS} can not be resolved.' ) elif isinstance(exc, ImportError): message = ( 'In your system is missing some required library to run Netflix.\r\n' 'Read how to install the add-on in the GitHub Readme.\r\n' f'Error details: {exc}') else: message = str(exc) self._set_service_status('error', message) return False def start_services(self): """Start the background services""" from resources.lib.services.library_updater import LibraryUpdateService self.nf_server_instance.server_activate() self.nf_server_thread.start() LOG.info('[NF_SERVER] Thread started') self.library_updater = LibraryUpdateService() # We reset the value in case of any eventuality (add-on disabled, update, etc) WndHomeProps[WndHomeProps.CURRENT_DIRECTORY] = None # Mark the service as active self._set_service_status('running') if not G.ADDON.getSettingBool('disable_startup_notification'): from resources.lib.kodi.ui import show_notification show_notification(get_local_string(30110)) def shutdown(self): """Stop the background services""" self._set_service_status('stopped') self.nf_server_instance.shutdown() self.nf_server_instance.server_close() self.nf_server_instance = None self.nf_server_thread.join() self.nf_server_thread = None LOG.info('Stopped MSL Service') def run(self): """Main loop. Runs until xbmc.Monitor requests abort""" try: self.start_services() except Exception as exc: # pylint: disable=broad-except self._set_service_status('stopped') import traceback from resources.lib.kodi.ui import show_addon_error_info LOG.error(traceback.format_exc()) show_addon_error_info(exc) return while not G.SETTINGS_MONITOR.abortRequested(): if self._tick_and_wait_for_abort(): break self.shutdown() def _tick_and_wait_for_abort(self): try: self.library_updater.on_service_tick() G.CACHE_MANAGEMENT.on_service_tick() except Exception as exc: # pylint: disable=broad-except import traceback from resources.lib.kodi.ui import show_notification LOG.error(traceback.format_exc()) show_notification(': '.join((exc.__class__.__name__, str(exc)))) return G.SETTINGS_MONITOR.waitForAbort(1) def _set_service_status(self, status, message=None): """Save the service status to a Kodi property""" from json import dumps status = {'status': status, 'message': message} WndHomeProps[WndHomeProps.SERVICE_STATUS] = dumps(status)
class NetflixService(object): """ Netflix addon service """ SERVERS = [] HOST_ADDRESS = '127.0.0.1' def __init__(self): self.window_cls = Window(10000) # Kodi home window # If you use multiple Kodi profiles you need to distinguish the property of current profile self.prop_nf_service_status = g.py2_encode( 'nf_service_status_' + get_current_kodi_profile_name()) self.controller = None self.library_updater = None self.settings_monitor = None def init_servers(self): """Initialize the http servers""" try: # Import modules here to intercept possible missing libraries on linux systems from resources.lib.services.msl.http_server import MSLTCPServer from resources.lib.services.nfsession.http_server import NetflixTCPServer from resources.lib.services.cache.http_server import CacheTCPServer # Do not change the init order of the servers, # MSLTCPServer must always be initialized first to get the DRM info self.SERVERS = [{ 'name': 'MSL', 'class': MSLTCPServer, 'instance': None, 'thread': None }, { 'name': 'NS', 'class': NetflixTCPServer, 'instance': None, 'thread': None }, { 'name': 'CACHE', 'class': CacheTCPServer, 'instance': None, 'thread': None }] for server in self.SERVERS: self._init_server(server) return True except Exception as exc: # pylint: disable=broad-except error( 'Background services do not start due to the following error') import traceback error(g.py2_decode(traceback.format_exc(), 'latin-1')) if isinstance(exc, gaierror): message = ( 'Something is wrong in your network localhost configuration.\r\n' 'It is possible that the hostname {} can not be resolved.' ).format(self.HOST_ADDRESS) elif ImportError(exc, ImportError): message = ( 'In your system is missing some required library to run Netflix.\r\n' 'Read how to install the add-on in the GitHub Readme.\r\n' 'Error details: {}'.format(exc)) else: message = unicode(exc) self._set_service_status('error', message) return False def _init_server(self, server): server['class'].allow_reuse_address = True server['instance'] = server['class']( (self.HOST_ADDRESS, select_port(server['name']))) server['thread'] = threading.Thread( target=server['instance'].serve_forever) def start_services(self): """ Start the background services """ from resources.lib.services.playback.action_controller import ActionController from resources.lib.services.library_updater import LibraryUpdateService from resources.lib.services.settings_monitor import SettingsMonitor for server in self.SERVERS: server['instance'].server_activate() server['instance'].timeout = 1 server['thread'].start() info('[{}] Thread started'.format(server['name'])) self.controller = ActionController() self.library_updater = LibraryUpdateService() self.settings_monitor = SettingsMonitor() # Mark the service as active self._set_service_status('running') if not g.ADDON.getSettingBool('disable_startup_notification'): from resources.lib.kodi.ui import show_notification show_notification(get_local_string(30110)) def shutdown(self): """ Stop the background services """ self._set_service_status('stopped') for server in self.SERVERS: server['instance'].shutdown() server['instance'].server_close() server['instance'] = None server['thread'].join() server['thread'] = None info('Stopped MSL Service') def run(self): """Main loop. Runs until xbmc.Monitor requests abort""" try: self.start_services() except Exception as exc: # pylint: disable=broad-except self._set_service_status('stopped') import traceback from resources.lib.kodi.ui import show_addon_error_info error(g.py2_decode(traceback.format_exc(), 'latin-1')) show_addon_error_info(exc) return while not self.controller.abortRequested(): if self._tick_and_wait_for_abort(): break self.shutdown() def _tick_and_wait_for_abort(self): try: self.controller.on_service_tick() self.library_updater.on_service_tick() g.CACHE_MANAGEMENT.on_service_tick() except Exception as exc: # pylint: disable=broad-except import traceback from resources.lib.kodi.ui import show_notification error(g.py2_decode(traceback.format_exc(), 'latin-1')) show_notification(': '.join( (exc.__class__.__name__, unicode(exc)))) return self.controller.waitForAbort(1) def _set_service_status(self, status, message=None): """Save the service status to a Kodi property""" from json import dumps status = {'status': status, 'message': message} self.window_cls.setProperty(self.prop_nf_service_status, dumps(status))
class NetflixService(object): """ Netflix addon service """ from resources.lib.services.msl.http_server import MSLTCPServer from resources.lib.services.nfsession.http_server import NetflixTCPServer from resources.lib.services.cache.http_server import CacheTCPServer # Do not change the init order of the servers, # MSLTCPServer must always be initialized first to get the DRM info SERVERS = [{ 'name': 'MSL', 'class': MSLTCPServer, 'instance': None, 'thread': None }, { 'name': 'NS', 'class': NetflixTCPServer, 'instance': None, 'thread': None }, { 'name': 'CACHE', 'class': CacheTCPServer, 'instance': None, 'thread': None }] def __init__(self): self.window_cls = Window(10000) # Kodi home window # If you use multiple Kodi profiles you need to distinguish the property of current profile self.prop_nf_service_status = g.py2_encode( 'nf_service_status_' + get_current_kodi_profile_name()) for server in self.SERVERS: self.init_server(server) self.controller = None self.library_updater = None self.settings_monitor = None def init_server(self, server): server['class'].allow_reuse_address = True server['instance'] = server['class']( ('127.0.0.1', select_port(server['name']))) server['thread'] = threading.Thread( target=server['instance'].serve_forever) def start_services(self): """ Start the background services """ from resources.lib.services.playback.controller import PlaybackController from resources.lib.services.library_updater import LibraryUpdateService from resources.lib.services.settings_monitor import SettingsMonitor for server in self.SERVERS: server['instance'].server_activate() server['instance'].timeout = 1 server['thread'].start() info('[{}] Thread started'.format(server['name'])) self.controller = PlaybackController() self.library_updater = LibraryUpdateService() self.settings_monitor = SettingsMonitor() # Mark the service as active self.window_cls.setProperty(self.prop_nf_service_status, 'running') if not g.ADDON.getSettingBool('disable_startup_notification'): from resources.lib.kodi.ui import show_notification show_notification(get_local_string(30110)) def shutdown(self): """ Stop the background services """ self.window_cls.setProperty(self.prop_nf_service_status, 'stopped') for server in self.SERVERS: server['instance'].shutdown() server['instance'].server_close() server['instance'] = None server['thread'].join() server['thread'] = None info('Stopped MSL Service') def run(self): """Main loop. Runs until xbmc.Monitor requests abort""" try: self.start_services() except Exception as exc: # pylint: disable=broad-except self.window_cls.setProperty(self.prop_nf_service_status, 'stopped') import traceback from resources.lib.kodi.ui import show_addon_error_info error(traceback.format_exc()) show_addon_error_info(exc) return while not self.controller.abortRequested(): if self._tick_and_wait_for_abort(): break self.shutdown() def _tick_and_wait_for_abort(self): try: self.controller.on_service_tick() self.library_updater.on_service_tick() g.CACHE_MANAGEMENT.on_service_tick() except Exception as exc: # pylint: disable=broad-except import traceback from resources.lib.kodi.ui import show_notification error(traceback.format_exc()) show_notification(': '.join( (exc.__class__.__name__, unicode(exc)))) return self.controller.waitForAbort(1)