diff --git a/resources/lib/DownloadUtils.py b/resources/lib/DownloadUtils.py
index 04d2da85..5c47144d 100644
--- a/resources/lib/DownloadUtils.py
+++ b/resources/lib/DownloadUtils.py
@@ -1,32 +1,38 @@
-import xbmc
-import xbmcgui
-import xbmcaddon
+# -*- coding: utf-8 -*-
+
+##################################################################################################
 
-import requests
 import json
+import requests
 import logging
 
-import Utils as utils
-from ClientInformation import ClientInformation
-from requests.packages.urllib3.exceptions import InsecureRequestWarning
+import xbmc
+import xbmcgui
+
+import utils
+import clientinfo
+
+##################################################################################################
 
 # 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 = ClientInformation()
-
+    clientInfo = clientinfo.ClientInfo()
     addonName = clientInfo.getAddonName()
-    addon = xbmcaddon.Addon()
-    WINDOW = xbmcgui.Window(10000)
 
     # Requests session
     s = None
-    timeout = 60
+    timeout = 30
+
 
     def __init__(self):
 
@@ -34,41 +40,44 @@ class DownloadUtils():
 
     def logMsg(self, msg, lvl=1):
 
-        self.className = self.__class__.__name__
-        utils.logMsg("%s %s" % (self.addonName, self.className), msg, int(lvl))
+        className = self.__class__.__name__
+        utils.logMsg("%s %s" % (self.addonName, className), msg, 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}/mediabrowser/Sessions/Capabilities/Full"
+        url = "{server}/emby/Sessions/Capabilities/Full?format=json"
         data = {
+            
             'PlayableMediaTypes': "Audio,Video",
             'SupportsMediaControl': True,
             'SupportedCommands': (
@@ -86,49 +95,57 @@ class DownloadUtils():
         }
 
         self.logMsg("Capabilities URL: %s" % url, 2)
-        self.logMsg("PostData: %s" % data, 2)
+        self.logMsg("Postdata: %s" % data, 2)
 
-        try:
-            self.downloadUrl(url, postBody=data, type="POST")
-            self.logMsg("Posted capabilities to %s" % self.server, 1)
-        except:
-            self.logMsg("Posted capabilities failed.")
+        self.downloadUrl(url, postBody=data, type="POST")
+        self.logMsg("Posted capabilities to %s" % self.server, 2)
 
         # Attempt at getting sessionId
-        url = "{server}/mediabrowser/Sessions?DeviceId=%s&format=json" % deviceId
-
+        url = "{server}/emby/Sessions?DeviceId=%s&format=json" % deviceId
+        result = self.downloadUrl(url)
         try:
-            result = self.downloadUrl(url)
-            self.logMsg("Session: %s" % result, 2)
-            
-            sessionId = result[0][u'Id']
-            self.logMsg("SessionId: %s" % sessionId)
-            self.WINDOW.setProperty("sessionId%s" % self.username, sessionId)
-        except:
+            sessionId = result[0]['Id']
+        
+        except (KeyError, TypeError):
             self.logMsg("Failed to retrieve sessionId.", 1)
+        
         else:
+            self.logMsg("Session: %s" % result, 2)
+            self.logMsg("SessionId: %s" % sessionId, 1)
+            utils.window('emby_sessionId', value=sessionId)
+            
             # Post any permanent additional users
-            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)
+            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()
 
-            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:
-                            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)
+                            userId = user['Id']
+                            url = (
+                                    "{server}/emby/Sessions/%s/Users/%s?format=json"
+                                    % (sessionId, userId)
+                            )
+                            self.downloadUrl(url, postBody={}, type="POST")
+
 
     def startSession(self):
 
-        self.deviceId = self.clientInfo.getMachineId()
+        self.deviceId = self.clientInfo.getDeviceId()
 
         # User is identified from this point
         # Attach authenticated header to the session
@@ -152,7 +169,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)
+        self.logMsg("Requests session started on: %s" % self.server, 1)
 
     def stopSession(self):
         try:
@@ -165,93 +182,116 @@ class DownloadUtils():
         clientInfo = self.clientInfo
 
         deviceName = clientInfo.getDeviceName()
-        deviceId = clientInfo.getMachineId()
+        deviceId = clientInfo.getDeviceId()
         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}        
-                    
-            self.logMsg("Header: %s" % header, 2)
-            return header
+            auth = (
+                'MediaBrowser UserId="%s", Client="Kodi", Device="%s", DeviceId="%s", Version="%s"'
+                % (userId, deviceName, deviceId, version))
+            header = {
 
-    def downloadUrl(self, url, postBody=None, type="GET", authenticate=True):
+                '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
+
+    def downloadUrl(self, url, postBody=None, type="GET", parameters=None, 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 and append api_key
-                    url = url.replace("{server}", self.server, 1)
-                    url = url.replace("{UserId}", self.userId, 1)
+                    # Replace for the real values
+                    url = url.replace("{server}", self.server)
+                    url = url.replace("{UserId}", self.userId)
 
-                    self.logMsg("URL: %s" % url, 2)
                     # Prepare request
                     if type == "GET":
-                        r = s.get(url, json=postBody, timeout=timeout)
+                        r = s.get(url, json=postBody, params=parameters, 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.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)
+                    self.userId = utils.window('emby_currUser')
+                    self.server = utils.window('emby_server%s' % self.userId)
+                    self.token = utils.window('emby_accessToken%s' % self.userId)
                     header = self.getHeader()
                     verifyssl = False
                     cert = None
 
                     # IF user enables ssl verification
-                    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
+                    if utils.settings('sslverify') == "true":
+                        verifyssl = True
+                    if utils.settings('sslcert') != "None":
+                        cert = utils.settings('sslcert')
 
-                    # Replace for the real values and append api_key
-                    url = url.replace("{server}", self.server, 1)
-                    url = url.replace("{UserId}", self.userId, 1)
+                    # Replace for the real values
+                    url = url.replace("{server}", self.server)
+                    url = url.replace("{UserId}", self.userId)
 
-                    self.logMsg("URL: %s" % url, 2)
                     # Prepare request
                     if type == "GET":
-                        r = requests.get(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl)
+                        r = requests.get(url,
+                                        json=postBody,
+                                        params=parameters,
+                                        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
 
@@ -263,41 +303,49 @@ class DownloadUtils():
                 
                 # Prepare request
                 if type == "GET":
-                    r = requests.get(url, json=postBody, headers=header, timeout=timeout, verify=verifyssl)
+                    r = requests.get(url,
+                                    json=postBody,
+                                    params=parameters,
+                                    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)
         
-            # Process the response
+            ##### THE RESPONSE #####
+            self.logMsg(r.url, 2)
             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":
-                        pass
-                    else:
+                    if r.headers.get('content-type') != "text/html":
                         self.logMsg("Unable to convert the response for: %s" % url, 1)
             else:
                 r.raise_for_status()
-
-            return default_link
         
-        # TO REVIEW EXCEPTIONS
+        ##### EXCEPTIONS #####
+
         except requests.exceptions.ConnectionError as e:
             # Make the addon aware of status
-            if WINDOW.getProperty("Server_online") != "false":
+            if utils.window('emby_online') != "false":
                 self.logMsg("Server unreachable at: %s" % url, 0)
                 self.logMsg(e, 2)
-                WINDOW.setProperty("Server_online", "false")
-            pass
+                utils.window('emby_online', value="false")
 
         except requests.exceptions.ConnectTimeout as e:
             self.logMsg("Server timeout at: %s" % url, 0)
@@ -307,29 +355,35 @@ class DownloadUtils():
 
             if r.status_code == 401:
                 # Unauthorized
-                status = WINDOW.getProperty("Server_status")
+                status = utils.window('emby_serverStatus')
 
-                if 'x-application-error-code' in r.headers:
+                if 'X-Application-Error-Code' in r.headers:
+                    # Emby server errors
                     if r.headers['X-Application-Error-Code'] == "ParentalControl":
                         # Parental control - access restricted
-                        WINDOW.setProperty("Server_status", "restricted")
-                        xbmcgui.Dialog().notification("Emby server", "Access restricted.", xbmcgui.NOTIFICATION_ERROR, time=5000)
+                        utils.window('emby_serverStatus', value="restricted")
+                        xbmcgui.Dialog().notification(
+                                                heading="Emby server",
+                                                message="Access restricted.",
+                                                icon=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 - admin restricted in some way
+                        # User tried to do something his emby account doesn't allow
                         pass
 
-                elif (status == "401") or (status == "Auth"):
-                    pass
-
-                else:
-                    # Tell UserClient token has been revoked.
-                    WINDOW.setProperty("Server_status", "401")
+                elif status not in ("401", "Auth"):
+                    # Tell userclient token has been revoked.
+                    utils.window('emby_serverStatus', value="401")
                     self.logMsg("HTTP Error: %s" % e, 0)
-                    xbmcgui.Dialog().notification("Error connecting", "Unauthorized.", xbmcgui.NOTIFICATION_ERROR)
+                    xbmcgui.Dialog().notification(
+                                            heading="Error connecting",
+                                            message="Unauthorized.",
+                                            icon=xbmcgui.NOTIFICATION_ERROR)
                     return 401
 
-            elif (r.status_code == 301) or (r.status_code == 302):
+            elif r.status_code in (301, 302):
                 # Redirects
                 pass
             elif r.status_code == 400:
@@ -344,4 +398,4 @@ class DownloadUtils():
             self.logMsg("Unknown error connecting to: %s" % url, 0)
             self.logMsg(e, 1)
 
-        return default_link
+        return default_link
\ No newline at end of file
diff --git a/resources/lib/Entrypoint.py b/resources/lib/Entrypoint.py
index 95a5a327..588b8d3c 100644
--- a/resources/lib/Entrypoint.py
+++ b/resources/lib/Entrypoint.py
@@ -1,100 +1,157 @@
-import xbmcaddon
-import xbmcplugin
+# -*- coding: utf-8 -*-
+
+#################################################################################################
+
+import json
+import os
+import sys
+import urlparse
+
 import xbmc
+import xbmcaddon
 import xbmcgui
 import xbmcvfs
-import os, sys
-import threading
-import json
-import urllib
-import time
+import xbmcplugin
 
-WINDOW = xbmcgui.Window(10000)
+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
 
-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
+#################################################################################################
 
 
-##### 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")
+def doPlayback(itemid, dbid):
 
-#### DO RESET AUTH #####    
+    emby = embyserver.Read_EmbyServer()
+    item = emby.getItem(itemid)
+    pbutils.PlaybackUtils(item).play(itemid, dbid)
+
+##### DO RESET AUTH #####
 def resetAuth():
     # User tried login and failed too many times
-    resp = xbmcgui.Dialog().yesno("Warning", "Emby might lock your account if you fail to log in too many times. Proceed anyway?")
+    resp = xbmcgui.Dialog().yesno(
+                heading="Warning",
+                line1=(
+                    "Emby might lock your account if you fail to log in too many times. "
+                    "Proceed anyway?"))
     if resp == 1:
-        xbmc.log("Reset login attempts.")
-        WINDOW.setProperty("Server_status", "Auth")
+        utils.logMsg("EMBY", "Reset login attempts.", 1)
+        utils.window('emby_serverStatus', value="Auth")
     else:
         xbmc.executebuiltin('Addon.OpenSettings(plugin.video.emby)')
 
-### ADD ADDITIONAL USERS ###
+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 #####
 def addUser():
 
-    doUtils = DownloadUtils()
-    clientInfo = ClientInformation()
-    currUser = WINDOW.getProperty("currUser")
-    deviceId = clientInfo.getMachineId()
+    doUtils = downloadutils.DownloadUtils()
+    clientInfo = clientinfo.ClientInfo()
+    deviceId = clientInfo.getDeviceId()
     deviceName = clientInfo.getDeviceName()
+    userid = utils.window('emby_currUser')
+    dialog = xbmcgui.Dialog()
 
     # Get session
-    url = "{server}/mediabrowser/Sessions?DeviceId=%s" % deviceId
+    url = "{server}/emby/Sessions?DeviceId=%s&format=json" % deviceId
     result = doUtils.downloadUrl(url)
     
     try:
-        sessionId = result[0][u'Id']
-        additionalUsers = result[0][u'AdditionalUsers']
+        sessionId = result[0]['Id']
+        additionalUsers = result[0]['AdditionalUsers']
         # Add user to session
         userlist = {}
         users = []
-        url = "{server}/mediabrowser/Users?IsDisabled=false&IsHidden=false"
+        url = "{server}/emby/Users?IsDisabled=false&IsHidden=false&format=json"
         result = doUtils.downloadUrl(url)
 
         # pull the list of users
         for user in result:
-            name = user[u'Name']
-            userId = user[u'Id']
-            if currUser not in name:
+            name = user['Name']
+            userId = user['Id']
+            if userid != userId:
                 userlist[name] = userId
                 users.append(name)
 
         # Display dialog if there's additional users
         if additionalUsers:
 
-            option = xbmcgui.Dialog().select("Add/Remove user from the session", ["Add user", "Remove user"])
+            option = 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[u'UserName']
-                userId = user[u'UserId']
+                name = user['UserName']
+                userId = user['UserId']
                 additionalUserlist[name] = userId
                 additionalUsername.append(name)
 
             if option == 1:
                 # User selected Remove user
-                resp = xbmcgui.Dialog().select("Remove user from the session", additionalUsername)
+                resp = dialog.select("Remove user from the session", additionalUsername)
                 if resp > -1:
                     selected = additionalUsername[resp]
                     selected_userId = additionalUserlist[selected]
-                    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)
+                    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)
 
                     # clear picture
-                    position = WINDOW.getProperty('EmbyAdditionalUserPosition.' + selected_userId)
-                    WINDOW.clearProperty('EmbyAdditionalUserImage.' + str(position))
+                    position = utils.window('EmbyAdditionalUserPosition.%s' % selected_userId)
+                    utils.window('EmbyAdditionalUserImage.%s' % position, clear=True)
                     return
                 else:
                     return
@@ -111,138 +168,143 @@ def addUser():
                 return
 
         # Subtract any additional users
-        xbmc.log("Displaying list of users: %s" % users)
-        resp = xbmcgui.Dialog().select("Add user to the session", users)
+        utils.logMsg("EMBY", "Displaying list of users: %s" % users)
+        resp = dialog.select("Add user to the session", users)
         # post additional user
         if resp > -1:
             selected = users[resp]
             selected_userId = userlist[selected]
-            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)
+            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)
 
     except:
-        xbmc.log("Failed to add user to session.")
-        xbmcgui.Dialog().notification("Error", "Unable to add/remove user from the session.", xbmcgui.NOTIFICATION_ERROR)
+        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)
 
-    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))
+    # 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)
 
-        url = "{server}/mediabrowser/Sessions?DeviceId=%s" % deviceId
+    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']
         result = doUtils.downloadUrl(url)
-        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
+        utils.window('EmbyAdditionalUserImage.%s' % count,
+            value=artwork.Artwork().getUserArtwork(result, 'Primary'))
+        utils.window('EmbyAdditionalUserPosition.%s' % additionaluser['UserId'], value=str(count))
+        count +=1
 
-# THEME MUSIC/VIDEOS
+##### THEME MUSIC/VIDEOS #####
 def getThemeMedia():
 
-    doUtils = DownloadUtils()
-    playUtils = PlayUtils()
-    
-    currUser = WINDOW.getProperty('currUser')
-    server = WINDOW.getProperty('server%s' % currUser)
+    doUtils = downloadutils.DownloadUtils()
+    dialog = xbmcgui.Dialog()
     playback = None
 
-    library = xbmc.translatePath("special://profile/addon_data/plugin.video.emby/library/").decode('utf-8')
-
     # Choose playback method
-    resp = xbmcgui.Dialog().select("Choose playback method for your themes", ["Direct Play", "Direct Stream"])
+    resp = dialog.select("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
-
-    # Set custom path for user
-    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)
-        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 = 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
-        
 
+    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)
 
+    # Set custom path for user
+    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)
+    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)')
+        return
+        
     # Get every user view Id
-    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)
-
+    embyconn = utils.kodiSQL('emby')
+    embycursor = embyconn.cursor()
+    emby_db = embydb.Embydb_Functions(embycursor)
+    viewids = emby_db.getViews()
+    embycursor.close()
 
     # Get Ids with Theme Videos
     itemIds = {}
-    for view in userViews:
-        url = "{server}/mediabrowser/Users/{UserId}/Items?HasThemeVideo=True&ParentId=%s&format=json" % view
+    for view in viewids:
+        url = "{server}/emby/Users/{UserId}/Items?HasThemeVideo=True&ParentId=%s&format=json" % view
         result = doUtils.downloadUrl(url)
-        if result[u'TotalRecordCount'] != 0:
-            for item in result[u'Items']:
-                itemId = item[u'Id']
-                folderName = item[u'Name']
+        if result['TotalRecordCount'] != 0:
+            for item in result['Items']:
+                itemId = item['Id']
+                folderName = item['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}/mediabrowser/Items/%s/ThemeVideos?format=json" % itemId
+        url = "{server}/emby/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[u'Items']:  
+        for theme in result['Items']:
+            putils = playutils.PlayUtils(theme)
             if playback == "DirectPlay":
-                playurl = playUtils.directPlay(theme)
+                playurl = putils.directPlay()
             else:
-                playurl = playUtils.directStream(result, server, theme[u'Id'], "ThemeVideo")
+                playurl = putils.directStream()
             pathstowrite += ('<file>%s</file>' % playurl.encode('utf-8'))
         
         # Check if the item has theme songs and add them   
-        url = "{server}/mediabrowser/Items/%s/ThemeSongs?format=json" % itemId
+        url = "{server}/emby/Items/%s/ThemeSongs?format=json" % itemId
         result = doUtils.downloadUrl(url)
 
         # May be more than one theme
-        for theme in result[u'Items']:  
+        for theme in result['Items']:
+            putils = playutils.PlayUtils(theme)  
             if playback == "DirectPlay":
-                playurl = playUtils.directPlay(theme)
+                playurl = putils.directPlay()
             else:
-                playurl = playUtils.directStream(result, server, theme[u'Id'], "Audio")
+                playurl = putils.directStream()
             pathstowrite += ('<file>%s</file>' % playurl.encode('utf-8'))
 
         nfo_file.write(
@@ -253,13 +315,13 @@ def getThemeMedia():
 
     # Get Ids with Theme songs
     musicitemIds = {}
-    for view in userViews:
-        url = "{server}/mediabrowser/Users/{UserId}/Items?HasThemeSong=True&ParentId=%s&format=json" % view
+    for view in viewids:
+        url = "{server}/emby/Users/{UserId}/Items?HasThemeSong=True&ParentId=%s&format=json" % view
         result = doUtils.downloadUrl(url)
-        if result[u'TotalRecordCount'] != 0:
-            for item in result[u'Items']:
-                itemId = item[u'Id']
-                folderName = item[u'Name']
+        if result['TotalRecordCount'] != 0:
+            for item in result['Items']:
+                itemId = item['Id']
+                folderName = item['Name']
                 folderName = utils.normalize_string(folderName.encode('utf-8'))
                 musicitemIds[itemId] = folderName
 
@@ -270,25 +332,27 @@ 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}/mediabrowser/Items/%s/ThemeSongs?format=json" % itemId
+        url = "{server}/emby/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[u'Items']:  
+        for theme in result['Items']: 
+            putils = playutils.PlayUtils(theme)
             if playback == "DirectPlay":
-                playurl = playUtils.directPlay(theme)
+                playurl = putils.directPlay()
             else:
-                playurl = playUtils.directStream(result, server, theme[u'Id'], "Audio")
+                playurl = putils.directStream()
             pathstowrite += ('<file>%s</file>' % playurl.encode('utf-8'))
 
         nfo_file.write(
@@ -297,398 +361,482 @@ def getThemeMedia():
         # Close nfo file
         nfo_file.close()
 
-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")
+    dialog.notification(
+            heading="Emby for Kodi",
+            message="Themes added!",
+            icon="special://home/addons/plugin.video.emby/icon.png",
+            time=1000,
+            sound=False)
 
 ##### BROWSE EMBY CHANNELS #####    
-def BrowseChannels(id, folderid=None):
+def BrowseChannels(itemid, 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}/mediabrowser/Channels/" + id + "/Items?userid={UserId}&folderid=" + folderid + "&format=json"
+        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"
     else:
-        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"
+        url = "{server}/emby/Channels/%s/Items?UserId={UserId}&format=json" % itemid
 
-    results = DownloadUtils().downloadUrl(url)
-    if results:
-        result = results.get("Items")
-        if(result == None):
-            result = []
+    result = doUtils.downloadUrl(url)
+    try:
+        channels = result['Items']
+    except TypeError:
+        pass
+    else:
+        for item in channels:
 
-        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":
+            API = api.API(item)
+            itemid = item['Id']
+            itemtype = item['Type']
+            title = item.get('Name', "Missing Title")
+            li = xbmcgui.ListItem(title)
+
+            if itemtype == "ChannelFolderItem":
                 isFolder = True
             else:
                 isFolder = False
-            item_type = str(type).encode('utf-8')
-            
-            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)
-            
+
+            channelId = item.get('ChannelId', "")
+            channelName = item.get('ChannelName', "")
+
+            premieredate = API.getPremiereDate()
             # Process Genres
-            genre = API().getGenre(item)
-                    
+            genre = API.getGenres()
             # 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
+            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
             
-            playCount = 0
-            if(userData != None and userData.get("Played") == True):
-                playCount = 1
+            playcount = userdata['PlayCount']
+            if playcount is None:
+                playcount = 0
+
             # Populate the details list
-            details={'title'        : tempTitle,
-                     'channelname'  : channelName,
-                     'plot'         : item.get("Overview"),
-                     'Overlay'      : overlay,
-                     'playcount'    : str(playCount)}
-            
-            if item.get("Type") == "ChannelVideoItem":
+            details = {
+
+                'title': title,
+                'channelname': channelName,
+                'plot': API.getOverview(),
+                'Overlay': str(overlay),
+                'playcount': str(playcount)
+            }
+
+            if itemtype == "ChannelVideoItem":
                 xbmcplugin.setContent(_addon_id, 'movies')
-            elif item.get("Type") == "ChannelAudioItem":
+            elif itemtype == "ChannelAudioItem":
                 xbmcplugin.setContent(_addon_id, 'songs')
 
-            # 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)
+            # Populate the extradata list and artwork
+            pbutils.PlaybackUtils(item).setArtwork(li)
+            extradata = {
 
-            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']
+                '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)
             
-            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)
+            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)
             else:
-                file = _addon_url + "?id=%s&mode=play"%id
-                liz.setProperty('IsPlayable', 'true')
-                xbmcplugin.addDirectoryItem(handle=_addon_id, url=file, listitem=liz)
+                path = "%s?id=%s&mode=play" % (_addon_url, itemid)
+                li.setProperty('IsPlayable', 'true')
+                xbmcplugin.addDirectoryItem(handle=_addon_id, url=path, listitem=li)
 
     xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
 
-##### 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 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": "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.
-    if json_result.has_key('result') and json_result['result'].has_key('tvshows'):
-        for item in json_result['result']['tvshows']:
-
-            # 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:
-                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]))
-
-def getInProgressEpisodes(tagname,limit):
-    count = 0
-    #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
-    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]))
-    
+##### LISTITEM SETUP FOR VIDEONODES #####
 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) })
+
+    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 = "%.2d" % float(item['episode'])
-        liz.setInfo( type="Video", infoLabels={ "Episode": item['episode'] })
-    
+        episode = item['episode']
+        metadata['Episode'] = episode
+
     if "season" in item:
-        season = "%.2d" % float(item['season'])
-        liz.setInfo( type="Video", infoLabels={ "Season": item['season'] })
-        
+        season = item['season']
+        metadata['Season'] = season
+
     if season and episode:
-        episodeno = "s%se%s" %(season,episode)
-        liz.setProperty("episodeno", episodeno)
-        
+        li.setProperty('episodeno', "s%.2de%.2d" % (season, episode))
+
     if "firstaired" in item:
-        liz.setInfo( type="Video", infoLabels={ "Premiered": item['firstaired'] })
-    
-    plot = item['plot']
-    liz.setInfo( type="Video", infoLabels={ "Plot": plot })
-    
+        metadata['Premiered'] = item['firstaired']
+
     if "showtitle" in item:
-        liz.setInfo( type="Video", infoLabels={ "TVshowTitle": item['showtitle'] })
-    
+        metadata['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'] })
+        metadata['Rating'] = str(round(float(item['rating']),1))
+
     if "director" in item:
-        liz.setInfo( type="Video", infoLabels={ "Director": " / ".join(item['director']) })
+        metadata['Director'] = " / ".join(item['director'])
+
     if "writer" in item:
-        liz.setInfo( type="Video", infoLabels={ "Writer": " / ".join(item['writer']) })
-        
+        metadata['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',''))
+        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:
-            liz.addStreamInfo( key, stream )
+            li.addStreamInfo(key, stream)
     
-    return liz
+    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 = {
+
+        '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
+    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)
+    # 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 = {
+
+                '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
+            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 RECENT EPISODES FOR TAGNAME #####    
+def getRecentEpisodes(tagname, limit):
+    
+    count = 0
+    # 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
+
+    xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
+
 ##### GET EXTRAFANART FOR LISTITEM #####
 def getExtraFanArt():
-    itemPath = ""
-    embyId = ""
     
-    #get extrafanart for listitem - this will only be used for skins that actually call the listitem's path + fanart dir... 
+    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... 
     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 ("/tvshows/" in itemPath or "/musicvideos/" in itemPath or "/movies/" in itemPath):
-            embyId = itemPath.split("/")[-2]
+        if any([x in itemPath for x in ['tvshows', 'musicvideos', 'movies']]):
+            params = urlparse.parse_qs(itemPath)
+            embyId = params['id'][0]
             
-            utils.logMsg("%s %s" % ("Emby addon", "getExtraFanArt"), "requesting extraFanArt for Id: " + embyId, 1)
+            utils.logMsg("EMBY", "Requesting extrafanart for Id: %s" % 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/" + embyId + "/")
+            # 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')
             
             if not xbmcvfs.exists(fanartDir):
-                #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) 
-                
+                # 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               
             else:
-                #use existing cached images
+                utils.logMsg("EMBY", "Found cached backdrop.", 2)
+                # Use existing cached images
                 dirs, files = xbmcvfs.listdir(fanartDir)
-                count = 1
                 for file in files:
-                    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)
+                    fanartFile = os.path.join(fanartDir, file)
+                    li = xbmcgui.ListItem(file, path=fanartFile)
+                    xbmcplugin.addDirectoryItem(
+                                            handle=int(sys.argv[1]),
+                                            url=fanartFile,
+                                            listitem=li)
     except Exception as e:
-        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")
+        utils.logMsg("EMBY", "Error getting extrafanart: %s" % e, 1)
     
+    # 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 2d4dc943..1b3f7862 100644
--- a/resources/lib/KodiMonitor.py
+++ b/resources/lib/KodiMonitor.py
@@ -1,150 +1,195 @@
+# -*- coding: utf-8 -*-
+
 #################################################################################################
-# Kodi  Monitor
-# Watched events that occur in Kodi, like setting media watched
-#################################################################################################
+
+import json
 
 import xbmc
 import xbmcgui
-import xbmcaddon
-import json
 
-import Utils as utils
-from WriteKodiVideoDB import WriteKodiVideoDB
-from ReadKodiDB import ReadKodiDB
-from PlayUtils import PlayUtils
-from DownloadUtils import DownloadUtils
-from PlaybackUtils import PlaybackUtils
+import clientinfo
+import downloadutils
+import embydb_functions as embydb
+import playbackutils as pbutils
+import utils
+
+#################################################################################################
 
 
-class Kodi_Monitor( xbmc.Monitor ):
-    
-    WINDOW = xbmcgui.Window(10000)
+class KodiMonitor(xbmc.Monitor):
 
-    def __init__(self, *args, **kwargs):
-        xbmc.Monitor.__init__(self)
 
-    def logMsg(self, msg, lvl = 1):
+    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)
 
-        className = self.__class__.__name__
-        utils.logMsg("%s %s" % ("EMBY", className), msg, int(lvl))
 
     def onScanStarted(self, library):
-        utils.window('kodiScan', value="true")
-        self.logMsg("Kodi library scan running.", 2)
-
+        self.logMsg("Kodi library scan %s running." % library, 2)
+        if library == "video":
+            utils.window('emby_kodiScan', value="true")
+            
     def onScanFinished(self, library):
-        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):
+        self.logMsg("Kodi library scan %s finished." % library, 2)
+        if library == "video":
+            utils.window('emby_kodiScan', clear=True)
 
-        WINDOW = self.WINDOW
-        downloadUtils = DownloadUtils()
-        #player started playing an item - 
-        if ("Playlist.OnAdd" in method or "Player.OnPlay" in method):
+    def onNotification(self, sender, method, data):
 
-            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)
+        doUtils = self.doUtils
+        if method not in ("Playlist.OnAdd"):
+            self.logMsg("Method: %s Data: %s" % (method, data), 1)
             
+        if data:
+            data = json.loads(data)
+
+
+        if method == "Player.OnPlay":
+            # Set up report progress for emby playback
+            item = data.get('item')
             try:
-                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)
+                kodiid = item['id']
+                type = item['type']
+            except (KeyError, TypeError):
+                self.logMsg("Properties already set for item.", 1)
             else:
-                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)
+                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.clearProperty(type, item)
-                    
-        if method == "System.OnWake":
-            xbmc.sleep(10000) #Allow network to wake up
-            WINDOW.setProperty("OnWakeSync", "true")
+                        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)
 
-        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
+                                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 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 == "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")
 
         elif method == "Playlist.OnClear":
-            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
+            utils.window('emby_customPlaylist', clear=True, windowid=10101)
+            #xbmcgui.Window(10101).clearProperties()
+            self.logMsg("Clear playlist properties.")
\ No newline at end of file
diff --git a/resources/lib/LibrarySync.py b/resources/lib/LibrarySync.py
index f50c88e2..d6d15d8e 100644
--- a/resources/lib/LibrarySync.py
+++ b/resources/lib/LibrarySync.py
@@ -1,1023 +1,181 @@
-#################################################################################################
-# LibrarySync
-#################################################################################################
+# -*- coding: utf-8 -*-
+
+##################################################################################################
+
+import sqlite3
+import threading
+from datetime import datetime, timedelta, time
 
 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 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
+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
 
-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 = {}
 
-    KodiMonitor = KodiMonitor.Kodi_Monitor()
-    clientInfo = ClientInformation()
-
-    addonName = clientInfo.getAddonName()
+    stop_thread = False
+    suspend_thread = False
 
+    # Track websocketclient updates
+    addedItems = []
     updateItems = []
     userdataItems = []
     removeItems = []
-    forceUpdate = False
+    forceLibraryUpdate = False
+    refresh_views = False
 
-    def __init__(self, *args):
+
+    def __init__(self):
 
         self.__dict__ = self._shared_state
-        threading.Thread.__init__(self, *args)
+        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)
 
     def logMsg(self, msg, lvl=1):
 
         className = self.__class__.__name__
-        utils.logMsg("%s %s" % (self.addonName, className), msg, int(lvl))
-        
-    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"
+        utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
 
-        ### 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 = urllib2.quote(lastSync)
+    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":
             
-            url = "{server}/Emby.Kodi.SyncQueue/{UserId}/GetItems?LastUpdateDT=" + lastSync + "&format=json"
-            utils.logMsg("Sync Database", "Incremental Sync Get Items URL : " + url, 0)
+            # 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()
             
-            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):
+            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()
+        
+        return completed
+
+    def fastSync(self):
+
+        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)
+
+        try:
+            processlist = {
                 
-                    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)
+                'added': result['ItemsAdded'],
+                'update': result['ItemsUpdated'],
+                'userdata': result['UserDataChanged'],
+                'remove': result['ItemsRemoved']
+            }
+            
+        except (KeyError, TypeError):
+            self.logMsg("Failed to retrieve latest updates using fast sync.", 1)
+            return False
         
-        #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)
+        else:
+            self.logMsg("Fast sync changes: %s" % result, 1)
+            for action in processlist:
+                self.triage_items(action, processlist[action])
+
             return True
 
-        try:
-            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()
-            
-        finally:
-            WINDOW.setProperty("SyncDatabaseRunning", "false")
-            utils.logMsg("Sync DB", "syncDatabase Exiting", 0)
+    def saveLastSync(self):
+        # Save last sync time
+        overlap = 2
 
-        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")
+        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")
         
-        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')
+        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)
 
-            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)
+        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)
 
-    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')
+        finally:
+            utils.settings('LastIncrementalSync', value=lastSync)
 
-            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 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
 
     def dbCommit(self, connection):
-        # Central commit, will verify if Kodi database
-        kodidb_scan = utils.window('kodiScan') == "true"
+        # Central commit, verifies if Kodi database update is running
+        kodidb_scan = utils.window('emby_kodiScan') == "true"
 
         while kodidb_scan:
-            
-            self.logMsg("Kodi scan running. Waiting...", 1)
-            kodidb_scan = utils.window('kodiScan') == "true"
 
-            if self.KodiMonitor.waitForAbort(1):
+            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):
                 # Abort was requested while waiting. We should exit
                 self.logMsg("Commit unsuccessful.", 1)
                 break
@@ -1025,32 +183,1027 @@ class LibrarySync(threading.Thread):
             connection.commit()
             self.logMsg("Commit successful.", 1)
 
-    def updateLibrary(self, type):
+    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"
 
-        self.logMsg("Updating %s library." % type, 1)
-        utils.window('kodiScan', value="true")
-        xbmc.executebuiltin('UpdateLibrary(%s)' % type)
+        utils.window('emby_dbScan', value="true")
+        # Add sources
+        utils.sourcesXML()
 
-    def ShouldStop(self):
-            
-        if(xbmc.abortRequested):
-            return True
-
-        if(WINDOW.getProperty("SyncDatabaseShouldStop") == "true"):
-            return True
-
-        return False
+        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()
         
-    def checkDBVersion(self, currVersion, minVersion):
-        currMajor, currMinor, currPatch = currVersion.split(".")
-        minMajor, minMinor, minPatch = minVersion.split(".")
+        # 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:
+            
+            musicconn = utils.kodiSQL('music')
+            musiccursor = musicconn.cursor()
+            
+            startTime = datetime.now()
+            completed = self.music(embycursor, musiccursor, pDialog, compare=manualrun)
+            if not completed:
+
+                utils.window('emby_dbScan', clear=True)
+                if pDialog:
+                    pDialog.close()
+
+                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()
+        
+        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(".")
+
         if currMajor > minMajor:
             return True
-        elif currMajor == minMajor and currMinor > minMinor:
-            return True
-        elif currMajor == minMajor and currMinor == minMinor and currPatch >= minPatch:
+        elif currMajor == minMajor and (currMinor > minMinor or
+                                       (currMinor == minMinor and currPatch >= minPatch)):
             return True
         else:
+            # Database out of date.
             return False
 
     def run(self):
@@ -1058,134 +1211,128 @@ class LibrarySync(threading.Thread):
         try:
             self.run_internal()
         except Exception as e:
-            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.")
+            xbmcgui.Dialog().ok(
+                        heading="Emby for Kodi",
+                        line1=(
+                            "Library sync thread has exited! "
+                            "You should restart Kodi now. "
+                            "Please report this on the forum."))
             raise
 
     def run_internal(self):
 
         startupComplete = False
-        kodiProfile = xbmc.translatePath("special://profile")
+        monitor = self.monitor
 
-        self.logMsg("--- Starting Library Sync Thread ---", 0)
+        self.logMsg("---===### Starting LibrarySync ###===---", 0)
 
-        while not self.KodiMonitor.abortRequested():
+        while not monitor.abortRequested():
 
-            # 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):
+            # In the event the server goes offline
+            while self.suspend_thread:
+                # Set in service.py
+                if monitor.waitForAbort(5):
                     # Abort was requested while waiting. We should exit
                     break
 
-            # 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:
+            if (utils.window('emby_dbCheck') != "true" and
+                    utils.settings('SyncInstallRunDone') == "true"):
                 
-                # 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'))
+                # 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")
+
+
+            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')))
                     break
 
-                # Run full sync
-                self.logMsg("DB Version: " + utils.settings("dbCreatedWithVersion"), 0)
-                self.logMsg("Doing_Db_Sync: syncDatabase (Started)", 1)
+                # Run start up sync
+                self.logMsg("Db version: %s" % utils.settings('dbCreatedWithVersion'), 0)
+                self.logMsg("SyncDatabase (started)", 1)
                 startTime = datetime.now()
-                libSync = self.FullLibrarySync()
+                librarySync = self.startSync()
                 elapsedTime = datetime.now() - startTime
-                self.logMsg("Doing_Db_Sync: syncDatabase (Finished in: %s) %s" % (str(elapsedTime).split('.')[0], libSync), 1)
+                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
 
-                if libSync:
-                    startupComplete = True
+            # Process updates
+            if utils.window('emby_dbScan') != "true":
+                self.incrementalSync()
 
-            # 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 (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)
 
-            
-            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)
+            if self.stop_thread:
+                # Set in service.py
+                self.logMsg("Service terminated thread.", 2)
                 break
 
-            if self.KodiMonitor.waitForAbort(1):
+            if monitor.waitForAbort(1):
                 # Abort was requested while waiting. We should exit
                 break
 
-        self.logMsg("--- Library Sync Thread stopped ---", 0)
+        self.logMsg("###===--- LibrarySync Stopped ---===###", 0)
 
-    def suspendClient(self):
-        self.suspendClient = True
-        self.logMsg("--- Library Sync Thread paused ---", 0)
+    def stopThread(self):
+        self.stop_thread = True
+        self.logMsg("Ending thread...", 2)
 
-    def resumeClient(self):
-        self.suspendClient = False
-        self.logMsg("--- Library Sync Thread resumed ---", 0)
\ No newline at end of file
+    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
diff --git a/resources/lib/PlayUtils.py b/resources/lib/PlayUtils.py
index e8b9be58..0a74690b 100644
--- a/resources/lib/PlayUtils.py
+++ b/resources/lib/PlayUtils.py
@@ -6,227 +6,279 @@ import xbmc
 import xbmcgui
 import xbmcvfs
 
-from ClientInformation import ClientInformation
-import Utils as utils
+import clientinfo
+import utils
 
 #################################################################################################
 
-class PlayUtils():
 
-    clientInfo = ClientInformation()
-    addonName = clientInfo.getAddonName()
+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)
 
     def logMsg(self, msg, lvl=1):
-        
-        className = self.__class__.__name__
-        utils.logMsg("%s %s" % (self.addonName, className), msg, int(lvl))
 
-    def getPlayUrl(self, server, id, result):
+        self.className = self.__class__.__name__
+        utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
+    
 
-        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")
+    def getPlayUrl(self):
 
-        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")
+        item = self.item
+        playurl = None
 
-        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
+        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")
 
-        return playurl.encode('utf-8')
+        elif self.isDirectPlay():
 
+            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")
 
-    def isDirectPlay(self, result, dialog = False):
-        # Requirements for Direct play:
-        # FileSystem, Accessible path
+        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
         if utils.settings('playFromStream') == "true":
-            # User forcing to play via HTTP instead of SMB
-            self.logMsg("Can't direct play: Play from HTTP is enabled.", 1)
+            # User forcing to play via HTTP
+            self.logMsg("Can't direct play, play from HTTP enabled.", 1)
             return False
 
-        # Avoid H265 1080p
         if (utils.settings('transcodeH265') == "true" and 
-            result['MediaSources'][0]['Name'].startswith("1080P/H265")):
+                result['MediaSources'][0]['Name'].startswith("1080P/H265")):
+            # Avoid H265 1080p
             self.logMsg("Option to transcode 1080P/H265 enabled.", 1)
             return False
 
-        canDirectPlay = result['MediaSources'][0]['SupportsDirectPlay']
-        # Make sure it's supported by server
+        canDirectPlay = item['MediaSources'][0]['SupportsDirectPlay']
+        # Make sure direct play is supported by the server
         if not canDirectPlay:
-            self.logMsg("Can't direct play: Server does not allow or support it.", 1)
+            self.logMsg("Can't direct play, server doesn't allow/support it.", 1)
             return False
 
-        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)
+        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)
 
+                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
 
-    def directPlay(self, result):
+        return True
+
+    def directPlay(self):
+
+        item = self.item
 
         try:
-            playurl = result['MediaSources'][0]['Path']
-        except KeyError:
-            playurl = result['Path']
+            playurl = item['MediaSources'][0]['Path']
+        except (IndexError, KeyError):
+            playurl = item['Path']
 
-        if 'VideoType' in result:
+        if item.get('VideoType'):
             # Specific format modification
-            if 'Dvd' in result['VideoType']:
+            type = item['VideoType']
+
+            if type == "Dvd":
                 playurl = "%s/VIDEO_TS/VIDEO_TS.IFO" % playurl
-            elif 'BluRay' in result['VideoType']:
+            elif type == "Bluray":
                 playurl = "%s/BDMV/index.bdmv" % playurl
 
-        # 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://")
+        # Assign network protocol
+        if playurl.startswith('\\\\'):
+            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):
 
-    def isDirectStream(self, result):
-        # Requirements for Direct stream:
-        # FileSystem or Remote, BitRate, supported encoding
+        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
 
-        # Avoid H265 1080p
         if (utils.settings('transcodeH265') == "true" and 
-            result['MediaSources'][0]['Name'].startswith("1080P/H265")):
+                result['MediaSources'][0]['Name'].startswith("1080P/H265")):
+            # Avoid H265 1080p
             self.logMsg("Option to transcode 1080P/H265 enabled.", 1)
             return False
 
-        canDirectStream = result['MediaSources'][0]['SupportsDirectStream']
-        # Make sure it's supported by server
+        # Requirement: BitRate, supported encoding
+        canDirectStream = item['MediaSources'][0]['SupportsDirectStream']
+        # Make sure the server supports it
         if not canDirectStream:
             return False
 
-        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)
+        # Verify the bitrate
+        if not self.isNetworkSufficient():
+            self.logMsg("The network speed is insufficient to direct stream file.", 1)
             return False
 
         return True
-  
-    def directStream(self, result, server, id, type = "Video"):
 
-        if result['Path'].endswith('.strm'):
+    def directStream(self):
+
+        item = self.item
+        server = self.server
+
+        itemid = item['Id']
+        type = item['Type']
+
+        if 'Path' in item and item['Path'].endswith('.strm'):
             # Allow strm loading when direct streaming
-            playurl = self.directPlay(result)
-            return playurl
-        
-        if "ThemeVideo" in type:
-            playurl = "%s/mediabrowser/Videos/%s/stream?static=true" % (server, id)
+            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)
 
-        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):
 
-    def isTranscoding(self, result):
-        # Last resort, no requirements
-        # BitRate
-        canTranscode = result['MediaSources'][0]['SupportsTranscoding']
-        # Make sure it's supported by server
+        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
         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, result, server, id):
+    def transcoding(self):
 
-        if result['Path'].endswith('.strm'):
+        item = self.item
+
+        if 'Path' in item and item['Path'].endswith('.strm'):
             # Allow strm loading when transcoding
-            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 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)
+            playurl = self.directPlay()
         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):
+            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))
+
+        return playurl
+
+    def getBitrate(self):
+
         # get the addon video quality
-        videoQuality = utils.settings('videoBitRate')
+        videoQuality = utils.settings('videoBitrate')
         bitrate = {
 
             '0': 664,
@@ -252,35 +304,8 @@ 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
 
-        # 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):
+    def audioSubsPref(self, url):
         # For transcoding only
         # Present the list of audio to select from
         audioStreamsList = {}
@@ -292,15 +317,21 @@ class PlayUtils():
         selectSubsIndex = ""
         playurlprefs = "%s" % url
 
-        mediaStream = mediaSources[0].get('MediaStreams')
-        for stream in mediaStream:
+        item = self.item
+        try:
+            mediasources = item['MediaSources'][0]
+            mediastreams = mediasources['MediaStreams']
+        except (TypeError, KeyError, IndexError):
+            return
+
+        for stream in mediastreams:
             # 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['ChannelLayout']
+                channelLayout = stream.get('ChannelLayout', "")
                
                 try:
                     track = "%s - %s - %s %s" % (index, stream['Language'], codec, channelLayout)
@@ -312,6 +343,8 @@ class PlayUtils():
                 audioStreams.append(track)
 
             elif 'Subtitle' in type:
+                if stream['IsExternal']:
+                    continue
                 try:
                     track = "%s - %s" % (index, stream['Language'])
                 except:
@@ -336,7 +369,7 @@ class PlayUtils():
                 selectAudioIndex = audioStreamsList[selected]
                 playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex
             else: # User backed out of selection
-                playurlprefs += "&AudioStreamIndex=%s" % mediaSources[0]['DefaultAudioStreamIndex']
+                playurlprefs += "&AudioStreamIndex=%s" % mediasources['DefaultAudioStreamIndex']
         else: # There's only one audiotrack.
             selectAudioIndex = audioStreamsList[audioStreams[0]]
             playurlprefs += "&AudioStreamIndex=%s" % selectAudioIndex
@@ -352,7 +385,7 @@ class PlayUtils():
                 selectSubsIndex = subtitleStreamsList[selected]
                 playurlprefs += "&SubtitleStreamIndex=%s" % selectSubsIndex
             else: # User backed out of selection
-                playurlprefs += "&SubtitleStreamIndex=%s" % mediaSources[0].get('DefaultSubtitleStreamIndex', "")
+                playurlprefs += "&SubtitleStreamIndex=%s" % mediasources.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 1172e52b..affa2b81 100644
--- a/resources/lib/PlaybackUtils.py
+++ b/resources/lib/PlaybackUtils.py
@@ -2,71 +2,69 @@
 
 #################################################################################################
 
-import datetime
-import json as json
+import json
 import sys
 
 import xbmc
-import xbmcaddon
-import xbmcplugin
 import xbmcgui
+import xbmcplugin
 
-from API import API
-from DownloadUtils import DownloadUtils
-from PlayUtils import PlayUtils
-from ClientInformation import ClientInformation
-import Utils as utils
+import api
+import artwork
+import clientinfo
+import downloadutils
+import playutils as putils
+import playlist
+import read_embyserver as embyserver
+import utils
 
 #################################################################################################
 
+
 class PlaybackUtils():
     
-    clientInfo = ClientInformation()
-    doUtils = DownloadUtils()
-    api = API()
+    
+    def __init__(self, item):
 
-    addon = xbmcaddon.Addon()
-    language = addon.getLocalizedString
-    addonName = clientInfo.getAddonName()
+        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()
 
     def logMsg(self, msg, lvl=1):
-        
-        className = self.__class__.__name__
-        utils.logMsg("%s %s" % (self.addonName, className), msg, int(lvl))
 
-    def PLAY(self, result, setup = "service"):
+        self.className = self.__class__.__name__
+        utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
 
-        self.logMsg("PLAY Called", 1)
 
-        api = self.api
+    def play(self, itemid, dbid=None):
+
+        self.logMsg("Play called.", 1)
+
         doUtils = self.doUtils
-        username = utils.window('currUser')
-        server = utils.window('server%s' % username)
+        item = self.item
+        API = self.API
+        listitem = xbmcgui.ListItem()
+        playutils = putils.PlayUtils(item)
 
-        id = result['Id']
-        userdata = result['UserData']
-        # Get the playurl - direct play, direct stream or transcoding
-        playurl = PlayUtils().getPlayUrl(server, id, result)
-        listItem = xbmcgui.ListItem()
+        playurl = playutils.getPlayUrl()
+        if not playurl:
+            return xbmcplugin.setResolvedUrl(int(sys.argv[1]), False, 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)
-        
+        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)
 
         ############### ORGANIZE CURRENT PLAYLIST ################
         
@@ -74,58 +72,45 @@ class PlaybackUtils():
         playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
         startPos = max(playlist.getposition(), 0) # Can return -1
         sizePlaylist = playlist.size()
-
-        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)
+        propertiesPlayback = utils.window('emby_playbackProps', windowid=10101) == "true"
+        introsPlaylist = False
+        dummyPlaylist = False
 
+        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 ################
         
-        # 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)
+        userdata = API.getUserData()
+        seektime = API.adjustResume(userdata['Resume'])
 
         # We need to ensure we add the intro and additional parts only once.
         # Otherwise we get a loop.
         if not propertiesPlayback:
 
-            utils.window('propertiesPlayback', value="true")
-            self.logMsg("Setting up properties in playlist.")
+            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
             
             ############### -- CHECK FOR INTROS ################
 
-            if utils.settings('disableCinema') == "false" and not seekTime:
+            if utils.settings('enableCinema') == "true" and not seektime:
                 # if we have any play them when the movie/show is not being resumed
-                url = "{server}/mediabrowser/Users/{UserId}/Items/%s/Intros?format=json&ImageTypeLimit=1&Fields=Etag" % id    
+                url = "{server}/emby/Users/{UserId}/Items/%s/Intros?format=json" % itemid    
                 intros = doUtils.downloadUrl(url)
 
                 if intros['TotalRecordCount'] != 0:
@@ -141,17 +126,15 @@ 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
-                            self.setProperties(introPlayurl, intro, introListItem)
-                            self.setListItemProps(server, introId, introListItem, intro)
-                            
-                            playlist.add(introPlayurl, introListItem, index=currentPosition)
+                            pbutils = PlaybackUtils(intro)
+                            pbutils.setProperties(introPlayurl, introListItem)
+
+                            self.pl.insertintoPlaylist(currentPosition, url=introPlayurl)
                             introsPlaylist = True
                             currentPosition += 1
 
@@ -159,109 +142,126 @@ 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.setListItemProps(server, id, listItem, result)
-                playlist.add(playurl, listItem, index=currentPosition)
-            
+                self.pl.addtoPlaylist(dbid, item['Type'].lower())
+
             # Ensure that additional parts are played after the main item
             currentPosition += 1
 
-
             ############### -- CHECK FOR ADDITIONAL PARTS ################
             
-            if result.get('PartCount'):
+            if item.get('PartCount'):
                 # Only add to the playlist after intros have played
-                partcount = result['PartCount']
-                url = "{server}/mediabrowser/Videos/%s/AdditionalParts" % id
+                partcount = item['PartCount']
+                url = "{server}/emby/Videos/%s/AdditionalParts?format=json" % itemid
                 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
-                    self.setProperties(additionalPlayurl, part, additionalListItem)
-                    self.setListItemProps(server, partId, additionalListItem, part)
+                    pbutils = PlaybackUtils(part)
+                    pbutils.setProperties(additionalPlayurl, additionalListItem)
+                    pbutils.setArtwork(additionalListItem)
 
                     playlist.add(additionalPlayurl, additionalListItem, index=currentPosition)
+                    self.pl.verifyPlaylist()
                     currentPosition += 1
 
-            
-            ############### 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
+            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)
                 
 
         # We just skipped adding properties. Reset flag for next time.
         elif propertiesPlayback:
             self.logMsg("Resetting properties playback flag.", 2)
-            utils.window('propertiesPlayback', clear=True)
+            utils.window('emby_playbackProps', clear=True, windowid=10101)
 
+        #self.pl.verifyPlaylist()
+        ########## SETUP MAIN ITEM ##########
 
-        self.verifyPlaylist()
+        # 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)
 
         ############### PLAYBACK ################
-        
-        if not homeScreen and not introsPlaylist:
-            
-            self.logMsg("Processed as a single item.", 1)
-            xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listItem)
 
-        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)
+        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)
 
         else:
             self.logMsg("Play as a regular item.", 1)
-            xbmc.Player().play(playlist, startpos=startPos)
+            xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem)
 
-                
-    def verifyPlaylist(self):
-        
-        playlistitems = '{"jsonrpc": "2.0", "method": "Playlist.GetItems", "params": { "playlistid": 1 }, "id": 1}'
-        items = xbmc.executeJSONRPC(playlistitems)
-        self.logMsg(items, 2)
+    def setProperties(self, playurl, listitem):
+        # Set all properties necessary for plugin path playback
+        item = self.item
+        itemid = item['Id']
+        itemtype = item['Type']
 
-    def removeFromPlaylist(self, pos):
+        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)
 
-        playlistremove = '{"jsonrpc": "2.0", "method": "Playlist.Remove", "params": { "playlistid": 1, "position": %d }, "id": 1}' % pos
-        result = xbmc.executeJSONRPC(playlistremove)
-        self.logMsg(result, 1)
+        if itemtype == "Episode":
+            utils.window('%s.refreshid' % embyitem, value=item.get('SeriesId'))
+        else:
+            utils.window('%s.refreshid' % embyitem, value=itemid)
 
+        # 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)
 
-    def externalSubs(self, id, playurl, mediaSources):
+        self.setArtwork(listitem)
+
+    def externalSubs(self, playurl):
 
-        username = utils.window('currUser')
-        server = utils.window('server%s' % username)
         externalsubs = []
         mapping = {}
 
-        mediaStream = mediaSources[0].get('MediaStreams')
+        item = self.item
+        itemid = item['Id']
+        try:
+            mediastreams = item['MediaSources'][0]['MediaStreams']
+        except (TypeError, KeyError, IndexError):
+            return
+
         kodiindex = 0
-        for stream in mediaStream:
-            
+        for stream in mediastreams:
+
             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 "Subtitle" in stream['Type'] and stream['IsExternal'] and stream['IsTextSubtitleStream']:
-                
-                playmethod = utils.window("%splaymethod" % playurl)
+            if (stream['Type'] == "Subtitle" and 
+                    stream['IsExternal'] and stream['IsTextSubtitleStream']):
 
-                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)
+                # Direct stream
+                url = ("%s/Videos/%s/%s/Subtitles/%s/Stream.srt"
+                        % (self.server, itemid, itemid, index))
                 
                 # map external subtitles for mapping
                 mapping[kodiindex] = index
@@ -269,69 +269,79 @@ class PlaybackUtils():
                 kodiindex += 1
         
         mapping = json.dumps(mapping)
-        utils.window('%sIndexMapping' % playurl, value=mapping)
+        utils.window('emby_%s.indexMapping' % playurl, value=mapping)
 
         return externalsubs
 
-
-    def setProperties(self, playurl, result, listItem):
-        # Set runtimeticks, type, refresh_id and item_id
-        id = result.get('Id')
-        type = result.get('Type', "")
-
-        utils.window("%sruntimeticks" % playurl, value=str(result.get('RunTimeTicks')))
-        utils.window("%stype" % playurl, value=type)
-        utils.window("%sitem_id" % playurl, value=id)
-
-        if type == "Episode":
-            utils.window("%srefresh_id" % playurl, value=result.get('SeriesId'))
-        else:
-            utils.window("%srefresh_id" % playurl, value=id)
-
-        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)
-
-    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):
+    def setArtwork(self, listItem):
         # Set up item and item info
-        api = self.api
+        item = self.item
+        artwork = self.artwork
 
-        type = result.get('Type')
-        people = api.getPeople(result)
-        studios = api.getStudios(result)
+        allartwork = artwork.getAllArtwork(item, parentInfo=True)
+        # Set artwork for listitem
+        arttypes = {
+
+            '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:
+
+            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)
+        else:
+            listItem.setArt({arttype: path})
+
+    def setListItem(self, listItem):
+
+        item = self.item
+        type = item['Type']
+        API = self.API
+        people = API.getPeople()
+        studios = API.getStudios()
 
         metadata = {
             
-            'title': result.get('Name', "Missing name"),
-            'year': result.get('ProductionYear'),
-            'plot': api.getOverview(result),
+            'title': item.get('Name', "Missing name"),
+            'year': item.get('ProductionYear'),
+            'plot': API.getOverview(),
             'director': people.get('Director'),
             'writer': people.get('Writer'),
-            'mpaa': api.getMpaa(result),
-            'genre': api.getGenre(result),
+            'mpaa': API.getMpaa(),
+            'genre': " / ".join(item['Genres']),
             'studio': " / ".join(studios),
-            'aired': api.getPremiereDate(result),
-            'rating': result.get('CommunityRating'),
-            'votes': result.get('VoteCount')
+            'aired': API.getPremiereDate(),
+            'rating': item.get('CommunityRating'),
+            'votes': item.get('VoteCount')
         }
 
         if "Episode" in type:
             # Only for tv shows
-            thumbId = result.get('SeriesId')
-            season = result.get('ParentIndexNumber', -1)
-            episode = result.get('IndexNumber', -1)
-            show = result.get('SeriesName', "")
+            thumbId = item.get('SeriesId')
+            season = item.get('ParentIndexNumber', -1)
+            episode = item.get('IndexNumber', -1)
+            show = item.get('SeriesName', "")
 
             metadata['TVShowTitle'] = show
             metadata['season'] = season
@@ -340,123 +350,4 @@ class PlaybackUtils():
         listItem.setProperty('IsPlayable', 'true')
         listItem.setProperty('IsFolder', 'false')
         listItem.setLabel(metadata['title'])
-        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
+        listItem.setInfo('video', infoLabels=metadata)
\ No newline at end of file
diff --git a/resources/lib/Player.py b/resources/lib/Player.py
index 0b760a88..b6d25e19 100644
--- a/resources/lib/Player.py
+++ b/resources/lib/Player.py
@@ -2,45 +2,47 @@
 
 #################################################################################################
 
-import json as json
+import json
 
 import xbmc
 import xbmcgui
 
-from DownloadUtils import DownloadUtils
-from WebSocketClient import WebSocketThread
-from ClientInformation import ClientInformation
-from LibrarySync import LibrarySync
-import Utils as utils
+import utils
+import clientinfo
+import downloadutils
+import kodidb_functions as kodidb
+import websocket_client as wsc
 
 #################################################################################################
 
-class Player( xbmc.Player ):
+
+class Player(xbmc.Player):
 
     # Borg - multiple instances, shared state
     _shared_state = {}
 
-    xbmcplayer = xbmc.Player()
-    doUtils = DownloadUtils()
-    clientInfo = ClientInformation()
-    ws = WebSocketThread()
-    librarySync = LibrarySync()
-
-    addonName = clientInfo.getAddonName()
-
-    played_information = {}
+    played_info = {}
     playStats = {}
     currentFile = None
 
-    def __init__(self, *args):
+
+    def __init__(self):
 
         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, int(lvl))
+        utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl)
+
 
     def GetPlayStats(self):
         return self.playStats
@@ -74,39 +76,50 @@ class Player( xbmc.Player ):
             self.currentFile = currentFile
             
             # We may need to wait for info to be set in kodi monitor
-            itemId = utils.window("%sitem_id" % currentFile)
+            itemId = utils.window("emby_%s.itemid" % currentFile)
             tryCount = 0
             while not itemId:
                 
                 xbmc.sleep(200)
-                itemId = utils.window("%sitem_id" % currentFile)
+                itemId = utils.window("emby_%s.itemid" % 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.
-                runtime = utils.window("%sruntimeticks" % currentFile)
-                refresh_id = utils.window("%srefresh_id" % currentFile)
-                playMethod = utils.window("%splaymethod" % currentFile)
-                itemType = utils.window("%stype" % currentFile)
+                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")
+
                 seekTime = xbmcplayer.getTime()
 
-
                 # Get playback volume
-                volume_query = '{"jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["volume","muted"]}, "id": 1}'
-                result = xbmc.executeJSONRPC(volume_query)
+                volume_query = {
+
+                    "jsonrpc": "2.0",
+                    "id": 1,
+                    "method": "Application.GetProperties",
+                    "params": {
+
+                        "properties": ["volume", "muted"] 
+                    }
+                }
+                result = xbmc.executeJSONRPC(json.dumps(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}/mediabrowser/Sessions/Playing"
+                url = "{server}/emby/Sessions/Playing"
                 postdata = {
 
                     'QueueableMediaTypes': "Video",
@@ -123,12 +136,22 @@ 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
-                    track_query = '{"jsonrpc": "2.0", "method": "Player.GetProperties",  "params": {"playerid": 1,"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]} , "id": 1}'
-                    result = xbmc.executeJSONRPC(track_query)
+                    tracks_query = {
+
+                        "jsonrpc": "2.0",
+                        "id": 1,
+                        "method": "Player.GetProperties",
+                        "params": {
+
+                            "playerid": 1,
+                            "properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]
+                        }
+                    }
+                    result = xbmc.executeJSONRPC(json.dumps(tracks_query))
                     result = json.loads(result)
                     result = result.get('result')
 
@@ -155,9 +178,9 @@ class Player( xbmc.Player ):
                         
                         # Number of audiotracks to help get Emby Index
                         audioTracks = len(xbmc.Player().getAvailableAudioStreams())
-                        mapping = utils.window("%sIndexMapping" % currentFile)
+                        mapping = utils.window("%s.indexMapping" % embyitem)
 
-                        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)
@@ -167,7 +190,8 @@ class Player( xbmc.Player ):
                                 postdata['SubtitleStreamIndex'] = externalIndex[str(indexSubs)]
                             else:
                                 # Internal subtitle currently selected
-                                postdata['SubtitleStreamIndex'] = indexSubs - len(externalIndex) + audioTracks + 1
+                                subindex = indexSubs - len(externalIndex) + audioTracks + 1
+                                postdata['SubtitleStreamIndex'] = subindex
                         
                         else: # Direct paths enabled scenario or no external subtitles set
                             postdata['SubtitleStreamIndex'] = indexSubs + audioTracks + 1
@@ -184,7 +208,7 @@ class Player( xbmc.Player ):
                     runtime = int(runtime)
                 except ValueError:
                     runtime = xbmcplayer.getTotalTime()
-                    self.logMsg("Runtime is missing, grabbing runtime from Kodi player: %s" % runtime, 1)
+                    self.logMsg("Runtime is missing, Kodi runtime: %s" % runtime, 1)
 
                 # Save data map for updates and position calls
                 data = {
@@ -200,8 +224,8 @@ class Player( xbmc.Player ):
                     'currentPosition': int(seekTime)
                 }
                 
-                self.played_information[currentFile] = data
-                self.logMsg("ADDING_FILE: %s" % self.played_information, 1)
+                self.played_info[currentFile] = data
+                self.logMsg("ADDING_FILE: %s" % self.played_info, 1)
 
                 # log some playback stats
                 '''if(itemType != None):
@@ -225,7 +249,7 @@ class Player( xbmc.Player ):
 
         # Get current file
         currentFile = self.currentFile
-        data = self.played_information.get(currentFile)
+        data = self.played_info.get(currentFile)
 
         # only report playback if emby has initiated the playback (item_id has value)
         if data:
@@ -239,15 +263,23 @@ class Player( xbmc.Player ):
 
 
             # Get playback volume
-            volume_query = '{"jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["volume","muted"]}, "id": 1}'
-            result = xbmc.executeJSONRPC(volume_query)
+            volume_query = {
+
+                    "jsonrpc": "2.0",
+                    "id": 1,
+                    "method": "Application.GetProperties",
+                    "params": {
+
+                        "properties": ["volume", "muted"] 
+                    }
+                }
+            result = xbmc.executeJSONRPC(json.dumps(volume_query))
             result = json.loads(result)
             result = result.get('result')
 
             volume = result.get('volume')
             muted = result.get('muted')
 
-
             # Postdata for the websocketclient report
             postdata = {
 
@@ -269,8 +301,18 @@ class Player( xbmc.Player ):
 
             else:
                 # Get current audio and subtitles track
-                track_query = '{"jsonrpc": "2.0", "method": "Player.GetProperties",  "params": {"playerid":1,"properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]} , "id": 1}'
-                result = xbmc.executeJSONRPC(track_query)
+                tracks_query = {
+
+                        "jsonrpc": "2.0",
+                        "id": 1,
+                        "method": "Player.GetProperties",
+                        "params": {
+
+                            "playerid": 1,
+                            "properties": ["currentsubtitle","currentaudiostream","subtitleenabled"]
+                        }
+                    }
+                result = xbmc.executeJSONRPC(json.dumps(tracks_query))
                 result = json.loads(result)
                 result = result.get('result')
 
@@ -297,7 +339,7 @@ class Player( xbmc.Player ):
                     
                     # Number of audiotracks to help get Emby Index
                     audioTracks = len(xbmc.Player().getAvailableAudioStreams())
-                    mapping = utils.window("%sIndexMapping" % currentFile)
+                    mapping = utils.window("emby_%s.indexMapping" % currentFile)
 
                     if mapping: # Set in PlaybackUtils.py
                         
@@ -306,13 +348,16 @@ class Player( xbmc.Player ):
 
                         if externalIndex.get(str(indexSubs)):
                             # If the current subtitle is in the mapping
-                            data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = [externalIndex[str(indexSubs)]] * 2
+                            subindex = [externalIndex[str(indexSubs)]] * 2
+                            data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = subindex
                         else:
                             # Internal subtitle currently selected
-                            data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = [indexSubs - len(externalIndex) + audioTracks + 1] * 2
+                            subindex = [indexSubs - len(externalIndex) + audioTracks + 1] * 2
+                            data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = subindex
                     
                     else: # Direct paths enabled scenario or no external subtitles set
-                        data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = [indexSubs + audioTracks + 1] * 2
+                        subindex = [indexSubs + audioTracks + 1] * 2
+                        data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = subindex
                 else:
                     data['SubtitleStreamIndex'], postdata['SubtitleStreamIndex'] = [""] * 2
 
@@ -326,8 +371,8 @@ class Player( xbmc.Player ):
         currentFile = self.currentFile
         self.logMsg("PLAYBACK_PAUSED: %s" % currentFile, 2)
 
-        if self.played_information.get(currentFile):
-            self.played_information[currentFile]['paused'] = True
+        if self.played_info.get(currentFile):
+            self.played_info[currentFile]['paused'] = True
         
             self.reportPlayback()
 
@@ -336,8 +381,8 @@ class Player( xbmc.Player ):
         currentFile = self.currentFile
         self.logMsg("PLAYBACK_RESUMED: %s" % currentFile, 2)
 
-        if self.played_information.get(currentFile):
-            self.played_information[currentFile]['paused'] = False
+        if self.played_info.get(currentFile):
+            self.played_info[currentFile]['paused'] = False
         
             self.reportPlayback()
 
@@ -346,15 +391,17 @@ class Player( xbmc.Player ):
         currentFile = self.currentFile
         self.logMsg("PLAYBACK_SEEK: %s" % currentFile, 2)
 
-        if self.played_information.get(currentFile):
+        if self.played_info.get(currentFile):
             position = self.xbmcplayer.getTime()
-            self.played_information[currentFile]['currentPosition'] = position
+            self.played_info[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 ):
@@ -364,14 +411,16 @@ class Player( xbmc.Player ):
 
     def stopAll(self):
 
-        if not self.played_information:
+        doUtils = self.doUtils
+
+        if not self.played_info:
             return 
             
-        self.logMsg("Played_information: %s" % self.played_information, 1)
+        self.logMsg("Played_information: %s" % self.played_info, 1)
         # Process each items
-        for item in self.played_information:
+        for item in self.played_info:
             
-            data = self.played_information.get(item)
+            data = self.played_info.get(item)
             if data:
                 
                 self.logMsg("Item path: %s" % item, 2)
@@ -379,47 +428,61 @@ 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:
-                    percentComplete = (currentPosition * 10000000) / int(runtime)
+                    try:
+                        percentComplete = (currentPosition * 10000000) / int(runtime)
+                    except ZeroDivisionError:
+                        # Runtime is 0.
+                        percentComplete = 0
+                        
                     markPlayedAt = float(utils.settings('markPlayed')) / 100
+                    self.logMsg(
+                        "Percent complete: %s Mark played at: %s"
+                        % (percentComplete, markPlayedAt), 1)
 
-                    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")
+                    # Prevent manually mark as watched in Kodi monitor
+                    utils.window('emby_skipWatched%s' % itemid, value="true")
 
                     self.stopPlayback(data)
-                    offerDelete = utils.settings('offerDelete') == "true"
-                    offerTypeDelete = False
+                    # 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")
 
-                    if type == "Episode" and utils.settings('offerDeleteTV') == "true":
-                        offerTypeDelete = True
+                    # Send the delete action to the server.
+                    offerDelete = False
 
-                    elif type == "Movie" and utils.settings('offerDeleteMovies') == "true":
-                        offerTypeDelete = True
+                    if type == "Episode" and utils.settings('deleteTV') == "true":
+                        offerDelete = True
+                    elif type == "Movie" and utils.settings('deleteMovies') == "true":
+                        offerDelete = True
 
-                    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")
+                    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")
     
-        self.played_information.clear()
+        self.played_info.clear()
     
     def stopPlayback(self, data):
         
@@ -429,12 +492,11 @@ class Player( xbmc.Player ):
         currentPosition = data['currentPosition']
         positionTicks = int(currentPosition * 10000000)
 
-        url = "{server}/mediabrowser/Sessions/Playing/Stopped"
+        url = "{server}/emby/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 f95f9291..a6562a53 100644
--- a/resources/lib/UserClient.py
+++ b/resources/lib/UserClient.py
@@ -1,22 +1,21 @@
-#################################################################################################
-# UserClient thread
-#################################################################################################
+# -*- coding: utf-8 -*-
+
+##################################################################################################
+
+import hashlib
+import threading
 
 import xbmc
 import xbmcgui
 import xbmcaddon
 import xbmcvfs
 
-import threading
-import hashlib
-import json as json
+import artwork
+import utils
+import clientinfo
+import downloadutils
 
-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):
@@ -24,16 +23,7 @@ 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
 
@@ -44,25 +34,25 @@ class UserClient(threading.Thread):
     HasAccess = True
     AdditionalUser = []
 
-    def __init__(self, *args):
+    userSettings = None
+
+
+    def __init__(self):
 
         self.__dict__ = self._shared_state
-        threading.Thread.__init__(self, *args)
+        self.addon = xbmcaddon.Addon()
+
+        self.addonName = clientinfo.ClientInfo().getAddonName()
+        self.doUtils = downloadutils.DownloadUtils()
+        self.logLevel = int(utils.settings('logLevel'))
+        
+        threading.Thread.__init__(self)
 
     def logMsg(self, msg, lvl=1):
         
         className = self.__class__.__name__
-        utils.logMsg("%s %s" % (self.addonName, className), str(msg), int(lvl))
+        utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
 
-    def getUsername(self):
-
-        username = utils.settings('username')
-
-        if (username == ""):
-            self.logMsg("No username saved.", 2)
-            return ""
-
-        return username
 
     def getAdditionalUsers(self):
 
@@ -71,11 +61,21 @@ 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:
+        except ValueError:
             logLevel = 0
         
         return logLevel
@@ -83,71 +83,84 @@ class UserClient(threading.Thread):
     def getUserId(self):
 
         username = self.getUsername()
-        w_userId = self.WINDOW.getProperty('userId%s' % username)
+        w_userId = utils.window('emby_userId%s' % username)
         s_userId = utils.settings('userId%s' % username)
 
         # Verify the window property
-        if (w_userId != ""):
-            self.logMsg("Returning userId from WINDOW for username: %s UserId: %s" % (username, w_userId), 2)
+        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)
             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)
-            return
+            self.logMsg("No userId saved for username: %s." % username, 1)
 
     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:
-            HTTPS = utils.settings('secondhttps')
+            # Alternate host
+            HTTPS = utils.settings('secondhttps') == "true"
             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 host == "":
+        if not host:
             self.logMsg("No server information saved.", 2)
-            return ""
+            return False
 
         # If https is true
-        if prefix and (HTTPS == "true"):
+        if prefix and HTTPS:
             server = "https://%s" % server
             return server
         # If https is false
-        elif prefix and (HTTPS == "false"):
+        elif prefix and not HTTPS:
             server = "http://%s" % server
             return server
         # If only the host:port is required
-        elif (prefix == False):
+        elif not prefix:
             return server
 
     def getToken(self):
 
         username = self.getUsername()
-        w_token = self.WINDOW.getProperty('accessToken%s' % username)
+        w_token = utils.window('emby_accessToken%s' % username)
         s_token = utils.settings('accessToken')
         
         # Verify the window property
-        if (w_token != ""):
-            self.logMsg("Returning accessToken from WINDOW for username: %s accessToken: %s" % (username, w_token), 2)
+        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)
             return w_token
         # Verify the settings
-        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)
+        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)
             return s_token
         else:
-            self.logMsg("No token found.")
+            self.logMsg("No token found.", 1)
             return ""
 
     def getSSLverify(self):
@@ -174,71 +187,63 @@ class UserClient(threading.Thread):
 
     def setUserPref(self):
 
-        player = Player()
-        server = self.getServer()
-        userId = self.getUserId()
-
-        url = "{server}/mediabrowser/Users/{UserId}?format=json"
-        result = self.doUtils.downloadUrl(url)
+        doUtils = self.doUtils
 
+        url = "{server}/emby/Users/{UserId}?format=json"
+        result = doUtils.downloadUrl(url)
+        self.userSettings = result
         # Set user image for skin display
-        self.WINDOW.setProperty("EmbyUserImage",API().getUserArtwork(result,"Primary"))
+        if result.get('PrimaryImageTag'):
+            utils.window('EmbyUserImage', value=artwork.Artwork().getUserArtwork(result, 'Primary'))
 
-        # Load the resume point from Emby and set as setting
-        url = "{server}/mediabrowser/System/Configuration?format=json"
-        result = self.doUtils.downloadUrl(url)
+        # Set resume point max
+        url = "{server}/emby/System/Configuration?format=json"
+        result = doUtils.downloadUrl(url)
 
         utils.settings('markPlayed', value=str(result['MaxResumePct']))
 
-        return True
-
     def getPublicUsers(self):
 
         server = self.getServer()
 
         # Get public Users
-        url = "%s/mediabrowser/Users/Public?format=json" % server
+        url = "%s/emby/Users/Public?format=json" % server
         result = self.doUtils.downloadUrl(url, authenticate=False)
         
-        users = []
-        
-        if (result != ""):
-            users = result
+        if result != "":
+            return result
         else:
             # Server connection failed
             return False
 
-        return users
-
     def hasAccess(self):
-
-        url = "{server}/mediabrowser/Users"
+        # hasAccess is verified in service.py
+        url = "{server}/emby/Users?format=json"
         result = self.doUtils.downloadUrl(url)
         
-        if result is False:
-            # Access is restricted
-            self.logMsg("Access is restricted.")
+        if result == False:
+            # Access is restricted, set in downloadutils.py via exception
+            self.logMsg("Access is restricted.", 1)
             self.HasAccess = False
-            return
-        elif self.WINDOW.getProperty('Server_online') != "true":
+        
+        elif utils.window('emby_online') != "true":
             # Server connection failed
-            return
+            pass
 
-        if self.WINDOW.getProperty("Server_status") == "restricted":
-            self.logMsg("Access is granted.")
+        elif utils.window('emby_serverStatus') == "restricted":
+            self.logMsg("Access is granted.", 1)
             self.HasAccess = True
-            self.WINDOW.setProperty("Server_status", "")
+            utils.window('emby_serverStatus', clear=True)
             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 = self.getUserId()
+        self.currUserId = userId
         self.currServer = self.getServer()
         self.currToken = self.getToken()
         self.ssl = self.getSSLverify()
@@ -246,21 +251,21 @@ class UserClient(threading.Thread):
 
         # Test the validity of current token
         if authenticated == False:
-            url = "%s/mediabrowser/Users/%s" % (self.currServer, self.currUserId)
-            WINDOW.setProperty("currUser", username)
-            WINDOW.setProperty("accessToken%s" % username, self.currToken)
+            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)
             result = doUtils.downloadUrl(url)
+
             if result == 401:
                 # Token is no longer valid
                 self.resetClient()
                 return False
 
         # Set to windows property
-        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)
+        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))
 
         # Set DownloadUtils values
         doUtils.setUsername(username)
@@ -273,188 +278,194 @@ class UserClient(threading.Thread):
         # Start DownloadUtils session
         doUtils.startSession()
         self.getAdditionalUsers()
-
-        self.currUser = username
         # Set user preferences in settings
+        self.currUser = username
         self.setUserPref()
+        
 
     def authenticate(self):
-
-        WINDOW = self.WINDOW
-        addon = self.addon
+        # Get /profile/addon_data
+        addondir = xbmc.translatePath(self.addon.getAddonInfo('profile')).decode('utf-8')
+        hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir)
 
         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 (hasSettings == 0):
-            self.logMsg("No settings.xml found.")
+        if not hasSettings:
+            self.logMsg("No settings.xml found.", 1)
             self.auth = False
             return
         # If no user information
-        if (server == "") or (username == ""):
-            self.logMsg("Missing server information.")
+        elif not server or not username:
+            self.logMsg("Missing server information.", 1)
             self.auth = False
             return
-        # If there's a token
-        if (self.getToken() != ""):
+        # If there's a token, load the user
+        elif self.getToken():
             result = self.loadCurrUser()
 
             if result == False:
                 pass
             else:
-                self.logMsg("Current user: %s" % self.currUser, 0)
-                self.logMsg("Current userId: %s" % self.currUserId, 0)
-                self.logMsg("Current accessToken: %s" % self.currToken, 0)
+                self.logMsg("Current user: %s" % self.currUser, 1)
+                self.logMsg("Current userId: %s" % self.currUserId, 1)
+                self.logMsg("Current accessToken: %s" % self.currToken, 2)
                 return
         
+        ##### AUTHENTICATE USER #####
+
         users = self.getPublicUsers()
         password = ""
         
         # Find user in list
         for user in users:
-            name = user[u'Name']
-            userHasPassword = False
+            name = user['Name']
 
-            if (unicode(username, 'utf-8') in name):
-                # Verify if user has a password
-                if (user.get("HasPassword") == True):
-                    userHasPassword = True
+            if username.decode('utf-8') in name:
                 # If user has password
-                if (userHasPassword):
-                    password = xbmcgui.Dialog().input("Enter password for user: %s" % username, option=xbmcgui.ALPHANUM_HIDE_INPUT)
+                if user['HasPassword'] == True:
+                    password = xbmcgui.Dialog().input(
+                        heading="Enter password for user: %s" % username,
+                        option=xbmcgui.ALPHANUM_HIDE_INPUT)
                     # If password dialog is cancelled
-                    if (password == ""):
+                    if not password:
                         self.logMsg("No password entered.", 0)
-                        self.WINDOW.setProperty("Server_status", "Stop")
+                        utils.window('emby_serverStatus', value="Stop")
                         self.auth = False
                         return
                 break
         else:
             # Manual login, user is hidden
-            password = xbmcgui.Dialog().input("Enter password for user: %s" % username, option=xbmcgui.ALPHANUM_HIDE_INPUT)
-            
+            password = xbmcgui.Dialog().input(
+                                    heading="Enter password for user: %s" % username,
+                                    option=xbmcgui.ALPHANUM_HIDE_INPUT)
         sha1 = hashlib.sha1(password)
         sha1 = sha1.hexdigest()    
 
         # Authenticate username and password
-        url = "%s/mediabrowser/Users/AuthenticateByName?format=json" % server
+        url = "%s/emby/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_Reponse: %s" % result, 1)
-            accessToken = result[u'AccessToken']
-        except:
-            pass
+            self.logMsg("Auth response: %s" % result, 1)
+            accessToken = result['AccessToken']
+        
+        except (KeyError, TypeError):
+            self.logMsg("Failed to retrieve the api key.", 1)
+            accessToken = None
 
-        if (result != None and accessToken != None):
+        if accessToken is not None:
             self.currUser = username
             xbmcgui.Dialog().notification("Emby server", "Welcome %s!" % self.currUser)
-            userId = result[u'User'][u'Id']
-            utils.settings("accessToken", accessToken)
-            utils.settings("userId%s" % username, userId)
-            self.logMsg("User Authenticated: %s" % accessToken)
+            userId = result['User']['Id']
+            utils.settings('accessToken', value=accessToken)
+            utils.settings('userId%s' % username, value=userId)
+            self.logMsg("User Authenticated: %s" % accessToken, 1)
             self.loadCurrUser(authenticated=True)
-            self.WINDOW.setProperty("Server_status", "")
+            utils.window('emby_serverStatus', clear=True)
             self.retry = 0
-            return
         else:
-            self.logMsg("User authentication failed.")
-            utils.settings("accessToken", "")
-            utils.settings("userId%s" % username, "")
+            self.logMsg("User authentication failed.", 1)
+            utils.settings('accessToken', value="")
+            utils.settings('userId%s' % username, value="")
             xbmcgui.Dialog().ok("Error connecting", "Invalid username or password.")
             
             # Give two attempts at entering password
-            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.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
             self.auth = False
-            return
 
     def resetClient(self):
 
-        username = self.getUsername()
         self.logMsg("Reset UserClient authentication.", 1)
-        if (self.currToken != None):
+        username = self.getUsername()
+        
+        if self.currToken is not None:
             # In case of 401, removed saved token
-            utils.settings("accessToken", "")
-            self.WINDOW.setProperty("accessToken%s" % username, "")
+            utils.settings('accessToken', value="")
+            utils.window('emby_accessToken%s' % username, clear=True)
             self.currToken = None
             self.logMsg("User token has been removed.", 1)
         
         self.auth = True
         self.currUser = None
-        return
         
-
     def run(self):
 
-        self.logMsg("|---- Starting UserClient ----|", 0)
+        monitor = xbmc.Monitor()
+        self.logMsg("----===## Starting UserClient ##===----", 0)
 
-        while not self.KodiMonitor.abortRequested():
+        while not monitor.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)) 
 
-            if (self.WINDOW.getProperty("Server_status") != ""):
-                status = self.WINDOW.getProperty("Server_status")
-                
+
+            status = utils.window('emby_serverStatus')
+            if status:
+                # Verify the connection status to server
                 if status == "restricted":
                     # Parental control is restricting access
                     self.HasAccess = False
 
                 elif status == "401":
-                    self.WINDOW.setProperty("Server_status", "Auth")
-                    # Revoked token
+                    # Unauthorized access, revoke token
+                    utils.window('emby_serverStatus', value="Auth")
                     self.resetClient()
 
-            if self.auth and (self.currUser == None):
-                status = self.WINDOW.getProperty("Server_status")
-                
-                if (status == "") or (status == "Auth"):
+            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
                     self.auth = False
                     self.authenticate()
                 
-            if (self.auth == False) and (self.currUser == None):
-                # Only if there's information found to login
+
+            if not self.auth and (self.currUser is None):
+                # If authenticate failed.
                 server = self.getServer()
                 username = self.getUsername()
-                status = self.WINDOW.getProperty("Server_status")
-
-                # If user didn't enter a password when prompted
-                if status == "Stop":
-                    pass
+                status = utils.window('emby_serverStatus')
                 
-                elif (server != "") and (username != ""):
-                    self.logMsg("Server found: %s" % server)
-                    self.logMsg("Username found: %s" % username)
+                # 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)
                     self.auth = True
 
-            # If stopping the client didn't work
+
             if self.stopClient == True:
+                # If stopping the client didn't work
                 break
                 
-            if self.KodiMonitor.waitForAbort(1):
+            if monitor.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):
-        # As last resort
+        # When emby for kodi terminates
         self.stopClient = True
\ No newline at end of file
diff --git a/resources/lib/Utils.py b/resources/lib/Utils.py
index 73c49c95..83e73e1d 100644
--- a/resources/lib/Utils.py
+++ b/resources/lib/Utils.py
@@ -1,59 +1,76 @@
-#################################################################################################
-# utils 
+# -*- coding: utf-8 -*-
+
 #################################################################################################
 
-import xbmc
-import xbmcgui
-import xbmcaddon
-import xbmcvfs
-import json
-import os
 import cProfile
-import pstats
-import time
 import inspect
+import pstats
 import sqlite3
-import string
+import time
 import unicodedata
 import xml.etree.ElementTree as etree
 
-from API import API
-from PlayUtils import PlayUtils
-from DownloadUtils import DownloadUtils
+import xbmc
+import xbmcaddon
+import xbmcgui
+import xbmcvfs
 
-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
-    logLevel = int(WINDOW.getProperty('getLogLevel'))
+    try:
+        logLevel = int(window('emby_logLevel'))
+    except ValueError:
+        logLevel = 0
     
-    if(logLevel >= level):
-        if(logLevel == 2): # inspect.stack() is expensive
+    if logLevel >= level:
+        
+        if logLevel == 2: # inspect.stack() is expensive
             try:
-                xbmc.log(title + " -> " + inspect.stack()[1][3] + " : " + str(msg))
+                xbmc.log("%s -> %s : %s" % (title, inspect.stack()[1][3], msg))
             except UnicodeEncodeError:
-                xbmc.log(title + " -> " + inspect.stack()[1][3] + " : " + str(msg.encode('utf-8')))
+                xbmc.log("%s -> %s : %s" % (title, inspect.stack()[1][3], msg.encode('utf-8')))
         else:
             try:
-                xbmc.log(title + " -> " + str(msg))
+                xbmc.log("%s -> %s" % (title, msg))
             except UnicodeEncodeError:
-                xbmc.log(title + " -> " + str(msg.encode('utf-8')))
+                xbmc.log("%s -> %s" % (title, msg.encode('utf-8')))
 
-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"):
+def window(property, value=None, clear=False, windowid=10000):
+    # Get or set window property
+    WINDOW = xbmcgui.Window(windowid)
     
-    if type == "music":
+    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":
         dbPath = getKodiMusicDBPath()
     elif type == "texture":
         dbPath = xbmc.translatePath("special://database/Textures13.db").decode('utf-8')
@@ -94,219 +111,140 @@ 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()
     
-def prettifyXml(elem):
-    rough_string = etree.tostring(elem, "utf-8")
-    reparsed = minidom.parseString(rough_string)
-    return reparsed.toprettyxml(indent="\t")
+    # 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 startProfiling():
+    
     pr = cProfile.Profile()
     pr.enable()
-    return pr
     
+    return pr
+
 def stopProfiling(pr, profileName):
+    
     pr.disable()
     ps = pstats.Stats(pr)
     
-    addondir = xbmc.translatePath(xbmcaddon.Addon(id='plugin.video.emby').getAddonInfo('profile'))    
+    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)
     
-    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 = open(profile, '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(str(ncalls) + "\t" + "{:10.4f}".format(total_time) + "\t" + "{:10.4f}".format(cumulative_time) + "\t" + func_name + "\t" + filename + "\r\n")
+            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))
         except ValueError:
-            f.write(str(ncalls) + "\t" + "{0}".format(total_time) + "\t" + "{0}".format(cumulative_time) + "\t" + func_name + "\t" + filename + "\r\n")
+            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.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(":", "")
@@ -327,99 +265,223 @@ def normalize_nodes(text):
     
     return text
 
-def reloadProfile():
-    # Useful to reload the add-on without restarting Kodi.
-    profile = xbmc.getInfoLabel('System.ProfileName')
-    xbmc.executebuiltin("LoadProfile(%s)" % profile)
-   
+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 reset():
+    return text
 
-    WINDOW = xbmcgui.Window( 10000 )
-    return_value = xbmcgui.Dialog().yesno("Warning", "Are you sure you want to reset your local Kodi database?")
+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
 
-    if return_value == 0:
-        return
+def sourcesXML():
+    # To make Master lock compatible
+    path = xbmc.translatePath("special://profile/").decode('utf-8')
+    xmlpath = "%ssources.xml" % path
 
-    # 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()
+    try:
+        xmlparse = etree.parse(xmlpath)
+    except: # Document is blank or missing
+        root = etree.Element('sources')
+    else:
+        root = xmlparse.getroot()
         
-    
-    # reset the install run flag
-    #settings('SyncInstallRunDone', "false")
-    #WINDOW.setProperty("SyncInstallRunDone", "false")
+
+    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()
 
     dialog = xbmcgui.Dialog()
-    # 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
+    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
diff --git a/resources/lib/VideoNodes.py b/resources/lib/VideoNodes.py
index 056ee13f..1dc9d5a6 100644
--- a/resources/lib/VideoNodes.py
+++ b/resources/lib/VideoNodes.py
@@ -1,466 +1,344 @@
-#################################################################################################
-# VideoNodes - utils to create video nodes listings in kodi for the emby addon
+# -*- coding: utf-8 -*-
+
 #################################################################################################
 
-
-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 Utils as utils
+import xbmc
+import xbmcaddon
+import xbmcvfs
 
-from ReadEmbyDB import ReadEmbyDB
-WINDOW = xbmcgui.Window(10000)
+import clientinfo
+import utils
 
-addonSettings = xbmcaddon.Addon()
-language = addonSettings.getLocalizedString
+#################################################################################################
 
-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'))
-        
-        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:
-            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)
+
+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:
-            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)
+            # 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:
-            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)
+            dirname = cleantagname
         
-        #some movies-only nodes
-        if type == "movies":
-            
-            #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:
-                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)
+        path = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
+        nodepath = xbmc.translatePath(
+                    "special://profile/library/video/Emby - %s/" % dirname).decode('utf-8')
 
-        #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)
-        
-        try:
-            etree.ElementTree(root).write(nodefile, xml_declaration=True)
-        except:
-            etree.ElementTree(root).write(nodefile)
-        
-        #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
+        # 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 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))               
-                
+        # 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)
 
-        except Exception as e:
-            utils.logMsg("Emby addon","Error while creating videonodes listings, restart required ?")
-            print e    
\ No newline at end of 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)
+        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
+            
+            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"})
+
+            try:
+                utils.indent(root)
+            except: pass
+            etree.ElementTree(root).write(nodeXML)
+
+    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 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 = [
+        
+            "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"
+        ]
+
+        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