From ad5744f4359718392c191e15db783bcd75ec317b Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Sat, 20 May 2017 20:24:47 +0200 Subject: [PATCH] Fix for Windows usernames with non-ASCII chars - Fixes #286 --- resources/lib/PlexAPI.py | 8 +++--- resources/lib/artwork.py | 6 ++--- resources/lib/entrypoint.py | 16 +++++------ resources/lib/librarysync.py | 4 +-- resources/lib/utils.py | 52 ++++++++++++++++-------------------- resources/lib/variables.py | 23 ++++++++-------- resources/lib/videonodes.py | 33 +++++++++++++---------- service.py | 9 ++++--- 8 files changed, 76 insertions(+), 75 deletions(-) diff --git a/resources/lib/PlexAPI.py b/resources/lib/PlexAPI.py index 7a6ce0c6..ac205fbc 100644 --- a/resources/lib/PlexAPI.py +++ b/resources/lib/PlexAPI.py @@ -2553,14 +2553,14 @@ class API(): if "\\" in path: if not path.endswith('\\'): # Add the missing backslash - check = exists_dir(tryEncode(path + "\\")) + check = exists_dir(path + "\\") else: - check = exists_dir(tryEncode(path)) + check = exists_dir(path) else: if not path.endswith('/'): - check = exists_dir(tryEncode(path + "/")) + check = exists_dir(path + "/") else: - check = exists_dir(tryEncode(path)) + check = exists_dir(path) if not check: if forceCheck is False: diff --git a/resources/lib/artwork.py b/resources/lib/artwork.py index 54e1d9a3..1a310921 100644 --- a/resources/lib/artwork.py +++ b/resources/lib/artwork.py @@ -13,7 +13,7 @@ from xbmc import executeJSONRPC, sleep, translatePath from xbmcvfs import exists from utils import window, settings, language as lang, kodiSQL, tryEncode, \ - thread_methods, dialog, exists_dir + thread_methods, dialog, exists_dir, tryDecode # Disable annoying requests warnings import requests.packages.urllib3 @@ -222,7 +222,7 @@ class Artwork(): if dialog('yesno', "Image Texture Cache", lang(39251)): log.info("Resetting all cache data first") # Remove all existing textures first - path = translatePath("special://thumbnails/") + path = tryDecode(translatePath("special://thumbnails/")) if exists_dir(path): rmtree(path, ignore_errors=True) @@ -423,7 +423,7 @@ class Artwork(): path = translatePath("special://thumbnails/%s" % cachedurl) log.debug("Deleting cached thumbnail: %s" % path) if exists(path): - rmtree(path, ignore_errors=True) + rmtree(tryDecode(path), ignore_errors=True) cursor.execute("DELETE FROM texture WHERE url = ?", (url,)) connection.commit() finally: diff --git a/resources/lib/entrypoint.py b/resources/lib/entrypoint.py index f690bc01..4745afea 100644 --- a/resources/lib/entrypoint.py +++ b/resources/lib/entrypoint.py @@ -12,7 +12,7 @@ from xbmc import sleep, executebuiltin, translatePath from xbmcgui import ListItem from utils import window, settings, language as lang, dialog, tryEncode, \ - CatchExceptions, JSONRPC, exists_dir, plex_command + CatchExceptions, JSONRPC, exists_dir, plex_command, tryDecode import downloadutils from PlexFunctions import GetPlexMetadata, GetPlexSectionResults, \ @@ -489,7 +489,6 @@ def getVideoFiles(plexId, params): except: log.error('Could not get file path for item %s' % plexId) return xbmcplugin.endOfDirectory(HANDLE) - path = tryEncode(path) # Assign network protocol if path.startswith('\\\\'): path = path.replace('\\\\', 'smb://') @@ -502,14 +501,14 @@ def getVideoFiles(plexId, params): if exists_dir(path): for root, dirs, files in walk(path): for directory in dirs: - item_path = join(root, directory) + item_path = tryEncode(join(root, directory)) li = ListItem(item_path, path=item_path) xbmcplugin.addDirectoryItem(handle=HANDLE, url=item_path, listitem=li, isFolder=True) for file in files: - item_path = join(root, file) + item_path = tryEncode(join(root, file)) li = ListItem(item_path, path=item_path) xbmcplugin.addDirectoryItem(handle=HANDLE, url=file, @@ -537,7 +536,8 @@ def getExtraFanArt(plexid, plexPath): # We need to store the images locally for this to work # because of the caching system in xbmc - fanartDir = translatePath("special://thumbnails/plex/%s/" % plexid) + fanartDir = tryDecode(translatePath( + "special://thumbnails/plex/%s/" % plexid)) if not exists_dir(fanartDir): # Download the images to the cache directory makedirs(fanartDir) @@ -550,19 +550,19 @@ def getExtraFanArt(plexid, plexPath): backdrops = api.getAllArtwork()['Backdrop'] for count, backdrop in enumerate(backdrops): # Same ordering as in artwork - fanartFile = join(fanartDir, "fanart%.3d.jpg" % count) + fanartFile = tryEncode(join(fanartDir, "fanart%.3d.jpg" % count)) li = ListItem("%.3d" % count, path=fanartFile) xbmcplugin.addDirectoryItem( handle=HANDLE, url=fanartFile, listitem=li) - copyfile(backdrop, fanartFile) + copyfile(backdrop, tryDecode(fanartFile)) else: log.info("Found cached backdrop.") # Use existing cached images for root, dirs, files in walk(fanartDir): for file in files: - fanartFile = join(root, file) + fanartFile = tryEncode(join(root, file)) li = ListItem(file, path=fanartFile) xbmcplugin.addDirectoryItem(handle=HANDLE, url=fanartFile, diff --git a/resources/lib/librarysync.py b/resources/lib/librarysync.py index 336d5d5e..6d4571ed 100644 --- a/resources/lib/librarysync.py +++ b/resources/lib/librarysync.py @@ -12,7 +12,7 @@ from xbmcvfs import exists from utils import window, settings, getUnixTimestamp, sourcesXML,\ thread_methods, create_actor_db_index, dialog, LogTime, getScreensaver,\ setScreensaver, playlistXSP, language as lang, DateToKodi, reset,\ - tryDecode, deletePlaylists, deleteNodes + tryDecode, deletePlaylists, deleteNodes, tryEncode import downloadutils import itemtypes import plexdb_functions as plexdb @@ -1507,7 +1507,7 @@ class LibrarySync(Thread): # Also runs when first installed # Verify the video database can be found videoDb = v.DB_VIDEO_PATH - if not exists(videoDb): + if not exists(tryEncode(videoDb)): # Database does not exists log.error("The current Kodi version is incompatible " "to know which Kodi versions are supported.") diff --git a/resources/lib/utils.py b/resources/lib/utils.py index bfbc983e..396488ea 100644 --- a/resources/lib/utils.py +++ b/resources/lib/utils.py @@ -111,12 +111,12 @@ def exists_dir(path): Safe way to check whether the directory path exists already (broken in Kodi <17) - Feed with encoded string + Feed with encoded string or unicode """ if KODIVERSION >= 17: - answ = exists(path) + answ = exists(tryEncode(path)) else: - dummyfile = join(path, 'dummyfile.txt') + dummyfile = join(tryDecode(path), 'dummyfile.txt') try: with open(dummyfile, 'w') as f: f.write('text') @@ -125,7 +125,7 @@ def exists_dir(path): answ = 0 else: # Folder exists. Delete file again. - delete(dummyfile) + delete(tryEncode(dummyfile)) answ = 1 return answ @@ -401,7 +401,7 @@ def reset(): # Remove all existing textures first path = xbmc.translatePath("special://thumbnails/") if exists(path): - rmtree(path, ignore_errors=True) + rmtree(tryDecode(path), ignore_errors=True) # remove all existing data from texture DB connection = kodiSQL('texture') cursor = connection.cursor() @@ -425,7 +425,7 @@ def reset(): line1=language(39603)): # Delete the settings addon = xbmcaddon.Addon() - addondir = xbmc.translatePath(addon.getAddonInfo('profile')) + addondir = tryDecode(xbmc.translatePath(addon.getAddonInfo('profile'))) dataPath = "%ssettings.xml" % addondir log.info("Deleting: settings.xml") remove(dataPath) @@ -688,7 +688,7 @@ def sourcesXML(): def passwordsXML(): # To add network credentials - path = xbmc.translatePath("special://userdata/") + path = tryDecode(xbmc.translatePath("special://userdata/")) xmlpath = "%spasswords.xml" % path try: @@ -801,7 +801,7 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False): """ Feed with tagname as unicode """ - path = xbmc.translatePath("special://profile/playlists/video/") + path = tryDecode(xbmc.translatePath("special://profile/playlists/video/")) if viewtype == "mixed": plname = "%s - %s" % (tagname, mediatype) xsppath = "%sPlex %s - %s.xsp" % (path, viewid, mediatype) @@ -810,12 +810,12 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False): xsppath = "%sPlex %s.xsp" % (path, viewid) # Create the playlist directory - if not exists(path): + if not exists(tryEncode(path)): log.info("Creating directory: %s" % path) makedirs(path) # Only add the playlist if it doesn't already exists - if exists(xsppath): + if exists(tryEncode(xsppath)): log.info('Path %s does exist' % xsppath) if delete: remove(xsppath) @@ -830,28 +830,22 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False): 'show': 'tvshows' } log.info("Writing playlist file to: %s" % xsppath) - try: - with open(xsppath, 'wb'): - tryEncode( - '\n' - '\n\t' - 'Plex %s\n\t' - 'all\n\t' - '\n\t\t' - '%s\n\t' - '\n' - '\n' - % (itemtypes.get(mediatype, mediatype), plname, tagname)) - except Exception as e: - log.error("Failed to create playlist: %s" % xsppath) - import traceback - log.exception("Traceback:\n%s" % traceback.format_exc()) - return + with open(xsppath, 'wb'): + tryEncode( + '\n' + '\n\t' + 'Plex %s\n\t' + 'all\n\t' + '\n\t\t' + '%s\n\t' + '\n' + '\n' + % (itemtypes.get(mediatype, mediatype), plname, tagname)) log.info("Successfully added playlist: %s" % tagname) def deletePlaylists(): # Clean up the playlists - path = xbmc.translatePath("special://profile/playlists/video/") + path = tryDecode(xbmc.translatePath("special://profile/playlists/video/")) for root, _, files in walk(path): for file in files: if file.startswith('Plex'): @@ -859,7 +853,7 @@ def deletePlaylists(): def deleteNodes(): # Clean up video nodes - path = xbmc.translatePath("special://profile/library/video/") + path = tryDecode(xbmc.translatePath("special://profile/library/video/")) for root, dirs, _ in walk(path): for directory in dirs: if directory.startswith('Plex-'): diff --git a/resources/lib/variables.py b/resources/lib/variables.py index fa3b8be5..95042299 100644 --- a/resources/lib/variables.py +++ b/resources/lib/variables.py @@ -2,7 +2,8 @@ import xbmc from xbmcaddon import Addon -# Paths are in string, not unicode! +# Paths are in unicode, otherwise Windows will throw fits +# For any file operations with KODI function, use encoded strings! def tryDecode(string, encoding='utf-8'): @@ -29,7 +30,7 @@ ADDON_VERSION = _ADDON.getAddonInfo('version') KODILANGUAGE = xbmc.getLanguage(xbmc.ISO_639_1) KODIVERSION = int(xbmc.getInfoLabel("System.BuildVersion")[:2]) KODILONGVERSION = xbmc.getInfoLabel('System.BuildVersion') -KODI_PROFILE = xbmc.translatePath("special://profile") +KODI_PROFILE = tryDecode(xbmc.translatePath("special://profile")) if xbmc.getCondVisibility('system.platform.osx'): PLATFORM = "MacOSX" @@ -70,8 +71,8 @@ _DB_VIDEO_VERSION = { 17: 107, # Krypton 18: 108 # Leia } -DB_VIDEO_PATH = xbmc.translatePath( - "special://database/MyVideos%s.db" % _DB_VIDEO_VERSION[KODIVERSION]) +DB_VIDEO_PATH = tryDecode(xbmc.translatePath( + "special://database/MyVideos%s.db" % _DB_VIDEO_VERSION[KODIVERSION])) _DB_MUSIC_VERSION = { 13: 46, # Gotham @@ -81,8 +82,8 @@ _DB_MUSIC_VERSION = { 17: 60, # Krypton 18: 62 # Leia } -DB_MUSIC_PATH = xbmc.translatePath( - "special://database/MyMusic%s.db" % _DB_MUSIC_VERSION[KODIVERSION]) +DB_MUSIC_PATH = tryDecode(xbmc.translatePath( + "special://database/MyMusic%s.db" % _DB_MUSIC_VERSION[KODIVERSION])) _DB_TEXTURE_VERSION = { 13: 13, # Gotham @@ -92,13 +93,13 @@ _DB_TEXTURE_VERSION = { 17: 13, # Krypton 18: 13 # Leia } -DB_TEXTURE_PATH = xbmc.translatePath( - "special://database/Textures%s.db" % _DB_TEXTURE_VERSION[KODIVERSION]) +DB_TEXTURE_PATH = tryDecode(xbmc.translatePath( + "special://database/Textures%s.db" % _DB_TEXTURE_VERSION[KODIVERSION])) -DB_PLEX_PATH = xbmc.translatePath("special://database/plex.db") +DB_PLEX_PATH = tryDecode(xbmc.translatePath("special://database/plex.db")) -EXTERNAL_SUBTITLE_TEMP_PATH = xbmc.translatePath( - "special://profile/addon_data/%s/temp/" % ADDON_ID) +EXTERNAL_SUBTITLE_TEMP_PATH = tryDecode(xbmc.translatePath( + "special://profile/addon_data/%s/temp/" % ADDON_ID)) # Multiply Plex time by this factor to receive Kodi time diff --git a/resources/lib/videonodes.py b/resources/lib/videonodes.py index b3c10ee4..12aed035 100644 --- a/resources/lib/videonodes.py +++ b/resources/lib/videonodes.py @@ -3,14 +3,13 @@ import logging from shutil import copytree import xml.etree.ElementTree as etree -from os import remove, listdir, makedirs -from os.path import isfile, join +from os import makedirs import xbmc from xbmcvfs import exists from utils import window, settings, language as lang, tryEncode, indent, \ - normalize_nodes, exists_dir + normalize_nodes, exists_dir, tryDecode import variables as v ############################################################################### @@ -63,9 +62,10 @@ class VideoNodes(object): dirname = viewid # Returns strings - path = xbmc.translatePath("special://profile/library/video/") - nodepath = xbmc.translatePath( - "special://profile/library/video/Plex-%s/" % dirname) + path = tryDecode(xbmc.translatePath( + "special://profile/library/video/")) + nodepath = tryDecode(xbmc.translatePath( + "special://profile/library/video/Plex-%s/" % dirname)) if delete: if exists_dir(nodepath): @@ -77,8 +77,10 @@ class VideoNodes(object): # Verify the video directory if not exists_dir(path): copytree( - src=xbmc.translatePath("special://xbmc/system/library/video"), - dst=xbmc.translatePath("special://profile/library/video")) + src=tryDecode(xbmc.translatePath( + "special://xbmc/system/library/video")), + dst=tryDecode(xbmc.translatePath( + "special://profile/library/video"))) # Create the node directory if mediatype != "photos": @@ -290,7 +292,7 @@ class VideoNodes(object): # To do: add our photos nodes to kodi picture sources somehow continue - if exists(nodeXML): + if exists(tryEncode(nodeXML)): # Don't recreate xml if already exists continue @@ -377,8 +379,9 @@ class VideoNodes(object): def singleNode(self, indexnumber, tagname, mediatype, itemtype): tagname = tryEncode(tagname) - cleantagname = normalize_nodes(tagname) - nodepath = xbmc.translatePath("special://profile/library/video/") + cleantagname = tryDecode(normalize_nodes(tagname)) + nodepath = tryDecode(xbmc.translatePath( + "special://profile/library/video/")) nodeXML = "%splex_%s.xml" % (nodepath, cleantagname) path = "library://video/plex_%s.xml" % cleantagname if v.KODIVERSION >= 17: @@ -391,8 +394,10 @@ class VideoNodes(object): if not exists_dir(nodepath): # We need to copy over the default items copytree( - src=xbmc.translatePath("special://xbmc/system/library/video"), - dst=xbmc.translatePath("special://profile/library/video")) + src=tryDecode(xbmc.translatePath( + "special://xbmc/system/library/video")), + dst=tryDecode(xbmc.translatePath( + "special://profile/library/video"))) labels = { 'Favorite movies': 30180, @@ -406,7 +411,7 @@ class VideoNodes(object): window('%s.content' % embynode, value=path) window('%s.type' % embynode, value=itemtype) - if exists(nodeXML): + if exists(tryEncode(nodeXML)): # Don't recreate xml if already exists return diff --git a/service.py b/service.py index ec8ca54a..601a2a59 100644 --- a/service.py +++ b/service.py @@ -30,7 +30,8 @@ sys_path.append(_base_resource) ############################################################################### -from utils import settings, window, language as lang, dialog, tryEncode +from utils import settings, window, language as lang, dialog, tryEncode, \ + tryDecode from userclient import UserClient import initialsetup from kodimonitor import KodiMonitor @@ -86,7 +87,7 @@ class Service(): window('plex_logLevel', value=str(logLevel)) window('plex_kodiProfile', - value=translatePath("special://profile")) + value=tryDecode(translatePath("special://profile"))) window('plex_context', value='true' if settings('enableContext') == "true" else "") window('fetch_pms_item_number', @@ -170,12 +171,12 @@ class Service(): counter = 0 while not __stop_PKC(): - if tryEncode(window('plex_kodiProfile')) != kodiProfile: + if window('plex_kodiProfile') != kodiProfile: # Profile change happened, terminate this thread and others log.warn("Kodi profile was: %s and changed to: %s. " "Terminating old PlexKodiConnect thread." % (kodiProfile, - tryEncode(window('plex_kodiProfile')))) + window('plex_kodiProfile'))) break # Before proceeding, need to make sure: