diff --git a/jellyfin_kodi/helper/__init__.py b/jellyfin_kodi/helper/__init__.py index 77d13ad1..e0496067 100644 --- a/jellyfin_kodi/helper/__init__.py +++ b/jellyfin_kodi/helper/__init__.py @@ -13,8 +13,6 @@ 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 diff --git a/jellyfin_kodi/helper/utils.py b/jellyfin_kodi/helper/utils.py index 55085b3f..9380f589 100644 --- a/jellyfin_kodi/helper/utils.py +++ b/jellyfin_kodi/helper/utils.py @@ -266,44 +266,6 @@ def values(item, keys): return (item[key.replace('{', "").replace('}', "")] if isinstance(key, text_type) and key.startswith('{') else key for key in keys) -def indent(elem, level=0): - - ''' Prettify xml docs. - ''' - try: - i = "\n" + level * " " - if len(elem): - if not elem.text or not elem.text.strip(): - elem.text = i + " " - if not elem.tail or not elem.tail.strip(): - elem.tail = i - for elem in elem: - indent(elem, level + 1) - if not elem.tail or not elem.tail.strip(): - elem.tail = i - else: - if level and (not elem.tail or not elem.tail.strip()): - elem.tail = i - except Exception as error: - LOG.exception(error) - return - - -def write_xml(content, file): - 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(b"'", b'"') - content = re.sub(b"<(.*?)>", replace_apostrophes, content) - - content = content.replace(b'?>', b' standalone="yes" ?>', 1) - infile.write(content) - - def delete_folder(path): ''' Delete objects from kodi cache diff --git a/jellyfin_kodi/helper/xmls.py b/jellyfin_kodi/helper/xmls.py index 62b6058c..015a6ca1 100644 --- a/jellyfin_kodi/helper/xmls.py +++ b/jellyfin_kodi/helper/xmls.py @@ -5,11 +5,11 @@ from __future__ import division, absolute_import, print_function, unicode_litera import logging import os -import xml.etree.ElementTree as etree +from lxml import etree from kodi_six import xbmc -from . import translate, indent, write_xml, dialog, settings +from . import translate, dialog, settings ################################################################################################# @@ -75,8 +75,8 @@ def sources(): except Exception as error: LOG.exception(error) - indent(xml) - write_xml(etree.tostring(xml, 'UTF-8'), file) + tree = etree.ElementTree(xml) + tree.write(file, pretty_print=True) def tvtunes_nfo(path, urls): @@ -95,8 +95,8 @@ def tvtunes_nfo(path, urls): for url in urls: etree.SubElement(xml, 'file').text = url - indent(xml) - write_xml(etree.tostring(xml, 'UTF-8'), path) + tree = etree.ElementTree(xml) + tree.write(path, pretty_print=True) def advanced_settings(): @@ -125,8 +125,8 @@ def advanced_settings(): LOG.warning("cleanonupdate disabled") video.remove(cleanonupdate) - indent(xml) - write_xml(etree.tostring(xml, 'UTF-8'), path) + tree = etree.ElementTree(xml) + tree.write(path, pretty_print=True) dialog("ok", heading="{jellyfin}", line1=translate(33097)) xbmc.executebuiltin('RestartApp') diff --git a/jellyfin_kodi/views.py b/jellyfin_kodi/views.py index b75063f7..b36557b0 100644 --- a/jellyfin_kodi/views.py +++ b/jellyfin_kodi/views.py @@ -6,13 +6,13 @@ from __future__ import division, absolute_import, print_function, unicode_litera import logging import os import shutil -import xml.etree.ElementTree as etree +from lxml import etree 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 +from helper import translate, api, window, event from jellyfin import Jellyfin ################################################################################################# @@ -126,8 +126,8 @@ def verify_kodi_defaults(): xml = etree.parse(file).getroot() xml.set('order', str(17 + index)) - indent(xml) - write_xml(etree.tostring(xml, 'UTF-8'), file) + tree = etree.ElementTree(xml) + tree.write(file, pretty_print=True) playlist_path = xbmc.translatePath("special://profile/playlists/video") @@ -272,7 +272,12 @@ class Views(object): file = os.path.join(path, "jellyfin%s%s.xsp" % (view['Media'], view['Id'])) try: - xml = etree.parse(file).getroot() + if os.path.isfile(file): + xml = etree.parse(file).getroot() + else: + xml = etree.Element('smartplaylist', {'type': view['Media']}) + etree.SubElement(xml, 'name') + etree.SubElement(xml, 'match') except Exception: LOG.warning("Unable to parse file '%s'", file) xml = etree.Element('smartplaylist', {'type': view['Media']}) @@ -292,8 +297,8 @@ class Views(object): rule = etree.SubElement(xml, 'rule', {'field': "tag", 'operator': "is"}) etree.SubElement(rule, 'value').text = view['Tag'] - indent(xml) - write_xml(etree.tostring(xml, 'UTF-8'), file) + tree = etree.ElementTree(xml) + tree.write(file, pretty_print=True) def add_nodes(self, path, view, mixed=False): @@ -316,7 +321,13 @@ class Views(object): file = os.path.join(path, "jellyfin_%s.xml" % view['Tag'].replace(" ", "")) try: - xml = etree.parse(file).getroot() + if os.path.isfile(file): + xml = etree.parse(file).getroot() + else: + xml = self.node_root('folder' if item_type == 'favorites' and view['Media'] == 'episodes' else 'filter', index) + etree.SubElement(xml, 'label') + etree.SubElement(xml, 'match') + etree.SubElement(xml, 'content') except Exception: LOG.warning("Unable to parse file '%s'", file) xml = self.node_root('folder' if item_type == 'favorites' and view['Media'] == 'episodes' else 'filter', index) @@ -348,8 +359,8 @@ class Views(object): else: self.node_all(xml) - indent(xml) - write_xml(etree.tostring(xml, 'UTF-8'), file) + tree = etree.ElementTree(xml) + tree.write(file, pretty_print=True) def node_root(self, root, index): @@ -372,8 +383,14 @@ class Views(object): index = self.sync['SortedViews'].index(view['Id']) try: - xml = etree.parse(file).getroot() - xml.set('order', str(index)) + if os.path.isfile(file): + xml = etree.parse(file).getroot() + xml.set('order', str(index)) + else: + xml = self.node_root('filter', index) + etree.SubElement(xml, 'label') + etree.SubElement(xml, 'match') + etree.SubElement(xml, 'content') except Exception as error: LOG.exception(error) xml = self.node_root('main', index) @@ -382,8 +399,8 @@ class Views(object): label = xml.find('label') label.text = view['Name'] if not mixed else "%s (%s)" % (view['Name'], translate(view['Media'])) - indent(xml) - write_xml(etree.tostring(xml, 'UTF-8'), file) + tree = etree.ElementTree(xml) + tree.write(file, pretty_print=True) def node(self, folder, view): @@ -412,7 +429,14 @@ class Views(object): def add_node(self, index, file, view, node, name): try: - xml = etree.parse(file).getroot() + if os.path.isfile(file): + xml = etree.parse(file).getroot() + else: + xml = self.node_root('filter', index) + etree.SubElement(xml, 'label') + etree.SubElement(xml, 'match') + etree.SubElement(xml, 'content') + except Exception: LOG.warning("Unable to parse file '%s'", file) xml = self.node_root('filter', index) @@ -437,13 +461,18 @@ class Views(object): etree.SubElement(rule, 'value').text = view['Tag'] getattr(self, 'node_' + node)(xml) # get node function based on node type - indent(xml) - write_xml(etree.tostring(xml, 'UTF-8'), file) + tree = etree.ElementTree(xml) + tree.write(file, pretty_print=True) def add_dynamic_node(self, index, file, view, node, name, path): try: - xml = etree.parse(file).getroot() + if os.path.isfile(file): + xml = etree.parse(file).getroot() + else: + xml = self.node_root('filter', index) + etree.SubElement(xml, 'label') + etree.SubElement(xml, 'content') except Exception: LOG.warning("Unable to parse file '%s'", file) xml = self.node_root('folder', index) @@ -454,8 +483,8 @@ class Views(object): label.text = name getattr(self, 'node_' + node)(xml, path) - indent(xml) - write_xml(etree.tostring(xml, 'UTF-8'), file) + tree = etree.ElementTree(xml) + tree.write(file, pretty_print=True) def node_all(self, root):