CastagnaIT/plugin.video.netflix

View on GitHub
resources/lib/run_service.py

Summary

Maintainability
A
0 mins
Test Coverage
# -*- coding: utf-8 -*-
"""
    Copyright (C) 2017 Sebastian Golasch (plugin.video.netflix)
    Functions for starting the service

    SPDX-License-Identifier: MIT
    See LICENSES/MIT.md for more information.
"""
import threading
from socket import gaierror
from resources.lib.common import select_port, WndHomeProps
from resources.lib.globals import G
from resources.lib.upgrade_controller import check_service_upgrade
from resources.lib.utils.logging import LOG


def _set_service_status(status, message=None):
    """Save the service status to Kodi homepage property"""
    from json import dumps
    status = {'status': status, 'message': message}
    WndHomeProps[WndHomeProps.SERVICE_STATUS] = dumps(status)


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)
            _set_service_status(G.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
        _set_service_status(G.SERVICE_STATUS_RUNNING)

    def shutdown(self):
        """Stop the background services"""
        _set_service_status(G.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
            _set_service_status(G.SERVICE_STATUS_ERROR, 'Internal add-on service error.')
            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 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)
    _set_service_status(G.SERVICE_STATUS_UPGRADE)
    check_service_upgrade()
    _set_service_status(G.SERVICE_STATUS_STARTUP)
    netflix_service = NetflixService()
    if netflix_service.init_servers():
        netflix_service.run()