chore(performance): Removes NetflixSession from the core addon & adds a HTTP proxy...
authorSebastian Golasch <public@asciidisco.com>
Tue, 7 Mar 2017 17:40:52 +0000 (18:40 +0100)
committerSebastian Golasch <public@asciidisco.com>
Tue, 7 Mar 2017 17:40:52 +0000 (18:40 +0100)
addon.py
addon.xml
resources/language/English/strings.po
resources/language/German/strings.po
resources/lib/KodiHelper.py
resources/lib/Navigation.py
resources/lib/NetflixHttpRequestHandler.py [new file with mode: 0644]
resources/lib/NetflixHttpSubRessourceHandler.py [new file with mode: 0644]
resources/settings.xml
service.py

index 5b2d7e8..4549bf9 100644 (file)
--- a/addon.py
+++ b/addon.py
@@ -4,7 +4,6 @@
 # Created on: 13.01.2017
 
 import sys
-from resources.lib.NetflixSession import NetflixSession
 from resources.lib.KodiHelper import KodiHelper
 from resources.lib.Navigation import Navigation
 from resources.lib.Library import Library
@@ -18,19 +17,12 @@ kodi_helper = KodiHelper(
     plugin_handle=plugin_handle,
     base_url=base_url
 )
-netflix_session = NetflixSession(
-    cookie_path=kodi_helper.cookie_path,
-    data_path=kodi_helper.data_path,
-    verify_ssl=kodi_helper.get_ssl_verification_setting(),
-    log_fn=kodi_helper.log
-)
 library = Library(
     root_folder=kodi_helper.base_data_path,
     library_settings=kodi_helper.get_custom_library_settings(),
     log_fn=kodi_helper.log
 )
 navigation = Navigation(
-    netflix_session=netflix_session,
     kodi_helper=kodi_helper,
     library=library,
     base_url=base_url,
index 920b7c1..64944e8 100644 (file)
--- a/addon.xml
+++ b/addon.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<addon id="plugin.video.netflix" name="Netflix" version="0.9.11" provider-name="libdev + jojo + asciidisco">
+<addon id="plugin.video.netflix" name="Netflix" version="0.10.1" provider-name="libdev + jojo + asciidisco">
   <requires>
     <import addon="xbmc.python" version="2.24.0"/>
     <import addon="script.module.beautifulsoup4" version="4.3.2"/>
index cfa6cd0..69e8fcb 100644 (file)
@@ -1,7 +1,7 @@
 # Kodi Media Center language file
 # Addon Name: Netflix
 # Addon id: plugin.video.netflix
-# Addon version: 0.9.11
+# Addon version: 0.10.1
 # Addon Provider: libdev + jojo + asciidisco
 msgid ""
 msgstr ""
index 494840c..7a5184c 100644 (file)
@@ -1,7 +1,7 @@
 # Kodi Media Center language file
 # Addon Name: Netflix
 # Addon id: plugin.video.netflix
-# Addon version: 0.9.11
+# Addon version: 0.10.1
 # Addon Provider: libdev + jojo + asciidisco
 msgid ""
 msgstr ""
index 7386ece..4407b87 100644 (file)
@@ -20,7 +20,7 @@ except:
 class KodiHelper:
     """Consumes all the configuration data from Kodi as well as turns data into lists of folders and videos"""
 
-    def __init__ (self, plugin_handle, base_url):
+    def __init__ (self, plugin_handle=None, base_url=None):
         """Fetches all needed info from Kodi & configures the baseline of the plugin
 
         Parameters
index d4d3f7f..db02598 100644 (file)
@@ -3,21 +3,20 @@
 # Module: Navigation
 # Created on: 13.01.2017
 
-from urllib import urlencode, unquote
+import urllib
+import urllib2
+import json
 from urlparse import parse_qsl
 from utils import noop, log
 
 class Navigation:
     """Routes to the correct subfolder, dispatches actions & acts as a controller for the Kodi view & the Netflix model"""
 
-    def __init__ (self, netflix_session, kodi_helper, library, base_url, log_fn=noop):
+    def __init__ (self, kodi_helper, library, base_url, log_fn=noop):
         """Takes the instances & configuration options needed to drive the plugin
 
         Parameters
         ----------
-        netflix_session : :obj:`NetflixSession`
-            instance of the NetflixSession class
-
         kodi_helper : :obj:`KodiHelper`
             instance of the KodiHelper class
 
@@ -30,12 +29,22 @@ class Navigation:
         log_fn : :obj:`fn`
              optional log function
         """
-        self.netflix_session = netflix_session
         self.kodi_helper = kodi_helper
         self.library = library
         self.base_url = base_url
         self.log = log_fn
 
+    def get_netflix_service_url (self):
+        return 'http://localhost:7005'
+
+    def call_netflix_service (self, params):
+        url_values = urllib.urlencode(params)
+        url = self.get_netflix_service_url()
+        full_url = url + '?' + url_values
+        data = urllib2.urlopen(full_url).read()
+        parsed_json = json.loads(data)
+        return parsed_json.get('result', None)
+
     @log
     def router (self, paramstring):
         """Route to the requested subfolder & dispatch actions along the way
@@ -49,12 +58,12 @@ class Navigation:
 
         # log out the user
         if 'action' in params.keys() and params['action'] == 'logout':
-            return self.netflix_session.logout()
+            return self.call_netflix_service({'method': 'logout'})
 
         # check login & try to relogin if necessary
         account = self.kodi_helper.get_credentials()
         if account['email'] != '' and account['password'] != '':
-            if self.netflix_session.is_logged_in(account=account) != True:
+            if self.call_netflix_service({'method': 'is_logged_in'}) != True:
                 if self.establish_session(account=account) != True:
                     return self.kodi_helper.show_login_failed_notification()
 
@@ -93,7 +102,7 @@ class Navigation:
             return self.add_to_list(video_id=params['id'])
         elif params['action'] == 'export':
             # adds a title to the users list on Netflix
-            alt_title = self.kodi_helper.show_add_to_library_title_dialog(original_title=unquote(params['title']).decode('utf8'))
+            alt_title = self.kodi_helper.show_add_to_library_title_dialog(original_title=urllib.unquote(params['title']).decode('utf8'))
             return self.export_to_library(video_id=params['id'], alt_title=alt_title)
         elif params['action'] == 'remove':
             # adds a title to the users list on Netflix
@@ -126,7 +135,7 @@ class Navigation:
             Offset to resume playback from (in seconds)
         """
         # widevine esn
-        esn = self.netflix_session.esn
+        esn = self.call_netflix_service({'method': 'get_esn'})
         return self.kodi_helper.play_item(esn=esn, video_id=video_id, start_offset=start_offset)
 
     @log
@@ -143,37 +152,10 @@ class Navigation:
         bool
             If no results are available
         """
-        has_search_results = False
-        search_results_raw = self.netflix_session.fetch_search_results(search_str=term)
+        search_contents = self.call_netflix_service({'method': 'search', 'term': term})
         # check for any errors
-        if self._is_dirty_response(response=search_results_raw):
+        if self._is_dirty_response(response=search_contents):
             return False
-
-        # determine if we found something
-        if 'search' in search_results_raw['value']:
-            for key in search_results_raw['value']['search'].keys():
-                if self.netflix_session._is_size_key(key=key) == False:
-                    has_search_results = search_results_raw['value']['search'][key]['titles']['length'] > 0
-                    if has_search_results == False:
-                        if search_results_raw['value']['search'][key].get('suggestions', False) != False:
-                            for entry in search_results_raw['value']['search'][key]['suggestions']:
-                                if self.netflix_session._is_size_key(key=entry) == False:
-                                    if search_results_raw['value']['search'][key]['suggestions'][entry]['relatedvideos']['length'] > 0:
-                                        has_search_results = True
-
-
-        # display that we haven't found a thing
-        if has_search_results == False:
-            return self.kodi_helper.build_no_search_results_available(build_url=self.build_url, action='search')
-
-        # list the search results
-        search_results = self.netflix_session.parse_search_results(response_data=search_results_raw)
-        # add more menaingful data to the search results
-        raw_search_contents = self.netflix_session.fetch_video_list_information(video_ids=search_results.keys())
-        # check for any errors
-        if self._is_dirty_response(response=raw_search_contents):
-            return False
-        search_contents = self.netflix_session.parse_video_list(response_data=raw_search_contents)
         actions = {'movie': 'play_video', 'show': 'season_list'}
         return self.kodi_helper.build_search_result_listing(video_list=search_contents, actions=actions, build_url=self.build_url)
 
@@ -185,11 +167,10 @@ class Navigation:
         user_list_id : :obj:`str`
             Type of list to display
         """
-        video_list_ids_raw = self.netflix_session.fetch_video_list_ids()
+        video_list_ids = self.call_netflix_service({'method': 'fetch_video_list_ids', 'type': type})
         # check for any errors
-        if self._is_dirty_response(response=video_list_ids_raw):
+        if self._is_dirty_response(response=video_list_ids):
             return False
-        video_list_ids = self.netflix_session.parse_video_list_ids(response_data=video_list_ids_raw)
         return self.kodi_helper.build_user_sub_listing(video_list_ids=video_list_ids[type], type=type, action='video_list', build_url=self.build_url)
 
     def show_episode_list (self, season_id):
@@ -204,12 +185,11 @@ class Navigation:
         if self.kodi_helper.has_cached_item(cache_id=cache_id):
             episode_list = self.kodi_helper.get_cached_item(cache_id=cache_id)
         else:
-            raw_episode_list = self.netflix_session.fetch_episodes_by_season(season_id=season_id)
+            episode_list = self.call_netflix_service({'method': 'fetch_episodes_by_season', 'season_id': season_id})
             # check for any errors
-            if self._is_dirty_response(response=raw_episode_list):
+            if self._is_dirty_response(response=episode_list):
                 return False
             # parse the raw Netflix data
-            episode_list = self.netflix_session.parse_episodes_by_season(response_data=raw_episode_list)
             self.kodi_helper.add_cached_item(cache_id=cache_id, contents=episode_list)
 
         # sort seasons by number (they´re coming back unsorted from the api)
@@ -238,15 +218,14 @@ class Navigation:
         if self.kodi_helper.has_cached_item(cache_id=cache_id):
             season_list = self.kodi_helper.get_cached_item(cache_id=cache_id)
         else:
-            season_list_raw = self.netflix_session.fetch_seasons_for_show(id=show_id);
+            season_list = self.call_netflix_service({'method': 'fetch_seasons_for_show', 'show_id': show_id})
             # check for any errors
-            if self._is_dirty_response(response=season_list_raw):
+            if self._is_dirty_response(response=season_list):
                 return False
             # check if we have sesons, announced shows that are not available yet have none
-            if 'seasons' not in season_list_raw['value']:
+            if len(season_list) == 0:
                 return self.kodi_helper.build_no_seasons_available()
             # parse the seasons raw response from Netflix
-            season_list = self.netflix_session.parse_seasons(id=show_id, response_data=season_list_raw)
             self.kodi_helper.add_cached_item(cache_id=cache_id, contents=season_list)
         # sort seasons by index by default (they´re coming back unsorted from the api)
         seasons_sorted = []
@@ -269,16 +248,13 @@ class Navigation:
         if self.kodi_helper.has_cached_item(cache_id=video_list_id):
             video_list = self.kodi_helper.get_cached_item(cache_id=video_list_id)
         else:
-            raw_video_list = self.netflix_session.fetch_video_list(list_id=video_list_id)
+            video_list = self.call_netflix_service({'method': 'fetch_video_list', 'list_id': video_list_id})
             # check for any errors
-            if self._is_dirty_response(response=raw_video_list):
+            if self._is_dirty_response(response=video_list):
                 return False
             # parse the video list ids
-            if 'videos' in raw_video_list['value'].keys():
-                video_list = self.netflix_session.parse_video_list(response_data=raw_video_list)
+            if len(video_list) > 0:
                 self.kodi_helper.add_cached_item(cache_id=video_list_id, contents=video_list)
-            else:
-                video_list = []
         actions = {'movie': 'play_video', 'show': 'season_list'}
         return self.kodi_helper.build_video_listing(video_list=video_list, actions=actions, type=type, build_url=self.build_url)
 
@@ -289,12 +265,11 @@ class Navigation:
             video_list_ids = self.kodi_helper.get_cached_item(cache_id=cache_id)
         else:
             # fetch video lists
-            raw_video_list_ids = self.netflix_session.fetch_video_list_ids()
+            video_list_ids = self.call_netflix_service({'method': 'fetch_video_list_ids'})
             # check for any errors
-            if self._is_dirty_response(response=raw_video_list_ids):
+            if self._is_dirty_response(response=video_list_ids):
                 return False
             # parse the video list ids
-            video_list_ids = self.netflix_session.parse_video_list_ids(response_data=raw_video_list_ids)
             self.kodi_helper.add_cached_item(cache_id=cache_id, contents=video_list_ids)
         # defines an order for the user list, as Netflix changes the order at every request
         user_list_order = ['queue', 'continueWatching', 'topTen', 'netflixOriginals', 'trendingNow', 'newRelease', 'popularTitles']
@@ -305,9 +280,7 @@ class Navigation:
     @log
     def show_profiles (self):
         """List the profiles for the active account"""
-        credentials = self.kodi_helper.get_credentials()
-        self.netflix_session.refresh_session_data(account=credentials)
-        profiles = self.netflix_session.profiles
+        profiles = self.call_netflix_service({'method': 'list_profiles'})
         return self.kodi_helper.build_profiles_listing(profiles=profiles, action='video_lists', build_url=self.build_url)
 
     @log
@@ -320,7 +293,7 @@ class Navigation:
             ID of the video list that should be displayed
         """
         rating = self.kodi_helper.show_rating_dialog()
-        return self.netflix_session.rate_video(video_id=video_id, rating=rating)
+        return self.call_netflix_service({'method': 'rate_video', 'video_id': video_id, 'rating': rating})
 
     @log
     def remove_from_list (self, video_id):
@@ -331,7 +304,7 @@ class Navigation:
         video_list_id : :obj:`str`
             ID of the video list that should be displayed
         """
-        self.netflix_session.remove_from_list(video_id=video_id)
+        self.call_netflix_service({'method': 'remove_from_list', 'video_id': video_id})
         return self.kodi_helper.refresh()
 
     @log
@@ -343,7 +316,7 @@ class Navigation:
         video_list_id : :obj:`str`
             ID of the video list that should be displayed
         """
-        self.netflix_session.add_to_list(video_id=video_id)
+        self.call_netflix_service({'method': 'add_to_list', 'video_id': video_id})
         return self.kodi_helper.refresh()
 
     @log
@@ -358,7 +331,7 @@ class Navigation:
         alt_title : :obj:`str`
             Alternative title (for the folder written to disc)
         """
-        metadata = self.netflix_session.fetch_metadata(id=video_id)
+        metadata = self.call_netflix_service({'method': 'fetch_metadata', 'video_id': video_id})
         # check for any errors
         if self._is_dirty_response(response=metadata):
             return False
@@ -384,7 +357,7 @@ class Navigation:
         video_id : :obj:`str`
             ID of the movie or show
         """
-        metadata = self.netflix_session.fetch_metadata(id=video_id)
+        metadata = self.call_netflix_service({'method': 'fetch_metadata', 'video_id': video_id})
         # check for any errors
         if self._is_dirty_response(response=metadata):
             return False
@@ -410,7 +383,8 @@ class Navigation:
         bool
             If we don't have an active session & the user couldn't be logged in
         """
-        return True if self.netflix_session.is_logged_in(account=account) else self.netflix_session.login(account=account)
+        is_logged_in = self.call_netflix_service({'method': 'is_logged_in'})
+        return True if is_logged_in else self.call_netflix_service({'method': 'login'})
 
     @log
     def before_routing_action (self, params):
@@ -448,7 +422,7 @@ class Navigation:
         # check and switch the profile if needed
         if self.check_for_designated_profile_change(params=params):
             self.kodi_helper.invalidate_memcache()
-            self.netflix_session.switch_profile(profile_id=params['profile_id'], account=credentials)
+            self.call_netflix_service({'method': 'switch_profile', 'profile_id': params['profile_id']})
         # check login, in case of main menu
         if 'action' not in params:
             self.establish_session(account=credentials)
@@ -468,9 +442,10 @@ class Navigation:
             Profile should be switched or not
         """
         # check if we need to switch the user
-        if 'guid' not in self.netflix_session.user_data:
+        user_data = self.call_netflix_service({'method': 'get_user_data'})
+        if 'guid' not in user_data:
             return False
-        current_profile_id = self.netflix_session.user_data['guid']
+        current_profile_id = user_data['guid']
         return 'profile_id' in params and current_profile_id != params['profile_id']
 
     def parse_paramters (self, paramstring):
@@ -541,4 +516,4 @@ class Navigation:
         str
             Url + querystring based on the param
         """
-        return self.base_url + '?' + urlencode(query)
+        return self.base_url + '?' + urllib.urlencode(query)
diff --git a/resources/lib/NetflixHttpRequestHandler.py b/resources/lib/NetflixHttpRequestHandler.py
new file mode 100644 (file)
index 0000000..1ab96db
--- /dev/null
@@ -0,0 +1,49 @@
+import BaseHTTPServer
+import json
+from types import FunctionType
+from urlparse import urlparse, parse_qs
+from resources.lib.KodiHelper import KodiHelper
+from resources.lib.NetflixSession import NetflixSession
+from resources.lib.NetflixHttpSubRessourceHandler import NetflixHttpSubRessourceHandler
+
+kodi_helper = KodiHelper()
+
+netflix_session = NetflixSession(
+    cookie_path=kodi_helper.cookie_path,
+    data_path=kodi_helper.data_path,
+    verify_ssl=kodi_helper.get_ssl_verification_setting(),
+    log_fn=kodi_helper.log
+)
+
+# get list of methods & instance form the sub ressource handler
+methods = [x for x, y in NetflixHttpSubRessourceHandler.__dict__.items() if type(y) == FunctionType]
+sub_res_handler = NetflixHttpSubRessourceHandler(kodi_helper=kodi_helper, netflix_session=netflix_session)
+
+class NetflixHttpRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+
+    def do_GET(self):
+        url = urlparse(self.path)
+        params = parse_qs(url.query)
+        method = params.get('method', [None])[0]
+
+        # not method given
+        if method == None:
+            self.send_error(500, 'No method declared')
+            return
+
+        # no existing method given
+        if method not in methods:
+            self.send_error(404, 'Method "' + str(method) + '" not found. Available methods: ' + str(methods))
+            return
+
+        # call method & get the result
+        result = getattr(sub_res_handler, method)(params)
+        self.send_response(200)
+        self.send_header('Content-type', 'application/json')
+        self.end_headers()
+        self.wfile.write(json.dumps({'method': method, 'result': result}));
+        return
+
+    def log_message(self, format, *args):
+        """Disable the BaseHTTPServer Log"""
+        return
diff --git a/resources/lib/NetflixHttpSubRessourceHandler.py b/resources/lib/NetflixHttpSubRessourceHandler.py
new file mode 100644 (file)
index 0000000..c7626ea
--- /dev/null
@@ -0,0 +1,115 @@
+class NetflixHttpSubRessourceHandler:
+
+    def __init__ (self, kodi_helper, netflix_session):
+        self.kodi_helper = kodi_helper
+        self.netflix_session = netflix_session
+        credentials = self.kodi_helper.get_credentials()
+
+        if self.netflix_session.is_logged_in(account=credentials):
+            self.netflix_session.refresh_session_data(account=credentials)
+        else:
+            self.netflix_session.login(account=credentials)
+        self.profiles = self.netflix_session.profiles
+
+    def is_logged_in (self, params):
+        credentials = self.kodi_helper.get_credentials()
+        return self.netflix_session.is_logged_in(account=credentials)
+
+    def logout (self, params):
+        return self.netflix_session.logout()
+
+    def list_profiles (self, params):
+        return self.profiles
+
+    def get_esn (self, params):
+        return self.netflix_session.esn
+
+    def fetch_video_list_ids (self, params):
+        video_list_ids_raw = self.netflix_session.fetch_video_list_ids()
+        if 'error' in video_list_ids_raw:
+            return video_list_ids_raw
+        return self.netflix_session.parse_video_list_ids(response_data=video_list_ids_raw)
+
+    def fetch_video_list (self, params):
+        list_id = params.get('list_id', [''])[0]
+        raw_video_list = self.netflix_session.fetch_video_list(list_id=list_id)
+        if 'error' in raw_video_list:
+            return raw_video_list
+        # parse the video list ids
+        if 'videos' in raw_video_list.get('value', {}).keys():
+            return self.netflix_session.parse_video_list(response_data=raw_video_list)
+        return []
+
+    def fetch_episodes_by_season (self, params):
+        raw_episode_list = self.netflix_session.fetch_episodes_by_season(season_id=params.get('season_id')[0])
+        if 'error' in raw_episode_list:
+            return raw_episode_list
+        return self.netflix_session.parse_episodes_by_season(response_data=raw_episode_list)
+
+    def fetch_seasons_for_show (self, params):
+        show_id = params.get('show_id', [''])[0]
+        raw_season_list = self.netflix_session.fetch_seasons_for_show(id=show_id)
+        if 'error' in raw_season_list:
+            return raw_season_list
+        # check if we have sesons, announced shows that are not available yet have none
+        if 'seasons' not in raw_season_list.get('value', {}):
+              return []
+        return self.netflix_session.parse_seasons(id=show_id, response_data=raw_season_list)
+
+    def rate_video (self, params):
+        video_id = params.get('video_id', [''])[0]
+        rating = params.get('rating', [''])[0]
+        return self.netflix_session.rate_video(video_id=video_id, rating=rating)
+
+    def remove_from_list (self, params):
+        video_id = params.get('video_id', [''])[0]
+        return self.netflix_session.remove_from_list(video_id=video_id)
+
+    def add_to_list (self, params):
+        video_id = params.get('video_id', [''])[0]
+        return self.netflix_session.add_to_list(video_id=video_id)
+
+    def fetch_metadata (self, params):
+        video_id = params.get('video_id', [''])[0]
+        return self.netflix_session.fetch_metadata(id=video_id)
+
+    def switch_profile (self, params):
+        credentials = self.kodi_helper.get_credentials()
+        profile_id = params.get('profile_id', [''])[0]
+        return self.netflix_session.switch_profile(profile_id=profile_id, account=credentials)
+
+    def get_user_data (self, params):
+        return self.netflix_session.user_data
+
+    def search (self, params):
+        term = params.get('term', [''])[0]
+        has_search_results = False
+        raw_search_results = self.netflix_session.fetch_search_results(search_str=term)
+        # check for any errors
+        if 'error' in raw_search_results:
+            return raw_search_results
+
+        # determine if we found something
+        if 'search' in raw_search_results['value']:
+            for key in raw_search_results['value']['search'].keys():
+                if self.netflix_session._is_size_key(key=key) == False:
+                    has_search_results = raw_search_results['value']['search'][key]['titles']['length'] > 0
+                    if has_search_results == False:
+                        if raw_search_results['value']['search'][key].get('suggestions', False) != False:
+                            for entry in raw_search_results['value']['search'][key]['suggestions']:
+                                if self.netflix_session._is_size_key(key=entry) == False:
+                                    if raw_search_results['value']['search'][key]['suggestions'][entry]['relatedvideos']['length'] > 0:
+                                        has_search_results = True
+
+        # display that we haven't found a thing
+        if has_search_results == False:
+            return []
+
+        # list the search results
+        search_results = self.netflix_session.parse_search_results(response_data=raw_search_results)
+        # add more menaingful data to the search results
+        raw_search_contents = self.netflix_session.fetch_video_list_information(video_ids=search_results.keys())
+        # check for any errors
+        if 'error' in raw_search_contents:
+            return raw_search_contents
+        return self.netflix_session.parse_video_list(response_data=raw_search_contents)
index 362b080..386f8dc 100644 (file)
@@ -13,8 +13,9 @@
     <setting id="enable_dolby_sound" type="bool" label="30033" default="true"/>
     <setting id="ssl_verification" type="bool" label="30024" default="true"/>
     <setting id="enable_tracking" type="bool" label="30032" default="true"/>
-    <setting id="esn" type="text" label="30034" value="" default=""/>    
+    <setting id="esn" type="text" label="30034" value="" default=""/>
     <setting id="tracking_id" value="" visible="false"/>
     <setting id="msl_service_port" value="8000" visible="false"/>
+    <setting id="netflix_service_port" value="8001" visible="false"/>
   </category>
 </settings>
index 1829fbd..a45340a 100644 (file)
@@ -1,16 +1,11 @@
 import threading
 import SocketServer
 import xbmc
-import xbmcaddon
 import socket
+from xbmcaddon import Addon
 from resources.lib.KodiHelper import KodiHelper
 from resources.lib.MSLHttpRequestHandler import MSLHttpRequestHandler
-
-addon = xbmcaddon.Addon()
-kodi_helper = KodiHelper(
-    plugin_handle=None,
-    base_url=None
-)
+from resources.lib.NetflixHttpRequestHandler import NetflixHttpRequestHandler
 
 def select_unused_port():
     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -19,28 +14,49 @@ def select_unused_port():
     sock.close()
     return port
 
+addon = Addon()
+kodi_helper = KodiHelper()
+
 port = select_unused_port()
 addon.setSetting('msl_service_port', str(port))
 kodi_helper.log(msg='Picked Port: ' + str(port))
 
-#Config the HTTP Server
+# server defaults
 SocketServer.TCPServer.allow_reuse_address = True
-server = SocketServer.TCPServer(('127.0.0.1', port), MSLHttpRequestHandler)
-server.server_activate()
-server.timeout = 1
+
+# configure the MSL Server
+msl_server = SocketServer.TCPServer(('127.0.0.1', port), MSLHttpRequestHandler)
+msl_server.server_activate()
+msl_server.timeout = 1
+
+# configure the Netflix Data Server
+nd_server = SocketServer.TCPServer(('127.0.0.1', 7005), NetflixHttpRequestHandler)
+nd_server.server_activate()
+nd_server.timeout = 1
 
 if __name__ == '__main__':
     monitor = xbmc.Monitor()
-    thread = threading.Thread(target=server.serve_forever)
-    thread.daemon = True
-    thread.start()
+
+    msl_thread = threading.Thread(target=msl_server.serve_forever)
+    msl_thread.daemon = True
+    msl_thread.start()
+
+    nd_thread = threading.Thread(target=nd_server.serve_forever)
+    nd_thread.daemon = True
+    nd_thread.start()
 
     while not monitor.abortRequested():
         if monitor.waitForAbort(5):
-            server.shutdown()
+            msl_server.shutdown()
+            nd_server.shutdown()
             break
 
-    server.server_close()
-    server.socket.close()
-    server.shutdown()
+    msl_server.server_close()
+    msl_server.socket.close()
+    msl_server.shutdown()
     kodi_helper.log(msg='Stopped MSL Service')
+
+    nd_server.server_close()
+    nd_server.socket.close()
+    nd_server.shutdown()
+    kodi_helper.log(msg='Stopped HTTP Service')