diff --git a/resources/lib/DownloadUtils.py b/resources/lib/DownloadUtils.py
index 5c47144d..04d2da85 100644
--- a/resources/lib/DownloadUtils.py
+++ b/resources/lib/DownloadUtils.py
@@ -1,38 +1,32 @@
-# -*- coding: utf-8 -*-
-
-##################################################################################################
-
-import json
-import requests
-import logging
-
 import xbmc
 import xbmcgui
+import xbmcaddon
 
-import utils
-import clientinfo
+import requests
+import json
+import logging
 
-##################################################################################################
+import Utils as utils
+from ClientInformation import ClientInformation
+from requests.packages.urllib3.exceptions import InsecureRequestWarning
 
 # Disable requests logging
-from requests.packages.urllib3.exceptions import InsecureRequestWarning
 requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
-#logging.getLogger('requests').setLevel(logging.WARNING)
-
-##################################################################################################
-
+#logging.getLogger("requests").setLevel(logging.WARNING)
 
 class DownloadUtils():
     
     # Borg - multiple instances, shared state
     _shared_state = {}
-    clientInfo = clientinfo.ClientInfo()
+    clientInfo = ClientInformation()
+
     addonName = clientInfo.getAddonName()
+    addon = xbmcaddon.Addon()
+    WINDOW = xbmcgui.Window(10000)
 
     # Requests session
     s = None
-    timeout = 30
-
+    timeout = 60
 
     def __init__(self):
 
@@ -40,44 +34,41 @@ class DownloadUtils():
 
     def logMsg(self, msg, lvl=1):
 
-        className = self.__class__.__name__
-        utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
-
+        self.className = self.__class__.__name__
+        utils.logMsg("%s %s" % (self.addonName, self.className), msg, int(lvl))
 
     def setUsername(self, username):
-        # Reserved for userclient only
+        # Reserved for UserClient only
         self.username = username
         self.logMsg("Set username: %s" % username, 2)
 
     def setUserId(self, userId):
-        # Reserved for userclient only
+        # Reserved for UserClient only
         self.userId = userId
         self.logMsg("Set userId: %s" % userId, 2)
 
     def setServer(self, server):
-        # Reserved for userclient only
+        # Reserved for UserClient only
         self.server = server
         self.logMsg("Set server: %s" % server, 2)
 
     def setToken(self, token):
-        # Reserved for userclient only
+        # Reserved for UserClient only
         self.token = token
         self.logMsg("Set token: %s" % token, 2)
 
     def setSSL(self, ssl, sslclient):
-        # Reserved for userclient only
+        # Reserved for UserClient only
         self.sslverify = ssl
         self.sslclient = sslclient
         self.logMsg("Verify SSL host certificate: %s" % ssl, 2)
         self.logMsg("SSL client side certificate: %s" % sslclient, 2)
 
-
     def postCapabilities(self, deviceId):
 
         # Post settings to session
-        url = "{server}/emby/Sessions/Capabilities/Full?format=json"
+        url = "{server}/mediabrowser/Sessions/Capabilities/Full"
         data = {
-            
             'PlayableMediaTypes': "Audio,Video",
             'SupportsMediaControl': True,
             'SupportedCommands': (
@@ -95,57 +86,49 @@ class DownloadUtils():
         }
 
         self.logMsg("Capabilities URL: %s" % url, 2)
-        self.logMsg("Postdata: %s" % data, 2)
+        self.logMsg("PostData: %s" % data, 2)
 
-        self.downloadUrl(url, postBody=data, type="POST")
-        self.logMsg("Posted capabilities to %s" % self.server, 2)
+        try:
+            self.downloadUrl(url, postBody=data, type="POST")
+            self.logMsg("Posted capabilities to %s" % self.server, 1)
+        except:
+            self.logMsg("Posted capabilities failed.")
 
         # Attempt at getting sessionId
-        url = "{server}/emby/Sessions?DeviceId=%s&format=json" % deviceId
-        result = self.downloadUrl(url)
+        url = "{server}/mediabrowser/Sessions?DeviceId=%s&format=json" % deviceId
+
         try:
-            sessionId = result[0]['Id']
-        
-        except (KeyError, TypeError):
-            self.logMsg("Failed to retrieve sessionId.", 1)
-        
-        else:
+            result = self.downloadUrl(url)
             self.logMsg("Session: %s" % result, 2)
-            self.logMsg("SessionId: %s" % sessionId, 1)
-            utils.window('emby_sessionId', value=sessionId)
             
+            sessionId = result[0][u'Id']
+            self.logMsg("SessionId: %s" % sessionId)
+            self.WINDOW.setProperty("sessionId%s" % self.username, sessionId)
+        except:
+            self.logMsg("Failed to retrieve sessionId.", 1)
+        else:
             # Post any permanent additional users
-            additionalUsers = utils.settings('additionalUsers')
-            if additionalUsers:
-                
-                additionalUsers = additionalUsers.split(',')
-                self.logMsg(
-                    "List of permanent users added to the session: %s"
-                    % additionalUsers, 1)
-
-                # Get the user list from server to get the userId
-                url = "{server}/emby/Users?format=json"
-                result = self.downloadUrl(url)
-
-                for additional in additionalUsers:
-                    addUser = additional.decode('utf-8').lower()
-
-                    # Compare to server users to list of permanent additional users
-                    for user in result:
-                        username = user['Name'].lower()
+            additionalUsers = utils.settings('additionalUsers').split(',')
+            self.logMsg("List of permanent users that should be added to the session: %s" % str(additionalUsers), 1)
+            # Get the user list from server to get the userId
+            url = "{server}/mediabrowser/Users?format=json"
+            result = self.downloadUrl(url)
 
+            if result:
+                for user in result:
+                    username = user['Name'].lower()
+                    userId = user['Id']
+                    for additional in additionalUsers:
+                        addUser = additional.decode('utf-8').lower()
                         if username in addUser:
-                            userId = user['Id']
-                            url = (
-                                    "{server}/emby/Sessions/%s/Users/%s?format=json"
-                                    % (sessionId, userId)
-                            )
-                            self.downloadUrl(url, postBody={}, type="POST")
-
+                            url = "{server}/mediabrowser/Sessions/%s/Users/%s" % (sessionId, userId)
+                            postdata = {}
+                            self.downloadUrl(url, postBody=postdata, type="POST")
+                            #xbmcgui.Dialog().notification("Success!", "%s added to viewing session" % username, time=1000)
 
     def startSession(self):
 
-        self.deviceId = self.clientInfo.getDeviceId()
+        self.deviceId = self.clientInfo.getMachineId()
 
         # User is identified from this point
         # Attach authenticated header to the session
@@ -169,7 +152,7 @@ class DownloadUtils():
         self.s.mount("http://", requests.adapters.HTTPAdapter(max_retries=1))
         self.s.mount("https://", requests.adapters.HTTPAdapter(max_retries=1))
 
-        self.logMsg("Requests session started on: %s" % self.server, 1)
+        self.logMsg("Requests session started on: %s" % self.server)
 
     def stopSession(self):
         try:
@@ -182,116 +165,93 @@ class DownloadUtils():
         clientInfo = self.clientInfo
 
         deviceName = clientInfo.getDeviceName()
-        deviceId = clientInfo.getDeviceId()
+        deviceId = clientInfo.getMachineId()
         version = clientInfo.getVersion()
 
         if not authenticate:
             # If user is not authenticated
-            auth = (
-                'MediaBrowser Client="Kodi", Device="%s", DeviceId="%s", Version="%s"'
-                % (deviceName, deviceId, version))
-            header = {
-
-                'Content-type': 'application/json',
-                'Accept-encoding': 'gzip',
-                'Accept-Charset': 'UTF-8,*',
-                'Authorization': auth
-            }      
+            auth = 'MediaBrowser Client="Kodi", Device="%s", DeviceId="%s", Version="%s"' % (deviceName, deviceId, version)
+            header = {'Content-type': 'application/json', 'Accept-encoding': 'gzip', 'Accept-Charset': 'UTF-8,*', 'Authorization': auth}      
+            
             self.logMsg("Header: %s" % header, 2)
+            return header
         
         else:
             userId = self.userId
             token = self.token
             # Attached to the requests session
-            auth = (
-                'MediaBrowser UserId="%s", Client="Kodi", Device="%s", DeviceId="%s", Version="%s"'
-                % (userId, deviceName, deviceId, version))
-            header = {
-
-                'Content-type': 'application/json',
-                'Accept-encoding': 'gzip',
-                'Accept-Charset': 'UTF-8,*',
-                'Authorization': auth,
-                'X-MediaBrowser-Token': token
-            }        
+            auth = 'MediaBrowser UserId="%s", Client="Kodi", Device="%s", DeviceId="%s", Version="%s"' % (userId, deviceName, deviceId, version)
+            header = {'Content-type': 'application/json', 'Accept-encoding': 'gzip', 'Accept-Charset': 'UTF-8,*', 'Authorization': auth, 'X-MediaBrowser-Token': token}        
+                    
             self.logMsg("Header: %s" % header, 2)
-        
-        return header
+            return header
 
-    def downloadUrl(self, url, postBody=None, type="GET", parameters=None, authenticate=True):
+    def downloadUrl(self, url, postBody=None, type="GET", authenticate=True):
         
         self.logMsg("=== ENTER downloadUrl ===", 2)
 
+        WINDOW = self.WINDOW
         timeout = self.timeout
         default_link = ""
 
         try:
+
             # If user is authenticated
             if (authenticate):
                 # Get requests session
                 try: 
                     s = self.s
-                    # Replace for the real values
-                    url = url.replace("{server}", self.server)
-                    url = url.replace("{UserId}", self.userId)
+                    # Replace for the real values and append api_key
+                    url = url.replace("{server}", self.server, 1)
+                    url = url.replace("{UserId}", self.userId, 1)
 
+                    self.logMsg("URL: %s" % url, 2)
                     # Prepare request
                     if type == "GET":
-                        r = s.get(url, json=postBody, params=parameters, timeout=timeout)
+                        r = s.get(url, json=postBody, timeout=timeout)
                     elif type == "POST":
                         r = s.post(url, json=postBody, timeout=timeout)
                     elif type == "DELETE":
                         r = s.delete(url, json=postBody, timeout=timeout)
                 
                 except AttributeError:
-                    # request session does not exists
+                    
                     # Get user information
-                    self.userId = utils.window('emby_currUser')
-                    self.server = utils.window('emby_server%s' % self.userId)
-                    self.token = utils.window('emby_accessToken%s' % self.userId)
+                    self.username = WINDOW.getProperty('currUser')
+                    self.userId = WINDOW.getProperty('userId%s' % self.username)
+                    self.server = WINDOW.getProperty('server%s' % self.username)
+                    self.token = WINDOW.getProperty('accessToken%s' % self.username)
                     header = self.getHeader()
                     verifyssl = False
                     cert = None
 
                     # IF user enables ssl verification
-                    if utils.settings('sslverify') == "true":
-                        verifyssl = True
-                    if utils.settings('sslcert') != "None":
-                        cert = utils.settings('sslcert')
+                    try:
+                        if utils.settings('sslverify') == "true":
+                            verifyssl = True
+                        if utils.settings('sslcert') != "None":
+                            cert = utils.settings('sslcert')
+                    except:
+                        self.logMsg("Could not load SSL settings.", 1)
+                        pass
 
-                    # Replace for the real values
-                    url = url.replace("{server}", self.server)
-                    url = url.replace("{UserId}", self.userId)
+                    # Replace for the real values and append api_key
+                    url = url.replace("{server}", self.server, 1)
+                    url = url.replace("{UserId}", self.userId, 1)
 
+                    self.logMsg("URL: %s" % url, 2)
                     # Prepare request
                     if type == "GET":
-                        r = requests.get(url,
-                                        json=postBody,
-                                        params=parameters,
-                                        headers=header,
-                                        timeout=timeout,
-                                        cert=cert,
-                                        verify=verifyssl)
-
+                        r = requests.get(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl)
                     elif type == "POST":
-                        r = requests.post(url,
-                                        json=postBody,
-                                        headers=header,
-                                        timeout=timeout,
-                                        cert=cert,
-                                        verify=verifyssl)
-
+                        r = requests.post(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl)
                     elif type == "DELETE":
-                        r = requests.delete(url,
-                                        json=postBody,
-                                        headers=header,
-                                        timeout=timeout,
-                                        cert=cert,
-                                        verify=verifyssl)
+                        r = requests.delete(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl)
 
             # If user is not authenticated
             elif not authenticate:
-
+                
+                self.logMsg("URL: %s" % url, 2)
                 header = self.getHeader(authenticate=False)
                 verifyssl = False
 
@@ -303,49 +263,41 @@ class DownloadUtils():
                 
                 # Prepare request
                 if type == "GET":
-                    r = requests.get(url,
-                                    json=postBody,
-                                    params=parameters,
-                                    headers=header,
-                                    timeout=timeout,
-                                    verify=verifyssl)
-
+                    r = requests.get(url, json=postBody, headers=header, timeout=timeout, verify=verifyssl)
                 elif type == "POST":
-                    r = requests.post(url,
-                                    json=postBody,
-                                    headers=header,
-                                    timeout=timeout,
-                                    verify=verifyssl)
+                    r = requests.post(url, json=postBody, headers=header, timeout=timeout, verify=verifyssl)
         
-            ##### THE RESPONSE #####
-            self.logMsg(r.url, 2)
+            # Process the response
             if r.status_code == 204:
                 # No body in the response
                 self.logMsg("====== 204 Success ======", 2)
+                return default_link
 
             elif r.status_code == requests.codes.ok:
-               
                 try: 
                     # UTF-8 - JSON object
                     r = r.json()
                     self.logMsg("====== 200 Success ======", 2)
                     self.logMsg("Response: %s" % r, 2)
                     return r
-
                 except:
-                    if r.headers.get('content-type') != "text/html":
+                    if r.headers.get('content-type') == "text/html":
+                        pass
+                    else:
                         self.logMsg("Unable to convert the response for: %s" % url, 1)
             else:
                 r.raise_for_status()
-        
-        ##### EXCEPTIONS #####
 
+            return default_link
+        
+        # TO REVIEW EXCEPTIONS
         except requests.exceptions.ConnectionError as e:
             # Make the addon aware of status
-            if utils.window('emby_online') != "false":
+            if WINDOW.getProperty("Server_online") != "false":
                 self.logMsg("Server unreachable at: %s" % url, 0)
                 self.logMsg(e, 2)
-                utils.window('emby_online', value="false")
+                WINDOW.setProperty("Server_online", "false")
+            pass
 
         except requests.exceptions.ConnectTimeout as e:
             self.logMsg("Server timeout at: %s" % url, 0)
@@ -355,35 +307,29 @@ class DownloadUtils():
 
             if r.status_code == 401:
                 # Unauthorized
-                status = utils.window('emby_serverStatus')
+                status = WINDOW.getProperty("Server_status")
 
-                if 'X-Application-Error-Code' in r.headers:
-                    # Emby server errors
+                if 'x-application-error-code' in r.headers:
                     if r.headers['X-Application-Error-Code'] == "ParentalControl":
                         # Parental control - access restricted
-                        utils.window('emby_serverStatus', value="restricted")
-                        xbmcgui.Dialog().notification(
-                                                heading="Emby server",
-                                                message="Access restricted.",
-                                                icon=xbmcgui.NOTIFICATION_ERROR,
-                                                time=5000)
+                        WINDOW.setProperty("Server_status", "restricted")
+                        xbmcgui.Dialog().notification("Emby server", "Access restricted.", xbmcgui.NOTIFICATION_ERROR, time=5000)
                         return False
-                    
                     elif r.headers['X-Application-Error-Code'] == "UnauthorizedAccessException":
-                        # User tried to do something his emby account doesn't allow
+                        # User tried to do something his emby account doesn't allow - admin restricted in some way
                         pass
 
-                elif status not in ("401", "Auth"):
-                    # Tell userclient token has been revoked.
-                    utils.window('emby_serverStatus', value="401")
+                elif (status == "401") or (status == "Auth"):
+                    pass
+
+                else:
+                    # Tell UserClient token has been revoked.
+                    WINDOW.setProperty("Server_status", "401")
                     self.logMsg("HTTP Error: %s" % e, 0)
-                    xbmcgui.Dialog().notification(
-                                            heading="Error connecting",
-                                            message="Unauthorized.",
-                                            icon=xbmcgui.NOTIFICATION_ERROR)
+                    xbmcgui.Dialog().notification("Error connecting", "Unauthorized.", xbmcgui.NOTIFICATION_ERROR)
                     return 401
 
-            elif r.status_code in (301, 302):
+            elif (r.status_code == 301) or (r.status_code == 302):
                 # Redirects
                 pass
             elif r.status_code == 400:
@@ -398,4 +344,4 @@ class DownloadUtils():
             self.logMsg("Unknown error connecting to: %s" % url, 0)
             self.logMsg(e, 1)
 
-        return default_link
\ No newline at end of file
+        return default_link
diff --git a/resources/lib/Entrypoint.py b/resources/lib/Entrypoint.py
index 588b8d3c..95a5a327 100644
--- a/resources/lib/Entrypoint.py
+++ b/resources/lib/Entrypoint.py
@@ -1,157 +1,100 @@
-# -*- coding: utf-8 -*-
-
-#################################################################################################
-
-import json
-import os
-import sys
-import urlparse
-
-import xbmc
 import xbmcaddon
+import xbmcplugin
+import xbmc
 import xbmcgui
 import xbmcvfs
-import xbmcplugin
+import os, sys
+import threading
+import json
+import urllib
+import time
 
-import artwork
-import utils
-import clientinfo
-import downloadutils
-import read_embyserver as embyserver
-import embydb_functions as embydb
-import playlist
-import playbackutils as pbutils
-import playutils
-import api
+WINDOW = xbmcgui.Window(10000)
 
-#################################################################################################
+import Utils as utils
+from ClientInformation import ClientInformation
+from PlaybackUtils import PlaybackUtils
+from PlayUtils import PlayUtils
+from DownloadUtils import DownloadUtils
+from ReadEmbyDB import ReadEmbyDB
+from API import API
+from UserPreferences import UserPreferences
 
 
-def doPlayback(itemid, dbid):
+##### Play items via plugin://plugin.video.emby/ #####
+def doPlayback(id):
+    url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json&ImageTypeLimit=1" % id
+    result = DownloadUtils().downloadUrl(url)
+    item = PlaybackUtils().PLAY(result, setup="default")
 
-    emby = embyserver.Read_EmbyServer()
-    item = emby.getItem(itemid)
-    pbutils.PlaybackUtils(item).play(itemid, dbid)
-
-##### DO RESET AUTH #####
+#### DO RESET AUTH #####    
 def resetAuth():
     # User tried login and failed too many times
-    resp = xbmcgui.Dialog().yesno(
-                heading="Warning",
-                line1=(
-                    "Emby might lock your account if you fail to log in too many times. "
-                    "Proceed anyway?"))
+    resp = xbmcgui.Dialog().yesno("Warning", "Emby might lock your account if you fail to log in too many times. Proceed anyway?")
     if resp == 1:
-        utils.logMsg("EMBY", "Reset login attempts.", 1)
-        utils.window('emby_serverStatus', value="Auth")
+        xbmc.log("Reset login attempts.")
+        WINDOW.setProperty("Server_status", "Auth")
     else:
         xbmc.executebuiltin('Addon.OpenSettings(plugin.video.emby)')
 
-def addDirectoryItem(label, path, folder=True):
-    li = xbmcgui.ListItem(label, path=path)
-    li.setThumbnailImage("special://home/addons/plugin.video.emby/icon.png")
-    li.setArt({"fanart":"special://home/addons/plugin.video.emby/fanart.jpg"})
-    li.setArt({"landscape":"special://home/addons/plugin.video.emby/fanart.jpg"})
-    xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=path, listitem=li, isFolder=folder)
-
-def doMainListing():
-    
-    xbmcplugin.setContent(int(sys.argv[1]), 'files')    
-    # Get emby nodes from the window props
-    embyprops = utils.window('Emby.nodes.total')
-    if embyprops:
-        totalnodes = int(embyprops)
-        for i in range(totalnodes):
-            path = utils.window('Emby.nodes.%s.index' % i)
-            if not path:
-                path = utils.window('Emby.nodes.%s.content' % i)
-            label = utils.window('Emby.nodes.%s.title' % i)
-            if path:
-                addDirectoryItem(label, path)
-    
-    # some extra entries for settings and stuff. TODO --> localize the labels
-    addDirectoryItem("Network credentials", "plugin://plugin.video.emby/?mode=passwords", False)
-    addDirectoryItem("Settings", "plugin://plugin.video.emby/?mode=settings", False)
-    addDirectoryItem("Add user to session", "plugin://plugin.video.emby/?mode=adduser", False)
-    #addDirectoryItem("Cache all images to Kodi texture cache (advanced)", "plugin://plugin.video.emby/?mode=texturecache")
-    addDirectoryItem("Perform manual sync", "plugin://plugin.video.emby/?mode=manualsync", False)
-    addDirectoryItem(
-        label="Repair local database (force update all content)",
-        path="plugin://plugin.video.emby/?mode=repair",
-        folder=False)
-    addDirectoryItem(
-        label="Perform local database reset (full resync)",
-        path="plugin://plugin.video.emby/?mode=reset",
-        folder=False)
-    addDirectoryItem(
-        label="Sync Emby Theme Media to Kodi",
-        path="plugin://plugin.video.emby/?mode=thememedia",
-        folder=False)
-    
-    xbmcplugin.endOfDirectory(int(sys.argv[1]))
-
-##### ADD ADDITIONAL USERS #####
+### ADD ADDITIONAL USERS ###
 def addUser():
 
-    doUtils = downloadutils.DownloadUtils()
-    clientInfo = clientinfo.ClientInfo()
-    deviceId = clientInfo.getDeviceId()
+    doUtils = DownloadUtils()
+    clientInfo = ClientInformation()
+    currUser = WINDOW.getProperty("currUser")
+    deviceId = clientInfo.getMachineId()
     deviceName = clientInfo.getDeviceName()
-    userid = utils.window('emby_currUser')
-    dialog = xbmcgui.Dialog()
 
     # Get session
-    url = "{server}/emby/Sessions?DeviceId=%s&format=json" % deviceId
+    url = "{server}/mediabrowser/Sessions?DeviceId=%s" % deviceId
     result = doUtils.downloadUrl(url)
     
     try:
-        sessionId = result[0]['Id']
-        additionalUsers = result[0]['AdditionalUsers']
+        sessionId = result[0][u'Id']
+        additionalUsers = result[0][u'AdditionalUsers']
         # Add user to session
         userlist = {}
         users = []
-        url = "{server}/emby/Users?IsDisabled=false&IsHidden=false&format=json"
+        url = "{server}/mediabrowser/Users?IsDisabled=false&IsHidden=false"
         result = doUtils.downloadUrl(url)
 
         # pull the list of users
         for user in result:
-            name = user['Name']
-            userId = user['Id']
-            if userid != userId:
+            name = user[u'Name']
+            userId = user[u'Id']
+            if currUser not in name:
                 userlist[name] = userId
                 users.append(name)
 
         # Display dialog if there's additional users
         if additionalUsers:
 
-            option = dialog.select("Add/Remove user from the session", ["Add user", "Remove user"])
+            option = xbmcgui.Dialog().select("Add/Remove user from the session", ["Add user", "Remove user"])
             # Users currently in the session
             additionalUserlist = {}
             additionalUsername = []
             # Users currently in the session
             for user in additionalUsers:
-                name = user['UserName']
-                userId = user['UserId']
+                name = user[u'UserName']
+                userId = user[u'UserId']
                 additionalUserlist[name] = userId
                 additionalUsername.append(name)
 
             if option == 1:
                 # User selected Remove user
-                resp = dialog.select("Remove user from the session", additionalUsername)
+                resp = xbmcgui.Dialog().select("Remove user from the session", additionalUsername)
                 if resp > -1:
                     selected = additionalUsername[resp]
                     selected_userId = additionalUserlist[selected]
-                    url = "{server}/emby/Sessions/%s/Users/%s" % (sessionId, selected_userId)
-                    doUtils.downloadUrl(url, postBody={}, type="DELETE")
-                    dialog.notification(
-                            heading="Success!",
-                            message="%s removed from viewing session" % selected,
-                            icon="special://home/addons/plugin.video.emby/icon.png",
-                            time=1000)
+                    url = "{server}/mediabrowser/Sessions/%s/Users/%s" % (sessionId, selected_userId)
+                    postdata = {}
+                    doUtils.downloadUrl(url, postBody=postdata, type="DELETE")
+                    xbmcgui.Dialog().notification("Success!", "%s removed from viewing session" % selected, time=1000)
 
                     # clear picture
-                    position = utils.window('EmbyAdditionalUserPosition.%s' % selected_userId)
-                    utils.window('EmbyAdditionalUserImage.%s' % position, clear=True)
+                    position = WINDOW.getProperty('EmbyAdditionalUserPosition.' + selected_userId)
+                    WINDOW.clearProperty('EmbyAdditionalUserImage.' + str(position))
                     return
                 else:
                     return
@@ -168,143 +111,138 @@ def addUser():
                 return
 
         # Subtract any additional users
-        utils.logMsg("EMBY", "Displaying list of users: %s" % users)
-        resp = dialog.select("Add user to the session", users)
+        xbmc.log("Displaying list of users: %s" % users)
+        resp = xbmcgui.Dialog().select("Add user to the session", users)
         # post additional user
         if resp > -1:
             selected = users[resp]
             selected_userId = userlist[selected]
-            url = "{server}/emby/Sessions/%s/Users/%s" % (sessionId, selected_userId)
-            doUtils.downloadUrl(url, postBody={}, type="POST")
-            dialog.notification(
-                    heading="Success!",
-                    message="%s added to viewing session" % selected,
-                    icon="special://home/addons/plugin.video.emby/icon.png",
-                    time=1000)
+            url = "{server}/mediabrowser/Sessions/%s/Users/%s" % (sessionId, selected_userId)
+            postdata = {}
+            doUtils.downloadUrl(url, postBody=postdata, type="POST")
+            xbmcgui.Dialog().notification("Success!", "%s added to viewing session" % selected, time=1000)
 
     except:
-        utils.logMsg("EMBY", "Failed to add user to session.")
-        dialog.notification(
-                heading="Error",
-                message="Unable to add/remove user from the session.",
-                icon=xbmcgui.NOTIFICATION_ERROR)
+        xbmc.log("Failed to add user to session.")
+        xbmcgui.Dialog().notification("Error", "Unable to add/remove user from the session.", xbmcgui.NOTIFICATION_ERROR)
 
-    # Add additional user images
-    # always clear the individual items first
-    totalNodes = 10
-    for i in range(totalNodes):
-        if not utils.window('EmbyAdditionalUserImage.%s' % i):
-            break
-        utils.window('EmbyAdditionalUserImage.%s' % i)
+    try:
+        # Add additional user images
+        #always clear the individual items first
+        totalNodes = 10
+        for i in range(totalNodes):
+            if not WINDOW.getProperty('EmbyAdditionalUserImage.' + str(i)):
+                break
+            WINDOW.clearProperty('EmbyAdditionalUserImage.' + str(i))
 
-    url = "{server}/emby/Sessions?DeviceId=%s" % deviceId
-    result = doUtils.downloadUrl(url)
-    additionalUsers = result[0]['AdditionalUsers']
-    count = 0
-    for additionaluser in additionalUsers:
-        url = "{server}/emby/Users/%s?format=json" % additionaluser['UserId']
+        url = "{server}/mediabrowser/Sessions?DeviceId=%s" % deviceId
         result = doUtils.downloadUrl(url)
-        utils.window('EmbyAdditionalUserImage.%s' % count,
-            value=artwork.Artwork().getUserArtwork(result, 'Primary'))
-        utils.window('EmbyAdditionalUserPosition.%s' % additionaluser['UserId'], value=str(count))
-        count +=1
+        additionalUsers = result[0][u'AdditionalUsers']
+        count = 0
+        for additionaluser in additionalUsers:
+            url = "{server}/mediabrowser/Users/%s?format=json" % (additionaluser[u'UserId'])
+            result = doUtils.downloadUrl(url)
+            WINDOW.setProperty("EmbyAdditionalUserImage." + str(count),API().getUserArtwork(result,"Primary"))
+            WINDOW.setProperty("EmbyAdditionalUserPosition." + str(additionaluser[u'UserId']),str(count))
+            count +=1
+    except:
+        pass
 
-##### THEME MUSIC/VIDEOS #####
+# THEME MUSIC/VIDEOS
 def getThemeMedia():
 
-    doUtils = downloadutils.DownloadUtils()
-    dialog = xbmcgui.Dialog()
+    doUtils = DownloadUtils()
+    playUtils = PlayUtils()
+    
+    currUser = WINDOW.getProperty('currUser')
+    server = WINDOW.getProperty('server%s' % currUser)
     playback = None
 
+    library = xbmc.translatePath("special://profile/addon_data/plugin.video.emby/library/").decode('utf-8')
+
     # Choose playback method
-    resp = dialog.select("Playback method for your themes", ["Direct Play", "Direct Stream"])
+    resp = xbmcgui.Dialog().select("Choose playback method for your themes", ["Direct Play", "Direct Stream"])
     if resp == 0:
+        # Direct Play
         playback = "DirectPlay"
     elif resp == 1:
+        # Direct Stream
         playback = "DirectStream"
-    else:
-        return
-
-    library = xbmc.translatePath(
-                "special://profile/addon_data/plugin.video.emby/library/").decode('utf-8')
-    # Create library directory
-    if not xbmcvfs.exists(library):
-        xbmcvfs.mkdir(library)
+    else:return
 
     # Set custom path for user
-    tvtunes_path = xbmc.translatePath(
-        "special://profile/addon_data/script.tvtunes/").decode('utf-8')
+    tvtunes_path = xbmc.translatePath("special://profile/addon_data/script.tvtunes/").decode('utf-8')
     if xbmcvfs.exists(tvtunes_path):
         tvtunes = xbmcaddon.Addon(id="script.tvtunes")
         tvtunes.setSetting('custom_path_enable', "true")
         tvtunes.setSetting('custom_path', library)
-        utils.logMsg("EMBY", "TV Tunes custom path is enabled and set.", 1)
+        xbmc.log("TV Tunes custom path is enabled and set.")
     else:
-        # if it does not exist this will not work so warn user
-        # often they need to edit the settings first for it to be created.
-        dialog.ok(
-            heading="Warning",
-            line1=(
-                "The settings file does not exist in tvtunes. ",
-                "Go to the tvtunes addon and change a setting, then come back and re-run."))
-        xbmc.executebuiltin('Addon.OpenSettings(script.tvtunes)')
+        # if it does not exist this will not work so warn user, often they need to edit the settings first for it to be created.
+        dialog = xbmcgui.Dialog()
+        dialog.ok('Warning', 'The settings file does not exist in tvtunes. Go to the tvtunes addon and change a setting, then come back and re-run')
         return
         
+
+    # Create library directory
+    if not xbmcvfs.exists(library):
+        xbmcvfs.mkdir(library)
+
     # Get every user view Id
-    embyconn = utils.kodiSQL('emby')
-    embycursor = embyconn.cursor()
-    emby_db = embydb.Embydb_Functions(embycursor)
-    viewids = emby_db.getViews()
-    embycursor.close()
+    userViews = []
+    url = "{server}/mediabrowser/Users/{UserId}/Items?format=json"
+    result = doUtils.downloadUrl(url)
+    
+    for view in result[u'Items']:
+        userviewId = view[u'Id']
+        userViews.append(userviewId)
+
 
     # Get Ids with Theme Videos
     itemIds = {}
-    for view in viewids:
-        url = "{server}/emby/Users/{UserId}/Items?HasThemeVideo=True&ParentId=%s&format=json" % view
+    for view in userViews:
+        url = "{server}/mediabrowser/Users/{UserId}/Items?HasThemeVideo=True&ParentId=%s&format=json" % view
         result = doUtils.downloadUrl(url)
-        if result['TotalRecordCount'] != 0:
-            for item in result['Items']:
-                itemId = item['Id']
-                folderName = item['Name']
+        if result[u'TotalRecordCount'] != 0:
+            for item in result[u'Items']:
+                itemId = item[u'Id']
+                folderName = item[u'Name']
                 folderName = utils.normalize_string(folderName.encode('utf-8'))
                 itemIds[itemId] = folderName
 
     # Get paths for theme videos
     for itemId in itemIds:
-        nfo_path = xbmc.translatePath(
-            "special://profile/addon_data/plugin.video.emby/library/%s/" % itemIds[itemId])
+        nfo_path = xbmc.translatePath("special://profile/addon_data/plugin.video.emby/library/%s/" % itemIds[itemId])
         # Create folders for each content
         if not xbmcvfs.exists(nfo_path):
             xbmcvfs.mkdir(nfo_path)
         # Where to put the nfos
         nfo_path = "%s%s" % (nfo_path, "tvtunes.nfo")
 
-        url = "{server}/emby/Items/%s/ThemeVideos?format=json" % itemId
+        url = "{server}/mediabrowser/Items/%s/ThemeVideos?format=json" % itemId
         result = doUtils.downloadUrl(url)
 
         # Create nfo and write themes to it
         nfo_file = open(nfo_path, 'w')
         pathstowrite = ""
         # May be more than one theme
-        for theme in result['Items']:
-            putils = playutils.PlayUtils(theme)
+        for theme in result[u'Items']:  
             if playback == "DirectPlay":
-                playurl = putils.directPlay()
+                playurl = playUtils.directPlay(theme)
             else:
-                playurl = putils.directStream()
+                playurl = playUtils.directStream(result, server, theme[u'Id'], "ThemeVideo")
             pathstowrite += ('<file>%s</file>' % playurl.encode('utf-8'))
         
         # Check if the item has theme songs and add them   
-        url = "{server}/emby/Items/%s/ThemeSongs?format=json" % itemId
+        url = "{server}/mediabrowser/Items/%s/ThemeSongs?format=json" % itemId
         result = doUtils.downloadUrl(url)
 
         # May be more than one theme
-        for theme in result['Items']:
-            putils = playutils.PlayUtils(theme)  
+        for theme in result[u'Items']:  
             if playback == "DirectPlay":
-                playurl = putils.directPlay()
+                playurl = playUtils.directPlay(theme)
             else:
-                playurl = putils.directStream()
+                playurl = playUtils.directStream(result, server, theme[u'Id'], "Audio")
             pathstowrite += ('<file>%s</file>' % playurl.encode('utf-8'))
 
         nfo_file.write(
@@ -315,13 +253,13 @@ def getThemeMedia():
 
     # Get Ids with Theme songs
     musicitemIds = {}
-    for view in viewids:
-        url = "{server}/emby/Users/{UserId}/Items?HasThemeSong=True&ParentId=%s&format=json" % view
+    for view in userViews:
+        url = "{server}/mediabrowser/Users/{UserId}/Items?HasThemeSong=True&ParentId=%s&format=json" % view
         result = doUtils.downloadUrl(url)
-        if result['TotalRecordCount'] != 0:
-            for item in result['Items']:
-                itemId = item['Id']
-                folderName = item['Name']
+        if result[u'TotalRecordCount'] != 0:
+            for item in result[u'Items']:
+                itemId = item[u'Id']
+                folderName = item[u'Name']
                 folderName = utils.normalize_string(folderName.encode('utf-8'))
                 musicitemIds[itemId] = folderName
 
@@ -332,27 +270,25 @@ def getThemeMedia():
         if itemId in itemIds:
             continue
         
-        nfo_path = xbmc.translatePath(
-            "special://profile/addon_data/plugin.video.emby/library/%s/" % musicitemIds[itemId])
+        nfo_path = xbmc.translatePath("special://profile/addon_data/plugin.video.emby/library/%s/" % musicitemIds[itemId])
         # Create folders for each content
         if not xbmcvfs.exists(nfo_path):
             xbmcvfs.mkdir(nfo_path)
         # Where to put the nfos
         nfo_path = "%s%s" % (nfo_path, "tvtunes.nfo")
         
-        url = "{server}/emby/Items/%s/ThemeSongs?format=json" % itemId
+        url = "{server}/mediabrowser/Items/%s/ThemeSongs?format=json" % itemId
         result = doUtils.downloadUrl(url)
 
         # Create nfo and write themes to it
         nfo_file = open(nfo_path, 'w')
         pathstowrite = ""
         # May be more than one theme
-        for theme in result['Items']: 
-            putils = playutils.PlayUtils(theme)
+        for theme in result[u'Items']:  
             if playback == "DirectPlay":
-                playurl = putils.directPlay()
+                playurl = playUtils.directPlay(theme)
             else:
-                playurl = putils.directStream()
+                playurl = playUtils.directStream(result, server, theme[u'Id'], "Audio")
             pathstowrite += ('<file>%s</file>' % playurl.encode('utf-8'))
 
         nfo_file.write(
@@ -361,482 +297,398 @@ def getThemeMedia():
         # Close nfo file
         nfo_file.close()
 
-    dialog.notification(
-            heading="Emby for Kodi",
-            message="Themes added!",
-            icon="special://home/addons/plugin.video.emby/icon.png",
-            time=1000,
-            sound=False)
+def userPreferences():
+    doUtils = DownloadUtils()
+    addonSettings = xbmcaddon.Addon(id='plugin.video.emby')
+    userPreferencesPage = UserPreferences("script-emby-kodi-UserPreferences.xml", addonSettings.getAddonInfo('path'), "default", "1080i")
+    url = "{server}/mediabrowser/Users/{UserId}" 
+    result = doUtils.downloadUrl(url)
+    configuration = result[u'Configuration']
+    userPreferencesPage.setConfiguration(configuration)
+    userPreferencesPage.setName(result[u'Name'])
+    userPreferencesPage.setImage(API().getUserArtwork(result,"Primary"))
+    
+    userPreferencesPage.doModal()
+    if userPreferencesPage.isSave():
+        url = "{server}/mediabrowser/Users/{UserId}/Configuration"
+        postdata = userPreferencesPage.getConfiguration()
+        doUtils.downloadUrl(url, postBody=postdata, type="POST")
 
 ##### BROWSE EMBY CHANNELS #####    
-def BrowseChannels(itemid, folderid=None):
+def BrowseChannels(id, folderid=None):
     
     _addon_id   =   int(sys.argv[1])
     _addon_url  =   sys.argv[0]
-    doUtils = downloadutils.DownloadUtils()
-    art = artwork.Artwork()
-
+    
     xbmcplugin.setContent(int(sys.argv[1]), 'files')
     if folderid:
-        url = (
-                "{server}/emby/Channels/%s/Items?userid={UserId}&folderid=%s&format=json"
-                % (itemid, folderid))
-    elif itemid == "0":
-        # id 0 is the root channels folder
-        url = "{server}/emby/Channels?{UserId}&format=json"
+        url = "{server}/mediabrowser/Channels/" + id + "/Items?userid={UserId}&folderid=" + folderid + "&format=json"
     else:
-        url = "{server}/emby/Channels/%s/Items?UserId={UserId}&format=json" % itemid
+        if id == "0": # id 0 is the root channels folder
+            url = "{server}/mediabrowser/Channels?{UserId}&format=json"
+        else:
+            url = "{server}/mediabrowser/Channels/" + id + "/Items?userid={UserId}&format=json"
 
-    result = doUtils.downloadUrl(url)
-    try:
-        channels = result['Items']
-    except TypeError:
-        pass
-    else:
-        for item in channels:
+    results = DownloadUtils().downloadUrl(url)
+    if results:
+        result = results.get("Items")
+        if(result == None):
+            result = []
 
-            API = api.API(item)
-            itemid = item['Id']
-            itemtype = item['Type']
-            title = item.get('Name', "Missing Title")
-            li = xbmcgui.ListItem(title)
-
-            if itemtype == "ChannelFolderItem":
+        item_count = len(result)
+        current_item = 1;
+            
+        for item in result:
+            id=str(item.get("Id")).encode('utf-8')
+            type=item.get("Type").encode('utf-8')
+            
+            
+            if(item.get("Name") != None):
+                tempTitle = item.get("Name")
+                tempTitle=tempTitle.encode('utf-8')
+            else:
+                tempTitle = "Missing Title"
+                
+            if type=="ChannelFolderItem":
                 isFolder = True
             else:
                 isFolder = False
-
-            channelId = item.get('ChannelId', "")
-            channelName = item.get('ChannelName', "")
-
-            premieredate = API.getPremiereDate()
-            # Process Genres
-            genre = API.getGenres()
-            # Process UserData
-            overlay = 0
-
-            userdata = API.getUserData()
-            seektime = userdata['Resume']
-            played = userdata['Played']
-            if played:
-                overlay = 7
-            else:
-                overlay = 6
-
-            favorite = userdata['Favorite']
-            if favorite:
-                overlay = 5
+            item_type = str(type).encode('utf-8')
             
-            playcount = userdata['PlayCount']
-            if playcount is None:
-                playcount = 0
-
+            if(item.get("ChannelId") != None):
+               channelId = str(item.get("ChannelId")).encode('utf-8')
+            
+            channelName = ''   
+            if(item.get("ChannelName") != None):
+               channelName = item.get("ChannelName").encode('utf-8')   
+               
+            if(item.get("PremiereDate") != None):
+                premieredatelist = (item.get("PremiereDate")).split("T")
+                premieredate = premieredatelist[0]
+            else:
+                premieredate = ""
+            
+            #mediaStreams=API().getMediaStreams(item, True)
+                    
+            #people = API().getPeople(item)
+            
+            # Process Genres
+            genre = API().getGenre(item)
+                    
+            # Process UserData
+            userData = item.get("UserData")
+            PlaybackPositionTicks = '100'
+            overlay = "0"
+            favorite = "False"
+            seekTime = 0
+            if(userData != None):
+                if userData.get("Played") != True:
+                    overlay = "7"
+                    watched = "true"
+                else:
+                    overlay = "6"
+                    watched = "false"
+                if userData.get("IsFavorite") == True:
+                    overlay = "5"
+                    favorite = "True"
+                else:
+                    favorite = "False"
+                if userData.get("PlaybackPositionTicks") != None:
+                    PlaybackPositionTicks = str(userData.get("PlaybackPositionTicks"))
+                    reasonableTicks = int(userData.get("PlaybackPositionTicks")) / 1000
+                    seekTime = reasonableTicks / 10000
+            
+            playCount = 0
+            if(userData != None and userData.get("Played") == True):
+                playCount = 1
             # Populate the details list
-            details = {
-
-                'title': title,
-                'channelname': channelName,
-                'plot': API.getOverview(),
-                'Overlay': str(overlay),
-                'playcount': str(playcount)
-            }
-
-            if itemtype == "ChannelVideoItem":
+            details={'title'        : tempTitle,
+                     'channelname'  : channelName,
+                     'plot'         : item.get("Overview"),
+                     'Overlay'      : overlay,
+                     'playcount'    : str(playCount)}
+            
+            if item.get("Type") == "ChannelVideoItem":
                 xbmcplugin.setContent(_addon_id, 'movies')
-            elif itemtype == "ChannelAudioItem":
+            elif item.get("Type") == "ChannelAudioItem":
                 xbmcplugin.setContent(_addon_id, 'songs')
 
-            # Populate the extradata list and artwork
-            pbutils.PlaybackUtils(item).setArtwork(li)
-            extradata = {
+            # Populate the extraData list
+            extraData={'thumb'        : API().getArtwork(item, "Primary")  ,
+                       'fanart_image' : API().getArtwork(item, "Backdrop") ,
+                       'poster'       : API().getArtwork(item, "poster") , 
+                       'tvshow.poster': API().getArtwork(item, "tvshow.poster") ,
+                       'banner'       : API().getArtwork(item, "Banner") ,
+                       'clearlogo'    : API().getArtwork(item, "Logo") ,
+                       'discart'      : API().getArtwork(item, "Disc") ,
+                       'clearart'     : API().getArtwork(item, "Art") ,
+                       'landscape'    : API().getArtwork(item, "Thumb") ,
+                       'id'           : id ,
+                       'rating'       : item.get("CommunityRating"),
+                       'year'         : item.get("ProductionYear"),
+                       'premieredate' : premieredate,
+                       'genre'        : genre,
+                       'playcount'    : str(playCount),
+                       'itemtype'     : item_type}
+                       
+            if extraData['thumb'] == '':
+                extraData['thumb'] = extraData['fanart_image']
+                
+            liz = xbmcgui.ListItem(tempTitle)
 
-                'id': itemid,
-                'rating': item.get('CommunityRating'),
-                'year': item.get('ProductionYear'),
-                'premieredate': premieredate,
-                'genre': genre,
-                'playcount': str(playcount),
-                'itemtype': itemtype
-            }
-            li.setInfo('video', infoLabels=extradata)
-            li.setThumbnailImage(art.getAllArtwork(item)['Primary'])
-            li.setIconImage('DefaultTVShows.png')
-
-            if itemtype == "Channel":
-                path = "%s?id=%s&mode=channels" % (_addon_url, itemid)
-                xbmcplugin.addDirectoryItem(handle=_addon_id, url=path, listitem=li, isFolder=True)
+            artTypes=['poster', 'tvshow.poster', 'fanart_image', 'clearlogo', 'discart', 'banner', 'clearart', 'landscape', 'small_poster', 'tiny_poster', 'medium_poster','small_fanartimage', 'medium_fanartimage', 'medium_landscape', 'fanart_noindicators']
             
-            elif isFolder:
-                path = "%s?id=%s&mode=channelsfolder&folderid=%s" % (_addon_url, channelId, itemid)
-                xbmcplugin.addDirectoryItem(handle=_addon_id, url=path, listitem=li, isFolder=True)
+            for artType in artTypes:
+                imagePath=str(extraData.get(artType,''))
+                liz=PlaybackUtils().setArt(liz,artType, imagePath)
+            
+            liz.setThumbnailImage(API().getArtwork(item, "Primary"))
+            liz.setIconImage('DefaultTVShows.png')
+            #liz.setInfo( type="Video", infoLabels={ "Rating": item.get("CommunityRating") })
+            #liz.setInfo( type="Video", infoLabels={ "Plot": item.get("Overview") })
+            
+            if type=="Channel":
+                file = _addon_url + "?id=%s&mode=channels"%id
+                xbmcplugin.addDirectoryItem(handle=_addon_id, url=file, listitem=liz, isFolder=True)
+            
+            elif isFolder == True:
+                file = _addon_url + "?id=%s&mode=channelsfolder&folderid=%s" %(channelId, id)
+                xbmcplugin.addDirectoryItem(handle=_addon_id, url=file, listitem=liz, isFolder=True)
             else:
-                path = "%s?id=%s&mode=play" % (_addon_url, itemid)
-                li.setProperty('IsPlayable', 'true')
-                xbmcplugin.addDirectoryItem(handle=_addon_id, url=path, listitem=li)
+                file = _addon_url + "?id=%s&mode=play"%id
+                liz.setProperty('IsPlayable', 'true')
+                xbmcplugin.addDirectoryItem(handle=_addon_id, url=file, listitem=liz)
 
     xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
 
-##### LISTITEM SETUP FOR VIDEONODES #####
-def createListItem(item):
-
-    title = item['title']
-    li = xbmcgui.ListItem(title)
-    li.setProperty('IsPlayable', "true")
-    
-    metadata = {
-
-        'Title': title,
-        'duration': str(item['runtime']/60),
-        'Plot': item['plot'],
-        'Playcount': item['playcount']
-    }
-
-    if "episode" in item:
-        episode = item['episode']
-        metadata['Episode'] = episode
-
-    if "season" in item:
-        season = item['season']
-        metadata['Season'] = season
-
-    if season and episode:
-        li.setProperty('episodeno', "s%.2de%.2d" % (season, episode))
-
-    if "firstaired" in item:
-        metadata['Premiered'] = item['firstaired']
-
-    if "showtitle" in item:
-        metadata['TVshowTitle'] = item['showtitle']
-
-    if "rating" in item:
-        metadata['Rating'] = str(round(float(item['rating']),1))
-
-    if "director" in item:
-        metadata['Director'] = " / ".join(item['director'])
-
-    if "writer" in item:
-        metadata['Writer'] = " / ".join(item['writer'])
-
-    if "cast" in item:
-        cast = []
-        castandrole = []
-        for person in item['cast']:
-            name = person['name']
-            cast.append(name)
-            castandrole.append((name, person['role']))
-        metadata['Cast'] = cast
-        metadata['CastAndRole'] = castandrole
-
-    li.setInfo(type="Video", infoLabels=metadata)  
-    li.setProperty('resumetime', str(item['resume']['position']))
-    li.setProperty('totaltime', str(item['resume']['total']))
-    li.setArt(item['art'])
-    li.setThumbnailImage(item['art'].get('thumb',''))
-    li.setIconImage('DefaultTVShows.png')
-    li.setProperty('dbid', str(item['episodeid']))
-    li.setProperty('fanart_image', item['art'].get('tvshow.fanart',''))
-    for key, value in item['streamdetails'].iteritems():
-        for stream in value:
-            li.addStreamInfo(key, stream)
-    
-    return li
-
 ##### GET NEXTUP EPISODES FOR TAGNAME #####    
-def getNextUpEpisodes(tagname, limit):
-    
-    count = 0
-    # if the addon is called with nextup parameter,
-    # we return the nextepisodes list of the given tagname
-    xbmcplugin.setContent(int(sys.argv[1]), 'episodes')
-    # First we get a list of all the TV shows - filtered by tag
-    query = {
+def getNextUpEpisodes(tagname,limit):
+    count=0
 
-        'jsonrpc': "2.0",
-        'id': "libTvShows",
-        'method': "VideoLibrary.GetTVShows",
-        'params': {
-
-            'sort': {'order': "descending", 'method': "lastplayed"},
-            'filter': {
-                'and': [
-                    {'operator': "true", 'field': "inprogress", 'value': ""},
-                    {'operator': "contains", 'field': "tag", 'value': "%s" % tagname}
-                ]},
-            'properties': ['title', 'studio', 'mpaa', 'file', 'art']
-        }
-    }
-    result = xbmc.executeJSONRPC(json.dumps(query))
-    result = json.loads(result)
-    # If we found any, find the oldest unwatched show for each one.
-    try:
-        items = result['result']['tvshows']
-    except (KeyError, TypeError):
-        pass
-    else:
-        for item in items:
-            if utils.settings('ignoreSpecialsNextEpisodes') == "true":
-                query = {
-
-                    'jsonrpc': "2.0",
-                    'id': 1,
-                    'method': "VideoLibrary.GetEpisodes",
-                    'params': {
-
-                        'tvshowid': item['tvshowid'],
-                        'sort': {'method': "episode"},
-                        'filter': {
-                            'and': [
-                                {'operator': "lessthan", 'field': "playcount", 'value': "1"},
-                                {'operator': "greaterthan", 'field': "season", 'value': "0"}
-                        ]},
-                        'properties': [
-                            "title", "playcount", "season", "episode", "showtitle",
-                            "plot", "file", "rating", "resume", "tvshowid", "art",
-                            "streamdetails", "firstaired", "runtime", "writer",
-                            "dateadded", "lastplayed"
-                        ],
-                        'limits': {"end": 1}
-                    }
-                }
-            else:
-                query = {
-
-                    'jsonrpc': "2.0",
-                    'id': 1,
-                    'method': "VideoLibrary.GetEpisodes",
-                    'params': {
-
-                        'tvshowid': item['tvshowid'],
-                        'sort': {'method': "episode"},
-                        'filter': {'operator': "lessthan", 'field': "playcount", 'value': "1"},
-                        'properties': [
-                            "title", "playcount", "season", "episode", "showtitle",
-                            "plot", "file", "rating", "resume", "tvshowid", "art",
-                            "streamdetails", "firstaired", "runtime", "writer",
-                            "dateadded", "lastplayed"
-                        ],
-                        'limits': {"end": 1}
-                    }
-                }
-
-            result = xbmc.executeJSONRPC(json.dumps(query))
-            result = json.loads(result)
-            try:
-                episodes = result['result']['episodes']
-            except (KeyError, TypeError):
-                pass
-            else:
-                for episode in episodes:
-                    li = createListItem(episode)
-                    xbmcplugin.addDirectoryItem(
-                                handle=int(sys.argv[1]),
-                                url=item['file'],
-                                listitem=li)
-                    count += 1
-
-            if count == limit:
-                break
-
-    xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
-
-##### GET INPROGRESS EPISODES FOR TAGNAME #####    
-def getInProgressEpisodes(tagname, limit):
-    
-    count = 0
-    # if the addon is called with inprogressepisodes parameter,
-    # we return the inprogressepisodes list of the given tagname
+    #if the addon is called with nextup parameter, we return the nextepisodes list of the given tagname
     xbmcplugin.setContent(int(sys.argv[1]), 'episodes')
     # First we get a list of all the in-progress TV shows - filtered by tag
-    query = {
-
-        'jsonrpc': "2.0",
-        'id': "libTvShows",
-        'method': "VideoLibrary.GetTVShows",
-        'params': {
-
-            'sort': {'order': "descending", 'method': "lastplayed"},
-            'filter': {
-                'and': [
-                    {'operator': "true", 'field': "inprogress", 'value': ""},
-                    {'operator': "contains", 'field': "tag", 'value': "%s" % tagname}
-                ]},
-            'properties': ['title', 'studio', 'mpaa', 'file', 'art']
-        }
-    }
-    result = xbmc.executeJSONRPC(json.dumps(query))
-    result = json.loads(result)
+    json_query_string = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetTVShows", "params": { "sort": { "order": "descending", "method": "lastplayed" }, "filter": {"and": [{"operator":"true", "field":"inprogress", "value":""}, {"operator": "is", "field": "tag", "value": "%s"}]}, "properties": [ "title", "studio", "mpaa", "file", "art" ]  }, "id": "libTvShows"}' %tagname)
+    
+    json_result = json.loads(json_query_string)
     # If we found any, find the oldest unwatched show for each one.
-    try:
-        items = result['result']['tvshows']
-    except (KeyError, TypeError):
-        pass
-    else:
-        for item in items:
-            query = {
+    if json_result.has_key('result') and json_result['result'].has_key('tvshows'):
+        for item in json_result['result']['tvshows']:
 
-                'jsonrpc': "2.0",
-                'id': 1,
-                'method': "VideoLibrary.GetEpisodes",
-                'params': {
-
-                    'tvshowid': item['tvshowid'],
-                    'sort': {'method': "episode"},
-                    'filter': {'operator': "true", 'field': "inprogress", 'value': ""},
-                    'properties': [
-                        "title", "playcount", "season", "episode", "showtitle", "plot",
-                        "file", "rating", "resume", "tvshowid", "art", "cast",
-                        "streamdetails", "firstaired", "runtime", "writer",
-                        "dateadded", "lastplayed"
-                    ]
-                }
-            }
-            result = xbmc.executeJSONRPC(json.dumps(query))
-            result = json.loads(result)
-            try:
-                episodes = result['result']['episodes']
-            except (KeyError, TypeError):
-                pass
+            # If Ignore Specials is true only choose episodes from seasons greater than 0.
+            if utils.settings("ignoreSpecialsNextEpisodes")=="true":
+                json_query2 = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetEpisodes", "params": { "tvshowid": %d, "sort": {"method":"episode"}, "filter": {"and": [ {"field": "playcount", "operator": "lessthan", "value":"1"}, {"field": "season", "operator": "greaterthan", "value": "0"} ]}, "properties": [ "title", "playcount", "season", "episode", "showtitle", "plot", "file", "rating", "resume", "tvshowid", "art", "streamdetails", "firstaired", "runtime", "writer", "dateadded", "lastplayed" ], "limits":{"end":1}}, "id": "1"}' %item['tvshowid'])
             else:
-                for episode in episodes:
-                    li = createListItem(episode)
-                    xbmcplugin.addDirectoryItem(
-                                handle=int(sys.argv[1]),
-                                url=item['file'],
-                                listitem=li)
-                    count += 1
+                json_query2 = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetEpisodes", "params": { "tvshowid": %d, "sort": {"method":"episode"}, "filter": {"field": "playcount", "operator": "lessthan", "value":"1"}, "properties": [ "title", "playcount", "season", "episode", "showtitle", "plot", "file", "rating", "resume", "tvshowid", "art", "streamdetails", "firstaired", "runtime", "writer", "dateadded", "lastplayed" ], "limits":{"end":1}}, "id": "1"}' %item['tvshowid'])
 
+            if json_query2:
+                json_query2 = json.loads(json_query2)
+                if json_query2.has_key('result') and json_query2['result'].has_key('episodes'):
+                    for item in json_query2['result']['episodes']:
+                        liz = createListItem(item)
+                        xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=item['file'], listitem=liz)
+                        count +=1
             if count == limit:
                 break
-
     xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
 
-##### GET RECENT EPISODES FOR TAGNAME #####    
-def getRecentEpisodes(tagname, limit):
-    
+def getInProgressEpisodes(tagname,limit):
     count = 0
-    # if the addon is called with recentepisodes parameter,
-    # we return the recentepisodes list of the given tagname
+    #if the addon is called with inprogressepisodes parameter, we return the inprogressepisodes list of the given tagname
+    xbmcplugin.setContent(int(sys.argv[1]), 'episodes')
+    # First we get a list of all the in-progress TV shows - filtered by tag
+    json_query_string = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetTVShows", "params": { "sort": { "order": "descending", "method": "lastplayed" }, "filter": {"and": [{"operator":"true", "field":"inprogress", "value":""}, {"operator": "contains", "field": "tag", "value": "%s"}]}, "properties": [ "title", "studio", "mpaa", "file", "art" ]  }, "id": "libTvShows"}' %tagname)
+    json_result = json.loads(json_query_string)
+    # If we found any, find all in progress episodes for each one.
+    if json_result.has_key('result') and json_result['result'].has_key('tvshows'):
+        for item in json_result['result']['tvshows']:
+            json_query2 = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetEpisodes", "params": { "tvshowid": %d, "sort": {"method":"episode"}, "filter": {"field": "inprogress", "operator": "true", "value":""}, "properties": [ "title", "playcount", "season", "episode", "showtitle", "plot", "file", "rating", "resume", "tvshowid", "art", "cast", "streamdetails", "firstaired", "runtime", "writer", "dateadded", "lastplayed" ]}, "id": "1"}' %item['tvshowid'])
+
+            if json_query2:
+                json_query2 = json.loads(json_query2)
+                if json_query2.has_key('result') and json_query2['result'].has_key('episodes'):
+                    for item in json_query2['result']['episodes']:
+                        liz = createListItem(item)
+                        xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=item['file'], listitem=liz)
+                        count +=1
+            if count == limit:
+                break
+    xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
+
+def getRecentEpisodes(tagname,limit):
+    #if the addon is called with recentepisodes parameter, we return the recentepisodes list of the given tagname
     xbmcplugin.setContent(int(sys.argv[1]), 'episodes')
     # First we get a list of all the TV shows - filtered by tag
-    query = {
-
-        'jsonrpc': "2.0",
-        'id': "libTvShows",
-        'method': "VideoLibrary.GetTVShows",
-        'params': {
-
-            'sort': {'order': "descending", 'method': "dateadded"},
-            'filter': {'operator': "contains", 'field': "tag", 'value': "%s" % tagname},
-            'properties': ["title","sorttitle"]
-        }
-    }
-    result = xbmc.executeJSONRPC(json.dumps(query))
-    result = json.loads(result)
-    # If we found any, find the oldest unwatched show for each one.
-    try:
-        items = result['result']['tvshows']
-    except (KeyError, TypeError):
-        pass
-    else:
-        allshowsIds = set()
-        for item in items:
-            allshowsIds.add(item['tvshowid'])
-
-        query = {
-
-            'jsonrpc': "2.0",
-            'id': 1,
-            'method': "VideoLibrary.GetEpisodes",
-            'params': {
-
-                'sort': {'order': "descending", 'method': "dateadded"},
-                'filter': {'operator': "lessthan", 'field': "playcount", 'value': "1"},
-                'properties': [
-                    "title", "playcount", "season", "episode", "showtitle", "plot",
-                    "file", "rating", "resume", "tvshowid", "art", "streamdetails",
-                    "firstaired", "runtime", "cast", "writer", "dateadded", "lastplayed"
-                ],
-                "limits": {"end": limit}
-            }
-        }
-        result = xbmc.executeJSONRPC(json.dumps(query))
-        result = json.loads(result)
-        try:
-            episodes = result['result']['episodes']
-        except (KeyError, TypeError):
-            pass
-        else:
-            for episode in episodes:
-                if episode['tvshowid'] in allshowsIds:
-                    li = createListItem(episode)
-                    xbmcplugin.addDirectoryItem(
-                                handle=int(sys.argv[1]),
-                                url=item['file'],
-                                listitem=li)
-                    count += 1
-
-                if count == limit:
-                    break
-
+    json_query_string = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetTVShows", "params": { "sort": { "order": "descending", "method": "dateadded" }, "properties": [ "title","sorttitle" ], "filter": {"operator": "contains", "field": "tag", "value": "%s"} }, "id": "libTvShows"}' %tagname)    
+    json_result = json.loads(json_query_string)
+    
+    # If we found any, put all tv show id's in a list
+    if json_result.has_key('result') and json_result['result'].has_key('tvshows'):
+        alltvshowIds = list()
+        for tvshow in json_result['result']['tvshows']:
+            alltvshowIds.append(tvshow["tvshowid"])
+        alltvshowIds = set(alltvshowIds)
+        
+        #get all recently added episodes
+        json_query2 = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetEpisodes", "params": { "sort": {"order": "descending", "method": "dateadded"}, "filter": {"field": "playcount", "operator": "lessthan", "value":"1"}, "properties": [ "title", "playcount", "season", "episode", "showtitle", "plot", "file", "rating", "resume", "tvshowid", "art", "streamdetails", "firstaired", "runtime", "cast", "writer", "dateadded", "lastplayed" ]}, "limits":{"end":%d}, "id": "1"}' %limit)
+        count = 0
+        if json_query2:
+            json_query2 = json.loads(json_query2)
+            if json_query2.has_key('result') and json_query2['result'].has_key('episodes'):
+                for item in json_query2['result']['episodes']:
+                    if item["tvshowid"] in alltvshowIds:
+                        liz = createListItem(item)
+                        xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=item['file'], listitem=liz)
+                        count += 1
+                    if count == limit:
+                        break
     xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
-
+    
+def createListItem(item):
+       
+    liz = xbmcgui.ListItem(item['title'])
+    liz.setInfo( type="Video", infoLabels={ "Title": item['title'] })
+    liz.setProperty('IsPlayable', 'true')
+    liz.setInfo( type="Video", infoLabels={ "duration": str(item['runtime']/60) })
+    
+    if "episode" in item:
+        episode = "%.2d" % float(item['episode'])
+        liz.setInfo( type="Video", infoLabels={ "Episode": item['episode'] })
+    
+    if "season" in item:
+        season = "%.2d" % float(item['season'])
+        liz.setInfo( type="Video", infoLabels={ "Season": item['season'] })
+        
+    if season and episode:
+        episodeno = "s%se%s" %(season,episode)
+        liz.setProperty("episodeno", episodeno)
+        
+    if "firstaired" in item:
+        liz.setInfo( type="Video", infoLabels={ "Premiered": item['firstaired'] })
+    
+    plot = item['plot']
+    liz.setInfo( type="Video", infoLabels={ "Plot": plot })
+    
+    if "showtitle" in item:
+        liz.setInfo( type="Video", infoLabels={ "TVshowTitle": item['showtitle'] })
+    
+    if "rating" in item:
+        liz.setInfo( type="Video", infoLabels={ "Rating": str(round(float(item['rating']),1)) })
+    liz.setInfo( type="Video", infoLabels={ "Playcount": item['playcount'] })
+    if "director" in item:
+        liz.setInfo( type="Video", infoLabels={ "Director": " / ".join(item['director']) })
+    if "writer" in item:
+        liz.setInfo( type="Video", infoLabels={ "Writer": " / ".join(item['writer']) })
+        
+    if "cast" in item:
+        listCast = []
+        listCastAndRole = []
+        for castmember in item["cast"]:
+            listCast.append( castmember["name"] )
+            listCastAndRole.append( (castmember["name"], castmember["role"]) ) 
+        cast = [listCast, listCastAndRole]
+        liz.setInfo( type="Video", infoLabels={ "Cast": cast[0] })
+        liz.setInfo( type="Video", infoLabels={ "CastAndRole": cast[1] })
+    
+    liz.setProperty("resumetime", str(item['resume']['position']))
+    liz.setProperty("totaltime", str(item['resume']['total']))
+    liz.setArt(item['art'])
+    liz.setThumbnailImage(item['art'].get('thumb',''))
+    liz.setIconImage('DefaultTVShows.png')
+    liz.setProperty("dbid", str(item['episodeid']))
+    liz.setProperty("fanart_image", item['art'].get('tvshow.fanart',''))
+    for key, value in item['streamdetails'].iteritems():
+        for stream in value:
+            liz.addStreamInfo( key, stream )
+    
+    return liz
+    
 ##### GET EXTRAFANART FOR LISTITEM #####
 def getExtraFanArt():
+    itemPath = ""
+    embyId = ""
     
-    emby = embyserver.Read_EmbyServer()
-    art = artwork.Artwork()
-    
-    # Get extrafanart for listitem 
-    # this will only be used for skins that actually call the listitem's path + fanart dir... 
+    #get extrafanart for listitem - this will only be used for skins that actually call the listitem's path + fanart dir... 
     try:
-        # Only do this if the listitem has actually changed
+        #only do this if the listitem has actually changed
         itemPath = xbmc.getInfoLabel("ListItem.FileNameAndPath")
             
         if not itemPath:
             itemPath = xbmc.getInfoLabel("ListItem.Path")
         
-        if any([x in itemPath for x in ['tvshows', 'musicvideos', 'movies']]):
-            params = urlparse.parse_qs(itemPath)
-            embyId = params['id'][0]
+        if ("/tvshows/" in itemPath or "/musicvideos/" in itemPath or "/movies/" in itemPath):
+            embyId = itemPath.split("/")[-2]
             
-            utils.logMsg("EMBY", "Requesting extrafanart for Id: %s" % embyId, 1)
+            utils.logMsg("%s %s" % ("Emby addon", "getExtraFanArt"), "requesting extraFanArt for Id: " + embyId, 1)
 
-            # We need to store the images locally for this to work
-            # because of the caching system in xbmc
-            fanartDir = xbmc.translatePath("special://thumbnails/emby/%s/" % embyId).decode('utf-8')
+            #we need to store the images locally for this to work because of the caching system in xbmc
+            fanartDir = xbmc.translatePath("special://thumbnails/emby/" + embyId + "/")
             
             if not xbmcvfs.exists(fanartDir):
-                # Download the images to the cache directory
-                xbmcvfs.mkdirs(fanartDir)
-                item = emby.getItem(embyId)
-                if item:
-                    backdrops = art.getAllArtwork(item)['Backdrop']
-                    tags = item['BackdropImageTags']
-                    count = 0
-                    for backdrop in backdrops:
-                        # Same ordering as in artwork
-                        tag = tags[count]
-                        fanartFile = os.path.join(fanartDir, "fanart%s.jpg" % tag)
-                        li = xbmcgui.ListItem(tag, path=fanartFile)
-                        xbmcplugin.addDirectoryItem(
-                                            handle=int(sys.argv[1]),
-                                            url=fanartFile,
-                                            listitem=li)
-                        xbmcvfs.copy(backdrop, fanartFile) 
-                        count += 1               
+                #download the images to the cache directory
+                xbmcvfs.mkdir(fanartDir)
+                item = ReadEmbyDB().getFullItem(embyId)
+                if item != None:
+                    if item.has_key("BackdropImageTags"):
+                        if(len(item["BackdropImageTags"]) > 0):
+                            WINDOW = xbmcgui.Window(10000)
+                            username = WINDOW.getProperty('currUser')
+                            server = WINDOW.getProperty('server%s' % username)
+                            totalbackdrops = len(item["BackdropImageTags"])
+                            count = 0
+                            for backdrop in item["BackdropImageTags"]: 
+                                backgroundUrl = "%s/mediabrowser/Items/%s/Images/Backdrop/%s/?MaxWidth=10000&MaxHeight=10000&Format=original&Tag=%s&EnableImageEnhancers=false" % (server, embyId, str(count), backdrop)
+                                count += 1
+                                fanartFile = os.path.join(fanartDir,"fanart" + backdrop + ".jpg")
+                                li = xbmcgui.ListItem(backdrop, path=fanartFile)
+                                xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=fanartFile, listitem=li)
+                                xbmcvfs.copy(backgroundUrl,fanartFile) 
+                
             else:
-                utils.logMsg("EMBY", "Found cached backdrop.", 2)
-                # Use existing cached images
+                #use existing cached images
                 dirs, files = xbmcvfs.listdir(fanartDir)
+                count = 1
                 for file in files:
-                    fanartFile = os.path.join(fanartDir, file)
-                    li = xbmcgui.ListItem(file, path=fanartFile)
-                    xbmcplugin.addDirectoryItem(
-                                            handle=int(sys.argv[1]),
-                                            url=fanartFile,
-                                            listitem=li)
+                    count +=1
+                    li = xbmcgui.ListItem(file, path=os.path.join(fanartDir,file))
+                    xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=os.path.join(fanartDir,file), listitem=li)
     except Exception as e:
-        utils.logMsg("EMBY", "Error getting extrafanart: %s" % e, 1)
+        utils.logMsg("%s %s" % ("Emby addon", "Error in getExtraFanArt"), str(e), 1)
+        pass
+    
+    #always do endofdirectory to prevent errors in the logs
+    xbmcplugin.endOfDirectory(int(sys.argv[1]))
+
+def addDirectoryItem(label, path, folder=True):
+    li = xbmcgui.ListItem(label, path=path)
+    li.setThumbnailImage("special://home/addons/plugin.video.emby/icon.png")
+    li.setArt({"fanart":"special://home/addons/plugin.video.emby/fanart.jpg"})
+    li.setArt({"landscape":"special://home/addons/plugin.video.emby/fanart.jpg"})
+    xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=path, listitem=li, isFolder=folder)    
+    
+# if the addon is called without parameters we show the listing...    
+def doMainListing():
+    
+    xbmcplugin.setContent(int(sys.argv[1]), 'files')    
+    #get emby nodes from the window props
+    embyProperty = WINDOW.getProperty("Emby.nodes.total")
+    if embyProperty:
+        totalNodes = int(embyProperty)
+        for i in range(totalNodes):
+            path = WINDOW.getProperty("Emby.nodes.%s.index" %str(i))
+            if not path:
+                path = WINDOW.getProperty("Emby.nodes.%s.content" %str(i))
+            label = WINDOW.getProperty("Emby.nodes.%s.title" %str(i))
+            if path:
+                addDirectoryItem(label, path)
+    
+    # some extra entries for settings and stuff. TODO --> localize the labels
+    addDirectoryItem("Settings", "plugin://plugin.video.emby/?mode=settings")
+    addDirectoryItem("Perform manual sync", "plugin://plugin.video.emby/?mode=manualsync")
+    addDirectoryItem("Add user to session", "plugin://plugin.video.emby/?mode=adduser")
+    addDirectoryItem("Configure user preferences", "plugin://plugin.video.emby/?mode=userprefs")
+    addDirectoryItem("Perform local database reset (full resync)", "plugin://plugin.video.emby/?mode=reset")
+    addDirectoryItem("Cache all images to Kodi texture cache (advanced)", "plugin://plugin.video.emby/?mode=texturecache")
+    addDirectoryItem("Sync Emby Theme Media to Kodi", "plugin://plugin.video.emby/?mode=thememedia")
     
-    # Always do endofdirectory to prevent errors in the logs
     xbmcplugin.endOfDirectory(int(sys.argv[1]))
\ No newline at end of file
diff --git a/resources/lib/KodiMonitor.py b/resources/lib/KodiMonitor.py
index 1b3f7862..2d4dc943 100644
--- a/resources/lib/KodiMonitor.py
+++ b/resources/lib/KodiMonitor.py
@@ -1,195 +1,150 @@
-# -*- coding: utf-8 -*-
-
 #################################################################################################
-
-import json
+# Kodi  Monitor
+# Watched events that occur in Kodi, like setting media watched
+#################################################################################################
 
 import xbmc
 import xbmcgui
+import xbmcaddon
+import json
 
-import clientinfo
-import downloadutils
-import embydb_functions as embydb
-import playbackutils as pbutils
-import utils
-
-#################################################################################################
+import Utils as utils
+from WriteKodiVideoDB import WriteKodiVideoDB
+from ReadKodiDB import ReadKodiDB
+from PlayUtils import PlayUtils
+from DownloadUtils import DownloadUtils
+from PlaybackUtils import PlaybackUtils
 
 
-class KodiMonitor(xbmc.Monitor):
+class Kodi_Monitor( xbmc.Monitor ):
+    
+    WINDOW = xbmcgui.Window(10000)
 
+    def __init__(self, *args, **kwargs):
+        xbmc.Monitor.__init__(self)
 
-    def __init__(self):
-
-        self.clientInfo = clientinfo.ClientInfo()
-        self.addonName = self.clientInfo.getAddonName()
-        self.doUtils = downloadutils.DownloadUtils()
-
-        self.logMsg("Kodi monitor started.", 1)
-
-    def logMsg(self, msg, lvl=1):
-
-        self.className = self.__class__.__name__
-        utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
+    def logMsg(self, msg, lvl = 1):
 
+        className = self.__class__.__name__
+        utils.logMsg("%s %s" % ("EMBY", className), msg, int(lvl))
 
     def onScanStarted(self, library):
-        self.logMsg("Kodi library scan %s running." % library, 2)
-        if library == "video":
-            utils.window('emby_kodiScan', value="true")
-            
+        utils.window('kodiScan', value="true")
+        self.logMsg("Kodi library scan running.", 2)
+
     def onScanFinished(self, library):
-        self.logMsg("Kodi library scan %s finished." % library, 2)
-        if library == "video":
-            utils.window('emby_kodiScan', clear=True)
+        utils.window('kodiScan', clear=True)
+        self.logMsg("Kodi library scan finished.", 2)
+        
+    #this library monitor is used to detect a watchedstate change by the user through the library
+    #as well as detect when a library item has been deleted to pass the delete to the Emby server
+    def onNotification  (self, sender, method, data):
 
-    def onNotification(self, sender, method, data):
+        WINDOW = self.WINDOW
+        downloadUtils = DownloadUtils()
+        #player started playing an item - 
+        if ("Playlist.OnAdd" in method or "Player.OnPlay" in method):
 
-        doUtils = self.doUtils
-        if method not in ("Playlist.OnAdd"):
-            self.logMsg("Method: %s Data: %s" % (method, data), 1)
+            jsondata = json.loads(data)
+            if jsondata:
+                if jsondata.has_key("item"):
+                    if jsondata.get("item").has_key("id") and jsondata.get("item").has_key("type"):
+                        id = jsondata.get("item").get("id")
+                        type = jsondata.get("item").get("type")
+                        
+                        if (utils.settings('useDirectPaths')=='true' and not type == "song") or (type == "song" and utils.settings('enableMusicSync') == "true"):
+                            
+                            if type == "song":
+                                connection = utils.KodiSQL('music')
+                                cursor = connection.cursor()
+                                embyid = ReadKodiDB().getEmbyIdByKodiId(id, type, connection, cursor)
+                                cursor.close()
+                            else:    
+                                embyid = ReadKodiDB().getEmbyIdByKodiId(id,type)
+
+                            if embyid:
+
+                                url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json" % embyid
+                                result = downloadUtils.downloadUrl(url)
+                                self.logMsg("Result: %s" % result, 2)
+                                
+                                playurl = None
+                                count = 0
+                                while not playurl and count < 2:
+                                    try:
+                                        playurl = xbmc.Player().getPlayingFile()
+                                    except RuntimeError:
+                                        xbmc.sleep(200)
+                                    else:
+                                        listItem = xbmcgui.ListItem()
+                                        PlaybackUtils().setProperties(playurl, result, listItem)
+
+                                        if type == "song" and utils.settings('directstreammusic') == "true":
+                                            utils.window('%splaymethod' % playurl, value="DirectStream")
+                                        else:
+                                            utils.window('%splaymethod' % playurl, value="DirectPlay")
+
+                                    count += 1
+        
+        if method == "VideoLibrary.OnUpdate":
+            # Triggers 4 times, the following is only for manually marking as watched/unwatched
+            jsondata = json.loads(data)
             
-        if data:
-            data = json.loads(data)
-
-
-        if method == "Player.OnPlay":
-            # Set up report progress for emby playback
-            item = data.get('item')
             try:
-                kodiid = item['id']
-                type = item['type']
-            except (KeyError, TypeError):
-                self.logMsg("Properties already set for item.", 1)
+                playcount = jsondata.get('playcount')
+                item = jsondata['item']['id']
+                type = jsondata['item']['type']
+                prop = utils.window('Played%s%s' % (type, item))
+            except:
+                self.logMsg("Could not process VideoLibrary.OnUpdate data.", 1)
             else:
-                if ((utils.settings('useDirectPaths') == "1" and not type == "song") or
-                        (type == "song" and utils.settings('disableMusic') == "false")):
-                    # Set up properties for player
-                    embyconn = utils.kodiSQL('emby')
-                    embycursor = embyconn.cursor()
-                    emby_db = embydb.Embydb_Functions(embycursor)
-                    emby_dbitem = emby_db.getItem_byKodiId(kodiid, type)
-                    try:
-                        itemid = emby_dbitem[0]
-                    except TypeError:
-                        self.logMsg("No kodiid returned.", 1)
-                    else:
-                        url = "{server}/emby/Users/{UserId}/Items/%s?format=json" % itemid
-                        result = doUtils.downloadUrl(url)
-                        self.logMsg("Item: %s" % result, 2)
+                self.logMsg("VideoLibrary.OnUpdate: %s" % data, 2)
+                if prop != "true":
+                    # Set property to prevent the multi triggering
+                    utils.window('Played%s%s' % (type, item), "true")
+                    WriteKodiVideoDB().updatePlayCountFromKodi(item, type, playcount)
 
-                        playurl = None
-                        count = 0
-                        while not playurl and count < 2:
-                            try:
-                                playurl = xbmc.Player().getPlayingFile()
-                            except RuntimeError:
-                                count += 1
-                                xbmc.sleep(200)
-                            else:
-                                listItem = xbmcgui.ListItem()
-                                playback = pbutils.PlaybackUtils(result)
+                self.clearProperty(type, item)
+                    
+        if method == "System.OnWake":
+            xbmc.sleep(10000) #Allow network to wake up
+            WINDOW.setProperty("OnWakeSync", "true")
 
-                                if type == "song" and utils.settings('streamMusic') == "true":
-                                    utils.window('emby_%s.playmethod' % playurl,
-                                        value="DirectStream")
-                                else:
-                                    utils.window('emby_%s.playmethod' % playurl,
-                                        value="DirectPlay")
-                                # Set properties for player.py
-                                playback.setProperties(playurl, listItem)
-                    finally:
-                        embycursor.close()
+        if method == "VideoLibrary.OnRemove":
+            xbmc.log('Intercepted remove from sender: ' + sender + ' method: ' + method + ' data: ' + data)
+            jsondata = json.loads(data)
+            id = ReadKodiDB().getEmbyIdByKodiId(jsondata.get("id"), jsondata.get("type"))
+            if id == None:
+                return            
+            xbmc.log("Deleting Emby ID: " + id + " from database")
+            connection = utils.KodiSQL()
+            cursor = connection.cursor()
+            cursor.execute("DELETE FROM emby WHERE emby_id = ?", (id,))
+            connection.commit()
+            cursor.close
             
-
-        elif method == "VideoLibrary.OnUpdate":
-            # Manually marking as watched/unwatched
-            playcount = data.get('playcount')
-            item = data.get('item')
-            try:
-                kodiid = item['id']
-                type = item['type']
-            except (KeyError, TypeError):
-                self.logMsg("Item is invalid for playstate update.", 1)
-            else:
-                # Send notification to the server.
-                embyconn = utils.kodiSQL('emby')
-                embycursor = embyconn.cursor()
-                emby_db = embydb.Embydb_Functions(embycursor)
-                emby_dbitem = emby_db.getItem_byKodiId(kodiid, type)
-                try:
-                    itemid = emby_dbitem[0]
-                except TypeError:
-                    self.logMsg("Could not find itemid in emby database.", 1)
-                else:
-                    # Stop from manually marking as watched unwatched, with actual playback.
-                    if utils.window('emby_skipWatched%s' % itemid) == "true":
-                        # property is set in player.py
-                        utils.window('emby_skipWatched%s' % itemid, clear=True)
-                    else:
-                        # notify the server
-                        url = "{server}/emby/Users/{UserId}/PlayedItems/%s?format=json" % itemid
-                        if playcount != 0:
-                            doUtils.downloadUrl(url, type="POST")
-                            self.logMsg("Mark as watched for itemid: %s" % itemid, 1)
-                        else:
-                            doUtils.downloadUrl(url, type="DELETE")
-                            self.logMsg("Mark as unwatched for itemid: %s" % itemid, 1)
-                finally:
-                    embycursor.close()
-
-
-        elif method == "VideoLibrary.OnRemove":
-
-            try:
-                kodiid = data['id']
-                type = data['type']
-            except (KeyError, TypeError):
-                self.logMsg("Item is invalid for emby deletion.", 1)
-            else:
-                # Send the delete action to the server.
-                offerDelete = False
-
-                if type == "episode" and utils.settings('deleteTV') == "true":
-                    offerDelete = True
-                elif type == "movie" and utils.settings('deleteMovies') == "true":
-                    offerDelete = True
-
-                if utils.settings('offerDelete') != "true":
-                    # Delete could be disabled, even if the subsetting is enabled.
-                    offerDelete = False
-
-                if offerDelete:
-                    embyconn = utils.kodiSQL('emby')
-                    embycursor = embyconn.cursor()
-                    emby_db = embydb.Embydb_Functions(embycursor)
-                    emby_dbitem = emby_db.getItem_byKodiId(kodiid, type)
-                    try:
-                        itemid = emby_dbitem[0]
-                    except TypeError:
-                        self.logMsg("Could not find itemid in emby database.", 1)
-                    else:
-                        if utils.settings('skipConfirmDelete') != "true":
-                            resp = xbmcgui.Dialog().yesno(
-                                                    heading="Confirm delete",
-                                                    line1="Delete file on Emby Server?")
-                            if not resp:
-                                self.logMsg("User skipped deletion.", 1)
-                                embycursor.close()
-                                return
-                        url = "{server}/emby/Items/%s?format=json" % itemid
-                        self.logMsg("Deleting request: %s" % itemid)
-                        doUtils.downloadUrl(url, type="DELETE")
-                    finally:
-                        embycursor.close()
-
-
-        elif method == "System.OnWake":
-            # Allow network to wake up
-            xbmc.sleep(10000)
-            utils.window('emby_onWake', value="true")
+            if jsondata:
+                if jsondata.get("type") == "episode" or "movie":
+                    url='{server}/mediabrowser/Items?Ids=' + id + '&format=json'
+                    #This is a check to see if the item exists on the server, if it doesn't it may have already been deleted by another client
+                    result = DownloadUtils().downloadUrl(url)
+                    item = result.get("Items")[0]
+                    if data:
+                        return_value = xbmcgui.Dialog().yesno("Confirm Delete", "Delete file on Emby Server?")
+                        if return_value:
+                            url='{server}/mediabrowser/Items/' + id
+                            xbmc.log('Deleting via URL: ' + url)
+                            DownloadUtils().downloadUrl(url, type="DELETE")
 
         elif method == "Playlist.OnClear":
-            utils.window('emby_customPlaylist', clear=True, windowid=10101)
-            #xbmcgui.Window(10101).clearProperties()
-            self.logMsg("Clear playlist properties.")
\ No newline at end of file
+            self.logMsg("Clear playback properties.", 2)
+            utils.window('propertiesPlayback', clear=True)
+                            
+    def clearProperty(self, type, id):
+        # The sleep is necessary since VideoLibrary.OnUpdate
+        # triggers 4 times in a row.
+        xbmc.sleep(100)
+        utils.window('Played%s%s' % (type,id), clear=True)
+            
+        # Clear the widget cache
+        utils.window('clearwidgetcache', value="clear")
\ No newline at end of file
diff --git a/resources/lib/LibrarySync.py b/resources/lib/LibrarySync.py
index d6d15d8e..f50c88e2 100644
--- a/resources/lib/LibrarySync.py
+++ b/resources/lib/LibrarySync.py
@@ -1,181 +1,1023 @@
-# -*- coding: utf-8 -*-
-
-##################################################################################################
-
-import sqlite3
-import threading
-from datetime import datetime, timedelta, time
+#################################################################################################
+# LibrarySync
+#################################################################################################
 
 import xbmc
 import xbmcgui
+import xbmcaddon
 import xbmcvfs
+import json
+import sqlite3
+import inspect
+import threading
+import urllib
+from datetime import datetime, timedelta, time
+from itertools import chain
+import urllib2
+import os
 
-import api
-import utils
-import clientinfo
-import downloadutils
-import itemtypes
-import embydb_functions as embydb
-import kodidb_functions as kodidb
-import read_embyserver as embyserver
-import userclient
-import videonodes
+import KodiMonitor
+from API import API
+import Utils as utils
+from ClientInformation import ClientInformation
+from DownloadUtils import DownloadUtils
+from ReadEmbyDB import ReadEmbyDB
+from ReadKodiDB import ReadKodiDB
+from WriteKodiVideoDB import WriteKodiVideoDB
+from WriteKodiMusicDB import WriteKodiMusicDB
+from VideoNodes import VideoNodes
 
-##################################################################################################
+addondir = xbmc.translatePath(xbmcaddon.Addon(id='plugin.video.emby').getAddonInfo('profile'))
+dataPath = os.path.join(addondir,"library")
+movieLibrary = os.path.join(dataPath,'movies')
+tvLibrary = os.path.join(dataPath,'tvshows')
 
+WINDOW = xbmcgui.Window( 10000 )
 
 class LibrarySync(threading.Thread):
 
     _shared_state = {}
 
-    stop_thread = False
-    suspend_thread = False
+    KodiMonitor = KodiMonitor.Kodi_Monitor()
+    clientInfo = ClientInformation()
+
+    addonName = clientInfo.getAddonName()
 
-    # Track websocketclient updates
-    addedItems = []
     updateItems = []
     userdataItems = []
     removeItems = []
-    forceLibraryUpdate = False
-    refresh_views = False
+    forceUpdate = False
 
-
-    def __init__(self):
+    def __init__(self, *args):
 
         self.__dict__ = self._shared_state
-        self.monitor = xbmc.Monitor()
-
-        self.clientInfo = clientinfo.ClientInfo()
-        self.addonName = self.clientInfo.getAddonName()
-        self.doUtils = downloadutils.DownloadUtils()
-        self.user = userclient.UserClient()
-        self.emby = embyserver.Read_EmbyServer()
-        self.vnodes = videonodes.VideoNodes()
-
-        threading.Thread.__init__(self)
+        threading.Thread.__init__(self, *args)
 
     def logMsg(self, msg, lvl=1):
 
         className = self.__class__.__name__
-        utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
-
-
-    def progressDialog(self, title, forced=False):
-
-        dialog = None
-
-        if utils.settings('dbSyncIndicator') == "true" or forced:
-            dialog = xbmcgui.DialogProgressBG()
-            dialog.create("Emby for Kodi", title)
-            self.logMsg("Show progress dialog: %s" % title, 2)
-
-        return dialog
-
-    def startSync(self):
-        # Run at start up - optional to use the server plugin
-        if utils.settings('SyncInstallRunDone') == "true":
-            
-            # Validate views
-            self.refreshViews()
-            completed = False
-            # Verify if server plugin is installed.
-            if utils.settings('serverSync') == "true":
-                # Try to use fast start up
-                url = "{server}/emby/Plugins?format=json"
-                result = self.doUtils.downloadUrl(url)
-
-                for plugin in result:
-                    if plugin['Name'] == "Emby.Kodi Sync Queue":
-                        self.logMsg("Found server plugin.", 2)
-                        completed = self.fastSync()
-            
-            if not completed:
-                # Fast sync failed or server plugin is not found
-                completed = self.fullSync(manualrun=True)
-        else:
-            # Install sync is not completed
-            completed = self.fullSync()
+        utils.logMsg("%s %s" % (self.addonName, className), msg, int(lvl))
         
-        return completed
+    def FullLibrarySync(self,manualRun=False):
+        
+        startupDone = WINDOW.getProperty("startup") == "done"
+        syncInstallRunDone = utils.settings("SyncInstallRunDone") == "true"
+        performMusicSync = utils.settings("enableMusicSync") == "true"
+        dbSyncIndication = utils.settings("dbSyncIndication") == "true"
 
-    def fastSync(self):
+        ### BUILD VIDEO NODES LISTING ###
+        VideoNodes().buildVideoNodesListing()
+        ### CREATE SOURCES ###
+        if utils.settings("Sources") != "true":
+            # Only create sources once
+            self.logMsg("Sources.xml created.", 0)
+            utils.createSources()
+            utils.settings("Sources", "true")  
+        
+        # just do a incremental sync if that is what is required
+        if(utils.settings("useIncSync") == "true" and utils.settings("SyncInstallRunDone") == "true") and manualRun == False:
+            utils.logMsg("Sync Database", "Using incremental sync instead of full sync useIncSync=True)", 0)
+            
+            du = DownloadUtils()
+            
+            lastSync = utils.settings("LastIncrenetalSync")
+            if(lastSync == None or len(lastSync) == 0):
+                lastSync = "2010-01-01T00:00:00Z"
+            utils.logMsg("Sync Database", "Incremental Sync Setting Last Run Time Loaded : " + lastSync, 0)
 
-        lastSync = utils.settings('LastIncrementalSync')
-        if not lastSync:
-            lastSync = "2010-01-01T00:00:00Z"
-        self.logMsg("Last sync run: %s" % lastSync, 1)
-
-        url = "{server}/emby/Emby.Kodi.SyncQueue/{UserId}/GetItems?format=json"
-        params = {'LastUpdateDT': lastSync}
-        result = self.doUtils.downloadUrl(url, parameters=params)
+            lastSync = urllib2.quote(lastSync)
+            
+            url = "{server}/Emby.Kodi.SyncQueue/{UserId}/GetItems?LastUpdateDT=" + lastSync + "&format=json"
+            utils.logMsg("Sync Database", "Incremental Sync Get Items URL : " + url, 0)
+            
+            try:
+                results = du.downloadUrl(url)
+                changedItems = results["ItemsUpdated"] + results["ItemsAdded"]
+                removedItems = results["ItemsRemoved"]
+                userChanges = results["UserDataChanged"]                
+            except:
+                utils.logMsg("Sync Database", "Incremental Sync Get Changes Failed", 0)
+                pass
+            else:
+                maxItems = int(utils.settings("incSyncMaxItems"))
+                utils.logMsg("Sync Database", "Incremental Sync Changes : " + str(results), 0)
+                if(len(changedItems) < maxItems and len(removedItems) < maxItems and len(userChanges) < maxItems):
+                
+                    WINDOW.setProperty("startup", "done")
+                    
+                    LibrarySync().remove_items(removedItems)
+                    LibrarySync().update_items(changedItems)
+                    LibrarySync().user_data_update(userChanges)
+                    
+                    return True
+                else:
+                    utils.logMsg("Sync Database", "Too Many For Incremental Sync (" + str(maxItems) + "), changedItems" + str(len(changedItems)) + " removedItems:" + str(len(removedItems)) + " userChanges:" + str(len(userChanges)), 0)
+        
+        #set some variable to check if this is the first run
+        WINDOW.setProperty("SyncDatabaseRunning", "true")     
+        
+        #show the progress dialog
+        pDialog = None
+        if (syncInstallRunDone == False or dbSyncIndication or manualRun):
+            pDialog = xbmcgui.DialogProgressBG()
+            pDialog.create('Emby for Kodi', 'Performing full sync')
+        
+        if(WINDOW.getProperty("SyncDatabaseShouldStop") ==  "true"):
+            utils.logMsg("Sync Database", "Can not start SyncDatabaseShouldStop=True", 0)
+            return True
 
         try:
-            processlist = {
-                
-                'added': result['ItemsAdded'],
-                'update': result['ItemsUpdated'],
-                'userdata': result['UserDataChanged'],
-                'remove': result['ItemsRemoved']
-            }
+            completed = True
+                        
+            ### PROCESS VIDEO LIBRARY ###
+            
+            #create the sql connection to video db
+            connection = utils.KodiSQL("video")
+            cursor = connection.cursor()
+            
+            #Add the special emby table
+            cursor.execute("CREATE TABLE IF NOT EXISTS emby(emby_id TEXT, kodi_id INTEGER, media_type TEXT, checksum TEXT, parent_id INTEGER, kodi_file_id INTEGER)")
+            try:
+                cursor.execute("ALTER TABLE emby ADD COLUMN kodi_file_id INTEGER")
+            except: pass
+            self.dbCommit(connection)
+            
+            # sync movies
+            self.MoviesFullSync(connection,cursor,pDialog)
+            
+            if (self.ShouldStop()):
+                return False
+            
+            #sync Tvshows and episodes
+            self.TvShowsFullSync(connection,cursor,pDialog)
+            
+            if (self.ShouldStop()):
+                return False
+                    
+            # sync musicvideos
+            self.MusicVideosFullSync(connection,cursor,pDialog)
+            
+            #close sql connection
+            cursor.close()
+            
+            ### PROCESS MUSIC LIBRARY ###
+            if performMusicSync:
+                #create the sql connection to music db
+                connection = utils.KodiSQL("music")
+                cursor = connection.cursor()
+                
+                #Add the special emby table
+                cursor.execute("CREATE TABLE IF NOT EXISTS emby(emby_id TEXT, kodi_id INTEGER, media_type TEXT, checksum TEXT, parent_id INTEGER, kodi_file_id INTEGER)")
+                try:
+                    cursor.execute("ALTER TABLE emby ADD COLUMN kodi_file_id INTEGER")
+                except: pass
+                self.dbCommit(connection)
+                
+                self.MusicFullSync(connection,cursor,pDialog)
+                cursor.close()
+            
+            # set the install done setting
+            if(syncInstallRunDone == False and completed):
+                utils.settings("SyncInstallRunDone", "true")
+                utils.settings("dbCreatedWithVersion", self.clientInfo.getVersion())    
+            
+            # Commit all DB changes at once and Force refresh the library
+            #xbmc.executebuiltin("UpdateLibrary(video)")
+            #self.updateLibrary("video")
+            #xbmc.executebuiltin("UpdateLibrary(music)")
+            
+            # set prop to show we have run for the first time
+            WINDOW.setProperty("startup", "done")
+            
+            # tell any widgets to refresh because the content has changed
+            WINDOW.setProperty("widgetreload", datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
+            
+            self.SaveLastSync()
             
-        except (KeyError, TypeError):
-            self.logMsg("Failed to retrieve latest updates using fast sync.", 1)
-            return False
-        
-        else:
-            self.logMsg("Fast sync changes: %s" % result, 1)
-            for action in processlist:
-                self.triage_items(action, processlist[action])
-
-            return True
-
-    def saveLastSync(self):
-        # Save last sync time
-        overlap = 2
-
-        url = "{server}/Emby.Kodi.SyncQueue/GetServerDateTime?format=json"
-        result = self.doUtils.downloadUrl(url)
-        try: # datetime fails when used more than once, TypeError
-            server_time = result['ServerDateTime']
-            server_time = datetime.strptime(server_time, "%Y-%m-%dT%H:%M:%SZ")
-        
-        except Exception as e:
-            # If the server plugin is not installed or an error happened.
-            self.logMsg("An exception occurred: %s" % e, 1)
-            time_now = datetime.utcnow()-timedelta(minutes=overlap)
-            lastSync = time_now.strftime('%Y-%m-%dT%H:%M:%SZ')
-            self.logMsg("New sync time: client time -%s min: %s" % (overlap, lastSync), 1)
-
-        else:
-            lastSync = (server_time - timedelta(minutes=overlap)).strftime('%Y-%m-%dT%H:%M:%SZ')
-            self.logMsg("New sync time: server time -%s min: %s" % (overlap, lastSync), 1)
-
         finally:
-            utils.settings('LastIncrementalSync', value=lastSync)
+            WINDOW.setProperty("SyncDatabaseRunning", "false")
+            utils.logMsg("Sync DB", "syncDatabase Exiting", 0)
 
-    def shouldStop(self):
-        # Checkpoint during the syncing process
-        if self.monitor.abortRequested():
-            return True
-        elif utils.window('emby_shouldStop') == "true":
-            return True
-        else: # Keep going
-            return False
+        if(pDialog != None):
+            pDialog.close()
+        
+        return True
+        
+    def SaveLastSync(self):
+        # save last sync time
+
+        du = DownloadUtils()    
+        url = "{server}/Emby.Kodi.SyncQueue/GetServerDateTime?format=json"
+            
+        try:
+            results = du.downloadUrl(url)
+            lastSync = results["ServerDateTime"]
+            self.logMsg("Sync Database, Incremental Sync Using Server Time: %s" % lastSync, 0)
+            lastSync = datetime.strptime(lastSync, "%Y-%m-%dT%H:%M:%SZ")
+            lastSync = (lastSync - timedelta(minutes=5)).strftime('%Y-%m-%dT%H:%M:%SZ')
+            self.logMsg("Sync Database, Incremental Sync Using Server Time -5 min: %s" % lastSync, 0)
+        except:
+            lastSync = (datetime.utcnow() - timedelta(minutes=5)).strftime('%Y-%m-%dT%H:%M:%SZ')
+            self.logMsg("Sync Database, Incremental Sync Using Client Time -5 min: %s" % lastSync, 0)
+            
+        self.logMsg("Sync Database, Incremental Sync Setting Last Run Time Saved: %s" % lastSync, 0)
+        utils.settings("LastIncrenetalSync", lastSync)
+
+    def MoviesFullSync(self,connection, cursor, pDialog):
+               
+        views = ReadEmbyDB().getCollections("movies")
+        
+        allKodiMovieIds = list()
+        allEmbyMovieIds = list()
+        
+        for view in views:
+            
+            allEmbyMovies = ReadEmbyDB().getMovies(view.get('id'))
+            allKodiMovies = ReadKodiDB().getKodiMovies(connection, cursor)
+            
+            for kodimovie in allKodiMovies:
+                allKodiMovieIds.append(kodimovie[1])
+            
+            title = view.get('title')
+            content = view.get('content')
+
+            if content == "mixed":
+                title = "%s - Movies" % title
+            
+            for kodimovie in allKodiMovies:
+                allKodiMovieIds.append(kodimovie[1])
+            
+            total = len(allEmbyMovies) + 1
+            count = 1
+            
+            #### PROCESS ADDS AND UPDATES ###
+            for item in allEmbyMovies:
+                
+                if (self.ShouldStop()):
+                    return False
+                
+                if not item.get('IsFolder'):                    
+                    allEmbyMovieIds.append(item["Id"])
+                    
+                    if(pDialog != None):
+                        progressTitle = "Processing " + view.get('title') + " (" + str(count) + " of " + str(total) + ")"
+                        percentage = int(((float(count) / float(total)) * 100))
+                        pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle)
+                        count += 1        
+                    
+                    kodiMovie = None
+                    for kodimovie in allKodiMovies:
+                        if kodimovie[1] == item["Id"]:
+                            kodiMovie = kodimovie
+                          
+                    if kodiMovie == None:
+                        WriteKodiVideoDB().addOrUpdateMovieToKodiLibrary(item["Id"],connection, cursor, title)
+                    else:
+                        if kodiMovie[2] != API().getChecksum(item):
+                            WriteKodiVideoDB().addOrUpdateMovieToKodiLibrary(item["Id"],connection, cursor, title)
+          
+          
+       
+        #### PROCESS BOX SETS #####
+        utils.logMsg("Sync Movies", "BoxSet Sync Started", 1)
+        boxsets = ReadEmbyDB().getBoxSets()
+            
+        total = len(boxsets) + 1
+        count = 1
+        for boxset in boxsets:
+            if(pDialog != None):
+                progressTitle = "Processing BoxSets" + " (" + str(count) + " of " + str(total-1) + ")"
+                percentage = int(((float(count) / float(total)) * 100))
+                pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle)
+                count += 1
+            if(self.ShouldStop()):
+                return False                
+            boxsetMovies = ReadEmbyDB().getMoviesInBoxSet(boxset["Id"])
+            WriteKodiVideoDB().addBoxsetToKodiLibrary(boxset, connection, cursor)
+                
+            WriteKodiVideoDB().removeMoviesFromBoxset(boxset, connection, cursor)
+            for boxsetMovie in boxsetMovies:
+                if(self.ShouldStop()):
+                    return False
+                WriteKodiVideoDB().updateBoxsetToKodiLibrary(boxsetMovie,boxset, connection, cursor)
+                    
+        utils.logMsg("Sync Movies", "BoxSet Sync Finished", 1)
+            
+        #### PROCESS DELETES #####
+        allEmbyMovieIds = set(allEmbyMovieIds)
+        for kodiId in allKodiMovieIds:
+            if not kodiId in allEmbyMovieIds:
+                WINDOW.setProperty(kodiId,"deleted")
+                WriteKodiVideoDB().deleteItemFromKodiLibrary(kodiId, connection, cursor)
+                
+        ### commit all changes to database ###
+        self.dbCommit(connection)
+
+    def MusicVideosFullSync(self,connection,cursor, pDialog):
+               
+        allKodiMusicvideoIds = list()
+        allEmbyMusicvideoIds = list()
+            
+        allEmbyMusicvideos = ReadEmbyDB().getMusicVideos()
+        allKodiMusicvideos = ReadKodiDB().getKodiMusicVideos(connection, cursor)
+        
+        for kodivideo in allKodiMusicvideos:
+            allKodiMusicvideoIds.append(kodivideo[1])
+        
+        total = len(allEmbyMusicvideos) + 1
+        count = 1
+        
+        #### PROCESS ADDS AND UPDATES ###
+        for item in allEmbyMusicvideos:
+            
+            if (self.ShouldStop()):
+                return False
+            
+            if not item.get('IsFolder'):                    
+                allEmbyMusicvideoIds.append(item["Id"])
+                
+                if(pDialog != None):
+                    progressTitle = "Processing MusicVideos (" + str(count) + " of " + str(total) + ")"
+                    percentage = int(((float(count) / float(total)) * 100))
+                    pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle)
+                    count += 1        
+                
+                kodiVideo = None
+                for kodivideo in allKodiMusicvideos:
+                    if kodivideo[1] == item["Id"]:
+                        kodiVideo = kodivideo
+                      
+                if kodiVideo == None:
+                    WriteKodiVideoDB().addOrUpdateMusicVideoToKodiLibrary(item["Id"],connection, cursor)
+                else:
+                    if kodiVideo[2] != API().getChecksum(item):
+                        WriteKodiVideoDB().addOrUpdateMusicVideoToKodiLibrary(item["Id"],connection, cursor)
+            
+        #### PROCESS DELETES #####
+        allEmbyMusicvideoIds = set(allEmbyMusicvideoIds)
+        for kodiId in allKodiMusicvideoIds:
+            if not kodiId in allEmbyMusicvideoIds:
+                WINDOW.setProperty(kodiId,"deleted")
+                WriteKodiVideoDB().deleteItemFromKodiLibrary(kodiId, connection, cursor)
+                
+        ### commit all changes to database ###
+        self.dbCommit(connection)
+    
+    def TvShowsFullSync(self,connection,cursor,pDialog):
+               
+        views = ReadEmbyDB().getCollections("tvshows")
+        
+        allKodiTvShowIds = list()
+        allEmbyTvShowIds = list()
+                
+        for view in views:
+            
+            allEmbyTvShows = ReadEmbyDB().getTvShows(view.get('id'))
+            allKodiTvShows = ReadKodiDB().getKodiTvShows(connection, cursor)
+            
+            title = view.get('title')
+            content = view.get('content')
+
+            if content == "mixed":
+                title = "%s - TV Shows" % title
+            
+            total = len(allEmbyTvShows) + 1
+            count = 1
+            
+            for kodishow in allKodiTvShows:
+                allKodiTvShowIds.append(kodishow[1])
+            
+            #### TVSHOW: PROCESS ADDS AND UPDATES ###
+            for item in allEmbyTvShows:
+                
+                if (self.ShouldStop()):
+                    return False
+                
+                if(pDialog != None):
+                    progressTitle = "Processing " + view.get('title') + " (" + str(count) + " of " + str(total) + ")"
+                    percentage = int(((float(count) / float(total)) * 100))
+                    pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle)
+                    count += 1                   
+
+                if utils.settings('syncEmptyShows') == "true" or (item.get('IsFolder') and item.get('RecursiveItemCount') != 0):
+                    allEmbyTvShowIds.append(item["Id"])
+                    
+                    #build a list with all Id's and get the existing entry (if exists) in Kodi DB
+                    kodiShow = None
+                    for kodishow in allKodiTvShows:
+                        if kodishow[1] == item["Id"]:
+                            kodiShow = kodishow
+                          
+                    if kodiShow == None:
+                        # Tv show doesn't exist in Kodi yet so proceed and add it
+                        WriteKodiVideoDB().addOrUpdateTvShowToKodiLibrary(item["Id"],connection, cursor, title)
+                    else:
+                        # If there are changes to the item, perform a full sync of the item
+                        if kodiShow[2] != API().getChecksum(item):
+                            WriteKodiVideoDB().addOrUpdateTvShowToKodiLibrary(item["Id"],connection, cursor, title)
+                            
+                    #### PROCESS EPISODES ######
+                    self.EpisodesFullSync(connection,cursor,item["Id"])
+            
+        #### TVSHOW: PROCESS DELETES #####
+        allEmbyTvShowIds = set(allEmbyTvShowIds)
+        for kodiId in allKodiTvShowIds:
+            if not kodiId in allEmbyTvShowIds:
+                WINDOW.setProperty(kodiId,"deleted")
+                WriteKodiVideoDB().deleteItemFromKodiLibrary(kodiId, connection, cursor)
+                
+        ### commit all changes to database ###
+        self.dbCommit(connection)
+         
+    def EpisodesFullSync(self,connection,cursor,showId):
+        
+        WINDOW = xbmcgui.Window( 10000 )
+        
+        allKodiEpisodeIds = list()
+        allEmbyEpisodeIds = list()
+        
+        # Get the kodi parent id
+        cursor.execute("SELECT kodi_id FROM emby WHERE emby_id=?",(showId,))
+        try:
+            kodiShowId = cursor.fetchone()[0]
+        except:
+            self.logMsg("Unable to find show itemId:%s" % showId, 1)
+            return
+        
+        allEmbyEpisodes = ReadEmbyDB().getEpisodes(showId)
+        allKodiEpisodes = ReadKodiDB().getKodiEpisodes(connection, cursor, kodiShowId)
+        
+        for kodiepisode in allKodiEpisodes:
+            allKodiEpisodeIds.append(kodiepisode[1])
+
+        #### EPISODES: PROCESS ADDS AND UPDATES ###
+        for item in allEmbyEpisodes:
+            
+            if (self.ShouldStop()):
+                    return False    
+            
+            allEmbyEpisodeIds.append(item["Id"])
+            
+            #get the existing entry (if exists) in Kodi DB
+            kodiEpisode = None
+            for kodiepisode in allKodiEpisodes:
+                if kodiepisode[1] == item["Id"]:
+                    kodiEpisode = kodiepisode
+                  
+            if kodiEpisode == None:
+                # Episode doesn't exist in Kodi yet so proceed and add it
+                WriteKodiVideoDB().addOrUpdateEpisodeToKodiLibrary(item["Id"], kodiShowId, connection, cursor)
+            else:
+                # If there are changes to the item, perform a full sync of the item
+                if kodiEpisode[2] != API().getChecksum(item):
+                    WriteKodiVideoDB().addOrUpdateEpisodeToKodiLibrary(item["Id"], kodiShowId, connection, cursor)
+        
+        #### EPISODES: PROCESS DELETES #####
+        allEmbyEpisodeIds = set(allEmbyEpisodeIds)
+        for kodiId in allKodiEpisodeIds:
+            if (not kodiId in allEmbyEpisodeIds):
+                WINDOW.setProperty(kodiId,"deleted")
+                WriteKodiVideoDB().deleteItemFromKodiLibrary(kodiId, connection, cursor)
+                
+    def MusicFullSync(self, connection,cursor, pDialog):
+
+        self.ProcessMusicArtists(connection,cursor,pDialog)
+        self.dbCommit(connection)
+        self.ProcessMusicAlbums(connection,cursor,pDialog)
+        self.dbCommit(connection)
+        self.ProcessMusicSongs(connection,cursor,pDialog)
+        
+        ### commit all changes to database ###
+        self.dbCommit(connection)
+    
+    def ProcessMusicSongs(self,connection,cursor,pDialog):
+               
+        allKodiSongIds = list()
+        allEmbySongIds = list()
+        
+        allEmbySongs = ReadEmbyDB().getMusicSongsTotal()
+        allKodiSongs = ReadKodiDB().getKodiMusicSongs(connection, cursor)
+        
+        for kodisong in allKodiSongs:
+            allKodiSongIds.append(kodisong[1])
+            
+        total = len(allEmbySongs) + 1
+        count = 1    
+        
+        #### PROCESS SONGS ADDS AND UPDATES ###
+        for item in allEmbySongs:
+            
+            if (self.ShouldStop()):
+                return False
+                             
+            allEmbySongIds.append(item["Id"])
+            
+            if(pDialog != None):
+                progressTitle = "Processing Music Songs (" + str(count) + " of " + str(total) + ")"
+                percentage = int(((float(count) / float(total)) * 100))
+                pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle)
+                count += 1        
+            
+            kodiSong = None
+            for kodisong in allKodiSongs:
+                if kodisong[1] == item["Id"]:
+                    kodiSong = kodisong
+                  
+            if kodiSong == None:
+                WriteKodiMusicDB().addOrUpdateSongToKodiLibrary(item,connection, cursor)
+            else:
+                if kodiSong[2] != API().getChecksum(item):
+                    WriteKodiMusicDB().addOrUpdateSongToKodiLibrary(item,connection, cursor)
+        
+        #### PROCESS DELETES #####
+        allEmbySongIds = set(allEmbySongIds)
+        for kodiId in allKodiSongIds:
+            if not kodiId in allEmbySongIds:
+                WINDOW.setProperty(kodiId,"deleted")
+                WriteKodiMusicDB().deleteItemFromKodiLibrary(kodiId, connection, cursor)
+        
+    def ProcessMusicArtists(self,connection,cursor,pDialog):
+               
+        allKodiArtistIds = list()
+        allEmbyArtistIds = list()
+        
+        allEmbyArtists = ReadEmbyDB().getMusicArtistsTotal()
+        allKodiArtists = ReadKodiDB().getKodiMusicArtists(connection, cursor)
+        
+        for kodiartist in allKodiArtists:
+            allKodiArtistIds.append(kodiartist[1])
+            
+        total = len(allEmbyArtists) + 1
+        count = 1    
+        
+        #### PROCESS ARTIST ADDS AND UPDATES ###
+        for item in allEmbyArtists:
+            
+            if (self.ShouldStop()):
+                return False
+                             
+            allEmbyArtistIds.append(item["Id"])
+            
+            if(pDialog != None):
+                progressTitle = "Processing Music Artists (" + str(count) + " of " + str(total) + ")"
+                percentage = int(((float(count) / float(total)) * 100))
+                pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle)
+                count += 1        
+            
+            kodiArtist = None
+            for kodiartist in allKodiArtists:
+                if kodiartist[1] == item["Id"]:
+                    kodiArtist = kodiartist
+                  
+            if kodiArtist == None:
+                WriteKodiMusicDB().addOrUpdateArtistToKodiLibrary(item,connection, cursor)
+            else:
+                if kodiArtist[2] != API().getChecksum(item):
+                    WriteKodiMusicDB().addOrUpdateArtistToKodiLibrary(item,connection, cursor)
+        
+        #### PROCESS DELETES #####
+        allEmbyArtistIds = set(allEmbyArtistIds)
+        for kodiId in allKodiArtistIds:
+            if not kodiId in allEmbyArtistIds:
+                WINDOW.setProperty(kodiId,"deleted")
+                WriteKodiMusicDB().deleteItemFromKodiLibrary(kodiId, connection, cursor)
+    
+    def ProcessMusicAlbums(self,connection,cursor,pDialog):
+               
+        allKodiAlbumIds = list()
+        allEmbyAlbumIds = list()
+        
+        allEmbyAlbums = ReadEmbyDB().getMusicAlbumsTotal()
+        allKodiAlbums = ReadKodiDB().getKodiMusicAlbums(connection, cursor)
+        
+        for kodialbum in allKodiAlbums:
+            allKodiAlbumIds.append(kodialbum[1])
+            
+        total = len(allEmbyAlbums) + 1
+        count = 1    
+        
+        #### PROCESS SONGS ADDS AND UPDATES ###
+        for item in allEmbyAlbums:
+            
+            if (self.ShouldStop()):
+                return False
+                             
+            allEmbyAlbumIds.append(item["Id"])
+            
+            if(pDialog != None):
+                progressTitle = "Processing Music Albums (" + str(count) + " of " + str(total) + ")"
+                percentage = int(((float(count) / float(total)) * 100))
+                pDialog.update(percentage, "Emby for Kodi - Running Sync", progressTitle)
+                count += 1        
+            
+            kodiAlbum = None
+            for kodialbum in allKodiAlbums:
+                if kodialbum[1] == item["Id"]:
+                    kodiAlbum = kodialbum
+                  
+            if kodiAlbum == None:
+                WriteKodiMusicDB().addOrUpdateAlbumToKodiLibrary(item,connection, cursor)
+            else:
+                if kodiAlbum[2] != API().getChecksum(item):
+                    WriteKodiMusicDB().addOrUpdateAlbumToKodiLibrary(item,connection, cursor)
+        
+        #### PROCESS DELETES #####
+        allEmbyAlbumIds = set(allEmbyAlbumIds)
+        for kodiId in allKodiAlbumIds:
+            if not kodiId in allEmbyAlbumIds:
+                WINDOW.setProperty(kodiId,"deleted")
+                WriteKodiMusicDB().deleteItemFromKodiLibrary(kodiId, connection, cursor)
+    
+    def IncrementalSync(self, itemList):
+        
+        startupDone = WINDOW.getProperty("startup") == "done"
+        
+        #only perform incremental scan when full scan is completed 
+        if startupDone:
+        
+            #this will only perform sync for items received by the websocket
+            dbSyncIndication = utils.settings("dbSyncIndication") == "true"
+            performMusicSync = utils.settings("enableMusicSync") == "true"
+            WINDOW.setProperty("SyncDatabaseRunning", "true")
+            
+            #show the progress dialog               
+            pDialog = None
+            if (dbSyncIndication and xbmc.Player().isPlaying() == False):
+                pDialog = xbmcgui.DialogProgressBG()
+                pDialog.create('Emby for Kodi', 'Incremental Sync')
+                self.logMsg("Doing LibraryChanged : Show Progress IncrementalSync()", 0);
+            
+            connection = utils.KodiSQL("video")
+            cursor = connection.cursor()
+            
+            try:
+                #### PROCESS MOVIES ####
+                views = ReadEmbyDB().getCollections("movies")
+                for view in views:
+                    allEmbyMovies = ReadEmbyDB().getMovies(view.get('id'), itemList)
+                    count = 1
+                    total = len(allEmbyMovies) + 1
+                    for item in allEmbyMovies:
+                        if(pDialog != None):
+                            progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
+                            percentage = int(((float(count) / float(total)) * 100))
+                            pDialog.update(percentage, "Emby for Kodi - Incremental Sync Movies", progressTitle)
+                            count = count + 1
+                        if not item.get('IsFolder'):
+                            WriteKodiVideoDB().addOrUpdateMovieToKodiLibrary(item["Id"],connection, cursor, view.get('title'))
+                            
+                #### PROCESS BOX SETS #####
+                boxsets = ReadEmbyDB().getBoxSets()
+                count = 1
+                total = len(boxsets) + 1
+                for boxset in boxsets:
+                    if(boxset["Id"] in itemList):
+                        utils.logMsg("IncrementalSync", "Updating box Set : " + str(boxset["Name"]), 1)
+                        boxsetMovies = ReadEmbyDB().getMoviesInBoxSet(boxset["Id"])
+                        WriteKodiVideoDB().addBoxsetToKodiLibrary(boxset, connection, cursor)
+                        if(pDialog != None):
+                            progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
+                            percentage = int(((float(count) / float(total)) * 100))
+                            pDialog.update(percentage, "Emby for Kodi - Incremental Sync BoxSet", progressTitle)
+                            count = count + 1
+                        WriteKodiVideoDB().removeMoviesFromBoxset(boxset, connection, cursor)
+                        for boxsetMovie in boxsetMovies:
+                            WriteKodiVideoDB().updateBoxsetToKodiLibrary(boxsetMovie, boxset, connection, cursor)      
+                    else:
+                        utils.logMsg("IncrementalSync", "Skipping Box Set : " + boxset["Name"], 1)
+                        
+                #### PROCESS TV SHOWS ####
+                views = ReadEmbyDB().getCollections("tvshows")              
+                for view in views:
+                    allEmbyTvShows = ReadEmbyDB().getTvShows(view.get('id'),itemList)
+                    count = 1
+                    total = len(allEmbyTvShows) + 1
+                    for item in allEmbyTvShows:
+                        if(pDialog != None):
+                            progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
+                            percentage = int(((float(count) / float(total)) * 100))
+                            pDialog.update(percentage, "Emby for Kodi - Incremental Sync Tv", progressTitle)
+                            count = count + 1                    
+                        if utils.settings('syncEmptyShows') == "true" or (item.get('IsFolder') and item.get('RecursiveItemCount') != 0):
+                            kodiId = WriteKodiVideoDB().addOrUpdateTvShowToKodiLibrary(item["Id"],connection, cursor, view.get('title'))
+                
+                
+                #### PROCESS OTHERS BY THE ITEMLIST ######
+                count = 1
+                total = len(itemList) + 1
+                for item in itemList:
+                        
+                    if(pDialog != None):
+                        progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
+                        percentage = int(((float(count) / float(total)) * 100))
+                        pDialog.update(percentage, "Emby for Kodi - Incremental Sync Items", progressTitle)
+                        count = count + 1                           
+                        
+                    MBitem = ReadEmbyDB().getItem(item)
+                    itemType = MBitem.get('Type', "")
+
+                    #### PROCESS EPISODES ######
+                    if "Episode" in itemType:
+
+                        #get the tv show
+                        cursor.execute("SELECT kodi_id FROM emby WHERE media_type='tvshow' AND emby_id=?", (MBitem.get("SeriesId"),))
+                        result = cursor.fetchone()
+                        if result:
+                            kodi_show_id = result[0]
+                        else:
+                            kodi_show_id = None
+
+                        if kodi_show_id:
+                            WriteKodiVideoDB().addOrUpdateEpisodeToKodiLibrary(MBitem["Id"], kodi_show_id, connection, cursor)
+                        else:
+                            #tv show doesn't exist
+                            #perform full tvshow sync instead so both the show and episodes get added
+                            self.TvShowsFullSync(connection,cursor,None)
+
+                    elif "Season" in itemType:
+
+                        #get the tv show
+                        cursor.execute("SELECT kodi_id FROM emby WHERE media_type='tvshow' AND emby_id=?", (MBitem.get("SeriesId"),))
+                        result = cursor.fetchone()
+                        if result:
+                            kodi_show_id = result[0]
+                            # update season
+                            WriteKodiVideoDB().updateSeasons(MBitem["SeriesId"], kodi_show_id, connection, cursor)
+                    
+                    #### PROCESS BOXSETS ######
+                    elif "BoxSet" in itemType:
+                        boxsetMovies = ReadEmbyDB().getMoviesInBoxSet(boxset["Id"])
+                        WriteKodiVideoDB().addBoxsetToKodiLibrary(boxset,connection, cursor)
+                        
+                        for boxsetMovie in boxsetMovies:
+                            WriteKodiVideoDB().updateBoxsetToKodiLibrary(boxsetMovie,boxset, connection, cursor)
+
+                    #### PROCESS MUSICVIDEOS ####
+                    elif "MusicVideo" in itemType:
+                        if not MBitem.get('IsFolder'):                    
+                            WriteKodiVideoDB().addOrUpdateMusicVideoToKodiLibrary(MBitem["Id"],connection, cursor)
+                        
+                ### commit all changes to database ###
+                self.dbCommit(connection)
+                cursor.close()
+
+                ### PROCESS MUSIC LIBRARY ###
+                if performMusicSync:
+                    connection = utils.KodiSQL("music")
+                    cursor = connection.cursor()
+                    for item in itemList:
+                        MBitem = ReadEmbyDB().getItem(item)
+                        itemType = MBitem.get('Type', "")
+                        
+                        if "MusicArtist" in itemType:
+                            WriteKodiMusicDB().addOrUpdateArtistToKodiLibrary(MBitem, connection, cursor)
+                        if "MusicAlbum" in itemType:
+                            WriteKodiMusicDB().addOrUpdateAlbumToKodiLibrary(MBitem, connection, cursor)
+                        if "Audio" in itemType:
+                            WriteKodiMusicDB().addOrUpdateSongToKodiLibrary(MBitem, connection, cursor)    
+                    self.dbCommit(connection)
+                    cursor.close()
+
+            finally:
+                if(pDialog != None):
+                    pDialog.close()
+
+                #self.updateLibrary("video")
+                WINDOW.setProperty("SyncDatabaseRunning", "false")
+                # tell any widgets to refresh because the content has changed
+                WINDOW.setProperty("widgetreload", datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
+
+    def removefromDB(self, itemList, deleteEmbyItem = False):
+    
+        dbSyncIndication = utils.settings("dbSyncIndication") == "true"
+    
+        #show the progress dialog               
+        pDialog = None
+        if (dbSyncIndication and xbmc.Player().isPlaying() == False):
+            pDialog = xbmcgui.DialogProgressBG()
+            pDialog.create('Emby for Kodi', 'Incremental Sync')    
+            self.logMsg("Doing LibraryChanged : Show Progress removefromDB()", 0);
+            
+        # Delete from Kodi before Emby
+        # To be able to get mediaType
+        doUtils = DownloadUtils()
+        video = {}
+        music = []
+        
+        # Database connection to myVideosXX.db
+        connectionvideo = utils.KodiSQL()
+        cursorvideo = connectionvideo.cursor()
+        # Database connection to myMusicXX.db
+        connectionmusic = utils.KodiSQL("music")
+        cursormusic = connectionmusic.cursor()
+
+        count = 1
+        total = len(itemList) + 1          
+        for item in itemList:
+        
+            if(pDialog != None):
+                progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
+                percentage = int(((float(count) / float(total)) * 100))
+                pDialog.update(percentage, "Emby for Kodi - Incremental Sync Delete ", progressTitle)
+                count = count + 1   
+                
+            # Sort by type for database deletion
+            try: # Search video database
+                self.logMsg("Check video database.", 1)
+                cursorvideo.execute("SELECT media_type FROM emby WHERE emby_id = ?", (item,))
+                mediatype = cursorvideo.fetchone()[0]
+                video[item] = mediatype
+                #video.append(itemtype)
+            except:
+                self.logMsg("Check music database.", 1)
+                try: # Search music database
+                    cursormusic.execute("SELECT media_type FROM emby WHERE emby_id = ?", (item,))
+                    cursormusic.fetchone()[0]
+                    music.append(item)
+                except: self.logMsg("Item %s is not found in Kodi database." % item, 1)
+
+        if len(video) > 0:
+            connection = connectionvideo
+            cursor = cursorvideo
+            # Process video library
+            count = 1
+            total = len(video) + 1                
+            for item in video:
+
+                if(pDialog != None):
+                    progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
+                    percentage = int(((float(count) / float(total)) * 100))
+                    pDialog.update(percentage, "Emby for Kodi - Incremental Sync Delete ", progressTitle)
+                    count = count + 1   
+                
+                type = video[item]
+                self.logMsg("Doing LibraryChanged: Items Removed: Calling deleteItemFromKodiLibrary: %s" % item, 1)
+
+                if "episode" in type:
+                    # Get the TV Show Id for reference later
+                    showId = ReadKodiDB().getShowIdByEmbyId(item, connection, cursor)
+                    self.logMsg("ShowId: %s" % showId, 1)
+                WriteKodiVideoDB().deleteItemFromKodiLibrary(item, connection, cursor)
+                # Verification
+                if "episode" in type:
+                    showTotalCount = ReadKodiDB().getShowTotalCount(showId, connection, cursor)
+                    self.logMsg("ShowTotalCount: %s" % showTotalCount, 1)
+                    # If there are no episodes left
+                    if showTotalCount == 0 or showTotalCount == None:
+                        # Delete show
+                        embyId = ReadKodiDB().getEmbyIdByKodiId(showId, "tvshow", connection, cursor)
+                        self.logMsg("Message: Doing LibraryChanged: Deleting show: %s" % embyId, 1)
+                        WriteKodiVideoDB().deleteItemFromKodiLibrary(embyId, connection, cursor)
+
+            self.dbCommit(connection)
+        # Close connection
+        cursorvideo.close()
+
+        if len(music) > 0:
+            connection = connectionmusic
+            cursor = cursormusic
+            #Process music library
+            if utils.settings('enableMusicSync') == "true":
+
+                for item in music:
+                    self.logMsg("Message : Doing LibraryChanged : Items Removed : Calling deleteItemFromKodiLibrary (musiclibrary): " + item, 0)
+                    WriteKodiMusicDB().deleteItemFromKodiLibrary(item, connection, cursor)
+
+                self.dbCommit(connection)
+        # Close connection
+        cursormusic.close()
+
+        if deleteEmbyItem:
+            for item in itemList:
+                url = "{server}/mediabrowser/Items/%s" % item
+                self.logMsg('Deleting via URL: %s' % url)
+                doUtils.downloadUrl(url, type = "DELETE")                            
+                xbmc.executebuiltin("Container.Refresh")
+
+        if(pDialog != None):
+            pDialog.close()
+              
+    def setUserdata(self, listItems):
+    
+        dbSyncIndication = utils.settings("dbSyncIndication") == "true"
+        musicenabled = utils.settings('enableMusicSync') == "true"
+    
+        #show the progress dialog               
+        pDialog = None
+        if (dbSyncIndication and xbmc.Player().isPlaying() == False):
+            pDialog = xbmcgui.DialogProgressBG()
+            pDialog.create('Emby for Kodi', 'Incremental Sync')
+            self.logMsg("Doing LibraryChanged : Show Progress setUserdata()", 0);
+
+        # We need to sort between video and music database
+        video = []
+        music = []
+        # Database connection to myVideosXX.db
+        connectionvideo = utils.KodiSQL()
+        cursorvideo = connectionvideo.cursor()
+        # Database connection to myMusicXX.db
+        connectionmusic = utils.KodiSQL('music')
+        cursormusic = connectionmusic.cursor()
+
+        count = 1
+        total = len(listItems) + 1        
+        for userdata in listItems:
+            # Sort between video and music
+            itemId = userdata['ItemId']
+                        
+            if(pDialog != None):
+                progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
+                percentage = int(((float(count) / float(total)) * 100))
+                pDialog.update(percentage, "Emby for Kodi - Incremental Sync User Data ", progressTitle)
+                count = count + 1               
+            
+            cursorvideo.execute("SELECT media_type FROM emby WHERE emby_id = ?", (itemId,))
+            try: # Search video database
+                self.logMsg("Check video database.", 2)
+                mediatype = cursorvideo.fetchone()[0]
+                video.append(userdata)
+            except:
+                if musicenabled:
+                    cursormusic.execute("SELECT media_type FROM emby WHERE emby_id = ?", (itemId,))
+                    try: # Search music database
+                        self.logMsg("Check the music database.", 2)
+                        mediatype = cursormusic.fetchone()[0]
+                        music.append(userdata)
+                    except: self.logMsg("Item %s is not found in Kodi database." % itemId, 1)
+                else:
+                    self.logMsg("Item %s is not found in Kodi database." % itemId, 1)
+
+        if len(video) > 0:
+            connection = connectionvideo
+            cursor = cursorvideo
+            # Process the userdata update for video library
+            count = 1
+            total = len(video) + 1              
+            for userdata in video:
+                if(pDialog != None):
+                    progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
+                    percentage = int(((float(count) / float(total)) * 100))
+                    pDialog.update(percentage, "Emby for Kodi - Incremental Sync User Data ", progressTitle)
+                    count = count + 1
+                WriteKodiVideoDB().updateUserdata(userdata, connection, cursor)
+
+            self.dbCommit(connection)
+            #self.updateLibrary("video")
+        # Close connection
+        cursorvideo.close()
+
+        if len(music) > 0:
+            connection = connectionmusic
+            cursor = cursormusic
+            #Process music library
+            count = 1
+            total = len(video) + 1
+            # Process the userdata update for music library
+            if musicenabled:
+                for userdata in music:
+                    if(pDialog != None):
+                        progressTitle = "Incremental Sync "+ " (" + str(count) + " of " + str(total) + ")"
+                        percentage = int(((float(count) / float(total)) * 100))
+                        pDialog.update(percentage, "Emby for Kodi - Incremental Sync User Data ", progressTitle)
+                        count = count + 1
+                    WriteKodiMusicDB().updateUserdata(userdata, connection, cursor)
+
+                self.dbCommit(connection)
+                #xbmc.executebuiltin("UpdateLibrary(music)")
+        # Close connection
+        cursormusic.close()
+        
+        if(pDialog != None):
+            pDialog.close()
+
+    def remove_items(self, itemsRemoved):
+        # websocket client
+        if(len(itemsRemoved) > 0):
+            self.logMsg("Doing LibraryChanged : Processing Deleted : " + str(itemsRemoved), 0)        
+            self.removeItems.extend(itemsRemoved)
+
+    def update_items(self, itemsToUpdate):
+        # websocket client
+        if(len(itemsToUpdate) > 0):
+            self.logMsg("Doing LibraryChanged : Processing Added and Updated : " + str(itemsToUpdate), 0)
+            self.updateItems.extend(itemsToUpdate)
+            
+    def user_data_update(self, userDataList):
+        # websocket client
+        if(len(userDataList) > 0):
+            self.logMsg("Doing LibraryChanged : Processing User Data Changed : " + str(userDataList), 0)
+            self.userdataItems.extend(userDataList)
 
     def dbCommit(self, connection):
-        # Central commit, verifies if Kodi database update is running
-        kodidb_scan = utils.window('emby_kodiScan') == "true"
+        # Central commit, will verify if Kodi database
+        kodidb_scan = utils.window('kodiScan') == "true"
 
         while kodidb_scan:
+            
+            self.logMsg("Kodi scan running. Waiting...", 1)
+            kodidb_scan = utils.window('kodiScan') == "true"
 
-            self.logMsg("Kodi scan is running. Waiting...", 1)
-            kodidb_scan = utils.window('emby_kodiScan') == "true"
-
-            if self.shouldStop():
-                self.logMsg("Commit unsuccessful. Sync terminated.", 1)
-                break
-
-            if self.monitor.waitForAbort(1):
+            if self.KodiMonitor.waitForAbort(1):
                 # Abort was requested while waiting. We should exit
                 self.logMsg("Commit unsuccessful.", 1)
                 break
@@ -183,1027 +1025,32 @@ class LibrarySync(threading.Thread):
             connection.commit()
             self.logMsg("Commit successful.", 1)
 
-    def fullSync(self, manualrun=False, repair=False):
-        # Only run once when first setting up. Can be run manually.
-        emby = self.emby
-        music_enabled = utils.settings('enableMusic') == "true"
+    def updateLibrary(self, type):
 
-        utils.window('emby_dbScan', value="true")
-        # Add sources
-        utils.sourcesXML()
+        self.logMsg("Updating %s library." % type, 1)
+        utils.window('kodiScan', value="true")
+        xbmc.executebuiltin('UpdateLibrary(%s)' % type)
 
-        embyconn = utils.kodiSQL('emby')
-        embycursor = embyconn.cursor()
-        # Create the tables for the emby database
-        # emby, view, version
-        embycursor.execute(
-            """CREATE TABLE IF NOT EXISTS emby(
-            emby_id TEXT UNIQUE, media_folder TEXT, emby_type TEXT, media_type TEXT, kodi_id INTEGER, 
-            kodi_fileid INTEGER, kodi_pathid INTEGER, parent_id INTEGER, checksum INTEGER)""")
-        embycursor.execute(
-            """CREATE TABLE IF NOT EXISTS view(
-            view_id TEXT UNIQUE, view_name TEXT, media_type TEXT, kodi_tagid INTEGER)""")
-        embycursor.execute("CREATE TABLE IF NOT EXISTS version(idVersion TEXT)")
-        embyconn.commit()
-        
-        # content sync: movies, tvshows, musicvideos, music
-        kodiconn = utils.kodiSQL('video')
-        kodicursor = kodiconn.cursor()
-
-        if manualrun:
-            message = "Manual sync"
-        elif repair:
-            message = "Repair sync"
-        else:
-            message = "Initial sync"
-        
-        pDialog = self.progressDialog("%s" % message, forced=True)
-        starttotal = datetime.now()
-
-        # Set views
-        self.maintainViews(embycursor, kodicursor)
-        embyconn.commit()
-        
-        # Sync video library
-        process = {
-
-            'movies': self.movies,
-            'musicvideos': self.musicvideos,
-            'tvshows': self.tvshows,
-            'homevideos': self.homevideos
-        }
-        for itemtype in process:
-            startTime = datetime.now()
-            completed = process[itemtype](embycursor, kodicursor, pDialog, compare=manualrun)
-            if not completed:
-                
-                utils.window('emby_dbScan', clear=True)
-                if pDialog:
-                    pDialog.close()
-
-                embycursor.close()
-                kodicursor.close()
-                return False
-            else:
-                self.dbCommit(kodiconn)
-                embyconn.commit()
-                elapsedTime = datetime.now() - startTime
-                self.logMsg(
-                    "SyncDatabase (finished %s in: %s)"
-                    % (itemtype, str(elapsedTime).split('.')[0]), 1)
-
-        # sync music
-        if music_enabled:
+    def ShouldStop(self):
             
-            musicconn = utils.kodiSQL('music')
-            musiccursor = musicconn.cursor()
-            
-            startTime = datetime.now()
-            completed = self.music(embycursor, musiccursor, pDialog, compare=manualrun)
-            if not completed:
+        if(xbmc.abortRequested):
+            return True
 
-                utils.window('emby_dbScan', clear=True)
-                if pDialog:
-                    pDialog.close()
+        if(WINDOW.getProperty("SyncDatabaseShouldStop") == "true"):
+            return True
 
-                embycursor.close()
-                musiccursor.close()
-                return False
-            else:
-                musicconn.commit()
-                embyconn.commit()
-                elapsedTime = datetime.now() - startTime
-                self.logMsg(
-                    "SyncDatabase (finished music in: %s)"
-                    % (str(elapsedTime).split('.')[0]), 1)
-            musiccursor.close()
-
-        if pDialog:
-            pDialog.close()
+        return False
         
-        embycursor.close()
-        kodicursor.close()
-        
-        utils.settings('SyncInstallRunDone', value="true")
-        utils.settings("dbCreatedWithVersion", self.clientInfo.getVersion())
-        self.saveLastSync()
-        # tell any widgets to refresh because the content has changed
-        utils.window('widgetreload', value=datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
-        xbmc.executebuiltin('UpdateLibrary(video)')
-        elapsedtotal = datetime.now() - starttotal
-
-        utils.window('emby_dbScan', clear=True)
-        xbmcgui.Dialog().notification(
-                        heading="Emby for Kodi",
-                        message="%s completed in: %s!" % 
-                                (message, str(elapsedtotal).split('.')[0]),
-                        icon="special://home/addons/plugin.video.emby/icon.png",
-                        sound=False)
-        return True
-
-
-    def refreshViews(self):
-
-        embyconn = utils.kodiSQL('emby')
-        embycursor = embyconn.cursor()
-        kodiconn = utils.kodiSQL('video')
-        kodicursor = kodiconn.cursor()
-
-        # Compare views, assign correct tags to items
-        self.maintainViews(embycursor, kodicursor)
-        
-        self.dbCommit(kodiconn)
-        kodicursor.close()
-
-        embyconn.commit()
-        embycursor.close()
-
-    def maintainViews(self, embycursor, kodicursor):
-        # Compare the views to emby
-        emby_db = embydb.Embydb_Functions(embycursor)
-        kodi_db = kodidb.Kodidb_Functions(kodicursor)
-        doUtils = self.doUtils
-        vnodes = self.vnodes
-        
-        # Get views
-        url = "{server}/emby/Users/{UserId}/Views?format=json"
-        result = doUtils.downloadUrl(url)
-        grouped_views = result['Items']
-
-        try:
-            groupedFolders = self.user.userSettings['Configuration']['GroupedFolders']
-        except TypeError:
-            url = "{server}/emby/Users/{UserId}?format=json"
-            result = doUtils.downloadUrl(url)
-            groupedFolders = result['Configuration']['GroupedFolders']
-
-        # total nodes for window properties
-        vnodes.clearProperties()
-        totalnodes = 0
-
-        # Set views for supported media type
-        mediatypes = ['movies', 'tvshows', 'musicvideos', 'homevideos', 'music']
-        for mediatype in mediatypes:
-
-            # Get media folders from server
-            folders = self.emby.getViews(mediatype, root=True)
-            for folder in folders:
-
-                folderid = folder['id']
-                foldername = folder['name']
-                viewtype = folder['type']
-                
-                if folderid in groupedFolders:
-                    # Media folders are grouped into userview
-                    for grouped_view in grouped_views:
-                        if (grouped_view['Type'] == "UserView" and 
-                            grouped_view['CollectionType'] == mediatype):
-                            # Take the name of the userview
-                            foldername = grouped_view['Name']
-                            break
-
-                # Get current media folders from emby database
-                view = emby_db.getView_byId(folderid)
-                try:
-                    current_viewname = view[0]
-                    current_viewtype = view[1]
-                    current_tagid = view[2]
-
-                except TypeError:
-                    self.logMsg("Creating viewid: %s in Emby database." % folderid, 1)
-                    tagid = kodi_db.createTag(foldername)
-                    # Create playlist for the video library
-                    if mediatype != "music":
-                        utils.playlistXSP(mediatype, foldername, viewtype)
-                        # Create the video node
-                        if mediatype != "musicvideos":
-                            vnodes.viewNode(totalnodes, foldername, mediatype, viewtype)
-                            totalnodes += 1
-                    # Add view to emby database
-                    emby_db.addView(folderid, foldername, viewtype, tagid)
-
-                else:
-                    self.logMsg(' '.join((
-
-                        "Found viewid: %s" % folderid,
-                        "viewname: %s" % current_viewname,
-                        "viewtype: %s" % current_viewtype,
-                        "tagid: %s" % current_tagid)), 2)
-
-                    # View was modified, update with latest info
-                    if current_viewname != foldername:
-                        self.logMsg("viewid: %s new viewname: %s" % (folderid, foldername), 1)
-                        tagid = kodi_db.createTag(foldername)
-                        
-                        # Update view with new info
-                        emby_db.updateView(foldername, tagid, folderid)
-
-                        if mediatype != "music":
-                            if emby_db.getView_byName(current_viewname) is None:
-                                # The tag could be a combined view. Ensure there's no other tags
-                                # with the same name before deleting playlist.
-                                utils.playlistXSP(
-                                    mediatype, current_viewname, current_viewtype, True)
-                                # Delete video node
-                                if mediatype != "musicvideos":
-                                    vnodes.viewNode(
-                                        indexnumber=totalnodes,
-                                        tagname=current_viewname,
-                                        mediatype=mediatype,
-                                        viewtype=current_viewtype,
-                                        delete=True)
-                            # Added new playlist
-                            utils.playlistXSP(mediatype, foldername, viewtype)
-                            # Add new video node
-                            if mediatype != "musicvideos":
-                                vnodes.viewNode(totalnodes, foldername, mediatype, viewtype)
-                                totalnodes += 1
-                        
-                        # Update items with new tag
-                        items = emby_db.getItem_byView(folderid)
-                        for item in items:
-                            # Remove the "s" from viewtype for tags
-                            kodi_db.updateTag(
-                                current_tagid, tagid, item[0], current_viewtype[:-1])
-                    else:
-                        if mediatype != "music":
-                            # Validate the playlist exists or recreate it
-                            utils.playlistXSP(mediatype, foldername, viewtype)
-                            # Create the video node if not already exists
-                            if mediatype != "musicvideos":
-                                vnodes.viewNode(totalnodes, foldername, mediatype, viewtype)
-                                totalnodes += 1
-        else:
-            # Add video nodes listings
-            vnodes.singleNode(totalnodes, "Favorite movies", "movies", "favourites")
-            totalnodes += 1
-            vnodes.singleNode(totalnodes, "Favorite tvshows", "tvshows", "favourites")
-            totalnodes += 1
-            vnodes.singleNode(totalnodes, "channels", "movies", "channels")
-            totalnodes += 1
-            # Save total
-            utils.window('Emby.nodes.total', str(totalnodes))
-
-
-    def movies(self, embycursor, kodicursor, pdialog, compare=False):
-        # Get movies from emby
-        emby = self.emby
-        emby_db = embydb.Embydb_Functions(embycursor)
-        movies = itemtypes.Movies(embycursor, kodicursor)
-
-        views = emby_db.getView_byType('movies')
-        views += emby_db.getView_byType('mixed')
-        self.logMsg("Media folders: %s" % views, 1)
-
-        if compare:
-            # Pull the list of movies and boxsets in Kodi
-            try:
-                all_kodimovies = dict(emby_db.getChecksum('Movie'))
-            except ValueError:
-                all_kodimovies = {}
-
-            try:
-                all_kodisets = dict(emby_db.getChecksum('BoxSet'))
-            except ValueError:
-                all_kodisets = {}
-
-            all_embymoviesIds = set()
-            all_embyboxsetsIds = set()
-            updatelist = []
-
-        ##### PROCESS MOVIES #####
-        for view in views:
-            
-            if self.shouldStop():
-                return False
-
-            # Get items per view
-            viewId = view['id']
-            viewName = view['name']
-
-            if pdialog:
-                pdialog.update(
-                        heading="Emby for Kodi",
-                        message="Gathering movies from view: %s..." % viewName)
-
-            if compare:
-                # Manual sync
-                if pdialog:
-                    pdialog.update(
-                            heading="Emby for Kodi",
-                            message="Comparing movies from view: %s..." % viewName)
-
-                all_embymovies = emby.getMovies(viewId, basic=True)
-                for embymovie in all_embymovies['Items']:
-
-                    if self.shouldStop():
-                        return False
-
-                    API = api.API(embymovie)
-                    itemid = embymovie['Id']
-                    all_embymoviesIds.add(itemid)
-
-                    
-                    if all_kodimovies.get(itemid) != API.getChecksum():
-                        # Only update if movie is not in Kodi or checksum is different
-                        updatelist.append(itemid)
-
-                self.logMsg("Movies to update for %s: %s" % (viewName, updatelist), 1)
-                embymovies = emby.getFullItems(updatelist)
-                total = len(updatelist)
-                del updatelist[:]
-            else:
-                # Initial or repair sync
-                all_embymovies = emby.getMovies(viewId)
-                total = all_embymovies['TotalRecordCount']
-                embymovies = all_embymovies['Items']
-
-
-            if pdialog:
-                pdialog.update(heading="Processing %s / %s items" % (viewName, total))
-
-            count = 0
-            for embymovie in embymovies:
-                # Process individual movies
-                if self.shouldStop():
-                    return False
-                
-                title = embymovie['Name']
-                if pdialog:
-                    percentage = int((float(count) / float(total))*100)
-                    pdialog.update(percentage, message=title)
-                    count += 1
-                movies.add_update(embymovie, viewName, viewId)
-        else:
-            self.logMsg("Movies finished.", 2)
-
-
-        ##### PROCESS BOXSETS #####
-        if pdialog:
-            pdialog.update(heading="Emby for Kodi", message="Gathering boxsets from server...")
-        
-        boxsets = emby.getBoxset()
-
-        if compare:
-            # Manual sync
-            embyboxsets = []
-
-            if pdialog:
-                pdialog.update(
-                        heading="Emby for Kodi",
-                        message="Comparing boxsets...")
-
-            for boxset in boxsets['Items']:
-
-                if self.shouldStop():
-                    return False
-
-                # Boxset has no real userdata, so using etag to compare
-                checksum = boxset['Etag']
-                itemid = boxset['Id']
-                all_embyboxsetsIds.add(itemid)
-
-                if all_kodisets.get(itemid) != checksum:
-                    # Only update if boxset is not in Kodi or checksum is different
-                    updatelist.append(itemid)
-                    embyboxsets.append(boxset)
-
-            self.logMsg("Boxsets to update: %s" % updatelist, 1)
-            total = len(updatelist)
-        else:
-            total = boxsets['TotalRecordCount']
-            embyboxsets = boxsets['Items']
-            
-
-        if pdialog:
-            pdialog.update(heading="Processing Boxsets / %s items" % total)
-
-        count = 0
-        for boxset in embyboxsets:
-            # Process individual boxset
-            if self.shouldStop():
-                return False
-
-            title = boxset['Name']
-            if pdialog:
-                percentage = int((float(count) / float(total))*100)
-                pdialog.update(percentage, message=title)
-                count += 1
-            movies.add_updateBoxset(boxset)
-        else:
-            self.logMsg("Boxsets finished.", 2)
-
-
-        ##### PROCESS DELETES #####
-        if compare:
-            # Manual sync, process deletes
-            for kodimovie in all_kodimovies:
-                if kodimovie not in all_embymoviesIds:
-                    movies.remove(kodimovie)
-            else:
-                self.logMsg("Movies compare finished.", 1)
-
-            for boxset in all_kodisets:
-                if boxset not in all_embyboxsetsIds:
-                    movies.remove(boxset)
-            else:
-                self.logMsg("Boxsets compare finished.", 1)
-
-        return True
-
-    def musicvideos(self, embycursor, kodicursor, pdialog, compare=False):
-        # Get musicvideos from emby
-        emby = self.emby
-        emby_db = embydb.Embydb_Functions(embycursor)
-        mvideos = itemtypes.MusicVideos(embycursor, kodicursor)
-
-        views = emby_db.getView_byType('musicvideos')
-        self.logMsg("Media folders: %s" % views, 1)
-
-        if compare:
-            # Pull the list of musicvideos in Kodi
-            try:
-                all_kodimvideos = dict(emby_db.getChecksum('MusicVideo'))
-            except ValueError:
-                all_kodimvideos = {}
-
-            all_embymvideosIds = set()
-            updatelist = []
-
-        for view in views:
-            
-            if self.shouldStop():
-                return False
-
-            # Get items per view
-            viewId = view['id']
-            viewName = view['name']
-
-            if pdialog:
-                pdialog.update(
-                        heading="Emby for Kodi",
-                        message="Gathering musicvideos from view: %s..." % viewName)
-
-            if compare:
-                # Manual sync
-                if pdialog:
-                    pdialog.update(
-                            heading="Emby for Kodi",
-                            message="Comparing musicvideos from view: %s..." % viewName)
-
-                all_embymvideos = emby.getMusicVideos(viewId, basic=True)
-                for embymvideo in all_embymvideos['Items']:
-
-                    if self.shouldStop():
-                        return False
-
-                    API = api.API(embymvideo)
-                    itemid = embymvideo['Id']
-                    all_embymvideosIds.add(itemid)
-
-                    
-                    if all_kodimvideos.get(itemid) != API.getChecksum():
-                        # Only update if musicvideo is not in Kodi or checksum is different
-                        updatelist.append(itemid)
-
-                self.logMsg("MusicVideos to update for %s: %s" % (viewName, updatelist), 1)
-                embymvideos = emby.getFullItems(updatelist)
-                total = len(updatelist)
-                del updatelist[:]
-            else:
-                # Initial or repair sync
-                all_embymvideos = emby.getMusicVideos(viewId)
-                total = all_embymvideos['TotalRecordCount']
-                embymvideos = all_embymvideos['Items']
-
-
-            if pdialog:
-                pdialog.update(heading="Processing %s / %s items" % (viewName, total))
-
-            count = 0
-            for embymvideo in embymvideos:
-                # Process individual musicvideo
-                if self.shouldStop():
-                    return False
-                
-                title = embymvideo['Name']
-                if pdialog:
-                    percentage = int((float(count) / float(total))*100)
-                    pdialog.update(percentage, message=title)
-                    count += 1
-                mvideos.add_update(embymvideo, viewName, viewId)
-        else:
-            self.logMsg("MusicVideos finished.", 2)
-        
-        ##### PROCESS DELETES #####
-        if compare:
-            # Manual sync, process deletes
-            for kodimvideo in all_kodimvideos:
-                if kodimvideo not in all_embymvideosIds:
-                    mvideos.remove(kodimvideo)
-            else:
-                self.logMsg("MusicVideos compare finished.", 1)
-
-        return True
-
-    def homevideos(self, embycursor, kodicursor, pdialog, compare=False):
-        # Get homevideos from emby
-        emby = self.emby
-        emby_db = embydb.Embydb_Functions(embycursor)
-        hvideos = itemtypes.HomeVideos(embycursor, kodicursor)
-
-        views = emby_db.getView_byType('homevideos')
-        self.logMsg("Media folders: %s" % views, 1)
-
-        if compare:
-            # Pull the list of homevideos in Kodi
-            try:
-                all_kodihvideos = dict(emby_db.getChecksum('Video'))
-            except ValueError:
-                all_kodihvideos = {}
-
-            all_embyhvideosIds = set()
-            updatelist = []
-
-        for view in views:
-            
-            if self.shouldStop():
-                return False
-
-            # Get items per view
-            viewId = view['id']
-            viewName = view['name']
-
-            if pdialog:
-                pdialog.update(
-                        heading="Emby for Kodi",
-                        message="Gathering homevideos from view: %s..." % viewName)
-            
-            all_embyhvideos = emby.getHomeVideos(viewId)
-
-            if compare:
-                # Manual sync
-                if pdialog:
-                    pdialog.update(
-                            heading="Emby for Kodi",
-                            message="Comparing homevideos from view: %s..." % viewName)
-
-                for embyhvideo in all_embyhvideos['Items']:
-
-                    if self.shouldStop():
-                        return False
-
-                    API = api.API(embyhvideo)
-                    itemid = embyhvideo['Id']
-                    all_embyhvideosIds.add(itemid)
-
-                    
-                    if all_kodihvideos.get(itemid) != API.getChecksum():
-                        # Only update if homemovie is not in Kodi or checksum is different
-                        updatelist.append(itemid)
-
-                self.logMsg("HomeVideos to update for %s: %s" % (viewName, updatelist), 1)
-                embyhvideos = emby.getFullItems(updatelist)
-                total = len(updatelist)
-                del updatelist[:]
-            else:
-                total = all_embyhvideos['TotalRecordCount']
-                embyhvideos = all_embyhvideos['Items']
-
-            if pdialog:
-                pdialog.update(heading="Processing %s / %s items" % (viewName, total))
-
-            count = 0
-            for embyhvideo in embyhvideos:
-                # Process individual homemovies
-                if self.shouldStop():
-                    return False
-                
-                title = embyhvideo['Name']
-                if pdialog:
-                    percentage = int((float(count) / float(total))*100)
-                    pdialog.update(percentage, message=title)
-                    count += 1
-                hvideos.add_update(embyhvideo, viewName, viewId)
-        else:
-            self.logMsg("HomeVideos finished.", 2)
-
-        ##### PROCESS DELETES #####
-        if compare:
-            # Manual sync, process deletes
-            for kodihvideo in all_kodihvideos:
-                if kodihvideo not in all_embyhvideosIds:
-                    hvideos.remove(kodihvideo)
-            else:
-                self.logMsg("HomeVideos compare finished.", 1)
-        
-        return True
-
-    def tvshows(self, embycursor, kodicursor, pdialog, compare=False):
-        # Get shows from emby
-        emby = self.emby
-        emby_db = embydb.Embydb_Functions(embycursor)
-        tvshows = itemtypes.TVShows(embycursor, kodicursor)
-
-        views = emby_db.getView_byType('tvshows')
-        views += emby_db.getView_byType('mixed')
-        self.logMsg("Media folders: %s" % views, 1)
-
-        if compare:
-            # Pull the list of movies and boxsets in Kodi
-            try:
-                all_koditvshows = dict(emby_db.getChecksum('Series'))
-            except ValueError:
-                all_koditvshows = {}
-
-            try:
-                all_kodiepisodes = dict(emby_db.getChecksum('Episode'))
-            except ValueError:
-                all_kodiepisodes = {}
-
-            all_embytvshowsIds = set()
-            all_embyepisodesIds = set()
-            updatelist = []
-
-
-        for view in views:
-            
-            if self.shouldStop():
-                return False
-
-            # Get items per view
-            viewId = view['id']
-            viewName = view['name']
-
-            if pdialog:
-                pdialog.update(
-                        heading="Emby for Kodi",
-                        message="Gathering tvshows from view: %s..." % viewName)
-
-            if compare:
-                # Manual sync
-                if pdialog:
-                    pdialog.update(
-                            heading="Emby for Kodi",
-                            message="Comparing tvshows from view: %s..." % viewName)
-
-                all_embytvshows = emby.getShows(viewId, basic=True)
-                for embytvshow in all_embytvshows['Items']:
-
-                    if self.shouldStop():
-                        return False
-
-                    API = api.API(embytvshow)
-                    itemid = embytvshow['Id']
-                    all_embytvshowsIds.add(itemid)
-
-                    
-                    if all_koditvshows.get(itemid) != API.getChecksum():
-                        # Only update if movie is not in Kodi or checksum is different
-                        updatelist.append(itemid)
-
-                self.logMsg("TVShows to update for %s: %s" % (viewName, updatelist), 1)
-                embytvshows = emby.getFullItems(updatelist)
-                total = len(updatelist)
-                del updatelist[:]
-            else:
-                all_embytvshows = emby.getShows(viewId)
-                total = all_embytvshows['TotalRecordCount']
-                embytvshows = all_embytvshows['Items']
-
-
-            if pdialog:
-                pdialog.update(heading="Processing %s / %s items" % (viewName, total))
-
-            count = 0
-            for embytvshow in embytvshows:
-                # Process individual show
-                if self.shouldStop():
-                    return False
-                
-                itemid = embytvshow['Id']
-                title = embytvshow['Name']
-                if pdialog:
-                    percentage = int((float(count) / float(total))*100)
-                    pdialog.update(percentage, message=title)
-                    count += 1
-                tvshows.add_update(embytvshow, viewName, viewId)
-
-                if not compare:
-                    # Process episodes
-                    all_episodes = emby.getEpisodesbyShow(itemid)
-                    for episode in all_episodes['Items']:
-
-                        # Process individual show
-                        if self.shouldStop():
-                            return False
-
-                        episodetitle = episode['Name']
-                        if pdialog:
-                            pdialog.update(percentage, message="%s - %s" % (title, episodetitle))
-                        tvshows.add_updateEpisode(episode)
-            else:
-                if compare:
-                    # Get all episodes in view
-                    if pdialog:
-                        pdialog.update(
-                                heading="Emby for Kodi",
-                                message="Comparing episodes from view: %s..." % viewName)
-
-                    all_embyepisodes = emby.getEpisodes(viewId, basic=True)
-                    for embyepisode in all_embyepisodes['Items']:
-
-                        if self.shouldStop():
-                            return False
-
-                        API = api.API(embyepisode)
-                        itemid = embyepisode['Id']
-                        all_embyepisodesIds.add(itemid)
-
-                        if all_kodiepisodes.get(itemid) != API.getChecksum():
-                            # Only update if movie is not in Kodi or checksum is different
-                            updatelist.append(itemid)
-
-                    self.logMsg("Episodes to update for %s: %s" % (viewName, updatelist), 1)
-                    embyepisodes = emby.getFullItems(updatelist)
-                    total = len(updatelist)
-                    del updatelist[:]
-
-                    for episode in embyepisodes:
-
-                        # Process individual episode
-                        if self.shouldStop():
-                            return False
-
-                        title = episode['SeriesName']
-                        episodetitle = episode['Name']
-                        if pdialog:
-                            pdialog.update(percentage, message="%s - %s" % (title, episodetitle))
-                        tvshows.add_updateEpisode(episode)
-        else:
-            self.logMsg("TVShows finished.", 2)
-        
-        ##### PROCESS DELETES #####
-        if compare:
-            # Manual sync, process deletes
-            for koditvshow in all_koditvshows:
-                if koditvshow not in all_embytvshowsIds:
-                    tvshows.remove(koditvshow)
-            else:
-                self.logMsg("TVShows compare finished.", 1)
-
-            for kodiepisode in all_kodiepisodes:
-                if kodiepisode not in all_embyepisodesIds:
-                    tvshows.remove(kodiepisode)
-            else:
-                self.logMsg("Episodes compare finished.", 1)
-
-        return True
-
-    def music(self, embycursor, kodicursor, pdialog, compare=False):
-        # Get music from emby
-        emby = self.emby
-        emby_db = embydb.Embydb_Functions(embycursor)
-        music = itemtypes.Music(embycursor, kodicursor)
-
-        if compare:
-            # Pull the list of movies and boxsets in Kodi
-            try:
-                all_kodiartists = dict(emby_db.getChecksum('MusicArtist'))
-            except ValueError:
-                all_kodiartists = {}
-
-            try:
-                all_kodialbums = dict(emby_db.getChecksum('MusicAlbum'))
-            except ValueError:
-                all_kodialbums = {}
-
-            try:
-                all_kodisongs = dict(emby_db.getChecksum('Audio'))
-            except ValueError:
-                all_kodisongs = {}
-
-            all_embyartistsIds = set()
-            all_embyalbumsIds = set()
-            all_embysongsIds = set()
-            updatelist = []
-
-        process = {
-
-            'artists': [emby.getArtists, music.add_updateArtist],
-            'albums': [emby.getAlbums, music.add_updateAlbum],
-            'songs': [emby.getSongs, music.add_updateSong]
-        }
-        types = ['artists', 'albums', 'songs']
-        for type in types:
-
-            if pdialog:
-                pdialog.update(
-                    heading="Emby for Kodi",
-                    message="Gathering %s..." % type)
-
-            if compare:
-                # Manual Sync
-                if pdialog:
-                    pdialog.update(
-                            heading="Emby for Kodi",
-                            message="Comparing %s..." % type)
-
-                if type != "artists":
-                    all_embyitems = process[type][0](basic=True)
-                else:
-                    all_embyitems = process[type][0]()
-                for embyitem in all_embyitems['Items']:
-
-                    if self.shouldStop():
-                        return False
-
-                    API = api.API(embyitem)
-                    itemid = embyitem['Id']
-                    if type == "artists":
-                        all_embyartistsIds.add(itemid)
-                        if all_kodiartists.get(itemid) != API.getChecksum():
-                            # Only update if artist is not in Kodi or checksum is different
-                            updatelist.append(itemid)
-                    elif type == "albums":
-                        all_embyalbumsIds.add(itemid)
-                        if all_kodialbums.get(itemid) != API.getChecksum():
-                            # Only update if album is not in Kodi or checksum is different
-                            updatelist.append(itemid)
-                    else:
-                        all_embysongsIds.add(itemid)
-                        if all_kodisongs.get(itemid) != API.getChecksum():
-                            # Only update if songs is not in Kodi or checksum is different
-                            updatelist.append(itemid)
-
-                self.logMsg("%s to update: %s" % (type, updatelist), 1)
-                embyitems = emby.getFullItems(updatelist)
-                total = len(updatelist)
-                del updatelist[:]
-            else:
-                all_embyitems = process[type][0]()
-                total = all_embyitems['TotalRecordCount']
-                embyitems = all_embyitems['Items']
-
-            if pdialog:
-                pdialog.update(heading="Processing %s / %s items" % (type, total))
-
-            count = 0
-            for embyitem in embyitems:
-                # Process individual item
-                if self.shouldStop():
-                    return False
-                
-                title = embyitem['Name']
-                if pdialog:
-                    percentage = int((float(count) / float(total))*100)
-                    pdialog.update(percentage, message=title)
-                    count += 1
-
-                process[type][1](embyitem)
-            else:
-                self.logMsg("%s finished." % type, 2)
-
-        ##### PROCESS DELETES #####
-        if compare:
-            # Manual sync, process deletes
-            for kodiartist in all_kodiartists:
-                if kodiartist not in all_embyartistsIds and all_kodiartists[kodiartist] is not None:
-                    music.remove(kodiartist)
-            else:
-                self.logMsg("Artist compare finished.", 1)
-
-            for kodialbum in all_kodialbums:
-                if kodialbum not in all_embyalbumsIds:
-                    music.remove(kodialbum)
-            else:
-                self.logMsg("Albums compare finished.", 1)
-
-            for kodisong in all_kodisongs:
-                if kodisong not in all_embysongsIds:
-                    music.remove(kodisong)
-            else:
-                self.logMsg("Songs compare finished.", 1)
-
-        return True
-
-    # Reserved for websocket_client.py and fast start
-    def triage_items(self, process, items):
-
-        processlist = {
-
-            'added': self.addedItems,
-            'update': self.updateItems,
-            'userdata': self.userdataItems,
-            'remove': self.removeItems
-        }
-        if items:
-            if process == "userdata":
-                itemids = []
-                for item in items:
-                    itemids.append(item['ItemId'])
-                items = itemids
-
-            self.logMsg("Queue %s: %s" % (process, items), 1)
-            processlist[process].extend(items)
-
-    def incrementalSync(self):
-        
-        embyconn = utils.kodiSQL('emby')
-        embycursor = embyconn.cursor()
-        kodiconn = utils.kodiSQL('video')
-        kodicursor = kodiconn.cursor()
-        emby = self.emby
-        emby_db = embydb.Embydb_Functions(embycursor)
-        pDialog = None
-
-        if self.refresh_views:
-            # Received userconfig update
-            self.refresh_views = False
-            self.maintainViews(embycursor, kodicursor)
-            self.forceLibraryUpdate = True
-
-        if self.addedItems or self.updateItems or self.userdataItems or self.removeItems:
-            # Only present dialog if we are going to process items
-            pDialog = self.progressDialog('Incremental sync')
-
-
-        process = {
-
-            'added': self.addedItems,
-            'update': self.updateItems,
-            'userdata': self.userdataItems,
-            'remove': self.removeItems
-        }
-        types = ['added', 'update', 'userdata', 'remove']
-        for type in types:
-
-            if process[type] and utils.window('emby_kodiScan') != "true":
-                
-                listItems = list(process[type])
-                del process[type][:] # Reset class list
-
-                items_process = itemtypes.Items(embycursor, kodicursor)
-                update = False
-
-                # Prepare items according to process type
-                if type == "added":
-                    items = emby.sortby_mediatype(listItems)
-
-                elif type in ("userdata", "remove"):
-                    items = emby_db.sortby_mediaType(listItems, unsorted=False)
-                
-                else:
-                    items = emby_db.sortby_mediaType(listItems)
-                    if items.get('Unsorted'):
-                        sorted_items = emby.sortby_mediatype(items['Unsorted'])
-                        doupdate = items_process.itemsbyId(sorted_items, "added", pDialog)
-                        if doupdate:
-                            update = True
-                        del items['Unsorted']
-
-                doupdate = items_process.itemsbyId(items, type, pDialog)
-                if doupdate:
-                    update = True
-                    
-                if update:
-                    self.forceLibraryUpdate = True
-
-
-        if self.forceLibraryUpdate:
-            # Force update the Kodi library
-            self.forceLibraryUpdate = False
-            self.dbCommit(kodiconn)
-            embyconn.commit()
-            self.saveLastSync()
-
-            # tell any widgets to refresh because the content has changed
-            utils.window('widgetreload', value=datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
-
-            self.logMsg("Updating video library.", 1)
-            utils.window('emby_kodiScan', value="true")
-            xbmc.executebuiltin('UpdateLibrary(video)')
-
-        if pDialog:
-            pDialog.close()
-
-        kodicursor.close()
-        embycursor.close()
-
-
-    def compareDBVersion(self, current, minimum):
-        # It returns True is database is up to date. False otherwise.
-        self.logMsg("current: %s minimum: %s" % (current, minimum), 1)
-        currMajor, currMinor, currPatch = current.split(".")
-        minMajor, minMinor, minPatch = minimum.split(".")
-
+    def checkDBVersion(self, currVersion, minVersion):
+        currMajor, currMinor, currPatch = currVersion.split(".")
+        minMajor, minMinor, minPatch = minVersion.split(".")
         if currMajor > minMajor:
             return True
-        elif currMajor == minMajor and (currMinor > minMinor or
-                                       (currMinor == minMinor and currPatch >= minPatch)):
+        elif currMajor == minMajor and currMinor > minMinor:
+            return True
+        elif currMajor == minMajor and currMinor == minMinor and currPatch >= minPatch:
             return True
         else:
-            # Database out of date.
             return False
 
     def run(self):
@@ -1211,128 +1058,134 @@ class LibrarySync(threading.Thread):
         try:
             self.run_internal()
         except Exception as e:
-            xbmcgui.Dialog().ok(
-                        heading="Emby for Kodi",
-                        line1=(
-                            "Library sync thread has exited! "
-                            "You should restart Kodi now. "
-                            "Please report this on the forum."))
+            xbmcgui.Dialog().ok("Emby for Kodi", "Library sync thread has crashed!", "You will need to restart Kodi.", "Please report this on the forum, we will need your log.")
             raise
 
     def run_internal(self):
 
         startupComplete = False
-        monitor = self.monitor
+        kodiProfile = xbmc.translatePath("special://profile")
 
-        self.logMsg("---===### Starting LibrarySync ###===---", 0)
+        self.logMsg("--- Starting Library Sync Thread ---", 0)
 
-        while not monitor.abortRequested():
+        while not self.KodiMonitor.abortRequested():
 
-            # In the event the server goes offline
-            while self.suspend_thread:
-                # Set in service.py
-                if monitor.waitForAbort(5):
+            # In the event the server goes offline after
+            # the thread has already been started.
+            while self.suspendClient == True:
+                # The service.py will change self.suspendClient to False
+                if self.KodiMonitor.waitForAbort(5):
                     # Abort was requested while waiting. We should exit
                     break
 
-            if (utils.window('emby_dbCheck') != "true" and
-                    utils.settings('SyncInstallRunDone') == "true"):
-                
-                # Verify the validity of the database
-                currentVersion = utils.settings('dbCreatedWithVersion')
-                minVersion = utils.window('emby_minDBVersion')
-                uptoDate = self.compareDBVersion(currentVersion, minVersion)
-
-                if not uptoDate:
-                    self.logMsg(
-                        "Db version out of date: %s minimum version required: %s"
-                        % (currentVersion, minVersion), 0)
-                    
-                    resp = xbmcgui.Dialog().yesno(
-                                            heading="Db Version",
-                                            line1=(
-                                                "Detected the database needs to be "
-                                                "recreated for this version of Emby for Kodi. "
-                                                "Proceed?"))
-                    if not resp:
-                        self.logMsg("Db version out of date! USER IGNORED!", 0)
-                        xbmcgui.Dialog().ok(
-                                        heading="Emby for Kodi",
-                                        line1=(
-                                            "Emby for Kodi may not work correctly "
-                                            "until the database is reset."))
-                    else:
-                        utils.reset()
-
-                utils.window('emby_dbCheck', value="true")
-
-
+            # Check if the version of Emby for Kodi the DB was created with is recent enough - controled by Window property set at top of service _INIT_
+            
+            # START TEMPORARY CODE
+            # Only get in here for a while, can be removed later
+            if utils.settings("dbCreatedWithVersion")=="" and utils.settings("SyncInstallRunDone") == "true":
+                self.logMsg("Unknown DB version", 0)
+                return_value = xbmcgui.Dialog().yesno("DB Version", "Can't detect version of Emby for Kodi the DB was created with.\nWas it at least version " + utils.window('minDBVersion') + "?")
+                if return_value == 0:
+                    utils.settings("dbCreatedWithVersion","0.0.0")
+                    self.logMsg("DB version out of date according to user", 0)
+                else:
+                    utils.settings("dbCreatedWithVersion", utils.window('minDBVersion'))   
+                    self.logMsg("DB version okay according to user", 0)                    
+            # END TEMPORARY CODE
+        
+            if (utils.settings("SyncInstallRunDone") == "true" and self.checkDBVersion(utils.settings("dbCreatedWithVersion"), utils.window('minDBVersion'))==False and utils.window('minDBVersionCheck') != "true"):
+                self.logMsg("DB version out of date according to check", 0)
+                return_value = xbmcgui.Dialog().yesno("DB Version", "Detected the DB needs to be recreated for\nthis version of Emby for Kodi.\nProceed?")
+                if return_value == 0:
+                    self.logMsg("DB version out of date !!! USER IGNORED !!!", 0)
+                    xbmcgui.Dialog().ok("Emby for Kodi","Emby for Kodi may not work\ncorrectly until the database is reset.\n")
+                    utils.window('minDBVersionCheck', value="true")
+                else:
+                    utils.reset()
+            
+            # Library sync
             if not startupComplete:
-                # Verify the video database can be found
-                videoDb = utils.getKodiVideoDBPath()
-                if not xbmcvfs.exists(videoDb):
-                    # Database does not exists
-                    self.logMsg(
-                            "The current Kodi version is incompatible "
-                            "with the Emby for Kodi add-on. Please visit "
-                            "https://github.com/MediaBrowser/Emby.Kodi/wiki "
-                            "to know which Kodi versions are supported.", 0)
-
-                    xbmcgui.Dialog().ok(
-                                    heading="Emby Warning",
-                                    line1=(
-                                        "Cancelling the database syncing process. "
-                                        "Current Kodi versoin: %s is unsupported. "
-                                        "Please verify your logs for more info."
-                                        % xbmc.getInfoLabel('System.BuildVersion')))
+                
+                # Verify the database for videos
+                videodb = utils.getKodiVideoDBPath()
+                if not xbmcvfs.exists(videodb):
+                    # Database does not exists.
+                    self.logMsg("The current Kodi version is incompatible with the Emby for Kodi add-on. Please visit here, to see currently supported Kodi versions: https://github.com/MediaBrowser/Emby.Kodi/wiki", 0)
+                    xbmcgui.Dialog().ok("Emby Warning", "Cancelling the database syncing process. Current Kodi version: %s is unsupported. Please verify your logs for more info." % xbmc.getInfoLabel('System.BuildVersion'))
                     break
 
-                # Run start up sync
-                self.logMsg("Db version: %s" % utils.settings('dbCreatedWithVersion'), 0)
-                self.logMsg("SyncDatabase (started)", 1)
+                # Run full sync
+                self.logMsg("DB Version: " + utils.settings("dbCreatedWithVersion"), 0)
+                self.logMsg("Doing_Db_Sync: syncDatabase (Started)", 1)
                 startTime = datetime.now()
-                librarySync = self.startSync()
+                libSync = self.FullLibrarySync()
                 elapsedTime = datetime.now() - startTime
-                self.logMsg(
-                    "SyncDatabase (finished in: %s) %s"
-                    % (str(elapsedTime).split('.')[0], librarySync), 1)
-                # Only try the initial sync once per kodi session regardless
-                # This will prevent an infinite loop in case something goes wrong.
-                startupComplete = True
+                self.logMsg("Doing_Db_Sync: syncDatabase (Finished in: %s) %s" % (str(elapsedTime).split('.')[0], libSync), 1)
 
-            # Process updates
-            if utils.window('emby_dbScan') != "true":
-                self.incrementalSync()
+                if libSync:
+                    startupComplete = True
 
-            if (utils.window('emby_onWake') == "true" and
-                    utils.window('emby_online') == "true"):
-                # Kodi is waking up
-                # Set in kodimonitor.py
-                utils.window('emby_onWake', clear=True)
-                if utils.window('emby_syncRunning') != "true":
-                    self.logMsg("SyncDatabase onWake (started)", 0)
-                    librarySync = self.startSync()
-                    self.logMsg("SyncDatabase onWake (finished) %s", librarySync, 0)
+            # Set via Kodi Monitor event
+            if utils.window('OnWakeSync') == "true" and utils.window('Server_online') == "true":
+                utils.window("OnWakeSync", clear=True)
+                if utils.window("SyncDatabaseRunning") != "true":
+                    self.logMsg("Doing_Db_Sync Post Resume: syncDatabase (Started)", 0)
+                    libSync = self.FullLibrarySync()
+                    self.logMsg("Doing_Db_Sync Post Resume: syncDatabase (Finished) " + str(libSync), 0)
 
-            if self.stop_thread:
-                # Set in service.py
-                self.logMsg("Service terminated thread.", 2)
+            
+            doSaveLastSync = False
+            
+            if len(self.updateItems) > 0 and utils.window('kodiScan') != "true":
+                # Add or update items
+                self.logMsg("Processing items: %s" % (str(self.updateItems)), 1)
+                listItems = self.updateItems
+                self.updateItems = []
+                self.IncrementalSync(listItems)
+                self.forceUpdate = True
+                doSaveLastSync = True
+
+            if len(self.userdataItems) > 0 and utils.window('kodiScan') != "true":
+                # Process userdata changes only
+                self.logMsg("Processing items: %s" % (str(self.userdataItems)), 1)
+                listItems = self.userdataItems
+                self.userdataItems = []
+                self.setUserdata(listItems)
+                self.forceUpdate = True
+                doSaveLastSync = True
+
+            if len(self.removeItems) > 0 and utils.window('kodiScan') != "true":
+                # Remove item from Kodi library
+                self.logMsg("Removing items: %s" % self.removeItems, 1)
+                listItems = self.removeItems
+                self.removeItems = []
+                self.removefromDB(listItems)
+                self.forceUpdate = True
+                doSaveLastSync = True
+                
+            if doSaveLastSync == True:
+                self.SaveLastSync()
+
+            if self.forceUpdate and not self.updateItems and not self.userdataItems and not self.removeItems:
+                # Force update Kodi library
+                self.forceUpdate = False
+                self.updateLibrary("video")
+
+            if utils.window("kodiProfile_emby") != kodiProfile:
+                # Profile change happened, terminate this thread
+                self.logMsg("Kodi profile was: %s and changed to: %s. Terminating Library thread." % (kodiProfile, utils.window("kodiProfile_emby")), 1)
                 break
 
-            if monitor.waitForAbort(1):
+            if self.KodiMonitor.waitForAbort(1):
                 # Abort was requested while waiting. We should exit
                 break
 
-        self.logMsg("###===--- LibrarySync Stopped ---===###", 0)
+        self.logMsg("--- Library Sync Thread stopped ---", 0)
 
-    def stopThread(self):
-        self.stop_thread = True
-        self.logMsg("Ending thread...", 2)
+    def suspendClient(self):
+        self.suspendClient = True
+        self.logMsg("--- Library Sync Thread paused ---", 0)
 
-    def suspendThread(self):
-        self.suspend_thread = True
-        self.logMsg("Pausing thread...", 0)
-
-    def resumeThread(self):
-        self.suspend_thread = False
-        self.logMsg("Resuming thread...", 0)
\ No newline at end of file
+    def resumeClient(self):
+        self.suspendClient = False
+        self.logMsg("--- Library Sync Thread resumed ---", 0)
\ No newline at end of file
diff --git a/resources/lib/PlayUtils.py b/resources/lib/PlayUtils.py
index 0a74690b..e8b9be58 100644
--- a/resources/lib/PlayUtils.py
+++ b/resources/lib/PlayUtils.py
@@ -6,279 +6,227 @@ import xbmc
 import xbmcgui
 import xbmcvfs
 
-import clientinfo
-import utils
+from ClientInformation import ClientInformation
+import Utils as utils
 
 #################################################################################################
 
-
 class PlayUtils():
-    
-    
-    def __init__(self, item):
 
-        self.item = item
-
-        self.clientInfo = clientinfo.ClientInfo()
-        self.addonName = self.clientInfo.getAddonName()
-
-        self.userid = utils.window('emby_currUser')
-        self.server = utils.window('emby_server%s' % self.userid)
+    clientInfo = ClientInformation()
+    addonName = clientInfo.getAddonName()
 
     def logMsg(self, msg, lvl=1):
+        
+        className = self.__class__.__name__
+        utils.logMsg("%s %s" % (self.addonName, className), msg, int(lvl))
 
-        self.className = self.__class__.__name__
-        utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
-    
+    def getPlayUrl(self, server, id, result):
 
-    def getPlayUrl(self):
+        if self.isDirectPlay(result,True):
+            # Try direct play
+            playurl = self.directPlay(result)
+            if playurl:
+                self.logMsg("File is direct playing.", 1)
+                utils.window("%splaymethod" % playurl.encode('utf-8'), value="DirectPlay")
 
-        item = self.item
-        playurl = None
+        elif self.isDirectStream(result):
+            # Try direct stream
+            playurl = self.directStream(result, server, id)
+            if playurl:
+                self.logMsg("File is direct streaming.", 1)
+                utils.window("%splaymethod" % playurl, value="DirectStream")
 
-        if item['MediaSources'][0]['Protocol'] == "Http":
-            # Only play as http
-            self.logMsg("File protocol is http.", 1)
-            playurl = self.httpPlay()
-            utils.window('emby_%s.playmethod' % playurl, value="DirectStream")
+        elif self.isTranscoding(result):
+            # Try transcoding
+            playurl = self.transcoding(result, server, id)
+            if playurl:
+                self.logMsg("File is transcoding.", 1)
+                utils.window("%splaymethod" % playurl, value="Transcode")
+        
+        else: # Error
+            utils.window("playurlFalse", value="true")
+            return
 
-        elif self.isDirectPlay():
+        return playurl.encode('utf-8')
 
-            self.logMsg("File is direct playing.", 1)
-            playurl = self.directPlay()
-            playurl = playurl.encode('utf-8')
-            # Set playmethod property
-            utils.window('emby_%s.playmethod' % playurl, value="DirectPlay")
 
-        elif self.isDirectStream():
-            
-            self.logMsg("File is direct streaming.", 1)
-            playurl = self.directStream()
-            # Set playmethod property
-            utils.window('emby_%s.playmethod' % playurl, value="DirectStream")
-
-        elif self.isTranscoding():
-            
-            self.logMsg("File is transcoding.", 1)
-            playurl = self.transcoding()
-            # Set playmethod property
-            utils.window('emby_%s.playmethod' % playurl, value="Transcode")
-
-        return playurl
-
-    def httpPlay(self):
-        # Audio, Video, Photo
-        item = self.item
-        server = self.server
-
-        itemid = item['Id']
-        mediatype = item['MediaType']
-
-        if type == "Audio":
-            playurl = "%s/emby/Audio/%s/stream" % (server, itemid)
-        else:
-            playurl = "%s/emby/Videos/%s/stream?static=true" % (server, itemid)
-
-        return playurl
-
-    def isDirectPlay(self):
-
-        item = self.item
-
-        # Requirement: Filesystem, Accessible path
+    def isDirectPlay(self, result, dialog = False):
+        # Requirements for Direct play:
+        # FileSystem, Accessible path
         if utils.settings('playFromStream') == "true":
-            # User forcing to play via HTTP
-            self.logMsg("Can't direct play, play from HTTP enabled.", 1)
+            # User forcing to play via HTTP instead of SMB
+            self.logMsg("Can't direct play: Play from HTTP is enabled.", 1)
             return False
 
+        # Avoid H265 1080p
         if (utils.settings('transcodeH265') == "true" and 
-                result['MediaSources'][0]['Name'].startswith("1080P/H265")):
-            # Avoid H265 1080p
+            result['MediaSources'][0]['Name'].startswith("1080P/H265")):
             self.logMsg("Option to transcode 1080P/H265 enabled.", 1)
             return False
 
-        canDirectPlay = item['MediaSources'][0]['SupportsDirectPlay']
-        # Make sure direct play is supported by the server
+        canDirectPlay = result['MediaSources'][0]['SupportsDirectPlay']
+        # Make sure it's supported by server
         if not canDirectPlay:
-            self.logMsg("Can't direct play, server doesn't allow/support it.", 1)
+            self.logMsg("Can't direct play: Server does not allow or support it.", 1)
             return False
 
-        location = item['LocationType']
-        if location == "FileSystem":
-            # Verify the path
-            if not self.fileExists():
-                self.logMsg("Unable to direct play.")
-                try:
-                    count = int(utils.settings('failCount'))
-                except ValueError:
-                    count = 0
-                self.logMsg("Direct play failed: %s times." % count, 1)
+        location = result['LocationType']
+        # File needs to be "FileSystem"
+        if 'FileSystem' in location:
+            # Verify if path is accessible
+            if self.fileExists(result):
+                return True
+            else:
+                self.logMsg("Unable to direct play. Verify the following path is accessible by the device: %s. You might also need to add SMB credentials in the add-on settings." % result['MediaSources'][0]['Path'], 1)
+                if dialog:
+                    
+                    failCount = int(utils.settings('directSteamFailedCount'))
+                    self.logMsg("Direct Play failCount: %s." % failCount, 1)
+                    
+                    if failCount < 2:
+                        # Let user know that direct play failed
+                        utils.settings('directSteamFailedCount', value=str(failCount + 1))
+                        xbmcgui.Dialog().notification("Emby server", "Unable to direct play. Verify your log for more information.", icon="special://home/addons/plugin.video.emby/icon.png", sound=False)
+                    elif utils.settings('playFromStream') != "true":
+                        # Permanently set direct stream as true
+                        utils.settings('playFromStream', value="true")
+                        xbmcgui.Dialog().notification("Emby server", "Enabled play from HTTP in add-on settings.", icon="special://home/addons/plugin.video.emby/icon.png", sound=False)
 
-                if count < 2:
-                    # Let the user know that direct play failed
-                    utils.settings('failCount', value=str(count+1))
-                    xbmcgui.Dialog().notification(
-                                        heading="Emby server",
-                                        message="Unable to direct play.",
-                                        icon="special://home/addons/plugin.video.emby/icon.png",
-                                        sound=False)
-                elif utils.settings('playFromStream') != "true":
-                    # Permanently set direct stream as true
-                    utils.settings('playFromStream', value="true")
-                    utils.settings('failCount', value="0")
-                    xbmcgui.Dialog().notification(
-                                        heading="Emby server",
-                                        message=("Direct play failed 3 times. Enabled play "
-                                                 "from HTTP in the add-on settings."),
-                                        icon="special://home/addons/plugin.video.emby/icon.png",
-                                        sound=False)
                 return False
 
-        return True
-
-    def directPlay(self):
-
-        item = self.item
+    def directPlay(self, result):
 
         try:
-            playurl = item['MediaSources'][0]['Path']
-        except (IndexError, KeyError):
-            playurl = item['Path']
+            playurl = result['MediaSources'][0]['Path']
+        except KeyError:
+            playurl = result['Path']
 
-        if item.get('VideoType'):
+        if 'VideoType' in result:
             # Specific format modification
-            type = item['VideoType']
-
-            if type == "Dvd":
+            if 'Dvd' in result['VideoType']:
                 playurl = "%s/VIDEO_TS/VIDEO_TS.IFO" % playurl
-            elif type == "Bluray":
+            elif 'BluRay' in result['VideoType']:
                 playurl = "%s/BDMV/index.bdmv" % playurl
 
-        # Assign network protocol
-        if playurl.startswith('\\\\'):
-            playurl = playurl.replace("\\\\", "smb://")
+        # Network - SMB protocol
+        if "\\\\" in playurl:
+            smbuser = utils.settings('smbusername')
+            smbpass = utils.settings('smbpassword')
+            # Network share
+            if smbuser:
+                playurl = playurl.replace("\\\\", "smb://%s:%s@" % (smbuser, smbpass))
+            else:
+                playurl = playurl.replace("\\\\", "smb://")
             playurl = playurl.replace("\\", "/")
-
+            
         if "apple.com" in playurl:
             USER_AGENT = "QuickTime/7.7.4"
             playurl += "?|User-Agent=%s" % USER_AGENT
 
         return playurl
 
-    def fileExists(self):
 
-        if 'Path' not in self.item:
-            # File has no path defined in server
-            return False
-
-        # Convert path to direct play
-        path = self.directPlay()
-        self.logMsg("Verifying path: %s" % path, 1)
-
-        if xbmcvfs.exists(path):
-            self.logMsg("Path exists.", 1)
-            return True
-
-        elif ":" not in path:
-            self.logMsg("Can't verify path, assumed linux. Still try to direct play.", 1)
-            return True
-
-        else:
-            self.logMsg("Failed to find file.")
-            return False
-
-    def isDirectStream(self):
-
-        item = self.item
+    def isDirectStream(self, result):
+        # Requirements for Direct stream:
+        # FileSystem or Remote, BitRate, supported encoding
 
+        # Avoid H265 1080p
         if (utils.settings('transcodeH265') == "true" and 
-                result['MediaSources'][0]['Name'].startswith("1080P/H265")):
-            # Avoid H265 1080p
+            result['MediaSources'][0]['Name'].startswith("1080P/H265")):
             self.logMsg("Option to transcode 1080P/H265 enabled.", 1)
             return False
 
-        # Requirement: BitRate, supported encoding
-        canDirectStream = item['MediaSources'][0]['SupportsDirectStream']
-        # Make sure the server supports it
+        canDirectStream = result['MediaSources'][0]['SupportsDirectStream']
+        # Make sure it's supported by server
         if not canDirectStream:
             return False
 
-        # Verify the bitrate
-        if not self.isNetworkSufficient():
-            self.logMsg("The network speed is insufficient to direct stream file.", 1)
+        location = result['LocationType']
+        # File can be FileSystem or Remote, not Virtual
+        if 'Virtual' in location:
+            self.logMsg("File location is virtual. Can't proceed.", 1)
+            return False
+
+        # Verify BitRate
+        if not self.isNetworkQualitySufficient(result):
+            self.logMsg("The network speed is insufficient to playback the file.", 1)
             return False
 
         return True
+  
+    def directStream(self, result, server, id, type = "Video"):
 
-    def directStream(self):
-
-        item = self.item
-        server = self.server
-
-        itemid = item['Id']
-        type = item['Type']
-
-        if 'Path' in item and item['Path'].endswith('.strm'):
+        if result['Path'].endswith('.strm'):
             # Allow strm loading when direct streaming
-            playurl = self.directPlay()
-        elif type == "Audio":
-            playurl = "%s/emby/Audio/%s/stream.mp3" % (server, itemid)
-        else:
-            playurl = "%s/emby/Videos/%s/stream?static=true" % (server, itemid)
+            playurl = self.directPlay(result)
+            return playurl
+        
+        if "ThemeVideo" in type:
+            playurl = "%s/mediabrowser/Videos/%s/stream?static=true" % (server, id)
 
+        elif "Video" in type:
+            playurl = "%s/mediabrowser/Videos/%s/stream?static=true" % (server, id)
+        
+        elif "Audio" in type:
+            playurl = "%s/mediabrowser/Audio/%s/stream.mp3" % (server, id)
+        
         return playurl
 
-    def isNetworkSufficient(self):
 
-        settings = self.getBitrate()*1000
-
-        try:
-            sourceBitrate = int(self.item['MediaSources'][0]['Bitrate'])
-        except (KeyError, TypeError):
-            self.logMsg("Bitrate value is missing.", 1)
-        else:
-            self.logMsg("The add-on settings bitrate is: %s, the video bitrate required is: %s"
-                        % (settings, sourceBitrate), 1)
-            if settings < sourceBitrate:
-                return False
-
-        return True
-
-    def isTranscoding(self):
-
-        item = self.item
-
-        canTranscode = item['MediaSources'][0]['SupportsTranscoding']
-        # Make sure the server supports it
+    def isTranscoding(self, result):
+        # Last resort, no requirements
+        # BitRate
+        canTranscode = result['MediaSources'][0]['SupportsTranscoding']
+        # Make sure it's supported by server
         if not canTranscode:
             return False
 
+        location = result['LocationType']
+        # File can be FileSystem or Remote, not Virtual
+        if 'Virtual' in location:
+            return False
+
         return True
 
-    def transcoding(self):
+    def transcoding(self, result, server, id):
 
-        item = self.item
-
-        if 'Path' in item and item['Path'].endswith('.strm'):
+        if result['Path'].endswith('.strm'):
             # Allow strm loading when transcoding
-            playurl = self.directPlay()
-        else:
-            itemid = item['Id']
-            deviceId = self.clientInfo.getDeviceId()
-            playurl = (
-                "%s/emby/Videos/%s/master.m3u8?MediaSourceId=%s"
-                % (self.server, itemid, itemid)
-            )
-            playurl = (
-                "%s&VideoCodec=h264&AudioCodec=ac3&MaxAudioChannels=6&deviceId=%s&VideoBitrate=%s"
-                % (playurl, deviceId, self.getBitrate()*1000))
+            playurl = self.directPlay(result)
+            return playurl
 
+        # Play transcoding
+        deviceId = self.clientInfo.getMachineId()
+        playurl = "%s/mediabrowser/Videos/%s/master.m3u8?mediaSourceId=%s" % (server, id, id)
+        playurl = "%s&VideoCodec=h264&AudioCodec=ac3&MaxAudioChannels=6&deviceId=%s&VideoBitrate=%s" % (playurl, deviceId, self.getVideoBitRate()*1000)
+        
+        playurl = self.audioSubsPref(playurl, result.get('MediaSources'))
+        self.logMsg("Playurl: %s" % playurl, 1)
+        
         return playurl
+        
 
-    def getBitrate(self):
+    def isNetworkQualitySufficient(self, result):
+        # Works out if the network quality can play directly or if transcoding is needed
+        settingsVideoBitRate = self.getVideoBitRate()
+        settingsVideoBitRate = settingsVideoBitRate * 1000
 
+        try:
+            mediaSources = result['MediaSources']
+            sourceBitRate = int(mediaSources[0]['Bitrate'])
+        except KeyError:
+            self.logMsg("Bitrate value is missing.", 1)
+        else:
+            self.logMsg("The video quality selected is: %s, the video bitrate required to direct stream is: %s." % (settingsVideoBitRate, sourceBitRate), 1)
+            if settingsVideoBitRate < sourceBitRate:
+                return False
+        
+        return True
+      
+    def getVideoBitRate(self):
         # get the addon video quality
-        videoQuality = utils.settings('videoBitrate')
+        videoQuality = utils.settings('videoBitRate')
         bitrate = {
 
             '0': 664,
@@ -304,8 +252,35 @@ class PlayUtils():
 
         # max bit rate supported by server (max signed 32bit integer)
         return bitrate.get(videoQuality, 2147483)
+            
+    def fileExists(self, result):
+        
+        if 'Path' not in result:
+            # File has no path in server
+            return False
 
-    def audioSubsPref(self, url):
+        # Convert Emby path to a path we can verify
+        path = self.directPlay(result)
+
+        try:
+            pathexists = xbmcvfs.exists(path)
+        except:
+            pathexists = False
+
+        # Verify the device has access to the direct path
+        if pathexists:
+            # Local or Network path
+            self.logMsg("Path exists.", 2)
+            return True
+        elif ":" not in path:
+            # Give benefit of the doubt for nfs.
+            self.logMsg("Can't verify path (assumed NFS). Still try direct play.", 2)
+            return True
+        else:
+            self.logMsg("Path is detected as follow: %s. Try direct streaming." % path, 2)
+            return False
+
+    def audioSubsPref(self, url, mediaSources):
         # For transcoding only
         # Present the list of audio to select from
         audioStreamsList = {}
@@ -317,21 +292,15 @@ class PlayUtils():
         selectSubsIndex = ""
         playurlprefs = "%s" % url
 
-        item = self.item
-        try:
-            mediasources = item['MediaSources'][0]
-            mediastreams = mediasources['MediaStreams']
-        except (TypeError, KeyError, IndexError):
-            return
-
-        for stream in mediastreams:
+        mediaStream = mediaSources[0].get('MediaStreams')
+        for stream in mediaStream:
             # Since Emby returns all possible tracks together, have to sort them.
             index = stream['Index']
             type = stream['Type']
 
             if 'Audio' in type:
                 codec = stream['Codec']
-                channelLayout = stream.get('ChannelLayout', "")
+                channelLayout = stream['ChannelLayout']
                
                 try:
                     track = "%s - %s - %s %s" % (index, stream['Language'], codec, channelLayout)
@@ -343,8 +312,6 @@ class PlayUtils():
                 audioStreams.append(track)
 
             elif 'Subtitle' in type:
-                if stream['IsExternal']:
-                    continue
                 try:
                     track = "%s - %s" % (index, stream['Language'])
                 except:
@@ -369,7 +336,7 @@ class PlayUtils():
                 selectAudioIndex = audioStreamsList[selected]
                 playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex
             else: # User backed out of selection
-                playurlprefs += "&AudioStreamIndex=%s" % mediasources['DefaultAudioStreamIndex']
+                playurlprefs += "&AudioStreamIndex=%s" % mediaSources[0]['DefaultAudioStreamIndex']
         else: # There's only one audiotrack.
             selectAudioIndex = audioStreamsList[audioStreams[0]]
             playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex
@@ -385,7 +352,7 @@ class PlayUtils():
                 selectSubsIndex = subtitleStreamsList[selected]
                 playurlprefs += "&SubtitleStreamIndex=%s" % selectSubsIndex
             else: # User backed out of selection
-                playurlprefs += "&SubtitleStreamIndex=%s" % mediasources.get('DefaultSubtitleStreamIndex', "")
+                playurlprefs += "&SubtitleStreamIndex=%s" % mediaSources[0].get('DefaultSubtitleStreamIndex', "")
 
         # Get number of channels for selected audio track
         audioChannels = audioStreamsChannelsList.get(selectAudioIndex, 0)
diff --git a/resources/lib/PlaybackUtils.py b/resources/lib/PlaybackUtils.py
index affa2b81..1172e52b 100644
--- a/resources/lib/PlaybackUtils.py
+++ b/resources/lib/PlaybackUtils.py
@@ -2,69 +2,71 @@
 
 #################################################################################################
 
-import json
+import datetime
+import json as json
 import sys
 
 import xbmc
-import xbmcgui
+import xbmcaddon
 import xbmcplugin
+import xbmcgui
 
-import api
-import artwork
-import clientinfo
-import downloadutils
-import playutils as putils
-import playlist
-import read_embyserver as embyserver
-import utils
+from API import API
+from DownloadUtils import DownloadUtils
+from PlayUtils import PlayUtils
+from ClientInformation import ClientInformation
+import Utils as utils
 
 #################################################################################################
 
-
 class PlaybackUtils():
     
-    
-    def __init__(self, item):
+    clientInfo = ClientInformation()
+    doUtils = DownloadUtils()
+    api = API()
 
-        self.item = item
-        self.API = api.API(self.item)
-
-        self.clientInfo = clientinfo.ClientInfo()
-        self.addonName = self.clientInfo.getAddonName()
-        self.doUtils = downloadutils.DownloadUtils()
-
-        self.userid = utils.window('emby_currUser')
-        self.server = utils.window('emby_server%s' % self.userid)
-
-        self.artwork = artwork.Artwork()
-        self.emby = embyserver.Read_EmbyServer()
-        self.pl = playlist.Playlist()
+    addon = xbmcaddon.Addon()
+    language = addon.getLocalizedString
+    addonName = clientInfo.getAddonName()
 
     def logMsg(self, msg, lvl=1):
+        
+        className = self.__class__.__name__
+        utils.logMsg("%s %s" % (self.addonName, className), msg, int(lvl))
 
-        self.className = self.__class__.__name__
-        utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
+    def PLAY(self, result, setup = "service"):
 
+        self.logMsg("PLAY Called", 1)
 
-    def play(self, itemid, dbid=None):
-
-        self.logMsg("Play called.", 1)
-
+        api = self.api
         doUtils = self.doUtils
-        item = self.item
-        API = self.API
-        listitem = xbmcgui.ListItem()
-        playutils = putils.PlayUtils(item)
+        username = utils.window('currUser')
+        server = utils.window('server%s' % username)
 
-        playurl = playutils.getPlayUrl()
-        if not playurl:
-            return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
+        id = result['Id']
+        userdata = result['UserData']
+        # Get the playurl - direct play, direct stream or transcoding
+        playurl = PlayUtils().getPlayUrl(server, id, result)
+        listItem = xbmcgui.ListItem()
 
-        if dbid is None:
-            # Item is not in Kodi database
-            listitem.setPath(playurl)
-            self.setProperties(playurl, listitem)
-            return xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
+        if utils.window('playurlFalse') == "true":
+            # Playurl failed - set in PlayUtils.py
+            utils.window('playurlFalse', clear=True)
+            self.logMsg("Failed to retrieve the playback path/url or dialog was cancelled.", 1)
+            return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listItem)
+
+        
+        ############### -- SETUP MAIN ITEM ################
+            
+        # Set listitem and properties for main item
+        self.logMsg("Returned playurl: %s" % playurl, 1)
+        listItem.setPath(playurl)
+        self.setProperties(playurl, result, listItem)
+
+        mainArt = API().getArtwork(result, "Primary")
+        listItem.setThumbnailImage(mainArt)
+        listItem.setIconImage(mainArt)
+        
 
         ############### ORGANIZE CURRENT PLAYLIST ################
         
@@ -72,45 +74,58 @@ class PlaybackUtils():
         playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
         startPos = max(playlist.getposition(), 0) # Can return -1
         sizePlaylist = playlist.size()
-        currentPosition = startPos
 
-        propertiesPlayback = utils.window('emby_playbackProps', windowid=10101) == "true"
+        propertiesPlayback = utils.window('propertiesPlayback') == "true"
         introsPlaylist = False
         dummyPlaylist = False
+        currentPosition = startPos
+
+        self.logMsg("Playlist start position: %s" % startPos, 2)
+        self.logMsg("Playlist plugin position: %s" % currentPosition, 2)
+        self.logMsg("Playlist size: %s" % sizePlaylist, 2)
 
-        self.logMsg("Playlist start position: %s" % startPos, 1)
-        self.logMsg("Playlist plugin position: %s" % currentPosition, 1)
-        self.logMsg("Playlist size: %s" % sizePlaylist, 1)
 
         ############### RESUME POINT ################
         
-        userdata = API.getUserData()
-        seektime = API.adjustResume(userdata['Resume'])
+        # Resume point for widget only
+        timeInfo = api.getTimeInfo(result)
+        jumpBackSec = int(utils.settings('resumeJumpBack'))
+        seekTime = round(float(timeInfo.get('ResumeTime')), 6)
+        if seekTime > jumpBackSec:
+            # To avoid negative bookmark
+            seekTime = seekTime - jumpBackSec
+
+        # Show the additional resume dialog if launched from a widget
+        if homeScreen and seekTime:
+            # Dialog presentation
+            displayTime = str(datetime.timedelta(seconds=(int(seekTime))))
+            display_list = ["%s %s" % (self.language(30106), displayTime), self.language(30107)]
+            resume_result = xbmcgui.Dialog().select(self.language(30105), display_list)
+
+            if resume_result == 0:
+                # User selected to resume, append resume point to listitem
+                listItem.setProperty('StartOffset', str(seekTime))
+            
+            elif resume_result > 0:
+                # User selected to start from beginning
+                seekTime = 0
+
+            else: # User cancelled the dialog
+                self.logMsg("User cancelled resume dialog.", 1)
+                return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listItem)
 
         # We need to ensure we add the intro and additional parts only once.
         # Otherwise we get a loop.
         if not propertiesPlayback:
 
-            utils.window('emby_playbackProps', value="true", windowid=10101)
-            self.logMsg("Setting up properties in playlist.", 1)
-
-            if (not homeScreen and not seektime and 
-                    utils.window('emby_customPlaylist', windowid=10101) != "true"):
-                
-                self.logMsg("Adding dummy file to playlist.", 2)
-                dummyPlaylist = True
-                playlist.add(playurl, listitem, index=startPos)
-                # Remove the original item from playlist 
-                self.pl.removefromPlaylist(startPos+1)
-                # Readd the original item to playlist - via jsonrpc so we have full metadata
-                self.pl.insertintoPlaylist(currentPosition+1, dbid, item['Type'].lower())
-                currentPosition += 1
+            utils.window('propertiesPlayback', value="true")
+            self.logMsg("Setting up properties in playlist.")
             
             ############### -- CHECK FOR INTROS ################
 
-            if utils.settings('enableCinema') == "true" and not seektime:
+            if utils.settings('disableCinema') == "false" and not seekTime:
                 # if we have any play them when the movie/show is not being resumed
-                url = "{server}/emby/Users/{UserId}/Items/%s/Intros?format=json" % itemid    
+                url = "{server}/mediabrowser/Users/{UserId}/Items/%s/Intros?format=json&ImageTypeLimit=1&Fields=Etag" % id    
                 intros = doUtils.downloadUrl(url)
 
                 if intros['TotalRecordCount'] != 0:
@@ -126,15 +141,17 @@ class PlaybackUtils():
                     if getTrailers:
                         for intro in intros['Items']:
                             # The server randomly returns intros, process them.
+                            introId = intro['Id']
+                            
+                            introPlayurl = PlayUtils().getPlayUrl(server, introId, intro)
                             introListItem = xbmcgui.ListItem()
-                            introPlayurl = putils.PlayUtils(intro).getPlayUrl()
                             self.logMsg("Adding Intro: %s" % introPlayurl, 1)
 
                             # Set listitem and properties for intros
-                            pbutils = PlaybackUtils(intro)
-                            pbutils.setProperties(introPlayurl, introListItem)
-
-                            self.pl.insertintoPlaylist(currentPosition, url=introPlayurl)
+                            self.setProperties(introPlayurl, intro, introListItem)
+                            self.setListItemProps(server, introId, introListItem, intro)
+                            
+                            playlist.add(introPlayurl, introListItem, index=currentPosition)
                             introsPlaylist = True
                             currentPosition += 1
 
@@ -142,126 +159,109 @@ class PlaybackUtils():
             ############### -- ADD MAIN ITEM ONLY FOR HOMESCREEN ###############
 
             if homeScreen and not sizePlaylist:
-                # Extend our current playlist with the actual item to play
-                # only if there's no playlist first
+                # Extend our current playlist with the actual item to play only if there's no playlist first
                 self.logMsg("Adding main item to playlist.", 1)
-                self.pl.addtoPlaylist(dbid, item['Type'].lower())
-
+                self.setListItemProps(server, id, listItem, result)
+                playlist.add(playurl, listItem, index=currentPosition)
+            
             # Ensure that additional parts are played after the main item
             currentPosition += 1
 
+
             ############### -- CHECK FOR ADDITIONAL PARTS ################
             
-            if item.get('PartCount'):
+            if result.get('PartCount'):
                 # Only add to the playlist after intros have played
-                partcount = item['PartCount']
-                url = "{server}/emby/Videos/%s/AdditionalParts?format=json" % itemid
+                partcount = result['PartCount']
+                url = "{server}/mediabrowser/Videos/%s/AdditionalParts" % id
                 parts = doUtils.downloadUrl(url)
+
                 for part in parts['Items']:
 
+                    partId = part['Id']
+                    additionalPlayurl = PlayUtils().getPlayUrl(server, partId, part)
                     additionalListItem = xbmcgui.ListItem()
-                    additionalPlayurl = putils.PlayUtils(part).getPlayUrl()
                     self.logMsg("Adding additional part: %s" % partcount, 1)
 
                     # Set listitem and properties for each additional parts
-                    pbutils = PlaybackUtils(part)
-                    pbutils.setProperties(additionalPlayurl, additionalListItem)
-                    pbutils.setArtwork(additionalListItem)
+                    self.setProperties(additionalPlayurl, part, additionalListItem)
+                    self.setListItemProps(server, partId, additionalListItem, part)
 
                     playlist.add(additionalPlayurl, additionalListItem, index=currentPosition)
-                    self.pl.verifyPlaylist()
                     currentPosition += 1
 
-            if dummyPlaylist:
-                # Added a dummy file to the playlist,
-                # because the first item is going to fail automatically.
-                self.logMsg("Processed as a playlist. First item is skipped.", 1)
-                return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listitem)
+            
+            ############### ADD DUMMY TO PLAYLIST #################
+
+            if (not homeScreen and introsPlaylist) or (homeScreen and sizePlaylist > 0):
+                # Playlist will fail on the current position. Adding dummy url
+                dummyPlaylist = True
+                self.logMsg("Adding dummy url to counter the setResolvedUrl error.", 2)
+                playlist.add(playurl, index=startPos)
+                currentPosition += 1
                 
 
         # We just skipped adding properties. Reset flag for next time.
         elif propertiesPlayback:
             self.logMsg("Resetting properties playback flag.", 2)
-            utils.window('emby_playbackProps', clear=True, windowid=10101)
+            utils.window('propertiesPlayback', clear=True)
 
-        #self.pl.verifyPlaylist()
-        ########## SETUP MAIN ITEM ##########
 
-        # For transcoding only, ask for audio/subs pref
-        if utils.window('emby_%s.playmethod' % playurl) == "Transcode":
-            playurl = playutils.audioSubsPref(playurl)
-            utils.window('emby_%s.playmethod' % playurl, value="Transcode")
-
-        listitem.setPath(playurl)
-        self.setProperties(playurl, listitem)
+        self.verifyPlaylist()
 
         ############### PLAYBACK ################
+        
+        if not homeScreen and not introsPlaylist:
+            
+            self.logMsg("Processed as a single item.", 1)
+            xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listItem)
 
-        if homeScreen and seektime:
-            self.logMsg("Play as a widget item.", 1)
-            self.setListItem(listitem)
-            xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
-
-        elif ((introsPlaylist and utils.window('emby_customPlaylist', windowid=10101) == "true") or
-            (homeScreen and not sizePlaylist)):
-            # Playlist was created just now, play it.
-            self.logMsg("Play playlist.", 1)
-            xbmc.Player().play(playlist, startpos=startPos)
+        elif dummyPlaylist:
+            # Added a dummy file to the playlist because the first item is going to fail automatically.
+            self.logMsg("Processed as a playlist. First item is skipped.", 1)
+            xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, listItem)
 
         else:
             self.logMsg("Play as a regular item.", 1)
-            xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
+            xbmc.Player().play(playlist, startpos=startPos)
 
-    def setProperties(self, playurl, listitem):
-        # Set all properties necessary for plugin path playback
-        item = self.item
-        itemid = item['Id']
-        itemtype = item['Type']
+                
+    def verifyPlaylist(self):
+        
+        playlistitems = '{"jsonrpc": "2.0", "method": "Playlist.GetItems", "params": { "playlistid": 1 }, "id": 1}'
+        items = xbmc.executeJSONRPC(playlistitems)
+        self.logMsg(items, 2)
 
-        embyitem = "emby_%s" % playurl
-        utils.window('%s.runtime' % embyitem, value=str(item.get('RunTimeTicks')))
-        utils.window('%s.type' % embyitem, value=itemtype)
-        utils.window('%s.itemid' % embyitem, value=itemid)
+    def removeFromPlaylist(self, pos):
 
-        if itemtype == "Episode":
-            utils.window('%s.refreshid' % embyitem, value=item.get('SeriesId'))
-        else:
-            utils.window('%s.refreshid' % embyitem, value=itemid)
+        playlistremove = '{"jsonrpc": "2.0", "method": "Playlist.Remove", "params": { "playlistid": 1, "position": %d }, "id": 1}' % pos
+        result = xbmc.executeJSONRPC(playlistremove)
+        self.logMsg(result, 1)
 
-        # Append external subtitles to stream
-        playmethod = utils.window('%s.playmethod' % embyitem)
-        # Only for direct play and direct stream
-        subtitles = self.externalSubs(playurl)
-        if playmethod in ("DirectStream", "Transcode"):
-            # Direct play automatically appends external
-            listitem.setSubtitles(subtitles)
 
-        self.setArtwork(listitem)
-
-    def externalSubs(self, playurl):
+    def externalSubs(self, id, playurl, mediaSources):
 
+        username = utils.window('currUser')
+        server = utils.window('server%s' % username)
         externalsubs = []
         mapping = {}
 
-        item = self.item
-        itemid = item['Id']
-        try:
-            mediastreams = item['MediaSources'][0]['MediaStreams']
-        except (TypeError, KeyError, IndexError):
-            return
-
+        mediaStream = mediaSources[0].get('MediaStreams')
         kodiindex = 0
-        for stream in mediastreams:
-
+        for stream in mediaStream:
+            
             index = stream['Index']
             # Since Emby returns all possible tracks together, have to pull only external subtitles.
             # IsTextSubtitleStream if true, is available to download from emby.
-            if (stream['Type'] == "Subtitle" and 
-                    stream['IsExternal'] and stream['IsTextSubtitleStream']):
+            if "Subtitle" in stream['Type'] and stream['IsExternal'] and stream['IsTextSubtitleStream']:
+                
+                playmethod = utils.window("%splaymethod" % playurl)
 
-                # Direct stream
-                url = ("%s/Videos/%s/%s/Subtitles/%s/Stream.srt"
-                        % (self.server, itemid, itemid, index))
+                if "DirectPlay" in playmethod:
+                    # Direct play, get direct path
+                    url = PlayUtils().directPlay(stream)
+                elif "DirectStream" in playmethod: # Direct stream
+                    url = "%s/Videos/%s/%s/Subtitles/%s/Stream.srt" % (server, id, id, index)
                 
                 # map external subtitles for mapping
                 mapping[kodiindex] = index
@@ -269,79 +269,69 @@ class PlaybackUtils():
                 kodiindex += 1
         
         mapping = json.dumps(mapping)
-        utils.window('emby_%s.indexMapping' % playurl, value=mapping)
+        utils.window('%sIndexMapping' % playurl, value=mapping)
 
         return externalsubs
 
-    def setArtwork(self, listItem):
-        # Set up item and item info
-        item = self.item
-        artwork = self.artwork
 
-        allartwork = artwork.getAllArtwork(item, parentInfo=True)
-        # Set artwork for listitem
-        arttypes = {
+    def setProperties(self, playurl, result, listItem):
+        # Set runtimeticks, type, refresh_id and item_id
+        id = result.get('Id')
+        type = result.get('Type', "")
 
-            'poster': "Primary",
-            'tvshow.poster': "Primary",
-            'clearart': "Art",
-            'tvshow.clearart': "Art",
-            'clearlogo': "Logo",
-            'tvshow.clearlogo': "Logo",
-            'discart': "Disc",
-            'fanart_image': "Backdrop",
-            'landscape': "Thumb"
-        }
-        for arttype in arttypes:
+        utils.window("%sruntimeticks" % playurl, value=str(result.get('RunTimeTicks')))
+        utils.window("%stype" % playurl, value=type)
+        utils.window("%sitem_id" % playurl, value=id)
 
-            art = arttypes[arttype]
-            if art == "Backdrop":
-                try: # Backdrop is a list, grab the first backdrop
-                    self.setArtProp(listItem, arttype, allartwork[art][0])
-                except: pass
-            else:
-                self.setArtProp(listItem, arttype, allartwork[art])
-
-    def setArtProp(self, listItem, arttype, path):
-        
-        if arttype in (
-                'thumb', 'fanart_image', 'small_poster', 'tiny_poster',
-                'medium_landscape', 'medium_poster', 'small_fanartimage',
-                'medium_fanartimage', 'fanart_noindicators'):
-            
-            listItem.setProperty(arttype, path)
+        if type == "Episode":
+            utils.window("%srefresh_id" % playurl, value=result.get('SeriesId'))
         else:
-            listItem.setArt({arttype: path})
+            utils.window("%srefresh_id" % playurl, value=id)
 
-    def setListItem(self, listItem):
+        if utils.window("%splaymethod" % playurl) != "Transcode":
+            # Only for direct play and direct stream
+            # Append external subtitles to stream
+            subtitleList = self.externalSubs(id, playurl, result['MediaSources'])
+            listItem.setSubtitles(subtitleList)
 
-        item = self.item
-        type = item['Type']
-        API = self.API
-        people = API.getPeople()
-        studios = API.getStudios()
+    def setArt(self, list, name, path):
+        
+        if name in ("thumb", "fanart_image", "small_poster", "tiny_poster", "medium_landscape", "medium_poster", "small_fanartimage", "medium_fanartimage", "fanart_noindicators"):
+            list.setProperty(name, path)
+        else:
+            list.setArt({name:path})
+        
+        return list
+    
+    def setListItemProps(self, server, id, listItem, result):
+        # Set up item and item info
+        api = self.api
+
+        type = result.get('Type')
+        people = api.getPeople(result)
+        studios = api.getStudios(result)
 
         metadata = {
             
-            'title': item.get('Name', "Missing name"),
-            'year': item.get('ProductionYear'),
-            'plot': API.getOverview(),
+            'title': result.get('Name', "Missing name"),
+            'year': result.get('ProductionYear'),
+            'plot': api.getOverview(result),
             'director': people.get('Director'),
             'writer': people.get('Writer'),
-            'mpaa': API.getMpaa(),
-            'genre': " / ".join(item['Genres']),
+            'mpaa': api.getMpaa(result),
+            'genre': api.getGenre(result),
             'studio': " / ".join(studios),
-            'aired': API.getPremiereDate(),
-            'rating': item.get('CommunityRating'),
-            'votes': item.get('VoteCount')
+            'aired': api.getPremiereDate(result),
+            'rating': result.get('CommunityRating'),
+            'votes': result.get('VoteCount')
         }
 
         if "Episode" in type:
             # Only for tv shows
-            thumbId = item.get('SeriesId')
-            season = item.get('ParentIndexNumber', -1)
-            episode = item.get('IndexNumber', -1)
-            show = item.get('SeriesName', "")
+            thumbId = result.get('SeriesId')
+            season = result.get('ParentIndexNumber', -1)
+            episode = result.get('IndexNumber', -1)
+            show = result.get('SeriesName', "")
 
             metadata['TVShowTitle'] = show
             metadata['season'] = season
@@ -350,4 +340,123 @@ class PlaybackUtils():
         listItem.setProperty('IsPlayable', 'true')
         listItem.setProperty('IsFolder', 'false')
         listItem.setLabel(metadata['title'])
-        listItem.setInfo('video', infoLabels=metadata)
\ No newline at end of file
+        listItem.setInfo('video', infoLabels=metadata)
+
+        # Set artwork for listitem
+        self.setArt(listItem,'poster', API().getArtwork(result, "Primary"))
+        self.setArt(listItem,'tvshow.poster', API().getArtwork(result, "SeriesPrimary"))
+        self.setArt(listItem,'clearart', API().getArtwork(result, "Art"))
+        self.setArt(listItem,'tvshow.clearart', API().getArtwork(result, "Art"))
+        self.setArt(listItem,'clearlogo', API().getArtwork(result, "Logo"))
+        self.setArt(listItem,'tvshow.clearlogo', API().getArtwork(result, "Logo"))
+        self.setArt(listItem,'discart', API().getArtwork(result, "Disc"))
+        self.setArt(listItem,'fanart_image', API().getArtwork(result, "Backdrop"))
+        self.setArt(listItem,'landscape', API().getArtwork(result, "Thumb"))
+    
+    def seekToPosition(self, seekTo):
+        # Set a loop to wait for positive confirmation of playback
+        count = 0
+        while not xbmc.Player().isPlaying():
+            count += 1
+            if count >= 10:
+                return
+            else:
+                xbmc.sleep(500)
+            
+        # Jump to seek position
+        count = 0
+        while xbmc.Player().getTime() < (seekToTime - 5) and count < 11: # only try 10 times
+            count += 1
+            xbmc.Player().seekTime(seekTo)
+            xbmc.sleep(100)
+    
+    def PLAYAllItems(self, items, startPositionTicks):
+        
+        self.logMsg("== ENTER: PLAYAllItems ==")
+        self.logMsg("Items: %s" % items)
+
+        doUtils = self.doUtils
+
+        playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
+        playlist.clear()
+        started = False
+
+        for itemId in items:
+            self.logMsg("Adding Item to playlist: %s" % itemId, 1)
+            url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json" % itemId
+            result = doUtils.downloadUrl(url)
+
+            addition = self.addPlaylistItem(playlist, result)
+            if not started and addition:
+                started = True
+                self.logMsg("Starting Playback Pre", 1)
+                xbmc.Player().play(playlist)
+
+        if not started:
+            self.logMsg("Starting Playback Post", 1)
+            xbmc.Player().play(playlist)
+
+        # Seek to position
+        if startPositionTicks:
+            seekTime = startPositionTicks / 10000000.0
+            self.seekToPosition(seekTime)
+    
+    def AddToPlaylist(self, itemIds):
+
+        self.logMsg("== ENTER: PLAYAllItems ==")
+        
+        doUtils = self.doUtils
+        playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
+
+        for itemId in itemIds:
+            self.logMsg("Adding Item to Playlist: %s" % itemId)
+            url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json" % itemId
+            result = doUtils.downloadUrl(url)
+
+            self.addPlaylistItem(playlist, result)
+        
+        return playlist
+    
+    def addPlaylistItem(self, playlist, item):
+
+        id = item['Id']
+        username = utils.window('currUser')
+        server = utils.window('server%s' % username)
+
+        playurl = PlayUtils().getPlayUrl(server, id, item)
+        
+        if utils.window('playurlFalse') == "true":
+            # Playurl failed - set in PlayUtils.py
+            utils.window('playurlFalse', clear=True)
+            self.logMsg("Failed to retrieve the playback path/url or dialog was cancelled.", 1)
+            return
+
+        self.logMsg("Playurl: %s" % playurl)
+
+        thumb = API().getArtwork(item, "Primary")
+        listItem = xbmcgui.ListItem(path=playurl, iconImage=thumb, thumbnailImage=thumb)
+        self.setListItemProps(server, id, listItem, item)
+        self.setProperties(playurl, item, listItem)
+
+        playlist.add(playurl, listItem)
+
+    # Not currently being used
+    '''def PLAYAllEpisodes(self, items):
+        WINDOW = xbmcgui.Window(10000)
+
+        username = WINDOW.getProperty('currUser')
+        userid = WINDOW.getProperty('userId%s' % username)
+        server = WINDOW.getProperty('server%s' % username)
+        
+        playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
+        playlist.clear()        
+        
+        for item in items:
+        
+            item_url = "{server}/mediabrowser/Users/{UserId}/Items/%s?format=json&ImageTypeLimit=1" % item["Id"]
+            jsonData = self.downloadUtils.downloadUrl(item_url)
+            
+            item_data = jsonData
+            self.addPlaylistItem(playlist, item_data, server, userid)
+        
+        xbmc.Player().play(playlist)'''
\ No newline at end of file
diff --git a/resources/lib/Player.py b/resources/lib/Player.py
index b6d25e19..0b760a88 100644
--- a/resources/lib/Player.py
+++ b/resources/lib/Player.py
@@ -2,47 +2,45 @@
 
 #################################################################################################
 
-import json
+import json as json
 
 import xbmc
 import xbmcgui
 
-import utils
-import clientinfo
-import downloadutils
-import kodidb_functions as kodidb
-import websocket_client as wsc
+from DownloadUtils import DownloadUtils
+from WebSocketClient import WebSocketThread
+from ClientInformation import ClientInformation
+from LibrarySync import LibrarySync
+import Utils as utils
 
 #################################################################################################
 
-
-class Player(xbmc.Player):
+class Player( xbmc.Player ):
 
     # Borg - multiple instances, shared state
     _shared_state = {}
 
-    played_info = {}
+    xbmcplayer = xbmc.Player()
+    doUtils = DownloadUtils()
+    clientInfo = ClientInformation()
+    ws = WebSocketThread()
+    librarySync = LibrarySync()
+
+    addonName = clientInfo.getAddonName()
+
+    played_information = {}
     playStats = {}
     currentFile = None
 
-
-    def __init__(self):
+    def __init__(self, *args):
 
         self.__dict__ = self._shared_state
-
-        self.clientInfo = clientinfo.ClientInfo()
-        self.addonName = self.clientInfo.getAddonName()
-        self.doUtils = downloadutils.DownloadUtils()
-        self.ws = wsc.WebSocket_Client()
-        self.xbmcplayer = xbmc.Player()
-
         self.logMsg("Starting playback monitor.", 2)
 
     def logMsg(self, msg, lvl=1):
         
         self.className = self.__class__.__name__
-        utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
-
+        utils.logMsg("%s %s" % (self.addonName, self.className), msg, int(lvl))
 
     def GetPlayStats(self):
         return self.playStats
@@ -76,50 +74,39 @@ class Player(xbmc.Player):
             self.currentFile = currentFile
             
             # We may need to wait for info to be set in kodi monitor
-            itemId = utils.window("emby_%s.itemid" % currentFile)
+            itemId = utils.window("%sitem_id" % currentFile)
             tryCount = 0
             while not itemId:
                 
                 xbmc.sleep(200)
-                itemId = utils.window("emby_%s.itemid" % currentFile)
+                itemId = utils.window("%sitem_id" % currentFile)
                 if tryCount == 20: # try 20 times or about 10 seconds
                     self.logMsg("Could not find itemId, cancelling playback report...", 1)
                     break
                 else: tryCount += 1
             
             else:
-                self.logMsg("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId), 0)
+                self.logMsg("ONPLAYBACK_STARTED: %s ITEMID: %s" % (currentFile, itemId), 0)
 
                 # Only proceed if an itemId was found.
-                embyitem = "emby_%s" % currentFile
-                runtime = utils.window("%s.runtime" % embyitem)
-                refresh_id = utils.window("%s.refreshid" % embyitem)
-                playMethod = utils.window("%s.playmethod" % embyitem)
-                itemType = utils.window("%s.type" % embyitem)
-                utils.window('emby_skipWatched%s' % itemId, value="true")
-
+                runtime = utils.window("%sruntimeticks" % currentFile)
+                refresh_id = utils.window("%srefresh_id" % currentFile)
+                playMethod = utils.window("%splaymethod" % currentFile)
+                itemType = utils.window("%stype" % currentFile)
                 seekTime = xbmcplayer.getTime()
 
+
                 # Get playback volume
-                volume_query = {
-
-                    "jsonrpc": "2.0",
-                    "id": 1,
-                    "method": "Application.GetProperties",
-                    "params": {
-
-                        "properties": ["volume", "muted"] 
-                    }
-                }
-                result = xbmc.executeJSONRPC(json.dumps(volume_query))
+                volume_query = '{"jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["volume","muted"]}, "id": 1}'
+                result = xbmc.executeJSONRPC(volume_query)
                 result = json.loads(result)
                 result = result.get('result')
-                
+
                 volume = result.get('volume')
                 muted = result.get('muted')
 
                 # Postdata structure to send to Emby server
-                url = "{server}/emby/Sessions/Playing"
+                url = "{server}/mediabrowser/Sessions/Playing"
                 postdata = {
 
                     'QueueableMediaTypes': "Video",
@@ -136,22 +123,12 @@ class Player(xbmc.Player):
                 if playMethod == "Transcode":
                     # property set in PlayUtils.py
                     postdata['AudioStreamIndex'] = utils.window("%sAudioStreamIndex" % currentFile)
-                    postdata['SubtitleStreamIndex'] = utils.window("%sSubtitleStreamIndex"
-                                                                    % currentFile)
+                    postdata['SubtitleStreamIndex'] = utils.window("%sSubtitleStreamIndex" % currentFile)
+
                 else:
                     # Get the current kodi audio and subtitles and convert to Emby equivalent
-                    tracks_query = {
-
-                        "jsonrpc": "2.0",
-                        "id": 1,
-                        "method": "Player.GetProperties",
-                        "params": {
-
-                            "playerid": 1,
-                            "properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]
-                        }
-                    }
-                    result = xbmc.executeJSONRPC(json.dumps(tracks_query))
+                    track_query = '{"jsonrpc": "2.0", "method": "Player.GetProperties",  "params": {"playerid": 1,"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]} , "id": 1}'
+                    result = xbmc.executeJSONRPC(track_query)
                     result = json.loads(result)
                     result = result.get('result')
 
@@ -178,9 +155,9 @@ class Player(xbmc.Player):
                         
                         # Number of audiotracks to help get Emby Index
                         audioTracks = len(xbmc.Player().getAvailableAudioStreams())
-                        mapping = utils.window("%s.indexMapping" % embyitem)
+                        mapping = utils.window("%sIndexMapping" % currentFile)
 
-                        if mapping: # Set in playbackutils.py
+                        if mapping: # Set in PlaybackUtils.py
                             
                             self.logMsg("Mapping for external subtitles index: %s" % mapping, 2)
                             externalIndex = json.loads(mapping)
@@ -190,8 +167,7 @@ class Player(xbmc.Player):
                                 postdata['SubtitleStreamIndex'] = externalIndex[str(indexSubs)]
                             else:
                                 # Internal subtitle currently selected
-                                subindex = indexSubs - len(externalIndex) + audioTracks + 1
-                                postdata['SubtitleStreamIndex'] = subindex
+                                postdata['SubtitleStreamIndex'] = indexSubs - len(externalIndex) + audioTracks + 1
                         
                         else: # Direct paths enabled scenario or no external subtitles set
                             postdata['SubtitleStreamIndex'] = indexSubs + audioTracks + 1
@@ -208,7 +184,7 @@ class Player(xbmc.Player):
                     runtime = int(runtime)
                 except ValueError:
                     runtime = xbmcplayer.getTotalTime()
-                    self.logMsg("Runtime is missing, Kodi runtime: %s" % runtime, 1)
+                    self.logMsg("Runtime is missing, grabbing runtime from Kodi player: %s" % runtime, 1)
 
                 # Save data map for updates and position calls
                 data = {
@@ -224,8 +200,8 @@ class Player(xbmc.Player):
                     'currentPosition': int(seekTime)
                 }
                 
-                self.played_info[currentFile] = data
-                self.logMsg("ADDING_FILE: %s" % self.played_info, 1)
+                self.played_information[currentFile] = data
+                self.logMsg("ADDING_FILE: %s" % self.played_information, 1)
 
                 # log some playback stats
                 '''if(itemType != None):
@@ -249,7 +225,7 @@ class Player(xbmc.Player):
 
         # Get current file
         currentFile = self.currentFile
-        data = self.played_info.get(currentFile)
+        data = self.played_information.get(currentFile)
 
         # only report playback if emby has initiated the playback (item_id has value)
         if data:
@@ -263,23 +239,15 @@ class Player(xbmc.Player):
 
 
             # Get playback volume
-            volume_query = {
-
-                    "jsonrpc": "2.0",
-                    "id": 1,
-                    "method": "Application.GetProperties",
-                    "params": {
-
-                        "properties": ["volume", "muted"] 
-                    }
-                }
-            result = xbmc.executeJSONRPC(json.dumps(volume_query))
+            volume_query = '{"jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["volume","muted"]}, "id": 1}'
+            result = xbmc.executeJSONRPC(volume_query)
             result = json.loads(result)
             result = result.get('result')
 
             volume = result.get('volume')
             muted = result.get('muted')
 
+
             # Postdata for the websocketclient report
             postdata = {
 
@@ -301,18 +269,8 @@ class Player(xbmc.Player):
 
             else:
                 # Get current audio and subtitles track
-                tracks_query = {
-
-                        "jsonrpc": "2.0",
-                        "id": 1,
-                        "method": "Player.GetProperties",
-                        "params": {
-
-                            "playerid": 1,
-                            "properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]
-                        }
-                    }
-                result = xbmc.executeJSONRPC(json.dumps(tracks_query))
+                track_query = '{"jsonrpc": "2.0", "method": "Player.GetProperties",  "params": {"playerid":1,"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]} , "id": 1}'
+                result = xbmc.executeJSONRPC(track_query)
                 result = json.loads(result)
                 result = result.get('result')
 
@@ -339,7 +297,7 @@ class Player(xbmc.Player):
                     
                     # Number of audiotracks to help get Emby Index
                     audioTracks = len(xbmc.Player().getAvailableAudioStreams())
-                    mapping = utils.window("emby_%s.indexMapping" % currentFile)
+                    mapping = utils.window("%sIndexMapping" % currentFile)
 
                     if mapping: # Set in PlaybackUtils.py
                         
@@ -348,16 +306,13 @@ class Player(xbmc.Player):
 
                         if externalIndex.get(str(indexSubs)):
                             # If the current subtitle is in the mapping
-                            subindex = [externalIndex[str(indexSubs)]] * 2
-                            data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = subindex
+                            data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = [externalIndex[str(indexSubs)]] * 2
                         else:
                             # Internal subtitle currently selected
-                            subindex = [indexSubs - len(externalIndex) + audioTracks + 1] * 2
-                            data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = subindex
+                            data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = [indexSubs - len(externalIndex) + audioTracks + 1] * 2
                     
                     else: # Direct paths enabled scenario or no external subtitles set
-                        subindex = [indexSubs + audioTracks + 1] * 2
-                        data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = subindex
+                        data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = [indexSubs + audioTracks + 1] * 2
                 else:
                     data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = [""] * 2
 
@@ -371,8 +326,8 @@ class Player(xbmc.Player):
         currentFile = self.currentFile
         self.logMsg("PLAYBACK_PAUSED: %s" % currentFile, 2)
 
-        if self.played_info.get(currentFile):
-            self.played_info[currentFile]['paused'] = True
+        if self.played_information.get(currentFile):
+            self.played_information[currentFile]['paused'] = True
         
             self.reportPlayback()
 
@@ -381,8 +336,8 @@ class Player(xbmc.Player):
         currentFile = self.currentFile
         self.logMsg("PLAYBACK_RESUMED: %s" % currentFile, 2)
 
-        if self.played_info.get(currentFile):
-            self.played_info[currentFile]['paused'] = False
+        if self.played_information.get(currentFile):
+            self.played_information[currentFile]['paused'] = False
         
             self.reportPlayback()
 
@@ -391,17 +346,15 @@ class Player(xbmc.Player):
         currentFile = self.currentFile
         self.logMsg("PLAYBACK_SEEK: %s" % currentFile, 2)
 
-        if self.played_info.get(currentFile):
+        if self.played_information.get(currentFile):
             position = self.xbmcplayer.getTime()
-            self.played_info[currentFile]['currentPosition'] = position
+            self.played_information[currentFile]['currentPosition'] = position
 
             self.reportPlayback()
     
     def onPlayBackStopped( self ):
         # Will be called when user stops xbmc playing a file
         self.logMsg("ONPLAYBACK_STOPPED", 2)
-        xbmcgui.Window(10101).clearProperties()
-        self.logMsg("Clear playlist properties.")
         self.stopAll()
 
     def onPlayBackEnded( self ):
@@ -411,16 +364,14 @@ class Player(xbmc.Player):
 
     def stopAll(self):
 
-        doUtils = self.doUtils
-
-        if not self.played_info:
+        if not self.played_information:
             return 
             
-        self.logMsg("Played_information: %s" % self.played_info, 1)
+        self.logMsg("Played_information: %s" % self.played_information, 1)
         # Process each items
-        for item in self.played_info:
+        for item in self.played_information:
             
-            data = self.played_info.get(item)
+            data = self.played_information.get(item)
             if data:
                 
                 self.logMsg("Item path: %s" % item, 2)
@@ -428,61 +379,47 @@ class Player(xbmc.Player):
 
                 runtime = data['runtime']
                 currentPosition = data['currentPosition']
-                itemid = data['item_id']
+                itemId = data['item_id']
                 refresh_id = data['refresh_id']
                 currentFile = data['currentfile']
                 type = data['Type']
                 playMethod = data['playmethod']
 
                 if currentPosition and runtime:
-                    try:
-                        percentComplete = (currentPosition * 10000000) / int(runtime)
-                    except ZeroDivisionError:
-                        # Runtime is 0.
-                        percentComplete = 0
-                        
+                    percentComplete = (currentPosition * 10000000) / int(runtime)
                     markPlayedAt = float(utils.settings('markPlayed')) / 100
-                    self.logMsg(
-                        "Percent complete: %s Mark played at: %s"
-                        % (percentComplete, markPlayedAt), 1)
 
-                    # Prevent manually mark as watched in Kodi monitor
-                    utils.window('emby_skipWatched%s' % itemid, value="true")
+                    self.logMsg("Percent complete: %s Mark played at: %s" % (percentComplete, markPlayedAt), 1)
+                    # Prevent manually mark as watched in Kodi monitor > WriteKodiVideoDB().UpdatePlaycountFromKodi()
+                    utils.window('SkipWatched%s' % itemId, "true")
 
                     self.stopPlayback(data)
-                    # Stop transcoding
-                    if playMethod == "Transcode":
-                        self.logMsg("Transcoding for %s terminated." % itemid, 1)
-                        deviceId = self.clientInfo.getDeviceId()
-                        url = "{server}/emby/Videos/ActiveEncodings?DeviceId=%s" % deviceId
-                        doUtils.downloadUrl(url, type="DELETE")
+                    offerDelete = utils.settings('offerDelete') == "true"
+                    offerTypeDelete = False
 
-                    # Send the delete action to the server.
-                    offerDelete = False
+                    if type == "Episode" and utils.settings('offerDeleteTV') == "true":
+                        offerTypeDelete = True
 
-                    if type == "Episode" and utils.settings('deleteTV') == "true":
-                        offerDelete = True
-                    elif type == "Movie" and utils.settings('deleteMovies') == "true":
-                        offerDelete = True
+                    elif type == "Movie" and utils.settings('offerDeleteMovies') == "true":
+                        offerTypeDelete = True
 
-                    if utils.settings('offerDelete') != "true":
-                        # Delete could be disabled, even if the subsetting is enabled.
-                        offerDelete = False
-
-                    if percentComplete >= markPlayedAt and offerDelete:
-                        if utils.settings('skipConfirmDelete') != "true":
-                            resp = xbmcgui.Dialog().yesno(
-                                                    heading="Confirm delete",
-                                                    line1="Delete file on Emby Server?")
-                            if not resp:
-                                self.logMsg("User skipped deletion.", 1)
-                                continue
-
-                        url = "{server}/emby/Items/%s?format=json" % itemid
-                        self.logMsg("Deleting request: %s" % itemid)
-                        doUtils.downloadUrl(url, type="DELETE")
+                    if percentComplete >= markPlayedAt and offerDelete and offerTypeDelete:
+                        # Make the bigger setting be able to disable option easily.
+                        self.logMsg("Offering deletion for: %s." % itemId, 1)
+                        return_value = xbmcgui.Dialog().yesno("Offer Delete", "Delete %s" % currentFile.split("/")[-1], "on Emby Server?")
+                        if return_value:
+                            # Delete Kodi entry before Emby
+                            listItem = [itemId]
+                            LibrarySync().removefromDB(listItem, True)
+                    
+                # Stop transcoding
+                if playMethod == "Transcode":
+                    self.logMsg("Transcoding for %s terminated." % itemId, 1)
+                    deviceId = self.clientInfo.getMachineId()
+                    url = "{server}/mediabrowser/Videos/ActiveEncodings?DeviceId=%s" % deviceId
+                    self.doUtils.downloadUrl(url, type="DELETE")
     
-        self.played_info.clear()
+        self.played_information.clear()
     
     def stopPlayback(self, data):
         
@@ -492,11 +429,12 @@ class Player(xbmc.Player):
         currentPosition = data['currentPosition']
         positionTicks = int(currentPosition * 10000000)
 
-        url = "{server}/emby/Sessions/Playing/Stopped"
+        url = "{server}/mediabrowser/Sessions/Playing/Stopped"
         postdata = {
             
             'ItemId': itemId,
             'MediaSourceId': itemId,
             'PositionTicks': positionTicks
         }
+            
         self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
\ No newline at end of file
diff --git a/resources/lib/UserClient.py b/resources/lib/UserClient.py
index a6562a53..f95f9291 100644
--- a/resources/lib/UserClient.py
+++ b/resources/lib/UserClient.py
@@ -1,21 +1,22 @@
-# -*- coding: utf-8 -*-
-
-##################################################################################################
-
-import hashlib
-import threading
+#################################################################################################
+# UserClient thread
+#################################################################################################
 
 import xbmc
 import xbmcgui
 import xbmcaddon
 import xbmcvfs
 
-import artwork
-import utils
-import clientinfo
-import downloadutils
+import threading
+import hashlib
+import json as json
 
-##################################################################################################
+import KodiMonitor
+import Utils as utils
+from ClientInformation import ClientInformation
+from DownloadUtils import DownloadUtils
+from Player import Player
+from API import API
 
 
 class UserClient(threading.Thread):
@@ -23,7 +24,16 @@ class UserClient(threading.Thread):
     # Borg - multiple instances, shared state
     _shared_state = {}
 
+    clientInfo = ClientInformation()
+    doUtils = DownloadUtils()
+    KodiMonitor = KodiMonitor.Kodi_Monitor()
+    
+    addonName = clientInfo.getAddonName()
+    addon = xbmcaddon.Addon()
+    WINDOW = xbmcgui.Window(10000)
+
     stopClient = False
+    logLevel = int(addon.getSetting('logLevel'))
     auth = True
     retry = 0
 
@@ -34,25 +44,25 @@ class UserClient(threading.Thread):
     HasAccess = True
     AdditionalUser = []
 
-    userSettings = None
-
-
-    def __init__(self):
+    def __init__(self, *args):
 
         self.__dict__ = self._shared_state
-        self.addon = xbmcaddon.Addon()
-
-        self.addonName = clientinfo.ClientInfo().getAddonName()
-        self.doUtils = downloadutils.DownloadUtils()
-        self.logLevel = int(utils.settings('logLevel'))
-        
-        threading.Thread.__init__(self)
+        threading.Thread.__init__(self, *args)
 
     def logMsg(self, msg, lvl=1):
         
         className = self.__class__.__name__
-        utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
+        utils.logMsg("%s %s" % (self.addonName, className), str(msg), int(lvl))
 
+    def getUsername(self):
+
+        username = utils.settings('username')
+
+        if (username == ""):
+            self.logMsg("No username saved.", 2)
+            return ""
+
+        return username
 
     def getAdditionalUsers(self):
 
@@ -61,21 +71,11 @@ class UserClient(threading.Thread):
         if additionalUsers:
             self.AdditionalUser = additionalUsers.split(',')
 
-    def getUsername(self):
-
-        username = utils.settings('username')
-
-        if not username:
-            self.logMsg("No username saved.", 2)
-            return ""
-
-        return username
-
     def getLogLevel(self):
 
         try:
             logLevel = int(utils.settings('logLevel'))
-        except ValueError:
+        except:
             logLevel = 0
         
         return logLevel
@@ -83,84 +83,71 @@ class UserClient(threading.Thread):
     def getUserId(self):
 
         username = self.getUsername()
-        w_userId = utils.window('emby_userId%s' % username)
+        w_userId = self.WINDOW.getProperty('userId%s' % username)
         s_userId = utils.settings('userId%s' % username)
 
         # Verify the window property
-        if w_userId:
-            if not s_userId:
-                # Save access token if it's missing from settings
-                utils.settings('userId%s' % username, value=w_userId)
-            self.logMsg(
-                "Returning userId from WINDOW for username: %s UserId: %s"
-                % (username, w_userId), 2)
+        if (w_userId != ""):
+            self.logMsg("Returning userId from WINDOW for username: %s UserId: %s" % (username, w_userId), 2)
             return w_userId
         # Verify the settings
-        elif s_userId:
-            self.logMsg(
-                "Returning userId from SETTINGS for username: %s userId: %s"
-                % (username, s_userId), 2)
+        elif (s_userId != ""):
+            self.logMsg("Returning userId from SETTINGS for username: %s userId: %s" % (username, s_userId), 2)
             return s_userId
         # No userId found
         else:
-            self.logMsg("No userId saved for username: %s." % username, 1)
+            self.logMsg("No userId saved for username: %s." % username)
+            return
 
     def getServer(self, prefix=True):
 
         alternate = utils.settings('altip') == "true"
+
+        # For https support
+        HTTPS = utils.settings('https')
+        host = utils.settings('ipaddress')
+        port = utils.settings('port')
+        # Alternate host
         if alternate:
-            # Alternate host
-            HTTPS = utils.settings('secondhttps') == "true"
+            HTTPS = utils.settings('secondhttps')
             host = utils.settings('secondipaddress')
             port = utils.settings('secondport')
-        else:
-            # Original host
-            HTTPS = utils.settings('https') == "true"
-            host = utils.settings('ipaddress')
-            port = utils.settings('port')
-
+            
         server = host + ":" + port
         
-        if not host:
+        if host == "":
             self.logMsg("No server information saved.", 2)
-            return False
+            return ""
 
         # If https is true
-        if prefix and HTTPS:
+        if prefix and (HTTPS == "true"):
             server = "https://%s" % server
             return server
         # If https is false
-        elif prefix and not HTTPS:
+        elif prefix and (HTTPS == "false"):
             server = "http://%s" % server
             return server
         # If only the host:port is required
-        elif not prefix:
+        elif (prefix == False):
             return server
 
     def getToken(self):
 
         username = self.getUsername()
-        w_token = utils.window('emby_accessToken%s' % username)
+        w_token = self.WINDOW.getProperty('accessToken%s' % username)
         s_token = utils.settings('accessToken')
         
         # Verify the window property
-        if w_token:
-            if not s_token:
-                # Save access token if it's missing from settings
-                utils.settings('accessToken', value=w_token)
-            self.logMsg(
-                "Returning accessToken from WINDOW for username: %s accessToken: %s"
-                % (username, w_token), 2)
+        if (w_token != ""):
+            self.logMsg("Returning accessToken from WINDOW for username: %s accessToken: %s" % (username, w_token), 2)
             return w_token
         # Verify the settings
-        elif s_token:
-            self.logMsg(
-                "Returning accessToken from SETTINGS for username: %s accessToken: %s"
-                % (username, s_token), 2)
-            utils.window('emby_accessToken%s' % username, value=s_token)
+        elif (s_token != ""):
+            self.logMsg("Returning accessToken from SETTINGS for username: %s accessToken: %s" % (username, s_token), 2)
+            self.WINDOW.setProperty('accessToken%s' % username, s_token)
             return s_token
         else:
-            self.logMsg("No token found.", 1)
+            self.logMsg("No token found.")
             return ""
 
     def getSSLverify(self):
@@ -187,63 +174,71 @@ class UserClient(threading.Thread):
 
     def setUserPref(self):
 
-        doUtils = self.doUtils
+        player = Player()
+        server = self.getServer()
+        userId = self.getUserId()
+
+        url = "{server}/mediabrowser/Users/{UserId}?format=json"
+        result = self.doUtils.downloadUrl(url)
 
-        url = "{server}/emby/Users/{UserId}?format=json"
-        result = doUtils.downloadUrl(url)
-        self.userSettings = result
         # Set user image for skin display
-        if result.get('PrimaryImageTag'):
-            utils.window('EmbyUserImage', value=artwork.Artwork().getUserArtwork(result, 'Primary'))
+        self.WINDOW.setProperty("EmbyUserImage",API().getUserArtwork(result,"Primary"))
 
-        # Set resume point max
-        url = "{server}/emby/System/Configuration?format=json"
-        result = doUtils.downloadUrl(url)
+        # Load the resume point from Emby and set as setting
+        url = "{server}/mediabrowser/System/Configuration?format=json"
+        result = self.doUtils.downloadUrl(url)
 
         utils.settings('markPlayed', value=str(result['MaxResumePct']))
 
+        return True
+
     def getPublicUsers(self):
 
         server = self.getServer()
 
         # Get public Users
-        url = "%s/emby/Users/Public?format=json" % server
+        url = "%s/mediabrowser/Users/Public?format=json" % server
         result = self.doUtils.downloadUrl(url, authenticate=False)
         
-        if result != "":
-            return result
+        users = []
+        
+        if (result != ""):
+            users = result
         else:
             # Server connection failed
             return False
 
+        return users
+
     def hasAccess(self):
-        # hasAccess is verified in service.py
-        url = "{server}/emby/Users?format=json"
+
+        url = "{server}/mediabrowser/Users"
         result = self.doUtils.downloadUrl(url)
         
-        if result == False:
-            # Access is restricted, set in downloadutils.py via exception
-            self.logMsg("Access is restricted.", 1)
+        if result is False:
+            # Access is restricted
+            self.logMsg("Access is restricted.")
             self.HasAccess = False
-        
-        elif utils.window('emby_online') != "true":
+            return
+        elif self.WINDOW.getProperty('Server_online') != "true":
             # Server connection failed
-            pass
+            return
 
-        elif utils.window('emby_serverStatus') == "restricted":
-            self.logMsg("Access is granted.", 1)
+        if self.WINDOW.getProperty("Server_status") == "restricted":
+            self.logMsg("Access is granted.")
             self.HasAccess = True
-            utils.window('emby_serverStatus', clear=True)
+            self.WINDOW.setProperty("Server_status", "")
             xbmcgui.Dialog().notification("Emby server", "Access is enabled.")
+        return
 
     def loadCurrUser(self, authenticated=False):
 
+        WINDOW = self.WINDOW
         doUtils = self.doUtils
         username = self.getUsername()
-        userId = self.getUserId()
-        
+
         # Only to be used if token exists
-        self.currUserId = userId
+        self.currUserId = self.getUserId()
         self.currServer = self.getServer()
         self.currToken = self.getToken()
         self.ssl = self.getSSLverify()
@@ -251,21 +246,21 @@ class UserClient(threading.Thread):
 
         # Test the validity of current token
         if authenticated == False:
-            url = "%s/emby/Users/%s?format=json" % (self.currServer, userId)
-            utils.window('emby_currUser', value=userId)
-            utils.window('emby_accessToken%s' % userId, value=self.currToken)
+            url = "%s/mediabrowser/Users/%s" % (self.currServer, self.currUserId)
+            WINDOW.setProperty("currUser", username)
+            WINDOW.setProperty("accessToken%s" % username, self.currToken)
             result = doUtils.downloadUrl(url)
-
             if result == 401:
                 # Token is no longer valid
                 self.resetClient()
                 return False
 
         # Set to windows property
-        utils.window('emby_currUser', value=userId)
-        utils.window('emby_accessToken%s' % userId, value=self.currToken)
-        utils.window('emby_server%s' % userId, value=self.currServer)
-        utils.window('emby_server_%s' % userId, value=self.getServer(prefix=False))
+        WINDOW.setProperty("currUser", username)
+        WINDOW.setProperty("accessToken%s" % username, self.currToken)
+        WINDOW.setProperty("server%s" % username, self.currServer)
+        WINDOW.setProperty("server_%s" % username, self.getServer(prefix=False))
+        WINDOW.setProperty("userId%s" % username, self.currUserId)
 
         # Set DownloadUtils values
         doUtils.setUsername(username)
@@ -278,194 +273,188 @@ class UserClient(threading.Thread):
         # Start DownloadUtils session
         doUtils.startSession()
         self.getAdditionalUsers()
-        # Set user preferences in settings
+
         self.currUser = username
+        # Set user preferences in settings
         self.setUserPref()
-        
 
     def authenticate(self):
-        # Get /profile/addon_data
-        addondir = xbmc.translatePath(self.addon.getAddonInfo('profile')).decode('utf-8')
-        hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir)
+
+        WINDOW = self.WINDOW
+        addon = self.addon
 
         username = self.getUsername()
         server = self.getServer()
+        addondir = xbmc.translatePath(self.addon.getAddonInfo('profile')).decode('utf-8')
+        hasSettings   = xbmcvfs.exists("%ssettings.xml" % addondir)
 
         # If there's no settings.xml
-        if not hasSettings:
-            self.logMsg("No settings.xml found.", 1)
+        if (hasSettings == 0):
+            self.logMsg("No settings.xml found.")
             self.auth = False
             return
         # If no user information
-        elif not server or not username:
-            self.logMsg("Missing server information.", 1)
+        if (server == "") or (username == ""):
+            self.logMsg("Missing server information.")
             self.auth = False
             return
-        # If there's a token, load the user
-        elif self.getToken():
+        # If there's a token
+        if (self.getToken() != ""):
             result = self.loadCurrUser()
 
             if result == False:
                 pass
             else:
-                self.logMsg("Current user: %s" % self.currUser, 1)
-                self.logMsg("Current userId: %s" % self.currUserId, 1)
-                self.logMsg("Current accessToken: %s" % self.currToken, 2)
+                self.logMsg("Current user: %s" % self.currUser, 0)
+                self.logMsg("Current userId: %s" % self.currUserId, 0)
+                self.logMsg("Current accessToken: %s" % self.currToken, 0)
                 return
         
-        ##### AUTHENTICATE USER #####
-
         users = self.getPublicUsers()
         password = ""
         
         # Find user in list
         for user in users:
-            name = user['Name']
+            name = user[u'Name']
+            userHasPassword = False
 
-            if username.decode('utf-8') in name:
+            if (unicode(username, 'utf-8') in name):
+                # Verify if user has a password
+                if (user.get("HasPassword") == True):
+                    userHasPassword = True
                 # If user has password
-                if user['HasPassword'] == True:
-                    password = xbmcgui.Dialog().input(
-                        heading="Enter password for user: %s" % username,
-                        option=xbmcgui.ALPHANUM_HIDE_INPUT)
+                if (userHasPassword):
+                    password = xbmcgui.Dialog().input("Enter password for user: %s" % username, option=xbmcgui.ALPHANUM_HIDE_INPUT)
                     # If password dialog is cancelled
-                    if not password:
+                    if (password == ""):
                         self.logMsg("No password entered.", 0)
-                        utils.window('emby_serverStatus', value="Stop")
+                        self.WINDOW.setProperty("Server_status", "Stop")
                         self.auth = False
                         return
                 break
         else:
             # Manual login, user is hidden
-            password = xbmcgui.Dialog().input(
-                                    heading="Enter password for user: %s" % username,
-                                    option=xbmcgui.ALPHANUM_HIDE_INPUT)
+            password = xbmcgui.Dialog().input("Enter password for user: %s" % username, option=xbmcgui.ALPHANUM_HIDE_INPUT)
+            
         sha1 = hashlib.sha1(password)
         sha1 = sha1.hexdigest()    
 
         # Authenticate username and password
-        url = "%s/emby/Users/AuthenticateByName?format=json" % server
+        url = "%s/mediabrowser/Users/AuthenticateByName?format=json" % server
         data = {'username': username, 'password': sha1}
         self.logMsg(data, 2)
 
         result = self.doUtils.downloadUrl(url, postBody=data, type="POST", authenticate=False)
 
+        accessToken = None
         try:
-            self.logMsg("Auth response: %s" % result, 1)
-            accessToken = result['AccessToken']
-        
-        except (KeyError, TypeError):
-            self.logMsg("Failed to retrieve the api key.", 1)
-            accessToken = None
+            self.logMsg("Auth_Reponse: %s" % result, 1)
+            accessToken = result[u'AccessToken']
+        except:
+            pass
 
-        if accessToken is not None:
+        if (result != None and accessToken != None):
             self.currUser = username
             xbmcgui.Dialog().notification("Emby server", "Welcome %s!" % self.currUser)
-            userId = result['User']['Id']
-            utils.settings('accessToken', value=accessToken)
-            utils.settings('userId%s' % username, value=userId)
-            self.logMsg("User Authenticated: %s" % accessToken, 1)
+            userId = result[u'User'][u'Id']
+            utils.settings("accessToken", accessToken)
+            utils.settings("userId%s" % username, userId)
+            self.logMsg("User Authenticated: %s" % accessToken)
             self.loadCurrUser(authenticated=True)
-            utils.window('emby_serverStatus', clear=True)
+            self.WINDOW.setProperty("Server_status", "")
             self.retry = 0
+            return
         else:
-            self.logMsg("User authentication failed.", 1)
-            utils.settings('accessToken', value="")
-            utils.settings('userId%s' % username, value="")
+            self.logMsg("User authentication failed.")
+            utils.settings("accessToken", "")
+            utils.settings("userId%s" % username, "")
             xbmcgui.Dialog().ok("Error connecting", "Invalid username or password.")
             
             # Give two attempts at entering password
-            if self.retry == 2:
-                self.logMsg(
-                    """Too many retries. You can retry by resetting 
-                    attempts in the addon settings.""", 1)
-                utils.window('emby_serverStatus', value="Stop")
-                xbmcgui.Dialog().ok(
-                    heading="Error connecting",
-                    line1="Failed to authenticate too many times.",
-                    line2="You can retry by resetting attempts in the addon settings.")
-
             self.retry += 1
+            if self.retry == 2:
+                self.logMsg("Too many retries. You can retry by selecting the option in the addon settings.")
+                self.WINDOW.setProperty("Server_status", "Stop")
+                xbmcgui.Dialog().ok("Error connecting", "Failed to authenticate too many times. You can retry by selecting the option in the addon settings.")
+            
             self.auth = False
+            return
 
     def resetClient(self):
 
-        self.logMsg("Reset UserClient authentication.", 1)
         username = self.getUsername()
-        
-        if self.currToken is not None:
+        self.logMsg("Reset UserClient authentication.", 1)
+        if (self.currToken != None):
             # In case of 401, removed saved token
-            utils.settings('accessToken', value="")
-            utils.window('emby_accessToken%s' % username, clear=True)
+            utils.settings("accessToken", "")
+            self.WINDOW.setProperty("accessToken%s" % username, "")
             self.currToken = None
             self.logMsg("User token has been removed.", 1)
         
         self.auth = True
         self.currUser = None
+        return
         
+
     def run(self):
 
-        monitor = xbmc.Monitor()
-        self.logMsg("----===## Starting UserClient ##===----", 0)
+        self.logMsg("|---- Starting UserClient ----|", 0)
 
-        while not monitor.abortRequested():
+        while not self.KodiMonitor.abortRequested():
 
             # Verify the log level
             currLogLevel = self.getLogLevel()
             if self.logLevel != currLogLevel:
                 # Set new log level
                 self.logLevel = currLogLevel
-                utils.window('emby_logLevel', value=str(currLogLevel))
                 self.logMsg("New Log Level: %s" % currLogLevel, 0)
+                self.WINDOW.setProperty('getLogLevel', str(currLogLevel)) 
 
-
-            status = utils.window('emby_serverStatus')
-            if status:
-                # Verify the connection status to server
+            if (self.WINDOW.getProperty("Server_status") != ""):
+                status = self.WINDOW.getProperty("Server_status")
+                
                 if status == "restricted":
                     # Parental control is restricting access
                     self.HasAccess = False
 
                 elif status == "401":
-                    # Unauthorized access, revoke token
-                    utils.window('emby_serverStatus', value="Auth")
+                    self.WINDOW.setProperty("Server_status", "Auth")
+                    # Revoked token
                     self.resetClient()
 
-            if self.auth and (self.currUser is None):
-                # Try to authenticate user
-                status = utils.window('emby_serverStatus')
-                if not status or status == "Auth":
-                    # Set auth flag because we no longer need
-                    # to authenticate the user
+            if self.auth and (self.currUser == None):
+                status = self.WINDOW.getProperty("Server_status")
+                
+                if (status == "") or (status == "Auth"):
                     self.auth = False
                     self.authenticate()
                 
-
-            if not self.auth and (self.currUser is None):
-                # If authenticate failed.
+            if (self.auth == False) and (self.currUser == None):
+                # Only if there's information found to login
                 server = self.getServer()
                 username = self.getUsername()
-                status = utils.window('emby_serverStatus')
+                status = self.WINDOW.getProperty("Server_status")
+
+                # If user didn't enter a password when prompted
+                if status == "Stop":
+                    pass
                 
-                # The status Stop is for when user cancelled password dialog.
-                if server and username and status != "Stop":
-                    # Only if there's information found to login
-                    self.logMsg("Server found: %s" % server, 2)
-                    self.logMsg("Username found: %s" % username, 2)
+                elif (server != "") and (username != ""):
+                    self.logMsg("Server found: %s" % server)
+                    self.logMsg("Username found: %s" % username)
                     self.auth = True
 
-
+            # If stopping the client didn't work
             if self.stopClient == True:
-                # If stopping the client didn't work
                 break
                 
-            if monitor.waitForAbort(1):
+            if self.KodiMonitor.waitForAbort(1):
                 # Abort was requested while waiting. We should exit
                 break
         
         self.doUtils.stopSession()    
-        self.logMsg("##===---- UserClient Stopped ----===##", 0)
+        self.logMsg("|---- UserClient Stopped ----|", 0)
 
     def stopClient(self):
-        # When emby for kodi terminates
+        # As last resort
         self.stopClient = True
\ No newline at end of file
diff --git a/resources/lib/Utils.py b/resources/lib/Utils.py
index 83e73e1d..73c49c95 100644
--- a/resources/lib/Utils.py
+++ b/resources/lib/Utils.py
@@ -1,76 +1,59 @@
-# -*- coding: utf-8 -*-
-
+#################################################################################################
+# utils 
 #################################################################################################
 
+import xbmc
+import xbmcgui
+import xbmcaddon
+import xbmcvfs
+import json
+import os
 import cProfile
-import inspect
 import pstats
-import sqlite3
 import time
+import inspect
+import sqlite3
+import string
 import unicodedata
 import xml.etree.ElementTree as etree
 
-import xbmc
-import xbmcaddon
-import xbmcgui
-import xbmcvfs
+from API import API
+from PlayUtils import PlayUtils
+from DownloadUtils import DownloadUtils
 
-#################################################################################################
+downloadUtils = DownloadUtils()
+addon = xbmcaddon.Addon()
+language = addon.getLocalizedString
 
-
-def logMsg(title, msg, level=1):
+ 
+def logMsg(title, msg, level = 1):
     
+    WINDOW = xbmcgui.Window(10000)
     # Get the logLevel set in UserClient
-    try:
-        logLevel = int(window('emby_logLevel'))
-    except ValueError:
-        logLevel = 0
+    logLevel = int(WINDOW.getProperty('getLogLevel'))
     
-    if logLevel >= level:
-        
-        if logLevel == 2: # inspect.stack() is expensive
+    if(logLevel >= level):
+        if(logLevel == 2): # inspect.stack() is expensive
             try:
-                xbmc.log("%s -> %s : %s" % (title, inspect.stack()[1][3], msg))
+                xbmc.log(title + " -> " + inspect.stack()[1][3] + " : " + str(msg))
             except UnicodeEncodeError:
-                xbmc.log("%s -> %s : %s" % (title, inspect.stack()[1][3], msg.encode('utf-8')))
+                xbmc.log(title + " -> " + inspect.stack()[1][3] + " : " + str(msg.encode('utf-8')))
         else:
             try:
-                xbmc.log("%s -> %s" % (title, msg))
+                xbmc.log(title + " -> " + str(msg))
             except UnicodeEncodeError:
-                xbmc.log("%s -> %s" % (title, msg.encode('utf-8')))
+                xbmc.log(title + " -> " + str(msg.encode('utf-8')))
 
-def window(property, value=None, clear=False, windowid=10000):
-    # Get or set window property
-    WINDOW = xbmcgui.Window(windowid)
+def convertEncoding(data):
+    #nasty hack to make sure we have a unicode string
+    try:
+        return data.decode('utf-8')
+    except:
+        return data
+          
+def KodiSQL(type="video"):
     
-    if clear:
-        WINDOW.clearProperty(property)
-    elif value is not None:
-        WINDOW.setProperty(property, value)
-    else:
-        return WINDOW.getProperty(property)
-
-def settings(setting, value=None):
-    # Get or add addon setting
-    addon = xbmcaddon.Addon(id='plugin.video.emby')
-    
-    if value is not None:
-        addon.setSetting(setting, value)
-    else:
-        return addon.getSetting(setting)
-
-def language(stringid):
-    # Central string retrieval
-    addon = xbmcaddon.Addon(id='plugin.video.emby')
-    string = addon.getLocalizedString(stringid)
-
-    return string
-
-def kodiSQL(type="video"):
-    
-    if type == "emby":
-        dbPath = xbmc.translatePath("special://database/emby.db").decode('utf-8')
-    elif type == "music":
+    if type == "music":
         dbPath = getKodiMusicDBPath()
     elif type == "texture":
         dbPath = xbmc.translatePath("special://database/Textures13.db").decode('utf-8')
@@ -111,140 +94,219 @@ def getKodiMusicDBPath():
                     "special://database/MyMusic%s.db"
                     % dbVersion.get(kodibuild, "")).decode('utf-8')
     return dbPath
-
-def reset():
-
-    dialog = xbmcgui.Dialog()
-
-    resp = dialog.yesno("Warning", "Are you sure you want to reset your local Kodi database?")
-    if resp == 0:
-        return
-
-    # first stop any db sync
-    window('emby_shouldStop', value="true")
-    count = 10
-    while window('emby_dbScan') == "true":
-        logMsg("EMBY", "Sync is running, will retry: %s..." % count)
-        count -= 1
-        if count == 0:
-            dialog.ok("Warning", "Could not stop the database from running. Try again.")
-            return
-        xbmc.sleep(1000)
-
-    # Clean up the playlists
-    path = xbmc.translatePath("special://profile/playlists/video/").decode('utf-8')
-    dirs, files = xbmcvfs.listdir(path)
-    for file in files:
-        if file.startswith('Emby'):
-            xbmcvfs.delete("%s%s" % (path, file))
-
-    # Clean up the video nodes
-    import shutil
-    path = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
-    dirs, files = xbmcvfs.listdir(path)
-    for dir in dirs:
-        if dir.startswith('Emby'):
-            shutil.rmtree("%s%s" % (path, dir))
-    for file in files:
-        if file.startswith('emby'):
-            xbmcvfs.delete("%s%s" % (path, file))
-
-    # Wipe the kodi databases
-    logMsg("EMBY", "Resetting the Kodi video database.")
-    connection = kodiSQL('video')
-    cursor = connection.cursor()
-    cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
-    rows = cursor.fetchall()
-    for row in rows:
-        tablename = row[0]
-        if tablename != "version":
-            cursor.execute("DELETE FROM " + tablename)
-    connection.commit()
-    cursor.close()
-
-    if settings('disableMusic') != "true":
-        logMsg("EMBY", "Resetting the Kodi music database.")
-        connection = kodiSQL('music')
-        cursor = connection.cursor()
-        cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
-        rows = cursor.fetchall()
-        for row in rows:
-            tablename = row[0]
-            if tablename != "version":
-                cursor.execute("DELETE FROM " + tablename)
-        connection.commit()
-        cursor.close()
-
-    # Wipe the emby database
-    logMsg("EMBY", "Resetting the Emby database.")
-    connection = kodiSQL('emby')
-    cursor = connection.cursor()
-    cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
-    rows = cursor.fetchall()
-    for row in rows:
-        tablename = row[0]
-        if tablename != "version":
-            cursor.execute("DELETE FROM " + tablename)
-    connection.commit()
-    cursor.close()
     
-    # reset the install run flag  
-    settings('SyncInstallRunDone', value="false")
-
-    # Remove emby info
-    resp = dialog.yesno("Warning", "Reset all Emby Addon settings?")
-    if resp == 1:
-        # Delete the settings
-        addon = xbmcaddon.Addon()
-        addondir = xbmc.translatePath(addon.getAddonInfo('profile')).decode('utf-8')
-        dataPath = "%ssettings.xml" % addondir
-        xbmcvfs.delete(dataPath)
-        logMsg("EMBY", "Deleting: settings.xml", 1)
-
-    dialog.ok(
-        heading="Emby for Kodi",
-        line1="Database reset has completed, Kodi will now restart to apply the changes.")
-    xbmc.executebuiltin('RestartApp')
+def prettifyXml(elem):
+    rough_string = etree.tostring(elem, "utf-8")
+    reparsed = minidom.parseString(rough_string)
+    return reparsed.toprettyxml(indent="\t")
 
 def startProfiling():
-    
     pr = cProfile.Profile()
     pr.enable()
-    
     return pr
-
-def stopProfiling(pr, profileName):
     
+def stopProfiling(pr, profileName):
     pr.disable()
     ps = pstats.Stats(pr)
     
-    profiles = xbmc.translatePath("%sprofiles/"
-                % xbmcaddon.Addon().getAddonInfo('profile')).decode('utf-8')
-
-    if not xbmcvfs.exists(profiles):
-        # Create the profiles folder
-        xbmcvfs.mkdir(profiles)
-
-    timestamp = time.strftime("%Y-%m-%d %H-%M-%S")
-    profile = "%s%s_profile_(%s).tab" % (profiles, profileName, timestamp)
+    addondir = xbmc.translatePath(xbmcaddon.Addon(id='plugin.video.emby').getAddonInfo('profile'))    
     
-    f = open(profile, 'wb')
+    fileTimeStamp = time.strftime("%Y-%m-%d %H-%M-%S")
+    tabFileNamepath = os.path.join(addondir, "profiles")
+    tabFileName = os.path.join(addondir, "profiles" , profileName + "_profile_(" + fileTimeStamp + ").tab")
+    
+    if not xbmcvfs.exists(tabFileNamepath):
+        xbmcvfs.mkdir(tabFileNamepath)
+    
+    f = open(tabFileName, 'wb')
     f.write("NumbCalls\tTotalTime\tCumulativeTime\tFunctionName\tFileName\r\n")
     for (key, value) in ps.stats.items():
         (filename, count, func_name) = key
         (ccalls, ncalls, total_time, cumulative_time, callers) = value
         try:
-            f.write(
-                "%s\t%s\t%s\t%s\t%s\r\n"
-                % (ncalls, "{:10.4f}".format(total_time),
-                    "{:10.4f}".format(cumulative_time), func_name, filename))
+            f.write(str(ncalls) + "\t" + "{:10.4f}".format(total_time) + "\t" + "{:10.4f}".format(cumulative_time) + "\t" + func_name + "\t" + filename + "\r\n")
         except ValueError:
-            f.write(
-                "%s\t%s\t%s\t%s\t%s\r\n"
-                % (ncalls, "{0}".format(total_time),
-                    "{0}".format(cumulative_time), func_name, filename))
+            f.write(str(ncalls) + "\t" + "{0}".format(total_time) + "\t" + "{0}".format(cumulative_time) + "\t" + func_name + "\t" + filename + "\r\n")
     f.close()
 
+def indent(elem, level=0):
+    # Prettify xml trees
+    i = "\n" + level*"  "
+    if len(elem):
+        if not elem.text or not elem.text.strip():
+          elem.text = i + "  "
+        if not elem.tail or not elem.tail.strip():
+          elem.tail = i
+        for elem in elem:
+          indent(elem, level+1)
+        if not elem.tail or not elem.tail.strip():
+          elem.tail = i
+    else:
+        if level and (not elem.tail or not elem.tail.strip()):
+          elem.tail = i
+
+def createSources():
+    # To make Master lock compatible
+    path = xbmc.translatePath("special://profile/").decode("utf-8")
+    xmlpath = "%ssources.xml" % path
+
+    if xbmcvfs.exists(xmlpath):
+        # Modify the existing file
+        try:
+            xmlparse = etree.parse(xmlpath)
+        except:
+            root = etree.Element('sources')
+        else:
+            root = xmlparse.getroot()
+
+        video = root.find('video')
+        if video is None:
+            video = etree.SubElement(root, 'video')
+    else:
+        # We need to create the file
+        root = etree.Element('sources')
+        video = etree.SubElement(root, 'video')
+
+
+    # Add elements
+    etree.SubElement(video, 'default', attrib={'pathversion': "1"})
+    
+    # First dummy source
+    source_one = etree.SubElement(video, 'source')
+    etree.SubElement(source_one, 'name').text = "Emby"
+    etree.SubElement(source_one, 'path', attrib={'pathversion': "1"}).text = (
+
+            "smb://embydummy/dummypath1/"
+        )
+    etree.SubElement(source_one, 'allowsharing').text = "true"
+    
+    # Second dummy source
+    source_two = etree.SubElement(video, 'source')
+    etree.SubElement(source_two, 'name').text = "Emby"
+    etree.SubElement(source_two, 'path', attrib={'pathversion': "1"}).text = (
+
+            "smb://embydummy/dummypath2/"
+        )
+    etree.SubElement(source_two, 'allowsharing').text = "true"
+
+    try:
+        indent(root)
+    except:pass
+    etree.ElementTree(root).write(xmlpath)
+
+def pathsubstitution(add=True):
+
+    path = xbmc.translatePath('special://userdata').decode('utf-8')
+    xmlpath = "%sadvancedsettings.xml" % path
+    xmlpathexists = xbmcvfs.exists(xmlpath)
+
+    # original address
+    originalServer = settings('ipaddress')
+    originalPort = settings('port')
+    originalHttp = settings('https') == "true"
+
+    if originalHttp:
+        originalHttp = "https"
+    else:
+        originalHttp = "http"
+
+    # Process add or deletion
+    if add:
+        # second address
+        secondServer = settings('secondipaddress')
+        secondPort = settings('secondport')
+        secondHttp = settings('secondhttps') == "true"
+
+        if secondHttp:
+            secondHttp = "https"
+        else:
+            secondHttp = "http"
+
+        logMsg("EMBY", "Original address: %s://%s:%s, alternate is: %s://%s:%s" % (originalHttp, originalServer, originalPort, secondHttp, secondServer, secondPort), 1)
+
+        if xmlpathexists:
+            # we need to modify the file.
+            try:
+                xmlparse = etree.parse(xmlpath)
+            except: # Document is blank
+                root = etree.Element('advancedsettings')
+            else:
+                root = xmlparse.getroot()
+            
+            pathsubs = root.find('pathsubstitution')
+            if pathsubs is None:
+                pathsubs = etree.SubElement(root, 'pathsubstitution')
+        else:
+            # we need to create the file.
+            root = etree.Element('advancedsettings')
+            pathsubs = etree.SubElement(root, 'pathsubstitution')
+        
+        substitute = etree.SubElement(pathsubs, 'substitute')
+        # From original address
+        etree.SubElement(substitute, 'from').text = "%s://%s:%s" % (originalHttp, originalServer, originalPort)
+        # To secondary address
+        etree.SubElement(substitute, 'to').text = "%s://%s:%s" % (secondHttp, secondServer, secondPort)
+
+        etree.ElementTree(root).write(xmlpath)
+        settings('pathsub', "true")
+
+    else: # delete the path substitution, we don't need it anymore.
+        logMsg("EMBY", "Alternate address is disabled, removing path substitution for: %s://%s:%s" % (originalHttp, originalServer, originalPort), 1)
+
+        xmlparse = etree.parse(xmlpath)
+        root = xmlparse.getroot()
+        
+        iterator = root.getiterator("pathsubstitution")
+
+        for substitutes in iterator:
+            for substitute in substitutes:
+                frominsert = substitute.find(".//from").text == "%s://%s:%s" % (originalHttp, originalServer, originalPort)
+
+                if frominsert:
+                    # Found a match, in case there's more than one substitution.
+                    substitutes.remove(substitute)
+
+        etree.ElementTree(root).write(xmlpath)
+        settings('pathsub', "false")
+
+
+def settings(setting, value = None):
+    # Get or add addon setting
+    addon = xbmcaddon.Addon()
+    if value:
+        addon.setSetting(setting, value)
+    else:
+        return addon.getSetting(setting)
+
+def window(property, value = None, clear = False):
+    # Get or set window property
+    WINDOW = xbmcgui.Window(10000)
+    if clear:
+        WINDOW.clearProperty(property)
+    elif value:
+        WINDOW.setProperty(property, value)
+    else:
+        return WINDOW.getProperty(property)
+
+def normalize_string(text):
+    # For theme media, do not modify unless
+    # modified in TV Tunes
+    text = text.replace(":", "")
+    text = text.replace("/", "-")
+    text = text.replace("\\", "-")
+    text = text.replace("<", "")
+    text = text.replace(">", "")
+    text = text.replace("*", "")
+    text = text.replace("?", "")
+    text = text.replace('|', "")
+    text = text.strip()
+    # Remove dots from the last character as windows can not have directories
+    # with dots at the end
+    text = text.rstrip('.')
+    text = unicodedata.normalize('NFKD', unicode(text, 'utf-8')).encode('ascii', 'ignore')
+
+    return text
+
 def normalize_nodes(text):
     # For video nodes
     text = text.replace(":", "")
@@ -265,223 +327,99 @@ def normalize_nodes(text):
     
     return text
 
-def normalize_string(text):
-    # For theme media, do not modify unless
-    # modified in TV Tunes
-    text = text.replace(":", "")
-    text = text.replace("/", "-")
-    text = text.replace("\\", "-")
-    text = text.replace("<", "")
-    text = text.replace(">", "")
-    text = text.replace("*", "")
-    text = text.replace("?", "")
-    text = text.replace('|', "")
-    text = text.strip()
-    # Remove dots from the last character as windows can not have directories
-    # with dots at the end
-    text = text.rstrip('.')
-    text = unicodedata.normalize('NFKD', unicode(text, 'utf-8')).encode('ascii', 'ignore')
+def reloadProfile():
+    # Useful to reload the add-on without restarting Kodi.
+    profile = xbmc.getInfoLabel('System.ProfileName')
+    xbmc.executebuiltin("LoadProfile(%s)" % profile)
+   
 
-    return text
+def reset():
 
-def indent(elem, level=0):
-    # Prettify xml trees
-    i = "\n" + level*"  "
-    if len(elem):
-        if not elem.text or not elem.text.strip():
-          elem.text = i + "  "
-        if not elem.tail or not elem.tail.strip():
-          elem.tail = i
-        for elem in elem:
-          indent(elem, level+1)
-        if not elem.tail or not elem.tail.strip():
-          elem.tail = i
-    else:
-        if level and (not elem.tail or not elem.tail.strip()):
-          elem.tail = i
+    WINDOW = xbmcgui.Window( 10000 )
+    return_value = xbmcgui.Dialog().yesno("Warning", "Are you sure you want to reset your local Kodi database?")
 
-def sourcesXML():
-    # To make Master lock compatible
-    path = xbmc.translatePath("special://profile/").decode('utf-8')
-    xmlpath = "%ssources.xml" % path
+    if return_value == 0:
+        return
 
-    try:
-        xmlparse = etree.parse(xmlpath)
-    except: # Document is blank or missing
-        root = etree.Element('sources')
-    else:
-        root = xmlparse.getroot()
+    # Because the settings dialog could be open
+    # it seems to override settings so we need to close it before we reset settings.
+    xbmc.executebuiltin("Dialog.Close(all,true)")
+    
+    #cleanup video nodes
+    import shutil
+    path = "special://profile/library/video/"
+    if xbmcvfs.exists(path):
+        allDirs, allFiles = xbmcvfs.listdir(path)
+        for dir in allDirs:
+            if dir.startswith("Emby "):
+                shutil.rmtree(xbmc.translatePath("special://profile/library/video/" + dir))
+        for file in allFiles:
+                if file.startswith("emby"):
+                    xbmcvfs.delete(path + file)
+
+    settings('SyncInstallRunDone', "false")
+    
+    # Ask if user information should be deleted too.
+    return_user = xbmcgui.Dialog().yesno("Warning", "Reset all Emby Addon settings?")
+    if return_user == 1:
+        WINDOW.setProperty('deletesettings', "true")
+        addon = xbmcaddon.Addon()
+        addondir = xbmc.translatePath(addon.getAddonInfo('profile')).decode('utf-8')
+        dataPath = "%ssettings.xml" % addondir
+        xbmcvfs.delete(dataPath)
+        logMsg("EMBY", "Deleting: settings.xml", 1)
+    
+    # first stop any db sync
+    WINDOW.setProperty("SyncDatabaseShouldStop", "true")
+    
+    count = 0
+    while(WINDOW.getProperty("SyncDatabaseRunning") == "true"):
+        xbmc.log("Sync Running, will wait : " + str(count))
+        count += 1
+        if(count > 10):
+            dialog = xbmcgui.Dialog()
+            dialog.ok('Warning', 'Could not stop DB sync, you should try again.')
+            return
+        xbmc.sleep(1000)
+       
+    # delete video db table data
+    print "Doing Video DB Reset"
+    connection = KodiSQL("video")
+    cursor = connection.cursor( )
+    cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
+    rows = cursor.fetchall()
+    for row in rows:
+        tableName = row[0]
+        if(tableName != "version"):
+            cursor.execute("DELETE FROM " + tableName)
+    cursor.execute("DROP TABLE IF EXISTS emby")
+    connection.commit()
+    cursor.close()
+    
+    if settings('enableMusicSync') == "true":
+        # delete video db table data
+        print "Doing Music DB Reset"
+        connection = KodiSQL("music")
+        cursor = connection.cursor( )
+        cursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"')
+        rows = cursor.fetchall()
+        for row in rows:
+            tableName = row[0]
+            if(tableName != "version"):
+                cursor.execute("DELETE FROM " + tableName)
+        cursor.execute("DROP TABLE IF EXISTS emby")
+        connection.commit()
+        cursor.close()
         
-
-    video = root.find('video')
-    if video is None:
-        video = etree.SubElement(root, 'video')
-        etree.SubElement(video, 'default', attrib={'pathversion': "1"})
-
-    # Add elements
-    for i in range(1, 3):
-
-            for source in root.findall('.//path'):
-                if source.text == "smb://embydummy/dummypath%s/" % i:
-                    # Already there, skip
-                    break
-            else:
-                source = etree.SubElement(video, 'source')
-                etree.SubElement(source, 'name').text = "Emby"
-                etree.SubElement(source, 'path', attrib={'pathversion': "1"}).text = (
-
-                    "smb://embydummy/dummypath%s/" % i
-                )
-                etree.SubElement(source, 'allowsharing').text = "true"
-    # Prettify and write to file
-    try:
-        indent(root)
-    except: pass
-    etree.ElementTree(root).write(xmlpath)
-
-def passwordsXML():
-
-    # To add network credentials
-    path = xbmc.translatePath("special://userdata/").decode('utf-8')
-    xmlpath = "%spasswords.xml" % path
-
-    try:
-        xmlparse = etree.parse(xmlpath)
-    except: # Document is blank or missing
-        root = etree.Element('passwords')
-    else:
-        root = xmlparse.getroot()
+    
+    # reset the install run flag
+    #settings('SyncInstallRunDone', "false")
+    #WINDOW.setProperty("SyncInstallRunDone", "false")
 
     dialog = xbmcgui.Dialog()
-    credentials = settings('networkCreds')
-    if credentials:
-        # Present user with options
-        option = dialog.select("Modify/Remove network credentials", ["Modify", "Remove"])
-
-        if option < 0:
-            # User cancelled dialog
-            return
-
-        elif option == 1:
-            # User selected remove
-            iterator = root.getiterator('passwords')
-
-            for paths in iterator:
-                for path in paths:
-                    if path.find('.//from').text == "smb://%s/" % credentials:
-                        paths.remove(path)
-                        logMsg("EMBY", "Successfully removed credentials for: %s"
-                                % credentials, 1)
-                        etree.ElementTree(root).write(xmlpath)
-                        break
-            else:
-                logMsg("EMBY", "Failed to find saved server: %s in passwords.xml" % credentials, 1)
-            
-            settings('networkCreds', value="")
-            xbmcgui.Dialog().notification(
-                                heading="Emby for Kodi",
-                                message="%s removed from passwords.xml!" % credentials,
-                                icon="special://home/addons/plugin.video.emby/icon.png",
-                                time=1000,
-                                sound=False)
-            return
-
-        elif option == 0:
-            # User selected to modify
-            server = dialog.input("Modify the computer name or ip address", credentials)
-            if not server:
-                return
-    else:
-        # No credentials added
-        dialog.ok(
-            heading="Network credentials",
-            line1= (
-                "Input the server name or IP address as indicated in your emby library paths. "
-                'For example, the server name: \\\\SERVER-PC\\path\\ is "SERVER-PC".'))
-        server = dialog.input("Enter the server name or IP address", settings('ipaddress'))
-        if not server:
-            return
-
-    # Network username
-    user = dialog.input("Enter the network username")
-    if not user:
-        return
-    # Network password
-    password = dialog.input(
-                        heading="Enter the network password",
-                        option=xbmcgui.ALPHANUM_HIDE_INPUT)
-    if not password:
-        return
-
-    # Add elements
-    for path in root.findall('.//path'):
-        if path.find('.//from').text.lower() == "smb://%s/" % server.lower():
-            # Found the server, rewrite credentials
-            path.find('.//to').text = "smb://%s:%s@%s/" % (user, password, server)
-            break
-    else:
-        # Server not found, add it.
-        path = etree.SubElement(root, 'path')
-        etree.SubElement(path, 'from', attrib={'pathversion': "1"}).text = "smb://%s/" % server
-        topath = "smb://%s:%s@%s/" % (user, password, server)
-        etree.SubElement(path, 'to', attrib={'pathversion': "1"}).text = topath
-        # Force Kodi to see the credentials without restarting
-        xbmcvfs.exists(topath)
-
-    # Add credentials    
-    settings('networkCreds', value="%s" % server)
-    logMsg("EMBY", "Added server: %s to passwords.xml" % server, 1)
-    # Prettify and write to file
-    try:
-        indent(root)
-    except: pass
-    etree.ElementTree(root).write(xmlpath)
-    
-    dialog.notification(
-            heading="Emby for Kodi",
-            message="%s added to passwords.xml!" % server,
-            icon="special://home/addons/plugin.video.emby/icon.png",
-            time=1000,
-            sound=False)
-
-def playlistXSP(mediatype, tagname, viewtype="", delete=False):
-    # Tagname is in unicode - actions: add or delete
-    tagname = tagname.encode('utf-8')
-    cleantagname = normalize_nodes(tagname)
-    path = xbmc.translatePath("special://profile/playlists/video/").decode('utf-8')
-    if viewtype == "mixed":
-        plname = "%s - %s" % (tagname, mediatype)
-        xsppath = "%sEmby %s - %s.xsp" % (path, cleantagname, mediatype)
-    else:
-        plname = tagname
-        xsppath = "%sEmby %s.xsp" % (path, cleantagname)
-
-    # Create the playlist directory
-    if not xbmcvfs.exists(path):
-        xbmcvfs.mkdirs(path)
-
-    # Only add the playlist if it doesn't already exists
-    if xbmcvfs.exists(xsppath):
-
-        if delete:
-            xbmcvfs.delete(xsppath)
-            logMsg("EMBY", "Successfully removed playlist: %s." % tagname, 1)
-        
-        return
-
-    # Using write process since there's no guarantee the xml declaration works with etree
-    itemtypes = {
-        'homevideos': "movies"
-    }
-    f = open(xsppath, 'w')
-    f.write(
-        '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n'
-        '<smartplaylist type="%s">\n\t'
-            '<name>Emby %s</name>\n\t'
-            '<match>all</match>\n\t'
-            '<rule field="tag" operator="is">\n\t\t'
-                '<value>%s</value>\n\t'
-            '</rule>'
-        % (itemtypes.get(mediatype, mediatype), plname, tagname))
-    f.close()
-    logMsg("EMBY", "Successfully added playlist: %s" % tagname)
\ No newline at end of file
+    # Reload would work instead of restart since the add-on is a service.
+    #dialog.ok('Emby Reset', 'Database reset has completed, Kodi will now restart to apply the changes.')
+    #WINDOW.clearProperty("SyncDatabaseShouldStop")
+    #reloadProfile()
+    dialog.ok('Emby Reset', 'Database reset has completed, Kodi will now restart to apply the changes.')
+    xbmc.executebuiltin("RestartApp")
\ No newline at end of file
diff --git a/resources/lib/VideoNodes.py b/resources/lib/VideoNodes.py
index 1dc9d5a6..056ee13f 100644
--- a/resources/lib/VideoNodes.py
+++ b/resources/lib/VideoNodes.py
@@ -1,344 +1,466 @@
-# -*- coding: utf-8 -*-
-
+#################################################################################################
+# VideoNodes - utils to create video nodes listings in kodi for the emby addon
 #################################################################################################
 
-import shutil
-import xml.etree.ElementTree as etree
 
 import xbmc
+import xbmcgui
 import xbmcaddon
 import xbmcvfs
+import json
+import os
+import shutil
+#import common elementree because cElementree has issues with kodi
+import xml.etree.ElementTree as etree
 
-import clientinfo
-import utils
+import Utils as utils
 
-#################################################################################################
+from ReadEmbyDB import ReadEmbyDB
+WINDOW = xbmcgui.Window(10000)
 
+addonSettings = xbmcaddon.Addon()
+language = addonSettings.getLocalizedString
 
-class VideoNodes(object):
-
-
-    def __init__(self):
-
-        clientInfo = clientinfo.ClientInfo()
-        self.addonName = clientInfo.getAddonName()
-
-        self.kodiversion = int(xbmc.getInfoLabel("System.BuildVersion")[:2])
-
-    def logMsg(self, msg, lvl=1):
-
-        className = self.__class__.__name__
-        utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
-
-
-    def commonRoot(self, order, label, tagname, roottype=1):
-
-        if roottype == 0:
-            # Index
-            root = etree.Element('node', attrib={'order': "%s" % order})
-        elif roottype == 1:
-            # Filter
-            root = etree.Element('node', attrib={'order': "%s" % order, 'type': "filter"})
-            etree.SubElement(root, 'match').text = "all"
-            # Add tag rule
-            rule = etree.SubElement(root, 'rule', attrib={'field': "tag", 'operator': "is"})
-            etree.SubElement(rule, 'value').text = tagname
-        else:
-            # Folder
-            root = etree.Element('node', attrib={'order': "%s" % order, 'type': "folder"})
-
-        etree.SubElement(root, 'label').text = label
-        etree.SubElement(root, 'icon').text = "special://home/addons/plugin.video.emby/icon.png"
-
-        return root
-
-    def viewNode(self, indexnumber, tagname, mediatype, viewtype, delete=False):
-
-        kodiversion = self.kodiversion
-
-        if mediatype == "homevideos":
-            # Treat homevideos as movies
-            mediatype = "movies"
-
-        tagname = tagname.encode('utf-8')
-        cleantagname = utils.normalize_nodes(tagname)
-        if viewtype == "mixed":
-            dirname = "%s - %s" % (cleantagname, mediatype)
-        else:
-            dirname = cleantagname
+class VideoNodes():   
+       
+   
+    def buildVideoNodeForView(self, tagname, type, windowPropId):
+        #this method will build a video node for a particular Emby view (= tag in kodi)
+        #we set some window props here to for easy future reference and to be used in skins (for easy access only)
+        tagname_normalized = utils.normalize_nodes(tagname.encode('utf-8'))
         
-        path = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
-        nodepath = xbmc.translatePath(
-                    "special://profile/library/video/Emby - %s/" % dirname).decode('utf-8')
-
-        # Verify the video directory
-        if not xbmcvfs.exists(path):
-            shutil.copytree(
-                src=xbmc.translatePath("special://xbmc/system/library/video/").decode('utf-8'),
-                dst=xbmc.translatePath("special://profile/library/video/").decode('utf-8'))
-            xbmcvfs.exists(path)
-
-        # Create the node directory
-        if not xbmcvfs.exists(nodepath):
-            # We need to copy over the default items
-            xbmcvfs.mkdirs(nodepath)
-        else:
-            if delete:
-                dirs, files = xbmcvfs.listdir(nodepath)
-                for file in files:
-                    xbmcvfs.delete(nodepath + file)
-
-                self.logMsg("Sucessfully removed videonode: %s." % tagname, 1)
-                return
-
-        # Create index entry
-        nodeXML = "%sindex.xml" % nodepath
-        # Set windows property
-        path = "library://video/Emby - %s/" % dirname
-        for i in range(1, indexnumber):
-            # Verify to make sure we don't create duplicates
-            if utils.window('Emby.nodes.%s.index' % i) == path:
-                return
-
-        utils.window('Emby.nodes.%s.index' % indexnumber, value=path)
-        # Root
-        root = self.commonRoot(order=0, label=dirname, tagname=tagname, roottype=0)
+        libraryPath = xbmc.translatePath("special://profile/library/video/Emby - %s/" %tagname_normalized)
+        kodiVersion = 14
+        if xbmc.getInfoLabel("System.BuildVersion").startswith("15") or xbmc.getInfoLabel("System.BuildVersion").startswith("16"):
+            kodiVersion = 15
+        
+        #create tag node - index
+        xbmcvfs.mkdir(libraryPath)
+        nodefile = os.path.join(libraryPath, "index.xml")
+        root = etree.Element("node", {"order":"0"})
+        etree.SubElement(root, "label").text = tagname
+        etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
+        path = "library://video/Emby - %s/" %tagname_normalized
+        WINDOW.setProperty("Emby.nodes.%s.index" %str(windowPropId),path)
         try:
-            utils.indent(root)
-        except: pass
-        etree.ElementTree(root).write(nodeXML)
-
-
-        nodetypes = {
-
-            '1': "all",
-            '2': "recent",
-            '3': "recentepisodes",
-            '4': "inprogress",
-            '5': "inprogressepisodes",
-            '6': "unwatched",
-            '7': "nextupepisodes",
-            '8': "sets",
-            '9': "genres",
-            '10': "random",
-            '11': "recommended"
-        }
-        mediatypes = {
-            # label according to nodetype per mediatype
-            'movies': {
-                '1': tagname,
-                '2': 30174,
-                '4': 30177,
-                '6': 30189,
-                '8': 20434,
-                '9': 135,
-                '10': 30229,
-                '11': 30230},
-
-            'tvshows': {
-                '1': tagname,
-                '2': 30170,
-                '3': 30175,
-                '4': 30171,
-                '5': 30178,
-                '7': 30179,
-                '9': 135,
-                '10': 30229,
-                '11': 30230},
-        }
-
-        nodes = mediatypes[mediatype]
-        for node in nodes:
-
-            nodetype = nodetypes[node]
-            nodeXML = "%s%s_%s.xml" % (nodepath, cleantagname, nodetype)
-            # Get label
-            stringid = nodes[node]
-            if node != '1':
-                label = utils.language(stringid)
-                if not label:
-                    label = xbmc.getLocalizedString(stringid)
-            else:
-                label = stringid
-
-            # Set window properties
-            if nodetype == "nextupepisodes":
-                # Custom query
-                path = "plugin://plugin.video.emby/?id=%s&mode=nextup&limit=25" % tagname
-            elif kodiversion == 14 and nodetype == "recentepisodes":
-                # Custom query
-                path = "plugin://plugin.video.emby/?id=%s&mode=recentepisodes&limit=25" % tagname
-            elif kodiversion == 14 and nodetype == "inprogressepisodes":
-                # Custom query
-                path = "plugin://plugin.video.emby/?id=%s&mode=inprogressepisodes&limit=25"% tagname
-            else:
-                path = "library://video/Emby - %s/%s_%s.xml" % (dirname, cleantagname, nodetype)
-            windowpath = "ActivateWindow(Video, %s, return)" % path
+            etree.ElementTree(root).write(nodefile, xml_declaration=True)
+        except:
+            etree.ElementTree(root).write(nodefile)
+        
+        #create tag node - all items
+        nodefile = os.path.join(libraryPath, tagname_normalized + "_all.xml")
+        root = etree.Element("node", {"order":"1", "type":"filter"})
+        etree.SubElement(root, "label").text = tagname
+        etree.SubElement(root, "match").text = "all"
+        etree.SubElement(root, "content").text = type
+        etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
+        etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle"
+        Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
+        WINDOW.setProperty("Emby.nodes.%s.title" %str(windowPropId),tagname)
+        path = "library://video/Emby - %s/%s_all.xml"%(tagname_normalized,tagname_normalized)
+        WINDOW.setProperty("Emby.nodes.%s.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
+        WINDOW.setProperty("Emby.nodes.%s.content" %str(windowPropId),path)
+        WINDOW.setProperty("Emby.nodes.%s.type" %str(windowPropId),type)
+        etree.SubElement(Rule, "value").text = tagname
+        try:
+            etree.ElementTree(root).write(nodefile, xml_declaration=True)
+        except:
+            etree.ElementTree(root).write(nodefile)
+        
+        #create tag node - recent items
+        nodefile = os.path.join(libraryPath, tagname_normalized + "_recent.xml")
+        root = etree.Element("node", {"order":"2", "type":"filter"})
+        if type == "tvshows":
+            label = language(30170)
+        else:
+            label = language(30174)
+        etree.SubElement(root, "label").text = label
+        etree.SubElement(root, "match").text = "all"
+        etree.SubElement(root, "content").text = type
+        etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
+        Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
+        etree.SubElement(Rule, "value").text = tagname
+        etree.SubElement(root, "order", {"direction":"descending"}).text = "dateadded"
+        #set limit to 25 --> currently hardcoded --> TODO: add a setting for this ?
+        etree.SubElement(root, "limit").text = "25"
+        #exclude watched items --> currently hardcoded --> TODO: add a setting for this ?
+        Rule2 = etree.SubElement(root, "rule", {"field":"playcount","operator":"is"})
+        etree.SubElement(Rule2, "value").text = "0"
+        WINDOW.setProperty("Emby.nodes.%s.recent.title" %str(windowPropId),label)
+        path = "library://video/Emby - %s/%s_recent.xml"%(tagname_normalized,tagname_normalized)
+        WINDOW.setProperty("Emby.nodes.%s.recent.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
+        WINDOW.setProperty("Emby.nodes.%s.recent.content" %str(windowPropId),path)
+        try:
+            etree.ElementTree(root).write(nodefile, xml_declaration=True)
+        except:
+            etree.ElementTree(root).write(nodefile)
+        
+        #create tag node - inprogress items
+        nodefile = os.path.join(libraryPath, tagname_normalized + "_progress.xml")
+        root = etree.Element("node", {"order":"3", "type":"filter"})
+        if type == "tvshows":
+            label = language(30171)
+        else:
+            label = language(30177)
+        etree.SubElement(root, "label").text = label
+        etree.SubElement(root, "match").text = "all"
+        etree.SubElement(root, "content").text = type
+        etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
+        Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
+        etree.SubElement(Rule, "value").text = tagname
+        #set limit to 25 --> currently hardcoded --> TODO: add a setting for this ?
+        etree.SubElement(root, "limit").text = "25"
+        Rule2 = etree.SubElement(root, "rule", {"field":"inprogress","operator":"true"})
+        WINDOW.setProperty("Emby.nodes.%s.inprogress.title" %str(windowPropId),label)
+        path = "library://video/Emby - %s/%s_progress.xml"%(tagname_normalized,tagname_normalized)
+        WINDOW.setProperty("Emby.nodes.%s.inprogress.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
+        WINDOW.setProperty("Emby.nodes.%s.inprogress.content" %str(windowPropId),path)
+        try:
+            etree.ElementTree(root).write(nodefile, xml_declaration=True)
+        except:
+            etree.ElementTree(root).write(nodefile)
+        
+        #some movies-only nodes
+        if type == "movies":
             
-            if nodetype == "all":
-
-                if viewtype == "mixed":
-                    templabel = dirname
-                else:
-                    templabel = label
-
-                embynode = "Emby.nodes.%s" % indexnumber
-                utils.window('%s.title' % embynode, value=templabel)
-                utils.window('%s.path' % embynode, value=windowpath)
-                utils.window('%s.content' % embynode, value=path)
-                utils.window('%s.type' % embynode, value=mediatype)
-            else:
-                embynode = "Emby.nodes.%s.%s" % (indexnumber, nodetype)
-                utils.window('%s.title' % embynode, value=label)
-                utils.window('%s.path' % embynode, value=windowpath)
-                utils.window('%s.content' % embynode, value=path)
-
-            if xbmcvfs.exists(nodeXML):
-                # Don't recreate xml if already exists
-                continue
-
-
-            # Create the root
-            if nodetype == "nextupepisodes" or (kodiversion == 14 and
-                                        nodetype in ('recentepisodes', 'inprogressepisodes')):
-                # Folder type with plugin path
-                root = self.commonRoot(order=node, label=label, tagname=tagname, roottype=2)
-                etree.SubElement(root, 'path').text = path
-                etree.SubElement(root, 'content').text = "episodes"
-            else:
-                root = self.commonRoot(order=node, label=label, tagname=tagname)
-                if nodetype in ('recentepisodes', 'inprogressepisodes'):
-                    etree.SubElement(root, 'content').text = "episodes"
-                else:
-                    etree.SubElement(root, 'content').text = mediatype
-
-                limit = "25"
-                # Elements per nodetype
-                if nodetype == "all":
-                    etree.SubElement(root, 'order', {'direction': "ascending"}).text = "sorttitle"
-                
-                elif nodetype == "recent":
-                    etree.SubElement(root, 'order', {'direction': "descending"}).text = "dateadded"
-                    etree.SubElement(root, 'limit').text = limit
-                    rule = etree.SubElement(root, 'rule', {'field': "playcount", 'operator': "is"})
-                    etree.SubElement(rule, 'value').text = "0"
-                
-                elif nodetype == "inprogress":
-                    etree.SubElement(root, 'rule', {'field': "inprogress", 'operator': "true"})
-                    etree.SubElement(root, 'limit').text = limit
-
-                elif nodetype == "genres":
-                    etree.SubElement(root, 'order', {'direction': "ascending"}).text = "sorttitle"
-                    etree.SubElement(root, 'group').text = "genres"
-                
-                elif nodetype == "unwatched":
-                    etree.SubElement(root, 'order', {'direction': "ascending"}).text = "sorttitle"
-                    rule = etree.SubElement(root, "rule", {'field': "playcount", 'operator': "is"})
-                    etree.SubElement(rule, 'value').text = "0"
-
-                elif nodetype == "sets":
-                    etree.SubElement(root, 'order', {'direction': "ascending"}).text = "sorttitle"
-                    etree.SubElement(root, 'group').text = "sets"
-
-                elif nodetype == "random":
-                    etree.SubElement(root, 'order', {'direction': "ascending"}).text = "random"
-                    etree.SubElement(root, 'limit').text = limit
-
-                elif nodetype == "recommended":
-                    etree.SubElement(root, 'order', {'direction': "descending"}).text = "rating"
-                    etree.SubElement(root, 'limit').text = limit
-                    rule = etree.SubElement(root, 'rule', {'field': "playcount", 'operator': "is"})
-                    etree.SubElement(rule, 'value').text = "0"
-                    rule2 = etree.SubElement(root, 'rule',
-                        attrib={'field': "rating", 'operator': "greaterthan"})
-                    etree.SubElement(rule2, 'value').text = "7"
-
-                elif nodetype == "recentepisodes":
-                    # Kodi Isengard, Jarvis
-                    etree.SubElement(root, 'order', {'direction': "descending"}).text = "dateadded"
-                    etree.SubElement(root, 'limit').text = limit
-                    rule = etree.SubElement(root, 'rule', {'field': "playcount", 'operator': "is"})
-                    etree.SubElement(rule, 'value').text = "0"
-
-                elif nodetype == "inprogressepisodes":
-                    # Kodi Isengard, Jarvis
-                    etree.SubElement(root, 'limit').text = "25"
-                    rule = etree.SubElement(root, 'rule',
-                        attrib={'field': "inprogress", 'operator':"true"})
-
+            #unwatched movies
+            nodefile = os.path.join(libraryPath, tagname_normalized + "_unwatched.xml")
+            root = etree.Element("node", {"order":"4", "type":"filter"})
+            label = language(30189)
+            etree.SubElement(root, "label").text = label
+            etree.SubElement(root, "match").text = "all"
+            etree.SubElement(root, "content").text = "movies"
+            etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
+            Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
+            etree.SubElement(Rule, "value").text = tagname
+            Rule = etree.SubElement(root, "rule", {"field":"playcount","operator":"is"})
+            etree.SubElement(Rule, "value").text = "0"
+            etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle"
+            Rule2 = etree.SubElement(root, "rule", {"field":"playcount","operator":"is"})
+            etree.SubElement(Rule2, "value").text = "0"
+            WINDOW.setProperty("Emby.nodes.%s.unwatched.title" %str(windowPropId),label)
+            path = "library://video/Emby - %s/%s_unwatched.xml"%(tagname_normalized,tagname_normalized)
+            WINDOW.setProperty("Emby.nodes.%s.unwatched.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
+            WINDOW.setProperty("Emby.nodes.%s.unwatched.content" %str(windowPropId),path)
             try:
-                utils.indent(root)
-            except: pass
-            etree.ElementTree(root).write(nodeXML)
+                etree.ElementTree(root).write(nodefile, xml_declaration=True)
+            except:
+                etree.ElementTree(root).write(nodefile)
+            
+            #sets
+            nodefile = os.path.join(libraryPath, tagname_normalized + "_sets.xml")
+            root = etree.Element("node", {"order":"9", "type":"filter"})
+            label = xbmc.getLocalizedString(20434)
+            etree.SubElement(root, "label").text = label
+            etree.SubElement(root, "match").text = "all"
+            etree.SubElement(root, "group").text = "sets"
+            etree.SubElement(root, "content").text = type
+            etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
+            etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle"
+            Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
+            etree.SubElement(Rule, "value").text = tagname
+            WINDOW.setProperty("Emby.nodes.%s.sets.title" %str(windowPropId),label)
+            path = "library://video/Emby - %s/%s_sets.xml"%(tagname_normalized,tagname_normalized)
+            WINDOW.setProperty("Emby.nodes.%s.sets.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
+            WINDOW.setProperty("Emby.nodes.%s.sets.content" %str(windowPropId),path)
+            try:
+                etree.ElementTree(root).write(nodefile, xml_declaration=True)
+            except:
+                etree.ElementTree(root).write(nodefile)
 
-    def singleNode(self, indexnumber, tagname, mediatype, itemtype):
-
-        tagname = tagname.encode('utf-8')
-        cleantagname = utils.normalize_nodes(tagname)
-        nodepath = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
-        nodeXML = "%semby_%s.xml" % (nodepath, cleantagname)
-        path = "library://video/emby_%s.xml" % (cleantagname)
-        windowpath = "ActivateWindow(Video, %s, return)" % path
+        #create tag node - genres
+        nodefile = os.path.join(libraryPath, tagname_normalized + "_genres.xml")
+        root = etree.Element("node", {"order":"9", "type":"filter"})
+        label = xbmc.getLocalizedString(135)
+        etree.SubElement(root, "label").text = label
+        etree.SubElement(root, "match").text = "all"
+        etree.SubElement(root, "group").text = "genres"
+        etree.SubElement(root, "content").text = type
+        etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
+        etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle"
+        Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
+        etree.SubElement(Rule, "value").text = tagname
+        WINDOW.setProperty("Emby.nodes.%s.genres.title" %str(windowPropId),label)
+        path = "library://video/Emby - %s/%s_genres.xml"%(tagname_normalized,tagname_normalized)
+        WINDOW.setProperty("Emby.nodes.%s.genres.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
+        WINDOW.setProperty("Emby.nodes.%s.genres.content" %str(windowPropId),path)
         
-        # Create the video node directory
-        if not xbmcvfs.exists(nodepath):
-            # We need to copy over the default items
-            shutil.copytree(
-                src=xbmc.translatePath("special://xbmc/system/library/video").decode('utf-8'),
-                dst=xbmc.translatePath("special://profile/library/video").decode('utf-8'))
-            xbmcvfs.exists(path)
-
-        labels = {
-
-            'Favorite movies': 30180,
-            'Favorite tvshows': 30181,
-            'channels': 30173
-        }
-        label = utils.language(labels[tagname])
-        embynode = "Emby.nodes.%s" % indexnumber
-        utils.window('%s.title' % embynode, value=label)
-        utils.window('%s.path' % embynode, value=windowpath)
-        utils.window('%s.content' % embynode, value=path)
-        utils.window('%s.type' % embynode, value=itemtype)
-
-        if xbmcvfs.exists(nodeXML):
-            # Don't recreate xml if already exists
-            return
-
-        if itemtype == "channels":
-            root = self.commonRoot(order=1, label=label, tagname=tagname, roottype=2)
-            etree.SubElement(root, 'path').text = "plugin://plugin.video.emby/?id=0&mode=channels"
-        else:
-            root = self.commonRoot(order=1, label=label, tagname=tagname)
-            etree.SubElement(root, 'order', {'direction': "ascending"}).text = "sorttitle"
-
-        etree.SubElement(root, 'content').text = mediatype
-
         try:
-            utils.indent(root)
-        except: pass
-        etree.ElementTree(root).write(nodeXML)
-
-    def clearProperties(self):
-
-        self.logMsg("Clearing nodes properties.", 1)
-        embyprops = utils.window('Emby.nodes.total')
-        propnames = [
+            etree.ElementTree(root).write(nodefile, xml_declaration=True)
+        except:
+            etree.ElementTree(root).write(nodefile)
         
-            "index","path","title","content",
-            "inprogress.content","inprogress.title",
-            "inprogress.content","inprogress.path",
-            "nextepisodes.title","nextepisodes.content",
-            "nextepisodes.path","unwatched.title",
-            "unwatched.content","unwatched.path",
-            "recent.title","recent.content","recent.path",
-            "recentepisodes.title","recentepisodes.content",
-            "recentepisodes.path","inprogressepisodes.title",
-            "inprogressepisodes.content","inprogressepisodes.path"
-        ]
+        #create tag node - random items
+        nodefile = os.path.join(libraryPath, tagname_normalized + "_random.xml")
+        root = etree.Element("node", {"order":"10", "type":"filter"})
+        label = language(30229)
+        etree.SubElement(root, "label").text = label
+        etree.SubElement(root, "match").text = "all"
+        etree.SubElement(root, "content").text = type
+        etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
+        Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
+        etree.SubElement(Rule, "value").text = tagname
+        #set limit to 25 --> currently hardcoded --> TODO: add a setting for this ?
+        etree.SubElement(root, "limit").text = "25"
+        etree.SubElement(root, "order", {"direction":"ascending"}).text = "random"
+        WINDOW.setProperty("Emby.nodes.%s.random.title" %str(windowPropId),label)
+        path = "library://video/Emby - %s/%s_random.xml"%(tagname_normalized,tagname_normalized)
+        WINDOW.setProperty("Emby.nodes.%s.random.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
+        WINDOW.setProperty("Emby.nodes.%s.random.content" %str(windowPropId),path)
+        try:
+            etree.ElementTree(root).write(nodefile, xml_declaration=True)
+        except:
+            etree.ElementTree(root).write(nodefile)
+        
+        #create tag node - recommended items
+        nodefile = os.path.join(libraryPath, tagname_normalized + "_recommended.xml")
+        root = etree.Element("node", {"order":"10", "type":"filter"})
+        label = language(30230)
+        etree.SubElement(root, "label").text = label
+        etree.SubElement(root, "match").text = "all"
+        etree.SubElement(root, "content").text = type
+        etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
+        Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
+        etree.SubElement(Rule, "value").text = tagname
+        Rule2 = etree.SubElement(root, "rule", {"field":"playcount","operator":"is"})
+        etree.SubElement(Rule2, "value").text = "0"
+        Rule3 = etree.SubElement(root, "rule", {"field":"rating","operator":"greaterthan"})
+        etree.SubElement(Rule3, "value").text = "7"
+        #set limit to 25 --> currently hardcoded --> TODO: add a setting for this ?
+        etree.SubElement(root, "limit").text = "25"
+        etree.SubElement(root, "order", {"direction":"descending"}).text = "rating"
+        WINDOW.setProperty("Emby.nodes.%s.random.title" %str(windowPropId),label)
+        path = "library://video/Emby - %s/%s_recommended.xml"%(tagname_normalized,tagname_normalized)
+        WINDOW.setProperty("Emby.nodes.%s.recommended.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
+        WINDOW.setProperty("Emby.nodes.%s.recommended.content" %str(windowPropId),path)
+        try:
+            etree.ElementTree(root).write(nodefile, xml_declaration=True)
+        except:
+            etree.ElementTree(root).write(nodefile)
+        
+        #### TAGS ONLY FOR TV SHOWS COLLECTIONS ####
+        if type == "tvshows":    
+            
+            #as from kodi isengard you can use tags for episodes to filter
+            #for below isengard we still use the plugin's entrypoint to build a listing
+            if kodiVersion == 15:
+                #create tag node - recent episodes
+                nodefile = os.path.join(libraryPath, tagname_normalized + "_recent_episodes.xml")
+                root = etree.Element("node", {"order":"3", "type":"filter"})
+                label = language(30175)
+                etree.SubElement(root, "label").text = label
+                etree.SubElement(root, "match").text = "all"
+                etree.SubElement(root, "content").text = "episodes"
+                etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
+                etree.SubElement(root, "order", {"direction":"descending"}).text = "dateadded"
+                Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
+                etree.SubElement(Rule, "value").text = tagname
+                #set limit to 25 --> currently hardcoded --> TODO: add a setting for this ?
+                etree.SubElement(root, "limit").text = "25"
+                #exclude watched items --> currently hardcoded --> TODO: add a setting for this ?
+                Rule2 = etree.SubElement(root, "rule", {"field":"playcount","operator":"is"})
+                etree.SubElement(Rule2, "value").text = "0"
+                WINDOW.setProperty("Emby.nodes.%s.recentepisodes.title" %str(windowPropId),label)
+                path = "library://video/Emby - %s/%s_recent_episodes.xml"%(tagname_normalized,tagname_normalized)
+                WINDOW.setProperty("Emby.nodes.%s.recentepisodes.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
+                WINDOW.setProperty("Emby.nodes.%s.recentepisodes.content" %str(windowPropId),path)
+                try:
+                    etree.ElementTree(root).write(nodefile, xml_declaration=True)
+                except:
+                    etree.ElementTree(root).write(nodefile)
+                
+                #create tag node - inprogress episodes
+                nodefile = os.path.join(libraryPath, tagname_normalized + "_progress_episodes.xml")
+                root = etree.Element("node", {"order":"4", "type":"filter"})
+                label = language(30178)
+                etree.SubElement(root, "label").text = label
+                etree.SubElement(root, "match").text = "all"
+                etree.SubElement(root, "content").text = "episodes"
+                etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
+                Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
+                etree.SubElement(Rule, "value").text = tagname
+                #set limit to 25 --> currently hardcoded --> TODO: add a setting for this ?
+                etree.SubElement(root, "limit").text = "25"
+                Rule2 = etree.SubElement(root, "rule", {"field":"inprogress","operator":"true"})
+                WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.title" %str(windowPropId),label)
+                path = "library://video/Emby - %s/%s_progress_episodes.xml"%(tagname_normalized,tagname_normalized)
+                WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
+                WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.content" %str(windowPropId),path)
+                try:
+                    etree.ElementTree(root).write(nodefile, xml_declaration=True)
+                except:
+                    etree.ElementTree(root).write(nodefile)
+                    
+            if kodiVersion == 14:
+                #create tag node - recent episodes
+                nodefile = os.path.join(libraryPath, tagname_normalized + "_recent_episodes.xml")
+                root = etree.Element("node", {"order":"4", "type":"folder"})
+                label = language(30175)
+                etree.SubElement(root, "label").text = label
+                etree.SubElement(root, "content").text = "episodes"
+                etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
+                path = "plugin://plugin.video.emby/?id=%s&mode=recentepisodes&limit=25" %tagname
+                etree.SubElement(root, "path").text = path
+                WINDOW.setProperty("Emby.nodes.%s.recentepisodes.title" %str(windowPropId),label)
+                path = "library://video/Emby - %s/%s_recent_episodes.xml"%(tagname_normalized,tagname_normalized)
+                WINDOW.setProperty("Emby.nodes.%s.recentepisodes.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
+                WINDOW.setProperty("Emby.nodes.%s.recentepisodes.content" %str(windowPropId),path)
+                try:
+                    etree.ElementTree(root).write(nodefile, xml_declaration=True)
+                except:
+                    etree.ElementTree(root).write(nodefile)
+                
+                #create tag node - inprogress items
+                nodefile = os.path.join(libraryPath, tagname_normalized + "_progress_episodes.xml")
+                root = etree.Element("node", {"order":"5", "type":"folder"})
+                label = language(30178)
+                etree.SubElement(root, "label").text = label
+                etree.SubElement(root, "content").text = "episodes"
+                etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
+                path = "plugin://plugin.video.emby/?id=%s&mode=inprogressepisodes&limit=25" %tagname
+                etree.SubElement(root, "path").text = path
+                WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.title" %str(windowPropId),label)
+                path = "library://video/Emby - %s/%s_progress_episodes.xml"%(tagname_normalized,tagname_normalized)
+                WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
+                WINDOW.setProperty("Emby.nodes.%s.inprogressepisodes.content" %str(windowPropId),path)
+                try:
+                    etree.ElementTree(root).write(nodefile, xml_declaration=True)
+                except:
+                    etree.ElementTree(root).write(nodefile)
+            
+            #create tag node - nextup items
+            #for nextup we always use the dynamic content approach with the plugin's entrypoint because it involves a custom query
+            nodefile = os.path.join(libraryPath, tagname_normalized + "_nextup_episodes.xml")
+            root = etree.Element("node", {"order":"6", "type":"folder"})
+            label = language(30179)
+            etree.SubElement(root, "label").text = label
+            etree.SubElement(root, "content").text = "episodes"
+            path = "plugin://plugin.video.emby/?id=%s&mode=nextup&limit=25" %tagname
+            etree.SubElement(root, "path").text = path
+            etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
+            WINDOW.setProperty("Emby.nodes.%s.nextepisodes.title" %str(windowPropId),label)
+            path = "library://video/Emby - %s/%s_nextup_episodes.xml"%(tagname_normalized,tagname_normalized)
+            WINDOW.setProperty("Emby.nodes.%s.nextepisodes.path" %str(windowPropId),"ActivateWindow(Video,%s,return)"%path)
+            WINDOW.setProperty("Emby.nodes.%s.nextepisodes.content" %str(windowPropId),path)        
+            try:
+                etree.ElementTree(root).write(nodefile, xml_declaration=True)
+            except:
+                etree.ElementTree(root).write(nodefile)
+    
+    def buildVideoNodesListing(self):
+            
+        try:
+        
+            # the library path doesn't exist on all systems
+            if not xbmcvfs.exists("special://profile/library/"):
+                xbmcvfs.mkdir("special://profile/library") 
+            if not xbmcvfs.exists("special://profile/library/video/"):
+                #we need to copy over the default items
+                shutil.copytree(xbmc.translatePath("special://xbmc/system/library/video"), xbmc.translatePath("special://profile/library/video"))
+            
+            #always cleanup existing Emby video nodes first because we don't want old stuff to stay in there
+            path = "special://profile/library/video/"
+            if xbmcvfs.exists(path):
+                allDirs, allFiles = xbmcvfs.listdir(path)
+                for dir in allDirs:
+                    if dir.startswith("Emby "):
+                        shutil.rmtree(xbmc.translatePath("special://profile/library/video/" + dir))
+                for file in allFiles:
+                    if file.startswith("emby"):
+                        xbmcvfs.delete(path + file)
+            
+            #we build up a listing and set window props for all nodes we created
+            #the window props will be used by the main entry point to quickly build up the listing and can be used in skins (like titan) too for quick reference
+            #comment marcelveldt: please leave the window props as-is because I will be referencing them in titan skin...
+            totalNodesCount = 0
+            
+            #build the listing for all views
+            views_movies = ReadEmbyDB().getCollections("movies")
+            if views_movies:
+                for view in views_movies:
+                    title = view.get('title')
+                    content = view.get('content')
+                    if content == "mixed":
+                        title = "%s - Movies" % title
+                    self.buildVideoNodeForView(title, "movies", totalNodesCount)
+                    totalNodesCount +=1
+                    
+            views_shows = ReadEmbyDB().getCollections("tvshows")
+            if views_shows:
+                for view in views_shows:
+                    title = view.get('title')
+                    content = view.get('content')
+                    if content == "mixed":
+                        title = "%s - TV Shows" % title
+                    self.buildVideoNodeForView(title, "tvshows", totalNodesCount)
+                    totalNodesCount +=1
 
-        if embyprops:
-            totalnodes = int(embyprops)
-            for i in range(totalnodes):
-                for prop in propnames:
-                    utils.window('Emby.nodes.%s.%s' % (str(i), prop), clear=True)
\ No newline at end of file
+            #create tag node for emby channels
+            nodefile = os.path.join(xbmc.translatePath("special://profile/library/video"), "emby_channels.xml")
+            root = etree.Element("node", {"order":"1", "type":"folder"})
+            label = language(30173)
+            etree.SubElement(root, "label").text = label
+            etree.SubElement(root, "content").text = "movies"
+            etree.SubElement(root, "path").text = "plugin://plugin.video.emby/?id=0&mode=channels"
+            etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
+            WINDOW.setProperty("Emby.nodes.%s.title" %str(totalNodesCount),label)
+            WINDOW.setProperty("Emby.nodes.%s.type" %str(totalNodesCount),"channels")
+            path = "library://video/emby_channels.xml"
+            WINDOW.setProperty("Emby.nodes.%s.path" %str(totalNodesCount),"ActivateWindow(Video,%s,return)"%path)
+            WINDOW.setProperty("Emby.nodes.%s.content" %str(totalNodesCount),path)
+            totalNodesCount +=1        
+            try:
+                etree.ElementTree(root).write(nodefile, xml_declaration=True)
+            except:
+                etree.ElementTree(root).write(nodefile)
+                   
+            #create tag node - favorite shows
+            nodefile = os.path.join(xbmc.translatePath("special://profile/library/video"),"emby_favorite_shows.xml")
+            root = etree.Element("node", {"order":"1", "type":"filter"})
+            label = language(30181)
+            etree.SubElement(root, "label").text = label
+            etree.SubElement(root, "match").text = "all"
+            etree.SubElement(root, "content").text = "tvshows"
+            etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
+            etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle"
+            Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
+            etree.SubElement(Rule, "value").text = "Favorite tvshows" #do not localize the tagname itself
+            WINDOW.setProperty("Emby.nodes.%s.title" %str(totalNodesCount),label)
+            WINDOW.setProperty("Emby.nodes.%s.type" %str(totalNodesCount),"favourites")
+            path = "library://video/emby_favorite_shows.xml"
+            WINDOW.setProperty("Emby.nodes.%s.path" %str(totalNodesCount),"ActivateWindow(Video,%s,return)"%path)
+            WINDOW.setProperty("Emby.nodes.%s.content" %str(totalNodesCount),path)
+            totalNodesCount +=1
+            try:
+                etree.ElementTree(root).write(nodefile, xml_declaration=True)
+            except:
+                etree.ElementTree(root).write(nodefile)
+            
+            #create tag node - favorite movies
+            nodefile = os.path.join(xbmc.translatePath("special://profile/library/video"),"emby_favorite_movies.xml")
+            root = etree.Element("node", {"order":"1", "type":"filter"})
+            label = language(30180)
+            etree.SubElement(root, "label").text = label
+            etree.SubElement(root, "match").text = "all"
+            etree.SubElement(root, "content").text = "movies"
+            etree.SubElement(root, "icon").text = "special://home/addons/plugin.video.emby/icon.png"
+            etree.SubElement(root, "order", {"direction":"ascending"}).text = "sorttitle"
+            Rule = etree.SubElement(root, "rule", {"field":"tag","operator":"is"})
+            etree.SubElement(Rule, "value").text = "Favorite movies" #do not localize the tagname itself
+            WINDOW.setProperty("Emby.nodes.%s.title" %str(totalNodesCount),label)
+            WINDOW.setProperty("Emby.nodes.%s.type" %str(totalNodesCount),"favourites")
+            path = "library://video/emby_favorite_movies.xml"
+            WINDOW.setProperty("Emby.nodes.%s.path" %str(totalNodesCount),"ActivateWindow(Video,%s,return)"%path)
+            WINDOW.setProperty("Emby.nodes.%s.content" %str(totalNodesCount),path)
+            totalNodesCount +=1
+            try:
+                etree.ElementTree(root).write(nodefile, xml_declaration=True)
+            except:
+                etree.ElementTree(root).write(nodefile)
+            
+            WINDOW.setProperty("Emby.nodes.total", str(totalNodesCount))               
+                
+
+        except Exception as e:
+            utils.logMsg("Emby addon","Error while creating videonodes listings, restart required ?")
+            print e    
\ No newline at end of file