def _skin_widget_call(window_cls):
    """
    Workaround to intercept calls made by the Skin Widgets currently in use.
    Currently, the Skin widgets associated with add-ons are executed at Kodi startup immediately
    without respecting any services needed by the add-ons. This is causing different
    kinds of problems like widgets not loaded, add-on warning message, etc...
    this loop freeze the add-on instance until the service is ready.
    """
    # Note to "Window.IsMedia":
    # All widgets will be either on Home or in a Custom Window, so "Window.IsMedia" will be false
    # When the user is browsing the plugin, Window.IsMedia will be true because video add-ons open
    # in MyVideoNav.xml (which is a Media window)
    # This is not a safe solution, because DEPENDS ON WHICH WINDOW IS OPEN,
    # for example it can fail if you open add-on video browser while widget is still loading.
    # Needed a proper solution by script.skinshortcuts / script.skin.helper.service, and forks
    limit_sec = 10
    if not getCondVisibility("Window.IsMedia"):
        monitor = Monitor()
        sec_elapsed = 0
        while not window_cls.getProperty('nf_service_status') == 'running':
            if sec_elapsed >= limit_sec or monitor.abortRequested() or monitor.waitForAbort(0.5):
                break
            sec_elapsed += 0.5
        debug('Skin widget workaround enabled - time elapsed: {}', sec_elapsed)
        return True
    return False
def generate_pairing_code():
    monitor = Monitor()
    progress = DialogProgress()
    progress.create(get_string(32000), get_string(32001))
    chromecast = YoutubeCastV1()
    pairing_code = chromecast.pair()

    i = 0

    if PY3:
        progress.update(i,
                        message="{} {}".format(get_string(32002),
                                               pairing_code))
    else:
        progress.update(i, get_string(32002), pairing_code)

    start_time = time.time()
    while not monitor.abortRequested(
    ) and not chromecast.has_client and not progress.iscanceled() and not (
            time.time() - start_time) > (60 * 1):
        i += 10
        if i > 100:
            i = 0

        if PY3:
            progress.update(i,
                            message="{} {}".format(get_string(32002),
                                                   pairing_code))
        else:
            progress.update(i, get_string(32002), pairing_code)

        monitor.waitForAbort(2)
    progress.close()
Example #3
0
def autostart():
    """
    Starts the cleaning service.
    """
    cleaner = Cleaner()
    monitor = Monitor()

    service_sleep = 4  # Lower than 4 causes too much stress on resource limited systems such as RPi
    ticker = 0
    delayed_completed = False

    while not monitor.abortRequested():
        if get_setting(service_enabled):
            scan_interval_ticker = get_setting(scan_interval) * 60 / service_sleep
            delayed_start_ticker = get_setting(delayed_start) * 60 / service_sleep

            if delayed_completed and ticker >= scan_interval_ticker:
                results = cleaner.clean_all()
                if results:
                    notify(results)
                ticker = 0
            elif not delayed_completed and ticker >= delayed_start_ticker:
                delayed_completed = True
                results = cleaner.clean_all()
                if results:
                    notify(results)
                ticker = 0

            xbmc.sleep(service_sleep * 1000)
            ticker += 1
        else:
            xbmc.sleep(service_sleep * 1000)

    print("Abort requested. Terminating.")
    return
Example #4
0
 def run(self):
     debug('START player bg service')
     m = Monitor()
     while not m.abortRequested():
         sleep(1000)
         try:
             self.periodical_check()
         except:
             debug('player bg service ERR {}'.format(
                 traceback.format_exc()))
     debug('END player bg service')
Example #5
0
def test_popup(window):
    popup = TestPopup(window, addon_path(), 'default', '1080i')
    popup.show()
    step = 0
    wait = 100
    timeout = 10000
    monitor = Monitor()
    while popup and step < timeout and not monitor.abortRequested():
        if popup.pause:
            continue
        sleep(wait)
        popup.update_progress_control(timeout, wait)
        step += wait
def _check_addon_external_call(window_cls, prop_nf_service_status):
    """Check system to verify if the calls to the add-on are originated externally"""
    # The calls that are made from outside do not respect and do not check whether the services required
    # for the add-on are actually working and operational, causing problems with the execution of the frontend.

    # A clear example are the Skin widgets, that are executed at Kodi startup immediately and this is cause of different
    # kinds of problems like widgets not loaded, add-on warning message, etc...

    # Cases where it can happen:
    # - Calls made by the Skin Widgets, Scripts, Kodi library
    # - Calls made by others Kodi windows (like file browser)
    # - Calls made by other add-ons

    # To try to solve the problem, when the service is not ready a loop will be started to freeze the add-on instance
    # until the service will be ready.

    is_other_plugin_name = getInfoLabel(
        'Container.PluginName') != g.ADDON.getAddonInfo('id')
    limit_sec = 10

    # Note to Kodi boolean condition "Window.IsMedia":
    # All widgets will be either on Home or in a Custom Window, so "Window.IsMedia" will be false
    # When the user is browsing the plugin, Window.IsMedia will be true because video add-ons open
    # in MyVideoNav.xml (which is a Media window)
    # This is not a safe solution, because DEPENDS ON WHICH WINDOW IS OPEN,
    # for example it can fail if you open add-on video browser while widget is still loading.
    # Needed a proper solution by script.skinshortcuts / script.skin.helper.service, and forks
    if is_other_plugin_name or not getCondVisibility("Window.IsMedia"):
        monitor = Monitor()
        sec_elapsed = 0
        while not _get_service_status(
                window_cls, prop_nf_service_status).get('status') == 'running':
            if sec_elapsed >= limit_sec or monitor.abortRequested(
            ) or monitor.waitForAbort(0.5):
                break
            sec_elapsed += 0.5
        debug(
            'Add-on was initiated by an external call - workaround enabled time elapsed {}s',
            sec_elapsed)
        g.IS_ADDON_EXTERNAL_CALL = True
        return True
    return False
Example #7
0
                except IOError as ioe:
                    log("removing unresponsive light %s" % light.label, level=LOGWARNING)
                    unresponsive.append(light)
            for light in unresponsive:
                del self.lights[light]

def log(msg, level=LOGNOTICE):
    xbmc.log("lifxbmc - %s" % msg.replace("\0", ""), level=level) # strings returned from lights sometimes have null chars

if __name__ == '__main__':
    monitor = Monitor()
    player = LifxPlayer()
    
    log("inited")    
     
    while not monitor.abortRequested():
        if monitor.waitForAbort(10):
            break
        
        try:
            lifx = LifxLAN()
            for light, color in lifx.get_color_all_lights():
                if len(addon.getSetting("group_filter")) > 0:
                    pass
                if light not in player:
                    log("discovered new light %s" % light.get_label())
                    player.add_light(light, color)
        except Exception as e:
            log("Exception while discovering lights: %s" % e, level=LOGERROR)

Example #8
0
if __name__ == '__main__':
    monitor = Monitor()

    # start thread for MLS servie
    msl_thread = threading.Thread(target=msl_server.serve_forever)
    msl_thread.daemon = True
    msl_thread.start()

    # start thread for Netflix HTTP service
    nd_thread = threading.Thread(target=nd_server.serve_forever)
    nd_thread.daemon = True
    nd_thread.start()

    # kill the services if kodi monitor tells us to
    while not monitor.abortRequested():
        if monitor.waitForAbort(5):
            msl_server.shutdown()
            nd_server.shutdown()
            break

    # MSL service shutdown sequence
    msl_server.server_close()
    msl_server.socket.close()
    msl_server.shutdown()
    kodi_helper.log(msg='Stopped MSL Service')

    # Netflix service shutdown sequence
    nd_server.server_close()
    nd_server.socket.close()
    nd_server.shutdown()
Example #9
0
class Service():

    server_online = True
    warn_auth = True

    user = None
    ws = None
    library = None
    plexCompanion = None

    user_running = False
    ws_running = False
    alexa_running = False
    library_running = False
    plexCompanion_running = False
    kodimonitor_running = False
    playback_starter_running = False
    image_cache_thread_running = False

    def __init__(self):
        # Initial logging
        LOG.info("======== START %s ========", v.ADDON_NAME)
        LOG.info("Platform: %s", v.PLATFORM)
        LOG.info("KODI Version: %s", v.KODILONGVERSION)
        LOG.info("%s Version: %s", v.ADDON_NAME, v.ADDON_VERSION)
        LOG.info("PKC Direct Paths: %s", settings('useDirectPaths') == "true")
        LOG.info("Number of sync threads: %s", settings('syncThreadNumber'))
        LOG.info("Full sys.argv received: %s", argv)
        self.monitor = Monitor()
        # Load/Reset PKC entirely - important for user/Kodi profile switch
        initialsetup.reload_pkc()

    def __stop_PKC(self):
        """
        Kodi's abortRequested is really unreliable :-(
        """
        return self.monitor.abortRequested() or state.STOP_PKC

    def ServiceEntryPoint(self):
        # Important: Threads depending on abortRequest will not trigger
        # if profile switch happens more than once.
        __stop_PKC = self.__stop_PKC
        monitor = self.monitor
        kodiProfile = v.KODI_PROFILE

        # Server auto-detect
        initialsetup.InitialSetup().setup()

        # Detect playback start early on
        self.command_pipeline = Monitor_Window()
        self.command_pipeline.start()

        # Initialize important threads, handing over self for callback purposes
        self.user = UserClient()
        self.ws = PMS_Websocket()
        self.alexa = Alexa_Websocket()
        self.library = LibrarySync()
        self.plexCompanion = PlexCompanion()
        self.specialMonitor = SpecialMonitor()
        self.playback_starter = Playback_Starter()
        self.playqueue = PlayqueueMonitor()
        if settings('enableTextureCache') == "true":
            self.image_cache_thread = Image_Cache_Thread()

        welcome_msg = True
        counter = 0
        while not __stop_PKC():

            if window('plex_kodiProfile') != kodiProfile:
                # Profile change happened, terminate this thread and others
                LOG.info(
                    "Kodi profile was: %s and changed to: %s. "
                    "Terminating old PlexKodiConnect thread.", kodiProfile,
                    window('plex_kodiProfile'))
                break

            # Before proceeding, need to make sure:
            # 1. Server is online
            # 2. User is set
            # 3. User has access to the server

            if window('plex_online') == "true":
                # Plex server is online
                # Verify if user is set and has access to the server
                if (self.user.currUser is not None) and self.user.HasAccess:
                    if not self.kodimonitor_running:
                        # Start up events
                        self.warn_auth = True
                        if welcome_msg is True:
                            # Reset authentication warnings
                            welcome_msg = False
                            dialog('notification',
                                   lang(29999),
                                   "%s %s" % (lang(33000), self.user.currUser),
                                   icon='{plex}',
                                   time=2000,
                                   sound=False)
                        # Start monitoring kodi events
                        self.kodimonitor_running = KodiMonitor()
                        self.specialMonitor.start()
                        # Start the Websocket Client
                        if not self.ws_running:
                            self.ws_running = True
                            self.ws.start()
                        # Start the Alexa thread
                        if (not self.alexa_running
                                and settings('enable_alexa') == 'true'):
                            self.alexa_running = True
                            self.alexa.start()
                        # Start the syncing thread
                        if not self.library_running:
                            self.library_running = True
                            self.library.start()
                        # Start the Plex Companion thread
                        if not self.plexCompanion_running:
                            self.plexCompanion_running = True
                            self.plexCompanion.start()
                        if not self.playback_starter_running:
                            self.playback_starter_running = True
                            self.playback_starter.start()
                        self.playqueue.start()
                        if (not self.image_cache_thread_running
                                and settings('enableTextureCache') == "true"):
                            self.image_cache_thread_running = True
                            self.image_cache_thread.start()
                else:
                    if (self.user.currUser is None) and self.warn_auth:
                        # Alert user is not authenticated and suppress future
                        # warning
                        self.warn_auth = False
                        LOG.warn("Not authenticated yet.")

                    # User access is restricted.
                    # Keep verifying until access is granted
                    # unless server goes offline or Kodi is shut down.
                    while self.user.HasAccess is False:
                        # Verify access with an API call
                        self.user.hasAccess()

                        if window('plex_online') != "true":
                            # Server went offline
                            break

                        if monitor.waitForAbort(3):
                            # Abort was requested while waiting. We should exit
                            break
            else:
                # Wait until Plex server is online
                # or Kodi is shut down.
                while not self.__stop_PKC():
                    server = self.user.getServer()
                    if server is False:
                        # No server info set in add-on settings
                        pass
                    elif check_connection(server, verifySSL=True) is False:
                        # Server is offline or cannot be reached
                        # Alert the user and suppress future warning
                        if self.server_online:
                            self.server_online = False
                            window('plex_online', value="false")
                            # Suspend threads
                            state.SUSPEND_LIBRARY_THREAD = True
                            LOG.error("Plex Media Server went offline")
                            if settings('show_pms_offline') == 'true':
                                dialog('notification',
                                       lang(33001),
                                       "%s %s" % (lang(29999), lang(33002)),
                                       icon='{plex}',
                                       sound=False)
                        counter += 1
                        # Periodically check if the IP changed, e.g. per minute
                        if counter > 20:
                            counter = 0
                            setup = initialsetup.InitialSetup()
                            tmp = setup.pick_pms()
                            if tmp is not None:
                                setup.write_pms_to_settings(tmp)
                    else:
                        # Server is online
                        counter = 0
                        if not self.server_online:
                            # Server was offline when Kodi started.
                            # Wait for server to be fully established.
                            if monitor.waitForAbort(5):
                                # Abort was requested while waiting.
                                break
                            self.server_online = True
                            # Alert the user that server is online.
                            if (welcome_msg is False and
                                    settings('show_pms_offline') == 'true'):
                                dialog('notification',
                                       lang(29999),
                                       lang(33003),
                                       icon='{plex}',
                                       time=5000,
                                       sound=False)
                        LOG.info("Server %s is online and ready.", server)
                        window('plex_online', value="true")
                        if state.AUTHENTICATED:
                            # Server got offline when we were authenticated.
                            # Hence resume threads
                            state.SUSPEND_LIBRARY_THREAD = False

                        # Start the userclient thread
                        if not self.user_running:
                            self.user_running = True
                            self.user.start()

                        break

                    if monitor.waitForAbort(3):
                        # Abort was requested while waiting.
                        break

            if monitor.waitForAbort(0.05):
                # Abort was requested while waiting. We should exit
                break
        # Terminating PlexKodiConnect

        # Tell all threads to terminate (e.g. several lib sync threads)
        state.STOP_PKC = True
        window('plex_service_started', clear=True)
        LOG.info("======== STOP %s ========", v.ADDON_NAME)
Example #10
0
class Service():

    server_online = True
    warn_auth = True

    user = None
    ws = None
    library = None
    plexCompanion = None

    user_running = False
    ws_running = False
    alexa_running = False
    library_running = False
    plexCompanion_running = False
    kodimonitor_running = False
    playback_starter_running = False
    image_cache_thread_running = False

    def __init__(self):
        # Initial logging
        LOG.info("======== START %s ========", v.ADDON_NAME)
        LOG.info("Platform: %s", v.PLATFORM)
        LOG.info("KODI Version: %s", v.KODILONGVERSION)
        LOG.info("%s Version: %s", v.ADDON_NAME, v.ADDON_VERSION)
        LOG.info("PKC Direct Paths: %s", settings('useDirectPaths') == "true")
        LOG.info("Number of sync threads: %s", settings('syncThreadNumber'))
        LOG.info("Full sys.argv received: %s", argv)

        # Reset window props for profile switch
        properties = [
            "plex_online", "plex_serverStatus", "plex_onWake",
            "plex_kodiScan",
            "plex_shouldStop", "plex_dbScan",
            "plex_initialScan", "plex_customplayqueue", "plex_playbackProps",
            "pms_token", "plex_token",
            "pms_server", "plex_machineIdentifier", "plex_servername",
            "plex_authenticated", "PlexUserImage", "useDirectPaths",
            "countError", "countUnauthorized",
            "plex_restricteduser", "plex_allows_mediaDeletion",
            "plex_command", "plex_result", "plex_force_transcode_pix"
        ]
        for prop in properties:
            window(prop, clear=True)

        # Clear video nodes properties
        videonodes.VideoNodes().clearProperties()

        # Init some stuff
        state.VERIFY_SSL_CERT = settings('sslverify') == 'true'
        state.SSL_CERT_PATH = settings('sslcert') \
            if settings('sslcert') != 'None' else None
        state.FULL_SYNC_INTERVALL = int(settings('fullSyncInterval')) * 60
        state.SYNC_THREAD_NUMBER = int(settings('syncThreadNumber'))
        state.SYNC_DIALOG = settings('dbSyncIndicator') == 'true'
        state.ENABLE_MUSIC = settings('enableMusic') == 'true'
        state.BACKGROUND_SYNC = settings(
            'enableBackgroundSync') == 'true'
        state.BACKGROUNDSYNC_SAFTYMARGIN = int(
            settings('backgroundsync_saftyMargin'))
        state.REPLACE_SMB_PATH = settings('replaceSMB') == 'true'
        state.REMAP_PATH = settings('remapSMB') == 'true'
        set_replace_paths()
        state.KODI_PLEX_TIME_OFFSET = float(settings('kodiplextimeoffset'))

        window('plex_minDBVersion', value="2.0.0")
        set_webserver()
        self.monitor = Monitor()
        window('plex_kodiProfile',
               value=tryDecode(translatePath("special://profile")))
        window('fetch_pms_item_number',
               value=settings('fetch_pms_item_number'))
        clientinfo.getDeviceId()

    def __stop_PKC(self):
        """
        Kodi's abortRequested is really unreliable :-(
        """
        return self.monitor.abortRequested() or state.STOP_PKC

    def ServiceEntryPoint(self):
        # Important: Threads depending on abortRequest will not trigger
        # if profile switch happens more than once.
        __stop_PKC = self.__stop_PKC
        monitor = self.monitor
        kodiProfile = v.KODI_PROFILE

        # Server auto-detect
        initialsetup.InitialSetup().setup()

        # Detect playback start early on
        self.command_pipeline = Monitor_Window()
        self.command_pipeline.start()

        # Initialize important threads, handing over self for callback purposes
        self.user = UserClient()
        self.ws = PMS_Websocket()
        self.alexa = Alexa_Websocket()
        self.library = LibrarySync()
        self.plexCompanion = PlexCompanion()
        self.specialMonitor = SpecialMonitor()
        self.playback_starter = Playback_Starter()
        self.playqueue = PlayqueueMonitor()
        if settings('enableTextureCache') == "true":
            self.image_cache_thread = Image_Cache_Thread()

        plx = PlexAPI.PlexAPI()

        welcome_msg = True
        counter = 0
        while not __stop_PKC():

            if window('plex_kodiProfile') != kodiProfile:
                # Profile change happened, terminate this thread and others
                LOG.info("Kodi profile was: %s and changed to: %s. "
                         "Terminating old PlexKodiConnect thread."
                         % (kodiProfile,
                            window('plex_kodiProfile')))
                break

            # Before proceeding, need to make sure:
            # 1. Server is online
            # 2. User is set
            # 3. User has access to the server

            if window('plex_online') == "true":
                # Plex server is online
                # Verify if user is set and has access to the server
                if (self.user.currUser is not None) and self.user.HasAccess:
                    if not self.kodimonitor_running:
                        # Start up events
                        self.warn_auth = True
                        if welcome_msg is True:
                            # Reset authentication warnings
                            welcome_msg = False
                            dialog('notification',
                                   lang(29999),
                                   "%s %s" % (lang(33000),
                                              self.user.currUser),
                                   icon='{plex}',
                                   time=2000,
                                   sound=False)
                        # Start monitoring kodi events
                        self.kodimonitor_running = KodiMonitor()
                        self.specialMonitor.start()
                        # Start the Websocket Client
                        if not self.ws_running:
                            self.ws_running = True
                            self.ws.start()
                        # Start the Alexa thread
                        if (not self.alexa_running and
                                settings('enable_alexa') == 'true'):
                            self.alexa_running = True
                            self.alexa.start()
                        # Start the syncing thread
                        if not self.library_running:
                            self.library_running = True
                            self.library.start()
                        # Start the Plex Companion thread
                        if not self.plexCompanion_running:
                            self.plexCompanion_running = True
                            self.plexCompanion.start()
                        if not self.playback_starter_running:
                            self.playback_starter_running = True
                            self.playback_starter.start()
                        self.playqueue.start()
                        if (not self.image_cache_thread_running and
                                settings('enableTextureCache') == "true"):
                            self.image_cache_thread_running = True
                            self.image_cache_thread.start()
                else:
                    if (self.user.currUser is None) and self.warn_auth:
                        # Alert user is not authenticated and suppress future
                        # warning
                        self.warn_auth = False
                        LOG.warn("Not authenticated yet.")

                    # User access is restricted.
                    # Keep verifying until access is granted
                    # unless server goes offline or Kodi is shut down.
                    while self.user.HasAccess is False:
                        # Verify access with an API call
                        self.user.hasAccess()

                        if window('plex_online') != "true":
                            # Server went offline
                            break

                        if monitor.waitForAbort(3):
                            # Abort was requested while waiting. We should exit
                            break
            else:
                # Wait until Plex server is online
                # or Kodi is shut down.
                while not self.__stop_PKC():
                    server = self.user.getServer()
                    if server is False:
                        # No server info set in add-on settings
                        pass
                    elif plx.CheckConnection(server, verifySSL=True) is False:
                        # Server is offline or cannot be reached
                        # Alert the user and suppress future warning
                        if self.server_online:
                            self.server_online = False
                            window('plex_online', value="false")
                            # Suspend threads
                            state.SUSPEND_LIBRARY_THREAD = True
                            LOG.error("Plex Media Server went offline")
                            if settings('show_pms_offline') == 'true':
                                dialog('notification',
                                       lang(33001),
                                       "%s %s" % (lang(29999), lang(33002)),
                                       icon='{plex}',
                                       sound=False)
                        counter += 1
                        # Periodically check if the IP changed, e.g. per minute
                        if counter > 20:
                            counter = 0
                            setup = initialsetup.InitialSetup()
                            tmp = setup.PickPMS()
                            if tmp is not None:
                                setup.WritePMStoSettings(tmp)
                    else:
                        # Server is online
                        counter = 0
                        if not self.server_online:
                            # Server was offline when Kodi started.
                            # Wait for server to be fully established.
                            if monitor.waitForAbort(5):
                                # Abort was requested while waiting.
                                break
                            self.server_online = True
                            # Alert the user that server is online.
                            if (welcome_msg is False and
                                    settings('show_pms_offline') == 'true'):
                                dialog('notification',
                                       lang(29999),
                                       lang(33003),
                                       icon='{plex}',
                                       time=5000,
                                       sound=False)
                        LOG.info("Server %s is online and ready." % server)
                        window('plex_online', value="true")
                        if state.AUTHENTICATED:
                            # Server got offline when we were authenticated.
                            # Hence resume threads
                            state.SUSPEND_LIBRARY_THREAD = False

                        # Start the userclient thread
                        if not self.user_running:
                            self.user_running = True
                            self.user.start()

                        break

                    if monitor.waitForAbort(3):
                        # Abort was requested while waiting.
                        break

            if monitor.waitForAbort(0.05):
                # Abort was requested while waiting. We should exit
                break
        # Terminating PlexKodiConnect

        # Tell all threads to terminate (e.g. several lib sync threads)
        state.STOP_PKC = True
        try:
            downloadutils.DownloadUtils().stopSession()
        except:
            pass
        window('plex_service_started', clear=True)
        LOG.info("======== STOP %s ========" % v.ADDON_NAME)
Example #11
0
thumb_req_port = select_unused_port()
ADDON.setSetting('thumbmail_port', str(thumb_req_port))

thumb_req_server = TCPServer(('127.0.0.1', thumb_req_port),
                             ThumbRequestHandler)
thumb_req_server.server_activate()
thumb_req_server.timeout = 1
utils.log('Started 7Plus Thumbnail HTTP server on port {0}'
          .format(thumb_req_port))

if __name__ == '__main__':
    mon = Monitor()

    # start thread for thumbnail HTTP service
    thumb_req_thread = threading.Thread(target=thumb_req_server.serve_forever)
    thumb_req_thread.daemon = True
    thumb_req_thread.start()

    # kill the services if kodi monitor tells us to
    while not mon.abortRequested():
        if mon.waitForAbort(5):
            thumb_req_server.shutdown()
            break

    # Netflix service shutdown sequence
    thumb_req_server.server_close()
    thumb_req_server.socket.close()
    thumb_req_server.shutdown()
    utils.log('Stopped 7Plus Thumbnail HTTP server')
Example #12
0
if __name__ == '__main__':
    MONITOR = Monitor()

    # start thread for MLS servie
    MSL_THREAD = threading.Thread(target=MSL_SERVER.serve_forever)
    MSL_THREAD.daemon = True
    MSL_THREAD.start()

    # start thread for Netflix HTTP service
    NS_THREAD = threading.Thread(target=NS_SERVER.serve_forever)
    NS_THREAD.daemon = True
    NS_THREAD.start()

    # kill the services if kodi monitor tells us to
    while not MONITOR.abortRequested():
        if MONITOR.waitForAbort(5):
            MSL_SERVER.shutdown()
            NS_SERVER.shutdown()
            break

    # MSL service shutdown sequence
    MSL_SERVER.server_close()
    MSL_SERVER.socket.close()
    MSL_SERVER.shutdown()
    KODI_HELPER.log(msg='Stopped MSL Service')

    # Netflix service shutdown sequence
    NS_SERVER.server_close()
    NS_SERVER.socket.close()
    NS_SERVER.shutdown()