diff --git a/README.md b/README.md index 98cd9589..b66adf8e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![stable version](https://img.shields.io/badge/stable_version-2.7.8-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip) -[![beta version](https://img.shields.io/badge/beta_version-2.7.8-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip) +[![stable version](https://img.shields.io/badge/stable_version-2.7.9-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip) +[![beta version](https://img.shields.io/badge/beta_version-2.7.9-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip) [![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation) [![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq) diff --git a/addon.xml b/addon.xml index 0a053171..3d543290 100644 --- a/addon.xml +++ b/addon.xml @@ -1,5 +1,5 @@ - + @@ -77,7 +77,13 @@ Нативна інтеграція Plex в Kodi Підключає Kodi до серверу Plex. Цей плагін передбачає, що ви керуєте всіма своїми відео за допомогою Plex (і ніяк не Kodi). Ви можете втратити дані, які вже зберігаються у відео та музичних БД Kodi (оскільки цей плагін безпосередньо їх змінює). Використовуйте на свій страх і ризик! Використовуйте на свій ризик - version 2.7.8: + version 2.7.9: +- Wait for PKC to authorize before loading widgets +- Fix UnicodeDecodeError for libraries with non-ASCII paths +- Fix TypeError on Kodi start +- Fix Kodi Masterlock for nfs paths (requires restart) + +version 2.7.8: - Fix widgets not working in some cases like NVidia Shield - Fix appending of show title, season and episode number - Fix node paths for skins diff --git a/changelog.txt b/changelog.txt index 01aa8610..e43ace09 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,9 @@ +version 2.7.9: +- Wait for PKC to authorize before loading widgets +- Fix UnicodeDecodeError for libraries with non-ASCII paths +- Fix TypeError on Kodi start +- Fix Kodi Masterlock for nfs paths (requires restart) + version 2.7.8: - Fix widgets not working in some cases like NVidia Shield - Fix appending of show title, season and episode number diff --git a/resources/lib/entrypoint.py b/resources/lib/entrypoint.py index 7ffdc36b..0c41266b 100644 --- a/resources/lib/entrypoint.py +++ b/resources/lib/entrypoint.py @@ -57,6 +57,30 @@ def guess_content_type(): return content_type +def _wait_for_auth(): + """ + Call to be sure that PKC is authenticated, e.g. for widgets on Kodi startup. + Will wait for at most 30s, then fail if not authenticated. Will set + xbmcplugin.endOfDirectory(int(argv[1]), False) if failed + + WARNING - this will potentially stall the shutdown of Kodi since we cannot + poll xbmc.Monitor().abortRequested() or waitForAbort() or + xbmc.abortRequested + """ + counter = 0 + startupdelay = int(utils.settings('startupDelay') or 0) + # Wait for + 10 seconds at most + startupdelay = 10 * startupdelay + 100 + while utils.window('plex_authenticated') != 'true': + counter += 1 + if counter == startupdelay: + LOG.error('Aborting view, we were not authenticated for PMS') + xbmcplugin.endOfDirectory(int(sys.argv[1]), False) + return False + xbmc.sleep(100) + return True + + def directory_item(label, path, folder=True): """ Adds a xbmcplugin.addDirectoryItem() directory itemlistitem @@ -228,6 +252,8 @@ def get_video_files(plex_id, params): if plex_id is None: LOG.info('No Plex ID found, abort getting Extras') return xbmcplugin.endOfDirectory(int(sys.argv[1])) + if not _wait_for_auth(): + return xbmcplugin.endOfDirectory(int(sys.argv[1]), False) app.init(entrypoint=True) item = PF.GetPlexMetadata(plex_id) try: @@ -285,6 +311,8 @@ def extra_fanart(plex_id, plex_path): # because of the caching system in xbmc fanart_dir = path_ops.translate_path("special://thumbnails/plex/%s/" % plex_id) + if not _wait_for_auth(): + return xbmcplugin.endOfDirectory(int(sys.argv[1]), False) if not path_ops.exists(fanart_dir): # Download the images to the cache directory path_ops.makedirs(fanart_dir) @@ -329,6 +357,8 @@ def playlists(content_type): """ content_type = content_type or guess_content_type() LOG.debug('Listing Plex %s playlists', content_type) + if not _wait_for_auth(): + return xbmcplugin.endOfDirectory(int(sys.argv[1]), False) app.init(entrypoint=True) from .playlists.pms import all_playlists xml = all_playlists() @@ -352,6 +382,8 @@ def hub(content_type): """ content_type = content_type or guess_content_type() LOG.debug('Showing Plex Hub entries for %s', content_type) + if not _wait_for_auth(): + return xbmcplugin.endOfDirectory(int(sys.argv[1]), False) app.init(entrypoint=True) xml = PF.get_plex_hub() try: @@ -385,6 +417,8 @@ def watchlater(): """ Listing for plex.tv Watch Later section (if signed in to plex.tv) """ + if not _wait_for_auth(): + return xbmcplugin.endOfDirectory(int(sys.argv[1]), False) if utils.window('plex_token') == '': LOG.error('No watch later - not signed in to plex.tv') return xbmcplugin.endOfDirectory(int(sys.argv[1]), False) @@ -412,6 +446,8 @@ def browse_plex(key=None, plex_type=None, section_id=None, synched=True, """ LOG.debug('Browsing to key %s, section %s, plex_type: %s, synched: %s, ' 'prompt "%s"', key, section_id, plex_type, synched, prompt) + if not _wait_for_auth(): + return xbmcplugin.endOfDirectory(int(sys.argv[1]), False) app.init(entrypoint=True) if prompt: prompt = utils.dialog('input', prompt) @@ -429,6 +465,7 @@ def browse_plex(key=None, plex_type=None, section_id=None, synched=True, except AttributeError: LOG.error('Could not browse to key %s, section %s', key, section_id) + return show_listing(xml, plex_type, section_id, synched, key) @@ -436,6 +473,8 @@ def extras(plex_id): """ Lists all extras for plex_id """ + if not _wait_for_auth(): + return xbmcplugin.endOfDirectory(int(sys.argv[1]), False) app.init(entrypoint=True) xml = PF.GetPlexMetadata(plex_id) try: diff --git a/resources/lib/initialsetup.py b/resources/lib/initialsetup.py index 884832c1..c9a87b8b 100644 --- a/resources/lib/initialsetup.py +++ b/resources/lib/initialsetup.py @@ -466,6 +466,31 @@ class InitialSetup(object): server['machineIdentifier'], server['ip'], server['port'], server['scheme']) + @staticmethod + def _add_sources(root, extension): + changed = False + count = 2 + for source in root.findall('.//path'): + if source.text == extension: + count -= 1 + if count == 0: + # sources already set + break + else: + # Missing smb:// occurences, re-add. + changed = True + for _ in range(0, count): + source = etree.SubElement(root, 'source') + etree.SubElement( + source, + 'name').text = "PlexKodiConnect Masterlock Hack" + etree.SubElement( + source, + 'path', + {'pathversion': "1"}).text = extension + etree.SubElement(source, 'allowsharing').text = "true" + return changed + def setup(self): """ Initial setup. Run once upon startup. @@ -507,28 +532,13 @@ class InitialSetup(object): with utils.XmlKodiSetting('sources.xml', force_create=True, top_element='sources') as xml: - root = xml.set_setting(['video']) - count = 2 - for source in root.findall('.//path'): - if source.text == "smb://": - count -= 1 - if count == 0: - # sources already set - break - else: - # Missing smb:// occurences, re-add. - for _ in range(0, count): - source = etree.SubElement(root, 'source') - etree.SubElement( - source, - 'name').text = "PlexKodiConnect Masterlock Hack" - etree.SubElement( - source, - 'path', - {'pathversion': "1"}).text = "smb://" - etree.SubElement(source, 'allowsharing').text = "true" - if reboot is False: - reboot = xml.write_xml + changed = False + for extension in ('smb://', 'nfs://'): + root = xml.set_setting(['video']) + changed = self._add_sources(root, extension) or changed + if changed: + xml.write_xml = True + reboot = True except utils.ParseError: pass diff --git a/resources/lib/widgets.py b/resources/lib/widgets.py index 5ee9facb..9c2018d2 100644 --- a/resources/lib/widgets.py +++ b/resources/lib/widgets.py @@ -57,24 +57,28 @@ def process_method_on_list(method_to_run, items): def get_clean_image(image): - '''helper to strip all kodi tags/formatting of an image path/url''' + ''' + helper to strip all kodi tags/formatting of an image path/url + Pass in either unicode or str; returns unicode + ''' if not image: return "" - if "music@" in image: + if not isinstance(image, str): + image = image.encode('utf-8') + if b"music@" in image: # fix for embedded images - thumbcache = xbmc.getCacheThumbName(image).replace(".tbn", ".jpg") - thumbcache = "special://thumbnails/%s/%s" % (thumbcache[0], thumbcache) + thumbcache = xbmc.getCacheThumbName(image) + thumbcache = thumbcache.replace(b".tbn", b".jpg") + thumbcache = b"special://thumbnails/%s/%s" % (thumbcache[0], thumbcache) if not xbmcvfs.exists(thumbcache): xbmcvfs.copy(image, thumbcache) image = thumbcache - if image and "image://" in image: - image = image.replace("image://", "") - image = urllib.unquote(image.encode("utf-8")) - if image.endswith("/"): + if image and b"image://" in image: + image = image.replace(b"image://", b"") + image = urllib.unquote(image) + if image.endswith(b"/"): image = image[:-1] - if not isinstance(image, unicode): - image = image.decode("utf8") - return image + return image.decode('utf-8') def generate_item(xml_element):