diff --git a/.env b/.env
new file mode 100644
index 00000000..a8471c01
--- /dev/null
+++ b/.env
@@ -0,0 +1 @@
+PYTHONPATH=jellyfin_kodi
diff --git a/.gitignore b/.gitignore
index 9547020e..b344f958 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 *.pyo
+__pycache__/
 __local__/
 machine_guid
 /resources/media/Thumbs.db
diff --git a/addon.xml b/addon.xml
index b3ab49cc..557bd9bb 100644
--- a/addon.xml
+++ b/addon.xml
@@ -7,6 +7,8 @@
     <import addon="xbmc.python" version="2.25.0"/>
     <import addon="script.module.requests" version="2.22.0"/>
     <import addon="script.module.dateutil" version="2.7.3"/>
+    <import addon="script.module.six" />
+    <import addon="script.module.kodi-six" />
     <import addon="script.module.addon.signals" version="0.0.1"/>
   </requires>
   <extension    point="xbmc.python.pluginsource"
diff --git a/context.py b/context.py
index 8e165a68..513b4be4 100644
--- a/context.py
+++ b/context.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
@@ -6,13 +7,12 @@ import logging
 import os
 import sys
 
-import xbmc
-import xbmcaddon
+from kodi_six import xbmc, xbmcaddon
 
 #################################################################################################
 
 __addon__ = xbmcaddon.Addon(id='plugin.video.jellyfin')
-__base__ = xbmc.translatePath(os.path.join(__addon__.getAddonInfo('path'), 'jellyfin_kodi')).decode('utf-8')
+__base__ = xbmc.translatePath(os.path.join(__addon__.getAddonInfo('path'), 'jellyfin_kodi'))
 
 sys.path.insert(0, __base__)
 
diff --git a/context_play.py b/context_play.py
index 1bb53e7d..aa657e31 100644
--- a/context_play.py
+++ b/context_play.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
@@ -6,13 +7,12 @@ import logging
 import os
 import sys
 
-import xbmc
-import xbmcaddon
+from kodi_six import xbmc, xbmcaddon
 
 #################################################################################################
 
 __addon__ = xbmcaddon.Addon(id='plugin.video.jellyfin')
-__base__ = xbmc.translatePath(os.path.join(__addon__.getAddonInfo('path'), 'jellyfin_kodi')).decode('utf-8')
+__base__ = xbmc.translatePath(os.path.join(__addon__.getAddonInfo('path'), 'jellyfin_kodi'))
 
 sys.path.insert(0, __base__)
 
diff --git a/default.py b/default.py
index f2251509..f96d0970 100644
--- a/default.py
+++ b/default.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
@@ -6,13 +7,12 @@ import logging
 import os
 import sys
 
-import xbmc
-import xbmcaddon
+from kodi_six import xbmc, xbmcaddon
 
 #################################################################################################
 
 __addon__ = xbmcaddon.Addon(id='plugin.video.jellyfin')
-__base__ = xbmc.translatePath(os.path.join(__addon__.getAddonInfo('path'), 'jellyfin_kodi')).decode('utf-8')
+__base__ = xbmc.translatePath(os.path.join(__addon__.getAddonInfo('path'), 'jellyfin_kodi'))
 
 sys.path.insert(0, __base__)
 
diff --git a/jellyfin_kodi/client.py b/jellyfin_kodi/client.py
index 3af18e8a..de03fcdd 100644
--- a/jellyfin_kodi/client.py
+++ b/jellyfin_kodi/client.py
@@ -1,13 +1,12 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
 import logging
 import os
 
-import xbmc
-import xbmcaddon
-import xbmcvfs
+from kodi_six import xbmc, xbmcaddon, xbmcvfs
 
 from helper import translate, window, settings, addon_id, dialog
 from helper.utils import create_id
@@ -63,7 +62,7 @@ def get_device_name():
         Otherwise fallback to the Kodi device name.
     '''
     if not settings('deviceNameOpt.bool'):
-        device_name = xbmc.getInfoLabel('System.FriendlyName').decode('utf-8')
+        device_name = xbmc.getInfoLabel('System.FriendlyName')
     else:
         device_name = settings('deviceName')
         device_name = device_name.replace("\"", "_")
@@ -86,7 +85,7 @@ def get_device_id(reset=False):
     if client_id:
         return client_id
 
-    directory = xbmc.translatePath('special://profile/addon_data/plugin.video.jellyfin/').decode('utf-8')
+    directory = xbmc.translatePath('special://profile/addon_data/plugin.video.jellyfin/')
 
     if not xbmcvfs.exists(directory):
         xbmcvfs.mkdir(directory)
@@ -98,7 +97,7 @@ def get_device_id(reset=False):
     if not client_id or reset:
         LOG.info("Generating a new GUID.")
 
-        client_id = str("%012X" % create_id())
+        client_id = str(create_id())
         file_guid = xbmcvfs.File(jellyfin_guid, 'w')
         file_guid.write(client_id)
 
diff --git a/jellyfin_kodi/connect.py b/jellyfin_kodi/connect.py
index 9fa86f4b..16694fa3 100644
--- a/jellyfin_kodi/connect.py
+++ b/jellyfin_kodi/connect.py
@@ -1,11 +1,11 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import logging
 
-import xbmc
-import xbmcaddon
+from kodi_six import xbmc, xbmcaddon
 
 import client
 from database import get_credentials, save_credentials
diff --git a/jellyfin_kodi/database/__init__.py b/jellyfin_kodi/database/__init__.py
index 44423bf6..844d38a4 100644
--- a/jellyfin_kodi/database/__init__.py
+++ b/jellyfin_kodi/database/__init__.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-
+from __future__ import division, absolute_import, print_function, unicode_literals
 #################################################################################################
 
 import datetime
@@ -8,10 +8,10 @@ import json
 import os
 import sqlite3
 
-import xbmc
-import xbmcvfs
+from kodi_six import xbmc, xbmcvfs
+from six import text_type
 
-import jellyfin_db
+from database import jellyfin_db
 from helper import translate, settings, window, dialog
 from objects import obj
 
@@ -22,9 +22,6 @@ LOG = logging.getLogger("JELLYFIN." + __name__)
 #################################################################################################
 
 
-UNICODE = type(u"")
-
-
 class Database(object):
 
     ''' This should be called like a context.
@@ -76,7 +73,7 @@ class Database(object):
 
     def _get_database(self, path, silent=False):
 
-        path = xbmc.translatePath(path).decode('utf-8')
+        path = xbmc.translatePath(path)
 
         if not silent:
 
@@ -104,7 +101,7 @@ class Database(object):
             xbmc.executebuiltin('UpdateLibrary(video)')
             xbmc.sleep(200)
 
-        databases = xbmc.translatePath("special://database/").decode('utf-8')
+        databases = xbmc.translatePath("special://database/")
         types = {
             'video': "MyVideos",
             'music': "MyMusic",
@@ -118,19 +115,19 @@ class Database(object):
 
             if (file.startswith(database) and not file.endswith('-wal') and not file.endswith('-shm') and not file.endswith('db-journal')):
 
-                st = xbmcvfs.Stat(databases + file.decode('utf-8'))
+                st = xbmcvfs.Stat(databases + file)
                 modified_int = st.st_mtime()
-                LOG.debug("Database detected: %s time: %s", file.decode('utf-8'), modified_int)
+                LOG.debug("Database detected: %s time: %s", file, modified_int)
 
                 if modified_int > modified['time']:
 
                     modified['time'] = modified_int
-                    modified['file'] = file.decode('utf-8')
+                    modified['file'] = file
 
         LOG.info("Discovered database: %s", modified)
         self.discovered_file = modified['file']
 
-        return xbmc.translatePath("special://database/%s" % modified['file']).decode('utf-8')
+        return xbmc.translatePath("special://database/%s" % modified['file'])
 
     def _sql(self, file):
 
@@ -251,7 +248,7 @@ def reset():
     if dialog("yesno", heading="{jellyfin}", line1=translate(33086)):
         reset_artwork()
 
-    addon_data = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/").decode('utf-8')
+    addon_data = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/")
 
     if dialog("yesno", heading="{jellyfin}", line1=translate(33087)):
 
@@ -317,17 +314,17 @@ def reset_artwork():
 
     ''' Remove all existing texture.
     '''
-    thumbnails = xbmc.translatePath('special://thumbnails/').decode('utf-8')
+    thumbnails = xbmc.translatePath('special://thumbnails/')
 
     if xbmcvfs.exists(thumbnails):
         dirs, ignore = xbmcvfs.listdir(thumbnails)
 
         for directory in dirs:
-            ignore, thumbs = xbmcvfs.listdir(os.path.join(thumbnails, directory.decode('utf-8')))
+            ignore, thumbs = xbmcvfs.listdir(os.path.join(thumbnails, directory))
 
             for thumb in thumbs:
                 LOG.debug("DELETE thumbnail %s", thumb)
-                xbmcvfs.delete(os.path.join(thumbnails, directory.decode('utf-8'), thumb.decode('utf-8')))
+                xbmcvfs.delete(os.path.join(thumbnails, directory, thumb))
 
     with Database('texture') as texdb:
         texdb.cursor.execute("SELECT tbl_name FROM sqlite_master WHERE type='table'")
@@ -343,7 +340,7 @@ def reset_artwork():
 
 def get_sync():
 
-    path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/").decode('utf-8')
+    path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/")
 
     if not xbmcvfs.exists(path):
         xbmcvfs.mkdirs(path)
@@ -364,7 +361,7 @@ def get_sync():
 
 def save_sync(sync):
 
-    path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/").decode('utf-8')
+    path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/")
 
     if not xbmcvfs.exists(path):
         xbmcvfs.mkdirs(path)
@@ -373,14 +370,14 @@ def save_sync(sync):
 
     with open(os.path.join(path, 'sync.json'), 'wb') as outfile:
         data = json.dumps(sync, sort_keys=True, indent=4, ensure_ascii=False)
-        if isinstance(data, UNICODE):
+        if isinstance(data, text_type):
             data = data.encode('utf-8')
         outfile.write(data)
 
 
 def get_credentials():
 
-    path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/").decode('utf-8')
+    path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/")
 
     if not xbmcvfs.exists(path):
         xbmcvfs.mkdirs(path)
@@ -424,14 +421,14 @@ def get_credentials():
 
 def save_credentials(credentials):
     credentials = credentials or {}
-    path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/").decode('utf-8')
+    path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/")
 
     if not xbmcvfs.exists(path):
         xbmcvfs.mkdirs(path)
     try:
         with open(os.path.join(path, 'data.json'), 'wb') as outfile:
             data = json.dumps(credentials, sort_keys=True, indent=4, ensure_ascii=False)
-            if isinstance(data, UNICODE):
+            if isinstance(data, text_type):
                 data = data.encode('utf-8')
             outfile.write(data)
     except Exception:
diff --git a/jellyfin_kodi/database/jellyfin_db.py b/jellyfin_kodi/database/jellyfin_db.py
index ca9cb50b..3367ab51 100644
--- a/jellyfin_kodi/database/jellyfin_db.py
+++ b/jellyfin_kodi/database/jellyfin_db.py
@@ -1,10 +1,10 @@
 # -*- coding: utf-8 -*-
-
+from __future__ import division, absolute_import, print_function, unicode_literals
 #################################################################################################
 
 import logging
 
-import queries as QU
+from database import queries as QU
 
 ##################################################################################################
 
diff --git a/jellyfin_kodi/database/queries.py b/jellyfin_kodi/database/queries.py
index 0efc1c75..66582caf 100644
--- a/jellyfin_kodi/database/queries.py
+++ b/jellyfin_kodi/database/queries.py
@@ -1,3 +1,4 @@
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 get_item = """
 SELECT      kodi_id, kodi_fileid, kodi_pathid, parent_id, media_type,
diff --git a/jellyfin_kodi/dialogs/__init__.py b/jellyfin_kodi/dialogs/__init__.py
index 5b9b227f..94a8b89c 100644
--- a/jellyfin_kodi/dialogs/__init__.py
+++ b/jellyfin_kodi/dialogs/__init__.py
@@ -1,4 +1,6 @@
-from serverconnect import ServerConnect
-from usersconnect import UsersConnect
-from loginmanual import LoginManual
-from servermanual import ServerManual
+from __future__ import division, absolute_import, print_function, unicode_literals
+
+from .serverconnect import ServerConnect
+from .usersconnect import UsersConnect
+from .loginmanual import LoginManual
+from .servermanual import ServerManual
diff --git a/jellyfin_kodi/dialogs/context.py b/jellyfin_kodi/dialogs/context.py
index 63ba0052..454f9275 100644
--- a/jellyfin_kodi/dialogs/context.py
+++ b/jellyfin_kodi/dialogs/context.py
@@ -1,12 +1,12 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import logging
 import os
 
-import xbmcgui
-import xbmcaddon
+from kodi_six import xbmcgui, xbmcaddon
 
 from helper import window, addon_id
 
diff --git a/jellyfin_kodi/dialogs/loginmanual.py b/jellyfin_kodi/dialogs/loginmanual.py
index 3a07c809..c93326d1 100644
--- a/jellyfin_kodi/dialogs/loginmanual.py
+++ b/jellyfin_kodi/dialogs/loginmanual.py
@@ -1,12 +1,13 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import logging
 import os
 
-import xbmcgui
-import xbmcaddon
+from six import iteritems
+from kodi_six import xbmcgui, xbmcaddon
 
 from helper import translate, addon_id
 
@@ -36,7 +37,7 @@ class LoginManual(xbmcgui.WindowXMLDialog):
 
     def set_args(self, **kwargs):
         # connect_manager, user_image, servers
-        for key, value in kwargs.iteritems():
+        for key, value in iteritems(kwargs):
             setattr(self, key, value)
 
     def is_logged_in(self):
diff --git a/jellyfin_kodi/dialogs/resume.py b/jellyfin_kodi/dialogs/resume.py
index ef10f353..716cda4e 100644
--- a/jellyfin_kodi/dialogs/resume.py
+++ b/jellyfin_kodi/dialogs/resume.py
@@ -1,11 +1,11 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import logging
 
-import xbmc
-import xbmcgui
+from kodi_six import xbmc, xbmcgui
 
 ##################################################################################################
 
diff --git a/jellyfin_kodi/dialogs/serverconnect.py b/jellyfin_kodi/dialogs/serverconnect.py
index ef7cb133..101dcd4e 100644
--- a/jellyfin_kodi/dialogs/serverconnect.py
+++ b/jellyfin_kodi/dialogs/serverconnect.py
@@ -1,11 +1,12 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import logging
 
-import xbmc
-import xbmcgui
+from six import iteritems
+from kodi_six import xbmc, xbmcgui
 
 from helper import translate
 from jellyfin.connection_manager import CONNECTION_STATE
@@ -44,7 +45,7 @@ class ServerConnect(xbmcgui.WindowXMLDialog):
 
     def set_args(self, **kwargs):
         # connect_manager, user_image, servers
-        for key, value in kwargs.iteritems():
+        for key, value in iteritems(kwargs):
             setattr(self, key, value)
 
     def is_server_selected(self):
diff --git a/jellyfin_kodi/dialogs/servermanual.py b/jellyfin_kodi/dialogs/servermanual.py
index 2e06e952..c1970fa5 100644
--- a/jellyfin_kodi/dialogs/servermanual.py
+++ b/jellyfin_kodi/dialogs/servermanual.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
@@ -6,8 +7,8 @@ import logging
 import os
 import re
 
-import xbmcgui
-import xbmcaddon
+from six import iteritems
+from kodi_six import xbmcgui, xbmcaddon
 
 from helper import translate, addon_id
 from jellyfin.connection_manager import CONNECTION_STATE
@@ -28,7 +29,7 @@ ERROR = {
 }
 
 # https://stackoverflow.com/a/17871737/1035647
-_IPV6_PATTERN = "^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"
+_IPV6_PATTERN = r"^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"
 _IPV6_RE = re.compile(_IPV6_PATTERN)
 ##################################################################################################
 
@@ -44,7 +45,7 @@ class ServerManual(xbmcgui.WindowXMLDialog):
 
     def set_args(self, **kwargs):
         # connect_manager, user_image, servers, jellyfin_connect
-        for key, value in kwargs.iteritems():
+        for key, value in iteritems(kwargs):
             setattr(self, key, value)
 
     def is_connected(self):
diff --git a/jellyfin_kodi/dialogs/usersconnect.py b/jellyfin_kodi/dialogs/usersconnect.py
index 4cf02b62..023f85be 100644
--- a/jellyfin_kodi/dialogs/usersconnect.py
+++ b/jellyfin_kodi/dialogs/usersconnect.py
@@ -1,11 +1,12 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import logging
 
-import xbmc
-import xbmcgui
+from six import iteritems
+from kodi_six import xbmc, xbmcgui
 
 ##################################################################################################
 
@@ -34,7 +35,7 @@ class UsersConnect(xbmcgui.WindowXMLDialog):
 
     def set_args(self, **kwargs):
         # connect_manager, user_image, servers
-        for key, value in kwargs.iteritems():
+        for key, value in iteritems(kwargs):
             setattr(self, key, value)
 
     def is_user_selected(self):
diff --git a/jellyfin_kodi/downloader.py b/jellyfin_kodi/downloader.py
index 54ace2ef..2a94c698 100644
--- a/jellyfin_kodi/downloader.py
+++ b/jellyfin_kodi/downloader.py
@@ -1,12 +1,14 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
 import logging
-import Queue
 import threading
 
-import xbmc
+from six.moves import queue as Queue
+
+from kodi_six import xbmc
 import requests
 from helper import settings, stop, event, window, create_id
 from jellyfin import Jellyfin
diff --git a/jellyfin_kodi/entrypoint/__init__.py b/jellyfin_kodi/entrypoint/__init__.py
index a55c913d..23cc4476 100644
--- a/jellyfin_kodi/entrypoint/__init__.py
+++ b/jellyfin_kodi/entrypoint/__init__.py
@@ -1,11 +1,11 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
 import logging
 
-import xbmc
-import xbmcvfs
+from kodi_six import xbmc, xbmcvfs
 
 from helper import loghandler
 from jellyfin import Jellyfin
diff --git a/jellyfin_kodi/entrypoint/context.py b/jellyfin_kodi/entrypoint/context.py
index 337ebf36..01b5f801 100644
--- a/jellyfin_kodi/entrypoint/context.py
+++ b/jellyfin_kodi/entrypoint/context.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
@@ -6,8 +7,7 @@ import json
 import logging
 import sys
 
-import xbmc
-import xbmcaddon
+from kodi_six import xbmc, xbmcaddon
 
 import database
 from dialogs import context
@@ -67,7 +67,7 @@ class Context(object):
             elif self.select_menu():
                 self.action_menu()
 
-                if self._selected_option.decode('utf-8') in (OPTIONS['Delete'], OPTIONS['AddFav'], OPTIONS['RemoveFav']):
+                if self._selected_option in (OPTIONS['Delete'], OPTIONS['AddFav'], OPTIONS['RemoveFav']):
 
                     xbmc.sleep(500)
                     xbmc.executebuiltin('Container.Refresh')
@@ -91,7 +91,7 @@ class Context(object):
             else:
                 LOG.info("media is unknown")
 
-        return media.decode('utf-8')
+        return media
 
     def get_item_id(self):
 
@@ -140,7 +140,7 @@ class Context(object):
 
     def action_menu(self):
 
-        selected = self._selected_option.decode('utf-8')
+        selected = self._selected_option
 
         if selected == OPTIONS['Refresh']:
             TheVoid('RefreshItem', {'ServerId': self.server, 'Id': self.item['Id']})
diff --git a/jellyfin_kodi/entrypoint/default.py b/jellyfin_kodi/entrypoint/default.py
index 03e77ae2..ba8a7573 100644
--- a/jellyfin_kodi/entrypoint/default.py
+++ b/jellyfin_kodi/entrypoint/default.py
@@ -1,19 +1,15 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
 import json
 import logging
 import sys
-import urlparse
-import urllib
 import os
 
-import xbmc
-import xbmcvfs
-import xbmcgui
-import xbmcplugin
-import xbmcaddon
+from six.moves.urllib.parse import parse_qsl, urlencode
+from kodi_six import xbmc, xbmcvfs, xbmcgui, xbmcplugin, xbmcaddon
 
 import client
 from database import reset, get_sync, Database, jellyfin_db, get_credentials
@@ -39,7 +35,7 @@ class Events(object):
         path = sys.argv[2]
 
         try:
-            params = dict(urlparse.parse_qsl(path[1:]))
+            params = dict(parse_qsl(path[1:]))
         except Exception:
             params = {}
 
@@ -146,7 +142,7 @@ def listing():
         context = []
 
         if view_id and node in ('movies', 'tvshows', 'musicvideos', 'music', 'mixed') and view_id not in whitelist:
-            label = "%s %s" % (label.decode('utf-8'), translate(33166))
+            label = "%s %s" % (label, translate(33166))
             context.append((translate(33123), "RunPlugin(plugin://plugin.video.jellyfin/?mode=synclib&id=%s)" % view_id))
 
         if view_id and node in ('movies', 'tvshows', 'musicvideos', 'music') and view_id in whitelist:
@@ -338,7 +334,7 @@ def browse(media, view_id=None, folder=None, server_id=None):
                     'folder': item['Id'],
                     'server': server_id
                 }
-                path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
+                path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
                 context = []
 
                 if item['Type'] in ('Series', 'Season', 'Playlist'):
@@ -361,7 +357,7 @@ def browse(media, view_id=None, folder=None, server_id=None):
                     'folder': 'genres-%s' % item['Id'],
                     'server': server_id
                 }
-                path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
+                path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
                 list_li.append((path, li, True))
 
             else:
@@ -371,7 +367,7 @@ def browse(media, view_id=None, folder=None, server_id=None):
                         'mode': "play",
                         'server': server_id
                     }
-                    path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
+                    path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
                     li.setProperty('path', path)
                     context = [(translate(13412), "RunPlugin(plugin://plugin.video.jellyfin/?mode=playlist&id=%s&server=%s)" % (item['Id'], server_id))]
 
@@ -415,7 +411,7 @@ def browse_subfolders(media, view_id, server_id=None):
             'folder': view_id if node[0] == 'all' else node[0],
             'server': server_id
         }
-        path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
+        path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
         directory(node[1] or view['Name'], path)
 
     xbmcplugin.setContent(int(sys.argv[1]), 'files')
@@ -440,7 +436,7 @@ def browse_letters(media, view_id, server_id=None):
             'folder': 'firstletter-%s' % node,
             'server': server_id
         }
-        path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
+        path = "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
         directory(node, path)
 
     xbmcplugin.setContent(int(sys.argv[1]), 'files')
@@ -497,7 +493,7 @@ def get_fanart(item_id, path, server_id=None):
     LOG.info("[ extra fanart ] %s", item_id)
     objects = Objects()
     list_li = []
-    directory = xbmc.translatePath("special://thumbnails/jellyfin/%s/" % item_id).decode('utf-8')
+    directory = xbmc.translatePath("special://thumbnails/jellyfin/%s/" % item_id)
     server = TheVoid('GetServerAddress', {'ServerId': server_id}).get()
 
     if not xbmcvfs.exists(directory):
@@ -520,7 +516,7 @@ def get_fanart(item_id, path, server_id=None):
         dirs, files = xbmcvfs.listdir(directory)
 
         for file in files:
-            fanart = os.path.join(directory, file.decode('utf-8'))
+            fanart = os.path.join(directory, file)
             li = xbmcgui.ListItem(file, path=fanart)
             list_li.append((fanart, li, False))
 
@@ -773,7 +769,7 @@ def get_themes():
     from helper.playutils import PlayUtils
     from helper.xmls import tvtunes_nfo
 
-    library = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/library").decode('utf-8')
+    library = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/library")
     play = settings('useDirectPaths') == "1"
 
     if not xbmcvfs.exists(library + '/'):
@@ -803,14 +799,14 @@ def get_themes():
 
         for item in result['Items']:
 
-            folder = normalize_string(item['Name'].encode('utf-8'))
+            folder = normalize_string(item['Name'])
             items[item['Id']] = folder
 
         result = TheVoid('GetThemes', {'Type': "Song", 'Id': view}).get()
 
         for item in result['Items']:
 
-            folder = normalize_string(item['Name'].encode('utf-8'))
+            folder = normalize_string(item['Name'])
             items[item['Id']] = folder
 
     for item in items:
@@ -828,9 +824,9 @@ def get_themes():
             putils = PlayUtils(theme, False, None, server, token)
 
             if play:
-                paths.append(putils.direct_play(theme['MediaSources'][0]).encode('utf-8'))
+                paths.append(putils.direct_play(theme['MediaSources'][0]))
             else:
-                paths.append(putils.direct_url(theme['MediaSources'][0]).encode('utf-8'))
+                paths.append(putils.direct_url(theme['MediaSources'][0]))
 
         tvtunes_nfo(nfo_file, paths)
 
@@ -868,7 +864,7 @@ def backup():
 
         delete_folder(backup)
 
-    addon_data = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin").decode('utf-8')
+    addon_data = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin")
     destination_data = os.path.join(backup, "addon_data", "plugin.video.jellyfin")
     destination_databases = os.path.join(backup, "Database")
 
@@ -883,18 +879,18 @@ def backup():
 
     databases = Objects().objects
 
-    db = xbmc.translatePath(databases['jellyfin']).decode('utf-8')
+    db = xbmc.translatePath(databases['jellyfin'])
     xbmcvfs.copy(db, os.path.join(destination_databases, db.rsplit('\\', 1)[1]))
     LOG.info("copied jellyfin.db")
 
-    db = xbmc.translatePath(databases['video']).decode('utf-8')
+    db = xbmc.translatePath(databases['video'])
     filename = db.rsplit('\\', 1)[1]
     xbmcvfs.copy(db, os.path.join(destination_databases, filename))
     LOG.info("copied %s", filename)
 
     if settings('enableMusic.bool'):
 
-        db = xbmc.translatePath(databases['music']).decode('utf-8')
+        db = xbmc.translatePath(databases['music'])
         filename = db.rsplit('\\', 1)[1]
         xbmcvfs.copy(db, os.path.join(destination_databases, filename))
         LOG.info("copied %s", filename)
diff --git a/jellyfin_kodi/entrypoint/service.py b/jellyfin_kodi/entrypoint/service.py
index 58fb8ef0..b3b0ed2b 100644
--- a/jellyfin_kodi/entrypoint/service.py
+++ b/jellyfin_kodi/entrypoint/service.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
@@ -9,8 +10,8 @@ from datetime import datetime
 
 # Workaround for threads using datetime: _striptime is locked
 import _strptime  # noqa:F401
-import xbmc
-import xbmcgui
+from kodi_six import xbmc, xbmcgui
+from six.moves import reload_module as reload
 
 import objects
 import connect
@@ -181,8 +182,8 @@ class Service(xbmc.Monitor):
 
                 if settings('connectMsg.bool'):
 
-                    users = [user for user in (settings('additionalUsers') or "").decode('utf-8').split(',') if user]
-                    users.insert(0, settings('username').decode('utf-8'))
+                    users = [user for user in (settings('additionalUsers') or "").split(',') if user]
+                    users.insert(0, settings('username'))
                     dialog("notification", heading="{jellyfin}", message="%s %s" % (translate(33000), ", ".join(users)),
                            icon="{jellyfin}", time=1500, sound=False)
 
diff --git a/jellyfin_kodi/full_sync.py b/jellyfin_kodi/full_sync.py
index 5138dd00..a3226a65 100644
--- a/jellyfin_kodi/full_sync.py
+++ b/jellyfin_kodi/full_sync.py
@@ -1,11 +1,12 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import datetime
 import logging
 
-import xbmc
+from kodi_six import xbmc
 
 import downloader as server
 import helper.xmls as xmls
diff --git a/jellyfin_kodi/helper/__init__.py b/jellyfin_kodi/helper/__init__.py
index a9db000e..77d13ad1 100644
--- a/jellyfin_kodi/helper/__init__.py
+++ b/jellyfin_kodi/helper/__init__.py
@@ -1,27 +1,29 @@
-from translate import translate
-from exceptions import LibraryException
+from __future__ import division, absolute_import, print_function, unicode_literals
 
-from utils import addon_id
-from utils import window
-from utils import settings
-from utils import kodi_version
-from utils import dialog
-from utils import find
-from utils import event
-from utils import validate
-from utils import values
-from utils import JSONRPC
-from utils import indent
-from utils import write_xml
-from utils import compare_version
-from utils import unzip
-from utils import create_id
-from utils import convert_to_local as Local
-from utils import has_attribute
+from .translate import translate
+from .exceptions import LibraryException
 
-from wrapper import progress
-from wrapper import catch
-from wrapper import silent_catch
-from wrapper import stop
-from wrapper import jellyfin_item
-from wrapper import library_check
+from .utils import addon_id
+from .utils import window
+from .utils import settings
+from .utils import kodi_version
+from .utils import dialog
+from .utils import find
+from .utils import event
+from .utils import validate
+from .utils import values
+from .utils import JSONRPC
+from .utils import indent
+from .utils import write_xml
+from .utils import compare_version
+from .utils import unzip
+from .utils import create_id
+from .utils import convert_to_local as Local
+from .utils import has_attribute
+
+from .wrapper import progress
+from .wrapper import catch
+from .wrapper import silent_catch
+from .wrapper import stop
+from .wrapper import jellyfin_item
+from .wrapper import library_check
diff --git a/jellyfin_kodi/helper/api.py b/jellyfin_kodi/helper/api.py
index e00acc00..2a175455 100644
--- a/jellyfin_kodi/helper/api.py
+++ b/jellyfin_kodi/helper/api.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
diff --git a/jellyfin_kodi/helper/exceptions.py b/jellyfin_kodi/helper/exceptions.py
index c2ed4685..c7ca2291 100644
--- a/jellyfin_kodi/helper/exceptions.py
+++ b/jellyfin_kodi/helper/exceptions.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
diff --git a/jellyfin_kodi/helper/loghandler.py b/jellyfin_kodi/helper/loghandler.py
index 8fe0ea17..c067a960 100644
--- a/jellyfin_kodi/helper/loghandler.py
+++ b/jellyfin_kodi/helper/loghandler.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 from __future__ import absolute_import
 from __future__ import print_function
@@ -9,8 +10,7 @@ import os
 import logging
 import traceback
 
-import xbmc
-import xbmcaddon
+from kodi_six import xbmc, xbmcaddon
 import database
 
 from . import window, settings
@@ -18,7 +18,7 @@ from . import window, settings
 ##################################################################################################
 
 __addon__ = xbmcaddon.Addon(id='plugin.video.jellyfin')
-__pluginpath__ = xbmc.translatePath(__addon__.getAddonInfo('path').decode('utf-8'))
+__pluginpath__ = xbmc.translatePath(__addon__.getAddonInfo('path'))
 
 ##################################################################################################
 
@@ -65,15 +65,15 @@ class LogHandler(logging.StreamHandler):
 
             if self.mask_info:
                 for server in self.sensitive['Server']:
-                    string = string.replace(server.encode('utf-8') or "{server}", "{jellyfin-server}")
+                    string = string.replace(server or "{server}", "{jellyfin-server}")
 
                 for token in self.sensitive['Token']:
-                    string = string.replace(token.encode('utf-8') or "{token}", "{jellyfin-token}")
+                    string = string.replace(token or "{token}", "{jellyfin-token}")
 
             try:
                 xbmc.log(string, level=xbmc.LOGNOTICE)
             except UnicodeEncodeError:
-                xbmc.log(string.encode('utf-8'), level=xbmc.LOGNOTICE)
+                xbmc.log(string, level=xbmc.LOGNOTICE)
 
     @classmethod
     def _get_log_level(cls, level):
diff --git a/jellyfin_kodi/helper/playutils.py b/jellyfin_kodi/helper/playutils.py
index 82b77a68..d4fbaec8 100644
--- a/jellyfin_kodi/helper/playutils.py
+++ b/jellyfin_kodi/helper/playutils.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
@@ -7,15 +8,13 @@ import os
 from uuid import uuid4
 import collections
 
-import xbmc
-import xbmcvfs
+from kodi_six import xbmc, xbmcvfs
 
-import api
 import client
 import requests
 from downloader import TheVoid
 
-from . import translate, settings, window, dialog
+from . import translate, settings, window, dialog, api
 
 #################################################################################################
 
@@ -212,7 +211,7 @@ class PlayUtils(object):
         self.item['PlaybackInfo'].update(self.info)
 
         API = api.API(self.item, self.info['ServerAddress'])
-        window('jellyfinfilename', value=API.get_file_path(source.get('Path')).encode('utf-8'))
+        window('jellyfinfilename', value=API.get_file_path(source.get('Path')))
 
     def live_stream(self, source):
 
@@ -484,7 +483,7 @@ class PlayUtils(object):
                 LOG.info("[ subtitles/%s ] %s", index, url)
 
                 if 'Language' in stream:
-                    filename = "Stream.%s.%s" % (stream['Language'].encode('utf-8'), stream['Codec'].encode('utf-8'))
+                    filename = "Stream.%s.%s" % (stream['Language'], stream['Codec'])
 
                     try:
                         subs.append(self.download_external_subs(url, filename))
@@ -506,7 +505,7 @@ class PlayUtils(object):
         ''' Download external subtitles to temp folder
             to be able to have proper names to streams.
         '''
-        temp = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/temp/").decode('utf-8')
+        temp = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/temp/")
 
         if not xbmcvfs.exists(temp):
             xbmcvfs.mkdir(temp)
diff --git a/jellyfin_kodi/helper/translate.py b/jellyfin_kodi/helper/translate.py
index 8059d377..d140b149 100644
--- a/jellyfin_kodi/helper/translate.py
+++ b/jellyfin_kodi/helper/translate.py
@@ -1,11 +1,11 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import logging
 
-import xbmc
-import xbmcaddon
+from kodi_six import xbmc, xbmcaddon
 
 ##################################################################################################
 
diff --git a/jellyfin_kodi/helper/utils.py b/jellyfin_kodi/helper/utils.py
index f6ca9320..67d4ecd2 100644
--- a/jellyfin_kodi/helper/utils.py
+++ b/jellyfin_kodi/helper/utils.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
@@ -8,18 +9,16 @@ import logging
 import os
 import re
 import unicodedata
-import urllib
 from uuid import uuid4
 from distutils.version import LooseVersion
 
 from dateutil import tz, parser
+from six import text_type, string_types, iteritems
+from six.moves.urllib.parse import quote_plus
 
-import xbmc
-import xbmcaddon
-import xbmcgui
-import xbmcvfs
+from kodi_six import xbmc, xbmcaddon, xbmcgui, xbmcvfs
 
-from translate import translate
+from .translate import translate
 
 #################################################################################################
 
@@ -122,7 +121,7 @@ def find(dict, item):
     if item in dict:
         return dict[item]
 
-    for key, value in sorted(dict.iteritems(), key=lambda (k, v): (v, k)):
+    for key, value in sorted(iteritems(dict), key=lambda kv: (kv[1], kv[0])):
 
         if re.match(key, item, re.I):
             return dict[key]
@@ -245,7 +244,7 @@ def validate(path):
     if window('jellyfin_pathverified.bool'):
         return True
 
-    path = path if os.path.supports_unicode_filenames else path.encode('utf-8')
+    path = path if os.path.supports_unicode_filenames else path
 
     if not xbmcvfs.exists(path):
         LOG.info("Could not find %s", path)
@@ -291,14 +290,17 @@ def indent(elem, level=0):
 
 
 def write_xml(content, file):
-    with open(file, 'w') as infile:
+    if isinstance(content, text_type):
+        content = content.encode('utf-8')
+
+    with open(file, 'wb') as infile:
 
         # replace apostrophes with double quotes only in xml keys, not texts
         def replace_apostrophes(match):
-            return match.group(0).replace("'", '"')
-        content = re.sub("<(.*?)>", replace_apostrophes, content)
+            return match.group(0).replace(b"'", b'"')
+        content = re.sub(b"<(.*?)>", replace_apostrophes, content)
 
-        content = content.replace('?>', ' standalone="yes" ?>', 1)
+        content = content.replace(b'?>', b' standalone="yes" ?>', 1)
         infile.write(content)
 
 
@@ -312,7 +314,7 @@ def delete_folder(path):
     delete_recursive(path, dirs)
 
     for file in files:
-        xbmcvfs.delete(os.path.join(path, file.decode('utf-8')))
+        xbmcvfs.delete(os.path.join(path, file))
 
     xbmcvfs.delete(path)
 
@@ -324,20 +326,20 @@ def delete_recursive(path, dirs):
     ''' Delete files and dirs recursively.
     '''
     for directory in dirs:
-        dirs2, files = xbmcvfs.listdir(os.path.join(path, directory.decode('utf-8')))
+        dirs2, files = xbmcvfs.listdir(os.path.join(path, directory))
 
         for file in files:
-            xbmcvfs.delete(os.path.join(path, directory.decode('utf-8'), file.decode('utf-8')))
+            xbmcvfs.delete(os.path.join(path, directory, file))
 
-        delete_recursive(os.path.join(path, directory.decode('utf-8')), dirs2)
-        xbmcvfs.rmdir(os.path.join(path, directory.decode('utf-8')))
+        delete_recursive(os.path.join(path, directory), dirs2)
+        xbmcvfs.rmdir(os.path.join(path, directory))
 
 
 def unzip(path, dest, folder=None):
 
     ''' Unzip file. zipfile module seems to fail on android with badziperror.
     '''
-    path = urllib.quote_plus(path)
+    path = quote_plus(path)
     root = "zip://" + path + '/'
 
     if folder:
@@ -352,7 +354,7 @@ def unzip(path, dest, folder=None):
         unzip_recursive(root, dirs, dest)
 
     for file in files:
-        unzip_file(os.path.join(root, file.decode('utf-8')), os.path.join(dest, file.decode('utf-8')))
+        unzip_file(os.path.join(root, file), os.path.join(dest, file))
 
     LOG.info("Unzipped %s", path)
 
@@ -361,8 +363,8 @@ def unzip_recursive(path, dirs, dest):
 
     for directory in dirs:
 
-        dirs_dir = os.path.join(path, directory.decode('utf-8'))
-        dest_dir = os.path.join(dest, directory.decode('utf-8'))
+        dirs_dir = os.path.join(path, directory)
+        dest_dir = os.path.join(dest, directory)
         xbmcvfs.mkdir(dest_dir)
 
         dirs2, files = xbmcvfs.listdir(dirs_dir)
@@ -371,7 +373,7 @@ def unzip_recursive(path, dirs, dest):
             unzip_recursive(dirs_dir, dirs2, dest_dir)
 
         for file in files:
-            unzip_file(os.path.join(dirs_dir, file.decode('utf-8')), os.path.join(dest_dir, file.decode('utf-8')))
+            unzip_file(os.path.join(dirs_dir, file), os.path.join(dest_dir, file))
 
 
 def unzip_file(path, dest):
@@ -390,7 +392,7 @@ def get_zip_directory(path, folder):
         return os.path.join(path, folder)
 
     for directory in dirs:
-        result = get_zip_directory(os.path.join(path, directory.decode('utf-8')), folder)
+        result = get_zip_directory(os.path.join(path, directory), folder)
         if result:
             return result
 
@@ -408,7 +410,7 @@ def copytree(path, dest):
         copy_recursive(path, dirs, dest)
 
     for file in files:
-        copy_file(os.path.join(path, file.decode('utf-8')), os.path.join(dest, file.decode('utf-8')))
+        copy_file(os.path.join(path, file), os.path.join(dest, file))
 
     LOG.info("Copied %s", path)
 
@@ -417,8 +419,8 @@ def copy_recursive(path, dirs, dest):
 
     for directory in dirs:
 
-        dirs_dir = os.path.join(path, directory.decode('utf-8'))
-        dest_dir = os.path.join(dest, directory.decode('utf-8'))
+        dirs_dir = os.path.join(path, directory)
+        dest_dir = os.path.join(dest, directory)
         xbmcvfs.mkdir(dest_dir)
 
         dirs2, files = xbmcvfs.listdir(dirs_dir)
@@ -427,7 +429,7 @@ def copy_recursive(path, dirs, dest):
             copy_recursive(dirs_dir, dirs2, dest_dir)
 
         for file in files:
-            copy_file(os.path.join(dirs_dir, file.decode('utf-8')), os.path.join(dest_dir, file.decode('utf-8')))
+            copy_file(os.path.join(dirs_dir, file), os.path.join(dest_dir, file))
 
 
 def copy_file(path, dest):
@@ -458,7 +460,7 @@ def normalize_string(text):
     text = text.strip()
 
     text = text.rstrip('.')
-    text = unicodedata.normalize('NFKD', unicode(text, 'utf-8')).encode('ascii', 'ignore')
+    text = unicodedata.normalize('NFKD', text_type(text, 'utf-8')).encode('ascii', 'ignore')
 
     return text
 
@@ -475,7 +477,7 @@ def convert_to_local(date):
     ''' Convert the local datetime to local.
     '''
     try:
-        date = parser.parse(date) if type(date) in (unicode, str) else date
+        date = parser.parse(date) if isinstance(date, string_types) else date
         date = date.replace(tzinfo=tz.tzutc())
         date = date.astimezone(tz.tzlocal())
 
@@ -485,6 +487,7 @@ def convert_to_local(date):
 
         return str(date)
 
+
 def has_attribute(obj, name):
     try:
         object.__getattribute__(obj, name)
diff --git a/jellyfin_kodi/helper/wrapper.py b/jellyfin_kodi/helper/wrapper.py
index 746fed5e..ccd49bdd 100644
--- a/jellyfin_kodi/helper/wrapper.py
+++ b/jellyfin_kodi/helper/wrapper.py
@@ -1,10 +1,11 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
 import logging
 
-import xbmcgui
+from kodi_six import xbmcgui
 
 from .utils import should_stop
 from .exceptions import LibraryException
diff --git a/jellyfin_kodi/helper/xmls.py b/jellyfin_kodi/helper/xmls.py
index cb0d8f39..62b6058c 100644
--- a/jellyfin_kodi/helper/xmls.py
+++ b/jellyfin_kodi/helper/xmls.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
@@ -6,7 +7,7 @@ import logging
 import os
 import xml.etree.ElementTree as etree
 
-import xbmc
+from kodi_six import xbmc
 
 from . import translate, indent, write_xml, dialog, settings
 
@@ -22,7 +23,7 @@ def sources():
     ''' Create master lock compatible sources.
         Also add the kodi.jellyfin.media source.
     '''
-    path = xbmc.translatePath("special://profile/").decode('utf-8')
+    path = xbmc.translatePath("special://profile/")
     file = os.path.join(path, 'sources.xml')
 
     try:
@@ -106,7 +107,7 @@ def advanced_settings():
     if settings('useDirectPaths') != "0":
         return
 
-    path = xbmc.translatePath("special://profile/").decode('utf-8')
+    path = xbmc.translatePath("special://profile/")
     file = os.path.join(path, 'advancedsettings.xml')
 
     try:
diff --git a/jellyfin_kodi/jellyfin/__init__.py b/jellyfin_kodi/jellyfin/__init__.py
index cffb1e6a..19dba025 100644
--- a/jellyfin_kodi/jellyfin/__init__.py
+++ b/jellyfin_kodi/jellyfin/__init__.py
@@ -1,10 +1,11 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
 import logging
 
-from client import JellyfinClient
+from .client import JellyfinClient
 from helper import has_attribute
 
 #################################################################################################
diff --git a/jellyfin_kodi/jellyfin/api.py b/jellyfin_kodi/jellyfin/api.py
index 4eab6872..ba43c4cd 100644
--- a/jellyfin_kodi/jellyfin/api.py
+++ b/jellyfin_kodi/jellyfin/api.py
@@ -1,4 +1,7 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
+
+
 def jellyfin_url(client, handler):
     return "%s/%s" % (client.config.data['auth.server'], handler)
 
diff --git a/jellyfin_kodi/jellyfin/client.py b/jellyfin_kodi/jellyfin/client.py
index ee9db7fd..d326f409 100644
--- a/jellyfin_kodi/jellyfin/client.py
+++ b/jellyfin_kodi/jellyfin/client.py
@@ -1,14 +1,15 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
 import logging
 
-import api
-from configuration import Config
-from http import HTTP
-from ws_client import WSClient
-from connection_manager import ConnectionManager, CONNECTION_STATE
+from . import api
+from .configuration import Config
+from .http import HTTP
+from .ws_client import WSClient
+from .connection_manager import ConnectionManager, CONNECTION_STATE
 
 #################################################################################################
 
diff --git a/jellyfin_kodi/jellyfin/configuration.py b/jellyfin_kodi/jellyfin/configuration.py
index 06e50088..b73f23ca 100644
--- a/jellyfin_kodi/jellyfin/configuration.py
+++ b/jellyfin_kodi/jellyfin/configuration.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ''' This will hold all configs from the client.
     Configuration set here will be used for the HTTP client.
diff --git a/jellyfin_kodi/jellyfin/connection_manager.py b/jellyfin_kodi/jellyfin/connection_manager.py
index c3195e49..2b0e4780 100644
--- a/jellyfin_kodi/jellyfin/connection_manager.py
+++ b/jellyfin_kodi/jellyfin/connection_manager.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
@@ -11,8 +12,8 @@ from distutils.version import LooseVersion
 
 import urllib3
 
-from credentials import Credentials
-from http import HTTP  # noqa: I201,I100
+from .credentials import Credentials
+from .http import HTTP  # noqa: I201,I100
 
 #################################################################################################
 
@@ -258,7 +259,7 @@ class ConnectionManager(object):
     def _server_discovery(self):
 
         MULTI_GROUP = ("<broadcast>", 7359)
-        MESSAGE = "who is JellyfinServer?"
+        MESSAGE = b"who is JellyfinServer?"
 
         sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
         sock.settimeout(1.0)  # This controls the socket.timeout exception
diff --git a/jellyfin_kodi/jellyfin/credentials.py b/jellyfin_kodi/jellyfin/credentials.py
index 0b61f3dd..2eeacb98 100644
--- a/jellyfin_kodi/jellyfin/credentials.py
+++ b/jellyfin_kodi/jellyfin/credentials.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
diff --git a/jellyfin_kodi/jellyfin/exceptions.py b/jellyfin_kodi/jellyfin/exceptions.py
index aea09675..6cd50511 100644
--- a/jellyfin_kodi/jellyfin/exceptions.py
+++ b/jellyfin_kodi/jellyfin/exceptions.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
diff --git a/jellyfin_kodi/jellyfin/http.py b/jellyfin_kodi/jellyfin/http.py
index 87756e54..f4cae0d3 100644
--- a/jellyfin_kodi/jellyfin/http.py
+++ b/jellyfin_kodi/jellyfin/http.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
@@ -7,7 +8,8 @@ import logging
 import time
 
 import requests
-from exceptions import HTTPException
+
+from .exceptions import HTTPException
 
 #################################################################################################
 
@@ -49,13 +51,13 @@ class HTTP(object):
 
         if '{server}' in string:
             if self.config.data['auth.server']:
-                string = string.decode('utf-8').replace("{server}", self.config.data['auth.server'])
+                string = string.replace("{server}", self.config.data['auth.server'])
             else:
                 raise Exception("Server address not set.")
 
         if '{UserId}'in string:
             if self.config.data['auth.user_id']:
-                string = string.decode('utf-8').replace("{UserId}", self.config.data['auth.user_id'])
+                string = string.replace("{UserId}", self.config.data['auth.user_id'])
             else:
                 raise Exception("UserId is not set.")
 
@@ -209,17 +211,17 @@ class HTTP(object):
     def _authorization(self, data):
 
         auth = "MediaBrowser "
-        auth += "Client=%s, " % self.config.data['app.name'].encode('utf-8')
-        auth += "Device=%s, " % self.config.data['app.device_name'].encode('utf-8')
-        auth += "DeviceId=%s, " % self.config.data['app.device_id'].encode('utf-8')
-        auth += "Version=%s" % self.config.data['app.version'].encode('utf-8')
+        auth += "Client=%s, " % self.config.data['app.name']
+        auth += "Device=%s, " % self.config.data['app.device_name']
+        auth += "DeviceId=%s, " % self.config.data['app.device_id']
+        auth += "Version=%s" % self.config.data['app.version']
 
         data['headers'].update({'x-emby-authorization': auth})
 
         if self.config.data.get('auth.token') and self.config.data.get('auth.user_id'):
 
-            auth += ', UserId=%s' % self.config.data['auth.user_id'].encode('utf-8')
-            data['headers'].update({'x-emby-authorization': auth, 'X-MediaBrowser-Token': self.config.data['auth.token'].encode('utf-8')})
+            auth += ', UserId=%s' % self.config.data['auth.user_id']
+            data['headers'].update({'x-emby-authorization': auth, 'X-MediaBrowser-Token': self.config.data['auth.token']})
 
         return data
 
diff --git a/jellyfin_kodi/jellyfin/websocket.py b/jellyfin_kodi/jellyfin/websocket.py
index 6cb9c009..2c6a706f 100644
--- a/jellyfin_kodi/jellyfin/websocket.py
+++ b/jellyfin_kodi/jellyfin/websocket.py
@@ -1,3 +1,4 @@
+from __future__ import division, absolute_import, print_function, unicode_literals
 """
 websocket - WebSocket client library for Python
 
@@ -33,7 +34,7 @@ except ImportError:
 
     HAVE_SSL = False
 
-from urlparse import urlparse
+
 import os
 import array
 import struct
@@ -44,6 +45,10 @@ import threading
 import time
 import logging
 
+from six import text_type, string_types, iteritems, int2byte, indexbytes
+from six.moves import range
+from six.moves.urllib.parse import urlparse
+
 """
 websocket python client.
 =========================
@@ -221,7 +226,7 @@ def create_connection(url, timeout=None, **options):
 
 
 _MAX_INTEGER = (1 << 32) - 1
-_AVAILABLE_KEY_CHARS = range(0x21, 0x2f + 1) + range(0x3a, 0x7e + 1)
+_AVAILABLE_KEY_CHARS = list(range(0x21, 0x2f + 1)) + list(range(0x3a, 0x7e + 1))
 _MAX_CHAR_BYTE = (1 << 8) - 1
 
 # ref. Websocket gets an update, and it breaks stuff.
@@ -304,7 +309,7 @@ class ABNF(object):
 
         opcode: operation code. please see OPCODE_XXX.
         """
-        if opcode == ABNF.OPCODE_TEXT and isinstance(data, unicode):
+        if opcode == ABNF.OPCODE_TEXT and isinstance(data, text_type):
             data = data.encode("utf-8")
         # mask must be set if send data from client
         return ABNF(1, 0, 0, 0, opcode, 1, data)
@@ -321,14 +326,14 @@ class ABNF(object):
         if length >= ABNF.LENGTH_63:
             raise ValueError("data is too long")
 
-        frame_header = chr(self.fin << 7 | self.rsv1 << 6 | self.rsv2 << 5 | self.rsv3 << 4 | self.opcode)
+        frame_header = int2byte(self.fin << 7 | self.rsv1 << 6 | self.rsv2 << 5 | self.rsv3 << 4 | self.opcode)
         if length < ABNF.LENGTH_7:
-            frame_header += chr(self.mask << 7 | length)
+            frame_header += int2byte(self.mask << 7 | length)
         elif length < ABNF.LENGTH_16:
-            frame_header += chr(self.mask << 7 | 0x7e)
+            frame_header += int2byte(self.mask << 7 | 0x7e)
             frame_header += struct.pack("!H", length)
         else:
-            frame_header += chr(self.mask << 7 | 0x7f)
+            frame_header += int2byte(self.mask << 7 | 0x7f)
             frame_header += struct.pack("!Q", length)
 
         if not self.mask:
@@ -352,7 +357,7 @@ class ABNF(object):
         """
         _m = array.array("B", mask_key)
         _d = array.array("B", data)
-        for i in xrange(len(_d)):
+        for i in range(len(_d)):
             _d[i] ^= _m[i % 4]
         return _d.tostring()
 
@@ -475,31 +480,39 @@ class WebSocket(object):
         self._handshake(hostname, port, resource, **options)
 
     def _handshake(self, host, port, resource, **options):
+        if isinstance(host, string_types):
+            host = host.encode('utf-8')
+        if isinstance(resource, string_types):
+            resource = resource.encode('utf-8')
+
         headers = []
-        headers.append("GET %s HTTP/1.1" % resource)
-        headers.append("Upgrade: websocket")
-        headers.append("Connection: Upgrade")
+        headers.append(b"GET %s HTTP/1.1" % resource)
+        headers.append(b"Upgrade: websocket")
+        headers.append(b"Connection: Upgrade")
         if port == 80:
             hostport = host
         else:
-            hostport = "%s:%d" % (host, port)
-        headers.append("Host: %s" % hostport)
+            hostport = b"%s:%d" % (host, port)
+        headers.append(b"Host: %s" % hostport)
 
         if "origin" in options:
-            headers.append("Origin: %s" % options["origin"])
+            headers.append(b"Origin: %s" % options["origin"])
         else:
-            headers.append("Origin: http://%s" % hostport)
+            headers.append(b"Origin: http://%s" % hostport)
 
         key = _create_sec_websocket_key()
-        headers.append("Sec-WebSocket-Key: %s" % key)
-        headers.append("Sec-WebSocket-Version: %s" % VERSION)
+        headers.append(b"Sec-WebSocket-Key: %s" % key)
+        headers.append(b"Sec-WebSocket-Version: %d" % VERSION)
         if "header" in options:
-            headers.extend(options["header"])
+            for header in options["header"]:
+                if isinstance(header, string_types):
+                    header = header.encode('utf-8')
+                headers.extend(header)
 
-        headers.append("")
-        headers.append("")
+        headers.append(b"")
+        headers.append(b"")
 
-        header_str = "\r\n".join(headers)
+        header_str = b"\r\n".join(headers)
         self._send(header_str)
         if traceEnabled:
             logger.debug("--- request header ---")
@@ -519,7 +532,7 @@ class WebSocket(object):
         self.connected = True
 
     def _validate_header(self, headers, key):
-        for k, v in _HEADERS_TO_CHECK.iteritems():
+        for k, v in iteritems(_HEADERS_TO_CHECK):
             r = headers.get(k, None)
             if not r:
                 return False
@@ -653,13 +666,13 @@ class WebSocket(object):
         # Header
         if self._frame_header is None:
             self._frame_header = self._recv_strict(2)
-        b1 = ord(self._frame_header[0])
+        b1 = indexbytes(self._frame_header, 0)
         fin = b1 >> 7 & 1
         rsv1 = b1 >> 6 & 1
         rsv2 = b1 >> 5 & 1
         rsv3 = b1 >> 4 & 1
         opcode = b1 & 0xf
-        b2 = ord(self._frame_header[1])
+        b2 = indexbytes(self._frame_header, 1)
         has_mask = b2 >> 7 & 1
         # Frame length
         if self._frame_length is None:
@@ -753,7 +766,7 @@ class WebSocket(object):
 
     def _recv(self, bufsize):
         try:
-            bytes = self.sock.recv(bufsize)
+            _bytes = self.sock.recv(bufsize)
         except socket.timeout as e:
             raise WebSocketTimeoutException(e.args[0])
         except SSLError as e:
@@ -761,16 +774,16 @@ class WebSocket(object):
                 raise WebSocketTimeoutException(e.args[0])
             else:
                 raise
-        if not bytes:
+        if not _bytes:
             raise WebSocketConnectionClosedException()
-        return bytes
+        return _bytes
 
     def _recv_strict(self, bufsize):
         shortage = bufsize - sum(len(x) for x in self._recv_buffer)
         while shortage > 0:
-            bytes = self._recv(shortage)
-            self._recv_buffer.append(bytes)
-            shortage -= len(bytes)
+            _bytes = self._recv(shortage)
+            self._recv_buffer.append(_bytes)
+            shortage -= len(_bytes)
         unified = "".join(self._recv_buffer)
         if shortage == 0:
             self._recv_buffer = []
diff --git a/jellyfin_kodi/jellyfin/ws_client.py b/jellyfin_kodi/jellyfin/ws_client.py
index a19d624d..da2efcbf 100644
--- a/jellyfin_kodi/jellyfin/ws_client.py
+++ b/jellyfin_kodi/jellyfin/ws_client.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
@@ -6,9 +7,9 @@ import json
 import logging
 import threading
 
-import xbmc
+from kodi_six import xbmc
 
-import websocket
+from . import websocket
 
 ##################################################################################################
 
diff --git a/jellyfin_kodi/library.py b/jellyfin_kodi/library.py
index 0d67c318..33ad98f6 100644
--- a/jellyfin_kodi/library.py
+++ b/jellyfin_kodi/library.py
@@ -1,14 +1,15 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import logging
-import Queue
 import threading
 from datetime import datetime, timedelta
 
-import xbmc
-import xbmcgui
+from six.moves import queue as Queue
+
+from kodi_six import xbmc, xbmcgui
 
 from objects import Movies, TVShows, MusicVideos, Music
 from database import Database, jellyfin_db, get_sync, save_sync
@@ -495,7 +496,7 @@ class Library(threading.Thread):
             "AddLibrarySelection": 33120
         }
         title = titles.get(mode, "Failed to get title {}".format(mode))
-        
+
         selection = dialog("multi", translate(title), choices)
 
         if selection is None:
diff --git a/jellyfin_kodi/monitor.py b/jellyfin_kodi/monitor.py
index 1a8b1d5e..a423303e 100644
--- a/jellyfin_kodi/monitor.py
+++ b/jellyfin_kodi/monitor.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
@@ -7,7 +8,7 @@ import json
 import logging
 import threading
 
-import xbmc
+from kodi_six import xbmc
 
 import connect
 import downloader
@@ -144,7 +145,7 @@ class Monitor(xbmc.Monitor):
             self.void_responder(data, item)
 
         elif method == 'GetServerAddress':
-            
+
             server_address = server.auth.get_server_info(server.auth.server_id)['address']
             self.void_responder(data, server_address)
 
@@ -291,7 +292,7 @@ class Monitor(xbmc.Monitor):
             for additional in users:
                 for user in all_users:
 
-                    if user['Name'].lower() in additional.decode('utf-8').lower():
+                    if user['Name'].lower() in additional.lower():
                         server.jellyfin.session_add_user(server.config.data['app.session'], user['Id'], True)
 
             self.additional_users(server)
diff --git a/jellyfin_kodi/objects/__init__.py b/jellyfin_kodi/objects/__init__.py
index 6d3c8152..469f3809 100644
--- a/jellyfin_kodi/objects/__init__.py
+++ b/jellyfin_kodi/objects/__init__.py
@@ -1,11 +1,13 @@
-from movies import Movies
-from musicvideos import MusicVideos
-from tvshows import TVShows
-from music import Music
-from obj import Objects
-from actions import Actions
-from actions import PlaylistWorker
-from actions import on_play, on_update, special_listener
-import utils
+from __future__ import division, absolute_import, print_function, unicode_literals
+
+from .movies import Movies
+from .musicvideos import MusicVideos
+from .tvshows import TVShows
+from .music import Music
+from .obj import Objects
+from .actions import Actions
+from .actions import PlaylistWorker
+from .actions import on_play, on_update, special_listener
+from . import utils
 
 Objects().mapping()
diff --git a/jellyfin_kodi/objects/actions.py b/jellyfin_kodi/objects/actions.py
index 394d9e2c..6313a446 100644
--- a/jellyfin_kodi/objects/actions.py
+++ b/jellyfin_kodi/objects/actions.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
@@ -7,14 +8,11 @@ import threading
 import sys
 from datetime import timedelta
 
-import xbmc
-import xbmcgui
-import xbmcplugin
-import xbmcaddon
+from kodi_six import xbmc, xbmcgui, xbmcplugin, xbmcaddon
 
 import database
 from downloader import TheVoid
-from obj import Objects
+from .obj import Objects
 from helper import translate, playutils, api, window, settings, dialog
 from dialogs import resume
 
diff --git a/jellyfin_kodi/objects/kodi/__init__.py b/jellyfin_kodi/objects/kodi/__init__.py
index 86ada83f..961219bf 100644
--- a/jellyfin_kodi/objects/kodi/__init__.py
+++ b/jellyfin_kodi/objects/kodi/__init__.py
@@ -1,6 +1,8 @@
-from kodi import Kodi
-from movies import Movies
-from musicvideos import MusicVideos
-from tvshows import TVShows
-from music import Music
-from artwork import Artwork
+from __future__ import division, absolute_import, print_function, unicode_literals
+
+from .kodi import Kodi
+from .movies import Movies
+from .musicvideos import MusicVideos
+from .tvshows import TVShows
+from .music import Music
+from .artwork import Artwork
diff --git a/jellyfin_kodi/objects/kodi/artwork.py b/jellyfin_kodi/objects/kodi/artwork.py
index 9de81a51..0625d75e 100644
--- a/jellyfin_kodi/objects/kodi/artwork.py
+++ b/jellyfin_kodi/objects/kodi/artwork.py
@@ -1,17 +1,18 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
 import logging
-import urllib
-import Queue
 import threading
 
-import xbmc
-import xbmcvfs
+from six.moves import queue as Queue
+from six.moves.urllib.parse import urlencode
 
-import queries as QU
-import queries_texture as QUTEX
+from kodi_six import xbmc, xbmcvfs
+
+from . import queries as QU
+from . import queries_texture as QUTEX
 from helper import settings
 import requests
 
@@ -139,10 +140,10 @@ class Artwork(object):
         ''' urlencode needs a utf-string.
             return the result as unicode
         '''
-        text = urllib.urlencode({'blahblahblah': text.encode('utf-8')})
+        text = urlencode({'blahblahblah': text})
         text = text[13:]
 
-        return text.decode('utf-8')
+        return text
 
     def add_worker(self):
 
@@ -170,7 +171,7 @@ class Artwork(object):
             except TypeError:
                 LOG.debug("Could not find cached url: %s", url)
             else:
-                thumbnails = xbmc.translatePath("special://thumbnails/%s" % cached).decode('utf-8')
+                thumbnails = xbmc.translatePath("special://thumbnails/%s" % cached)
                 xbmcvfs.delete(thumbnails)
                 texturedb.cursor.execute(QUTEX.delete_cache, (url,))
                 LOG.info("DELETE cached %s", cached)
diff --git a/jellyfin_kodi/objects/kodi/kodi.py b/jellyfin_kodi/objects/kodi/kodi.py
index 844d3ca4..e34d57d2 100644
--- a/jellyfin_kodi/objects/kodi/kodi.py
+++ b/jellyfin_kodi/objects/kodi/kodi.py
@@ -1,11 +1,12 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import logging
 
-import artwork
-import queries as QU
+from . import artwork
+from . import queries as QU
 from helper import values
 
 ##################################################################################################
diff --git a/jellyfin_kodi/objects/kodi/movies.py b/jellyfin_kodi/objects/kodi/movies.py
index 38fd9329..40c442a1 100644
--- a/jellyfin_kodi/objects/kodi/movies.py
+++ b/jellyfin_kodi/objects/kodi/movies.py
@@ -1,11 +1,12 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import logging
 
-from kodi import Kodi
-import queries as QU
+from .kodi import Kodi
+from . import queries as QU
 
 ##################################################################################################
 
diff --git a/jellyfin_kodi/objects/kodi/music.py b/jellyfin_kodi/objects/kodi/music.py
index 6b676b1d..89213e94 100644
--- a/jellyfin_kodi/objects/kodi/music.py
+++ b/jellyfin_kodi/objects/kodi/music.py
@@ -1,11 +1,12 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import logging
 
-import queries_music as QU
-from kodi import Kodi
+from . import queries_music as QU
+from .kodi import Kodi
 
 ##################################################################################################
 
diff --git a/jellyfin_kodi/objects/kodi/musicvideos.py b/jellyfin_kodi/objects/kodi/musicvideos.py
index 764a79ba..70de6cdf 100644
--- a/jellyfin_kodi/objects/kodi/musicvideos.py
+++ b/jellyfin_kodi/objects/kodi/musicvideos.py
@@ -1,11 +1,12 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import logging
 
-import queries as QU
-from kodi import Kodi
+from . import queries as QU
+from .kodi import Kodi
 
 ##################################################################################################
 
diff --git a/jellyfin_kodi/objects/kodi/queries.py b/jellyfin_kodi/objects/kodi/queries.py
index 75fb15da..fea7dba6 100644
--- a/jellyfin_kodi/objects/kodi/queries.py
+++ b/jellyfin_kodi/objects/kodi/queries.py
@@ -1,3 +1,4 @@
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ''' Queries for the Kodi database. obj reflect key/value to retrieve from jellyfin items.
     Some functions require additional information, therefore obj do not always reflect
diff --git a/jellyfin_kodi/objects/kodi/queries_music.py b/jellyfin_kodi/objects/kodi/queries_music.py
index b9105a7a..836883bc 100644
--- a/jellyfin_kodi/objects/kodi/queries_music.py
+++ b/jellyfin_kodi/objects/kodi/queries_music.py
@@ -1,3 +1,4 @@
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 create_artist = """
 SELECT      coalesce(max(idArtist), 1)
diff --git a/jellyfin_kodi/objects/kodi/queries_texture.py b/jellyfin_kodi/objects/kodi/queries_texture.py
index 25960941..d60505b7 100644
--- a/jellyfin_kodi/objects/kodi/queries_texture.py
+++ b/jellyfin_kodi/objects/kodi/queries_texture.py
@@ -1,3 +1,4 @@
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 get_cache = """
 SELECT      cachedurl
diff --git a/jellyfin_kodi/objects/kodi/tvshows.py b/jellyfin_kodi/objects/kodi/tvshows.py
index 4d620e87..b9feda4d 100644
--- a/jellyfin_kodi/objects/kodi/tvshows.py
+++ b/jellyfin_kodi/objects/kodi/tvshows.py
@@ -1,11 +1,12 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import logging
 
-import queries as QU
-from kodi import Kodi
+from . import queries as QU
+from .kodi import Kodi
 
 ##################################################################################################
 
diff --git a/jellyfin_kodi/objects/movies.py b/jellyfin_kodi/objects/movies.py
index ffb57906..5cd8f3ab 100644
--- a/jellyfin_kodi/objects/movies.py
+++ b/jellyfin_kodi/objects/movies.py
@@ -1,13 +1,14 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import logging
-import urllib
+from six.moves.urllib.parse import urlencode
 
 import downloader as server
-from obj import Objects
-from kodi import Movies as KodiDb, queries as QU
+from .obj import Objects
+from .kodi import Movies as KodiDb, queries as QU
 from database import jellyfin_db, queries as QUEM
 from helper import api, stop, validate, jellyfin_item, library_check, values, settings, Local
 
@@ -177,12 +178,12 @@ class Movies(KodiDb):
         else:
             obj['Path'] = "plugin://plugin.video.jellyfin/%s/" % obj['LibraryId']
             params = {
-                'filename': obj['Filename'].encode('utf-8'),
+                'filename': obj['Filename'],
                 'id': obj['Id'],
                 'dbid': obj['MovieId'],
                 'mode': "play"
             }
-            obj['Filename'] = "%s?%s" % (obj['Path'], urllib.urlencode(params))
+            obj['Filename'] = "%s?%s" % (obj['Path'], urlencode(params))
 
     @stop()
     @jellyfin_item()
diff --git a/jellyfin_kodi/objects/music.py b/jellyfin_kodi/objects/music.py
index 81f37e9c..be8b5922 100644
--- a/jellyfin_kodi/objects/music.py
+++ b/jellyfin_kodi/objects/music.py
@@ -1,12 +1,13 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import datetime
 import logging
 
-from obj import Objects
-from kodi import Music as KodiDb, queries_music as QU
+from .obj import Objects
+from .kodi import Music as KodiDb, queries_music as QU
 from database import jellyfin_db, queries as QUEM
 from helper import api, stop, validate, jellyfin_item, values, library_check, Local
 
diff --git a/jellyfin_kodi/objects/musicvideos.py b/jellyfin_kodi/objects/musicvideos.py
index fe24e6cc..b4cca7b2 100644
--- a/jellyfin_kodi/objects/musicvideos.py
+++ b/jellyfin_kodi/objects/musicvideos.py
@@ -1,14 +1,15 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import datetime
 import logging
 import re
-import urllib
+from six.moves.urllib.parse import urlencode
 
-from obj import Objects
-from kodi import MusicVideos as KodiDb, queries as QU
+from .obj import Objects
+from .kodi import MusicVideos as KodiDb, queries as QU
 from database import jellyfin_db, queries as QUEM
 from helper import api, stop, validate, library_check, jellyfin_item, values, Local
 
@@ -165,12 +166,12 @@ class MusicVideos(KodiDb):
         else:
             obj['Path'] = "plugin://plugin.video.jellyfin/%s/" % obj['LibraryId']
             params = {
-                'filename': obj['Filename'].encode('utf-8'),
+                'filename': obj['Filename'],
                 'id': obj['Id'],
                 'dbid': obj['MvideoId'],
                 'mode': "play"
             }
-            obj['Filename'] = "%s?%s" % (obj['Path'], urllib.urlencode(params))
+            obj['Filename'] = "%s?%s" % (obj['Path'], urlencode(params))
 
     @stop()
     @jellyfin_item()
diff --git a/jellyfin_kodi/objects/obj.py b/jellyfin_kodi/objects/obj.py
index ac1a7aa2..1cca13e4 100644
--- a/jellyfin_kodi/objects/obj.py
+++ b/jellyfin_kodi/objects/obj.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
@@ -6,6 +7,8 @@ import json
 import logging
 import os
 
+from six import iteritems
+
 ##################################################################################################
 
 LOG = logging.getLogger("JELLYFIN." + __name__)
@@ -53,7 +56,7 @@ class Objects(object):
 
         mapping = self.objects[mapping_name]
 
-        for key, value in mapping.iteritems():
+        for key, value in iteritems(mapping):
 
             self.mapped_item[key] = None
             params = value.split(',')
@@ -144,7 +147,7 @@ class Objects(object):
 
         result = False
 
-        for key, value in filters.iteritems():
+        for key, value in iteritems(filters):
 
             inverse = False
 
diff --git a/jellyfin_kodi/objects/tvshows.py b/jellyfin_kodi/objects/tvshows.py
index 9922641d..9eedf007 100644
--- a/jellyfin_kodi/objects/tvshows.py
+++ b/jellyfin_kodi/objects/tvshows.py
@@ -1,14 +1,16 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 ##################################################################################################
 
 import logging
 import sqlite3
-import urllib
 from ntpath import dirname
 
-from obj import Objects
-from kodi import TVShows as KodiDb, queries as QU
+from six.moves.urllib.parse import urlencode
+
+from .obj import Objects
+from .kodi import TVShows as KodiDb, queries as QU
 import downloader as server
 from database import jellyfin_db, queries as QUEM
 from helper import api, stop, validate, jellyfin_item, library_check, settings, values, Local
@@ -392,12 +394,12 @@ class TVShows(KodiDb):
         else:
             obj['Path'] = "plugin://plugin.video.jellyfin/%s/" % obj['SeriesId']
             params = {
-                'filename': obj['Filename'].encode('utf-8'),
+                'filename': obj['Filename'],
                 'id': obj['Id'],
                 'dbid': obj['EpisodeId'],
                 'mode': "play"
             }
-            obj['Filename'] = "%s?%s" % (obj['Path'], urllib.urlencode(params))
+            obj['Filename'] = "%s?%s" % (obj['Path'], urlencode(params))
 
     def get_show_id(self, obj):
         obj['ShowId'] = self.jellyfin_db.get_item_by_id(*values(obj, QUEM.get_item_series_obj))
diff --git a/jellyfin_kodi/objects/utils.py b/jellyfin_kodi/objects/utils.py
index f0da50c8..cbed4e74 100644
--- a/jellyfin_kodi/objects/utils.py
+++ b/jellyfin_kodi/objects/utils.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
diff --git a/jellyfin_kodi/player.py b/jellyfin_kodi/player.py
index 0d342058..cd758fb1 100644
--- a/jellyfin_kodi/player.py
+++ b/jellyfin_kodi/player.py
@@ -1,12 +1,12 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
 import logging
 import os
 
-import xbmc
-import xbmcvfs
+from kodi_six import xbmc, xbmcvfs
 
 from objects.obj import Objects
 from helper import translate, api, window, settings, dialog, event, silent_catch, JSONRPC
@@ -85,7 +85,7 @@ class Player(xbmc.Player):
                 return
 
         for item in items:
-            if item['Path'] == current_file.decode('utf-8'):
+            if item['Path'] == current_file:
                 items.pop(items.index(item))
 
                 break
@@ -412,13 +412,13 @@ class Player(xbmc.Player):
                 LOG.info("<[ transcode/%s ]", item['Id'])
                 item['Server'].jellyfin.close_transcode(item['DeviceId'])
 
-            path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/temp/").decode('utf-8')
+            path = xbmc.translatePath("special://profile/addon_data/plugin.video.jellyfin/temp/")
 
             if xbmcvfs.exists(path):
                 dirs, files = xbmcvfs.listdir(path)
 
                 for file in files:
-                    xbmcvfs.delete(os.path.join(path, file.decode('utf-8')))
+                    xbmcvfs.delete(os.path.join(path, file))
 
             result = item['Server'].jellyfin.get_item(item['Id']) or {}
 
diff --git a/jellyfin_kodi/setup.py b/jellyfin_kodi/setup.py
index 77cce34b..3305c485 100644
--- a/jellyfin_kodi/setup.py
+++ b/jellyfin_kodi/setup.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
diff --git a/jellyfin_kodi/views.py b/jellyfin_kodi/views.py
index a236ed58..b75063f7 100644
--- a/jellyfin_kodi/views.py
+++ b/jellyfin_kodi/views.py
@@ -1,15 +1,15 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
 import logging
 import os
 import shutil
-import urllib
 import xml.etree.ElementTree as etree
 
-import xbmc
-import xbmcvfs
+from six.moves.urllib.parse import urlencode
+from kodi_six import xbmc, xbmcvfs
 
 from database import Database, jellyfin_db, get_sync, save_sync
 from helper import translate, api, indent, write_xml, window, event
@@ -108,13 +108,13 @@ def verify_kodi_defaults():
 
     ''' Make sure we have the kodi default folder in place.
     '''
-    node_path = xbmc.translatePath("special://profile/library/video").decode('utf-8')
+    node_path = xbmc.translatePath("special://profile/library/video")
 
     if not xbmcvfs.exists(node_path):
         try:
             shutil.copytree(
-                src=xbmc.translatePath("special://xbmc/system/library/video").decode('utf-8'),
-                dst=xbmc.translatePath("special://profile/library/video").decode('utf-8'))
+                src=xbmc.translatePath("special://xbmc/system/library/video"),
+                dst=xbmc.translatePath("special://profile/library/video"))
         except Exception as error:
             LOG.warning(error)
             xbmcvfs.mkdir(node_path)
@@ -129,7 +129,7 @@ def verify_kodi_defaults():
             indent(xml)
             write_xml(etree.tostring(xml, 'UTF-8'), file)
 
-    playlist_path = xbmc.translatePath("special://profile/playlists/video").decode('utf-8')
+    playlist_path = xbmc.translatePath("special://profile/playlists/video")
 
     if not xbmcvfs.exists(playlist_path):
         xbmcvfs.mkdirs(playlist_path)
@@ -223,8 +223,8 @@ class Views(object):
 
         ''' Set up playlists, video nodes, window prop.
         '''
-        node_path = xbmc.translatePath("special://profile/library/video").decode('utf-8')
-        playlist_path = xbmc.translatePath("special://profile/playlists/video").decode('utf-8')
+        node_path = xbmc.translatePath("special://profile/library/video")
+        playlist_path = xbmc.translatePath("special://profile/playlists/video")
         index = 0
 
         with Database('jellyfin') as jellyfindb:
@@ -779,16 +779,16 @@ class Views(object):
 
             window_prop = "Jellyfin.nodes.%s" % index
             window('%s.index' % window_prop, path.replace('all.xml', ""))  # dir
-            window('%s.title' % window_prop, view['Name'].encode('utf-8'))
+            window('%s.title' % window_prop, view['Name'])
             window('%s.content' % window_prop, path)
 
         elif node == 'browse':
 
             window_prop = "Jellyfin.nodes.%s" % index
-            window('%s.title' % window_prop, view['Name'].encode('utf-8'))
+            window('%s.title' % window_prop, view['Name'])
         else:
             window_prop = "Jellyfin.nodes.%s.%s" % (index, node)
-            window('%s.title' % window_prop, node_label.encode('utf-8'))
+            window('%s.title' % window_prop, node_label)
             window('%s.content' % window_prop, path)
 
         window('%s.id' % window_prop, view['Id'])
@@ -831,17 +831,17 @@ class Views(object):
 
             window_prop = "Jellyfin.wnodes.%s" % index
             window('%s.index' % window_prop, path.replace('all.xml', ""))  # dir
-            window('%s.title' % window_prop, view['Name'].encode('utf-8'))
+            window('%s.title' % window_prop, view['Name'])
             window('%s.content' % window_prop, path)
 
         elif node == 'browse':
 
             window_prop = "Jellyfin.wnodes.%s" % index
-            window('%s.title' % window_prop, view['Name'].encode('utf-8'))
+            window('%s.title' % window_prop, view['Name'])
             window('%s.content' % window_prop, path)
         else:
             window_prop = "Jellyfin.wnodes.%s.%s" % (index, node)
-            window('%s.title' % window_prop, node_label.encode('utf-8'))
+            window('%s.title' % window_prop, node_label)
             window('%s.content' % window_prop, path)
 
         window('%s.id' % window_prop, view['Id'])
@@ -881,7 +881,7 @@ class Views(object):
             'mode': "nextepisodes",
             'limit': self.limit
         }
-        return "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
+        return "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
 
     def window_browse(self, view, node=None):
 
@@ -896,7 +896,7 @@ class Views(object):
         if node:
             params['folder'] = node
 
-        return "%s?%s" % ("plugin://plugin.video.jellyfin/", urllib.urlencode(params))
+        return "%s?%s" % ("plugin://plugin.video.jellyfin/", urlencode(params))
 
     def window_clear(self, name=None):
 
@@ -932,23 +932,23 @@ class Views(object):
 
         ''' Remove all jellyfin playlists.
         '''
-        path = xbmc.translatePath("special://profile/playlists/video/").decode('utf-8')
+        path = xbmc.translatePath("special://profile/playlists/video/")
         _, files = xbmcvfs.listdir(path)
         for file in files:
-            if file.decode('utf-8').startswith('jellyfin'):
-                self.delete_playlist(os.path.join(path, file.decode('utf-8')))
+            if file.startswith('jellyfin'):
+                self.delete_playlist(os.path.join(path, file))
 
     def delete_playlist_by_id(self, view_id):
 
         ''' Remove playlist based based on view_id.
         '''
-        path = xbmc.translatePath("special://profile/playlists/video/").decode('utf-8')
+        path = xbmc.translatePath("special://profile/playlists/video/")
         _, files = xbmcvfs.listdir(path)
         for file in files:
-            file = file.decode('utf-8')
+            file = file
 
             if file.startswith('jellyfin') and file.endswith('%s.xsp' % view_id):
-                self.delete_playlist(os.path.join(path, file.decode('utf-8')))
+                self.delete_playlist(os.path.join(path, file))
 
     def delete_node(self, path):
 
@@ -959,37 +959,37 @@ class Views(object):
 
         ''' Remove node and children files.
         '''
-        path = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
+        path = xbmc.translatePath("special://profile/library/video/")
         dirs, files = xbmcvfs.listdir(path)
 
         for file in files:
 
             if file.startswith('jellyfin'):
-                self.delete_node(os.path.join(path, file.decode('utf-8')))
+                self.delete_node(os.path.join(path, file))
 
         for directory in dirs:
 
             if directory.startswith('jellyfin'):
-                _, files = xbmcvfs.listdir(os.path.join(path, directory.decode('utf-8')))
+                _, files = xbmcvfs.listdir(os.path.join(path, directory))
 
                 for file in files:
-                    self.delete_node(os.path.join(path, directory.decode('utf-8'), file.decode('utf-8')))
+                    self.delete_node(os.path.join(path, directory, file))
 
-                xbmcvfs.rmdir(os.path.join(path, directory.decode('utf-8')))
+                xbmcvfs.rmdir(os.path.join(path, directory))
 
     def delete_node_by_id(self, view_id):
 
         ''' Remove node and children files based on view_id.
         '''
-        path = xbmc.translatePath("special://profile/library/video/").decode('utf-8')
+        path = xbmc.translatePath("special://profile/library/video/")
         dirs, files = xbmcvfs.listdir(path)
 
         for directory in dirs:
 
             if directory.startswith('jellyfin') and directory.endswith(view_id):
-                _, files = xbmcvfs.listdir(os.path.join(path, directory.decode('utf-8')))
+                _, files = xbmcvfs.listdir(os.path.join(path, directory))
 
                 for file in files:
-                    self.delete_node(os.path.join(path, directory.decode('utf-8'), file.decode('utf-8')))
+                    self.delete_node(os.path.join(path, directory, file))
 
-                xbmcvfs.rmdir(os.path.join(path, directory.decode('utf-8')))
+                xbmcvfs.rmdir(os.path.join(path, directory))
diff --git a/jellyfin_kodi/webservice.py b/jellyfin_kodi/webservice.py
index dc68e69a..db63a07a 100644
--- a/jellyfin_kodi/webservice.py
+++ b/jellyfin_kodi/webservice.py
@@ -1,14 +1,15 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
-import BaseHTTPServer
+from six.moves import BaseHTTPServer
 import logging
-import httplib
+from six.moves import http_client as httplib
 import threading
-import urlparse
+from six.moves.urllib.parse import parse_qsl
 
-import xbmc
+from kodi_six import xbmc
 
 #################################################################################################
 
@@ -97,7 +98,7 @@ class requestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
             if '?' in path:
                 path = path.split('?', 1)[1]
 
-            params = dict(urlparse.parse_qsl(path))
+            params = dict(parse_qsl(path))
         except Exception:
             params = {}
 
diff --git a/service.py b/service.py
index f807e14f..a1951880 100644
--- a/service.py
+++ b/service.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from __future__ import division, absolute_import, print_function, unicode_literals
 
 #################################################################################################
 
@@ -7,13 +8,12 @@ import os
 import threading
 import sys
 
-import xbmc
-import xbmcaddon
+from kodi_six import xbmc, xbmcaddon
 
 #################################################################################################
 
 __addon__ = xbmcaddon.Addon(id='plugin.video.jellyfin')
-__base__ = xbmc.translatePath(os.path.join(__addon__.getAddonInfo('path'), 'jellyfin_kodi')).decode('utf-8')
+__base__ = xbmc.translatePath(os.path.join(__addon__.getAddonInfo('path'), 'jellyfin_kodi'))
 
 sys.path.insert(0, __base__)
 
@@ -54,11 +54,11 @@ class ServiceManager(threading.Thread):
             LOG.exception(error)
 
             if service is not None:
-
-                if 'ExitService' not in error:
+                # TODO: fix this properly as to not match on str()
+                if 'ExitService' not in str(error):
                     service.shutdown()
 
-                if 'RestartService' in error:
+                if 'RestartService' in str(error):
                     service.reload_objects()
 
             self.exception = error
@@ -79,7 +79,7 @@ if __name__ == "__main__":
             session.start()
             session.join()  # Block until the thread exits.
 
-            if 'RestartService' in session.exception:
+            if 'RestartService' in str(session.exception):
                 continue
 
         except Exception as error: