From ee7672a0a1efaf22a641c4aa910cab4178bc91e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Odd=20Str=C3=A5b=C3=B8?= Date: Thu, 3 Sep 2020 22:36:34 +0200 Subject: [PATCH] Handle empty files in profile video XMLs --- jellyfin_kodi/views.py | 59 +++++++++++++++++++++++++++----------- tests/test_helper_utils.py | 18 ++++++++++++ 2 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 tests/test_helper_utils.py diff --git a/jellyfin_kodi/views.py b/jellyfin_kodi/views.py index 3c3c1a7a..7345c299 100644 --- a/jellyfin_kodi/views.py +++ b/jellyfin_kodi/views.py @@ -105,29 +105,56 @@ DYNNODES = { def verify_kodi_defaults(): - ''' Make sure we have the kodi default folder in place. ''' - node_path = xbmc.translatePath("special://profile/library/video") - if not os.path.exists(node_path): - try: - shutil.copytree( - 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) + source_base_path = xbmc.translatePath("special://xbmc/system/library/video") + dest_base_path = xbmc.translatePath("special://profile/library/video") + # Make sure the files exist in the local profile. + # TODO: Investigate why this is needed. + # I would think Kodi pulls data from the default profile + # if we don't do this. + for source_path, dirs, files in os.walk(source_base_path): + relative_path = os.path.relpath(source_path, source_base_path) + dest_path = os.path.join(dest_base_path, relative_path) + + if not os.path.exists(dest_path): + os.mkdir(dest_path) + + for file_name in files: + dest_file = os.path.join(dest_path, file_name) + copy = False + + if not os.path.exists(dest_file): + copy = True + elif os.path.splitext(file_name)[1].lower() == '.xml': + try: + etree.parse(dest_file) + except etree.ParseError: + LOG.warning("Unable to parse `{}`, recovering from default.".format(dest_file)) + copy = True + + if copy: + source_file = os.path.join(source_path, file_name) + LOG.debug("Copying `{}` -> `{}`".format(source_file, dest_file)) + xbmcvfs.copy(source_file, dest_file) + + # This code seems to enforce a fixed ordering. + # Is it really desirable to force this on users? + # The default (system wide) order is [10, 20, 30] in Kodi 19. for index, node in enumerate(['movies', 'tvshows', 'musicvideos']): - file = os.path.join(node_path, node, "index.xml") + file_name = os.path.join(dest_base_path, node, "index.xml") - if xbmcvfs.exists(file): + if xbmcvfs.exists(file_name): + try: + tree = etree.parse(file_name) + except etree.ParseError: + LOG.error("Unable to parse `{}`".format(file_name)) + LOG.exception("We ensured the file was OK above, something is wrong!") - xml = etree.parse(file).getroot() - xml.set('order', str(17 + index)) - tree = etree.ElementTree(xml) - tree.write(file) + tree.getroot().set('order', str(17 + index)) + tree.write(file_name) playlist_path = xbmc.translatePath("special://profile/playlists/video") diff --git a/tests/test_helper_utils.py b/tests/test_helper_utils.py new file mode 100644 index 00000000..366ea5a9 --- /dev/null +++ b/tests/test_helper_utils.py @@ -0,0 +1,18 @@ +import sys + +import pytest + +sys.path.insert(0, 'jellyfin_kodi') + +from helper.utils import values # noqa: E402 + +item1 = {'foo': 123, 'bar': 456, 'baz': 789} + + +@pytest.mark.parametrize("item,keys,expected", [ + (item1, ['{foo}', '{baz}'], [123, 789]), + (item1, ['{foo}', 'bar'], [123, 'bar']), + (item1, ['{foo}', 'bar', 321], [123, 'bar', 321]), +]) +def test_values(item, keys, expected): + assert list(values(item, keys)) == expected