mirror of
https://github.com/rembo10/headphones.git
synced 2026-03-21 20:29:27 +00:00
- Delete from Releases when deleting artist/album - Searcher - Size limits not quite working - Searcher - 1st newznab used even if disabled - Rutracker search stopped working for me, fixed by updating Beautiful Soup. Moved bs4 and html5lib to lib and ensured (I think) it’s imported from the right place
338 lines
12 KiB
Python
338 lines
12 KiB
Python
from __future__ import absolute_import, division, unicode_literals
|
|
from six import text_type
|
|
|
|
import re
|
|
|
|
from . import _base
|
|
from .. import ihatexml
|
|
from .. import constants
|
|
from ..constants import namespaces
|
|
from ..utils import moduleFactoryFactory
|
|
|
|
tag_regexp = re.compile("{([^}]*)}(.*)")
|
|
|
|
|
|
def getETreeBuilder(ElementTreeImplementation, fullTree=False):
|
|
ElementTree = ElementTreeImplementation
|
|
ElementTreeCommentType = ElementTree.Comment("asd").tag
|
|
|
|
class Element(_base.Node):
|
|
def __init__(self, name, namespace=None):
|
|
self._name = name
|
|
self._namespace = namespace
|
|
self._element = ElementTree.Element(self._getETreeTag(name,
|
|
namespace))
|
|
if namespace is None:
|
|
self.nameTuple = namespaces["html"], self._name
|
|
else:
|
|
self.nameTuple = self._namespace, self._name
|
|
self.parent = None
|
|
self._childNodes = []
|
|
self._flags = []
|
|
|
|
def _getETreeTag(self, name, namespace):
|
|
if namespace is None:
|
|
etree_tag = name
|
|
else:
|
|
etree_tag = "{%s}%s" % (namespace, name)
|
|
return etree_tag
|
|
|
|
def _setName(self, name):
|
|
self._name = name
|
|
self._element.tag = self._getETreeTag(self._name, self._namespace)
|
|
|
|
def _getName(self):
|
|
return self._name
|
|
|
|
name = property(_getName, _setName)
|
|
|
|
def _setNamespace(self, namespace):
|
|
self._namespace = namespace
|
|
self._element.tag = self._getETreeTag(self._name, self._namespace)
|
|
|
|
def _getNamespace(self):
|
|
return self._namespace
|
|
|
|
namespace = property(_getNamespace, _setNamespace)
|
|
|
|
def _getAttributes(self):
|
|
return self._element.attrib
|
|
|
|
def _setAttributes(self, attributes):
|
|
# Delete existing attributes first
|
|
# XXX - there may be a better way to do this...
|
|
for key in list(self._element.attrib.keys()):
|
|
del self._element.attrib[key]
|
|
for key, value in attributes.items():
|
|
if isinstance(key, tuple):
|
|
name = "{%s}%s" % (key[2], key[1])
|
|
else:
|
|
name = key
|
|
self._element.set(name, value)
|
|
|
|
attributes = property(_getAttributes, _setAttributes)
|
|
|
|
def _getChildNodes(self):
|
|
return self._childNodes
|
|
|
|
def _setChildNodes(self, value):
|
|
del self._element[:]
|
|
self._childNodes = []
|
|
for element in value:
|
|
self.insertChild(element)
|
|
|
|
childNodes = property(_getChildNodes, _setChildNodes)
|
|
|
|
def hasContent(self):
|
|
"""Return true if the node has children or text"""
|
|
return bool(self._element.text or len(self._element))
|
|
|
|
def appendChild(self, node):
|
|
self._childNodes.append(node)
|
|
self._element.append(node._element)
|
|
node.parent = self
|
|
|
|
def insertBefore(self, node, refNode):
|
|
index = list(self._element).index(refNode._element)
|
|
self._element.insert(index, node._element)
|
|
node.parent = self
|
|
|
|
def removeChild(self, node):
|
|
self._element.remove(node._element)
|
|
node.parent = None
|
|
|
|
def insertText(self, data, insertBefore=None):
|
|
if not(len(self._element)):
|
|
if not self._element.text:
|
|
self._element.text = ""
|
|
self._element.text += data
|
|
elif insertBefore is None:
|
|
# Insert the text as the tail of the last child element
|
|
if not self._element[-1].tail:
|
|
self._element[-1].tail = ""
|
|
self._element[-1].tail += data
|
|
else:
|
|
# Insert the text before the specified node
|
|
children = list(self._element)
|
|
index = children.index(insertBefore._element)
|
|
if index > 0:
|
|
if not self._element[index - 1].tail:
|
|
self._element[index - 1].tail = ""
|
|
self._element[index - 1].tail += data
|
|
else:
|
|
if not self._element.text:
|
|
self._element.text = ""
|
|
self._element.text += data
|
|
|
|
def cloneNode(self):
|
|
element = type(self)(self.name, self.namespace)
|
|
for name, value in self.attributes.items():
|
|
element.attributes[name] = value
|
|
return element
|
|
|
|
def reparentChildren(self, newParent):
|
|
if newParent.childNodes:
|
|
newParent.childNodes[-1]._element.tail += self._element.text
|
|
else:
|
|
if not newParent._element.text:
|
|
newParent._element.text = ""
|
|
if self._element.text is not None:
|
|
newParent._element.text += self._element.text
|
|
self._element.text = ""
|
|
_base.Node.reparentChildren(self, newParent)
|
|
|
|
class Comment(Element):
|
|
def __init__(self, data):
|
|
# Use the superclass constructor to set all properties on the
|
|
# wrapper element
|
|
self._element = ElementTree.Comment(data)
|
|
self.parent = None
|
|
self._childNodes = []
|
|
self._flags = []
|
|
|
|
def _getData(self):
|
|
return self._element.text
|
|
|
|
def _setData(self, value):
|
|
self._element.text = value
|
|
|
|
data = property(_getData, _setData)
|
|
|
|
class DocumentType(Element):
|
|
def __init__(self, name, publicId, systemId):
|
|
Element.__init__(self, "<!DOCTYPE>")
|
|
self._element.text = name
|
|
self.publicId = publicId
|
|
self.systemId = systemId
|
|
|
|
def _getPublicId(self):
|
|
return self._element.get("publicId", "")
|
|
|
|
def _setPublicId(self, value):
|
|
if value is not None:
|
|
self._element.set("publicId", value)
|
|
|
|
publicId = property(_getPublicId, _setPublicId)
|
|
|
|
def _getSystemId(self):
|
|
return self._element.get("systemId", "")
|
|
|
|
def _setSystemId(self, value):
|
|
if value is not None:
|
|
self._element.set("systemId", value)
|
|
|
|
systemId = property(_getSystemId, _setSystemId)
|
|
|
|
class Document(Element):
|
|
def __init__(self):
|
|
Element.__init__(self, "DOCUMENT_ROOT")
|
|
|
|
class DocumentFragment(Element):
|
|
def __init__(self):
|
|
Element.__init__(self, "DOCUMENT_FRAGMENT")
|
|
|
|
def testSerializer(element):
|
|
rv = []
|
|
|
|
def serializeElement(element, indent=0):
|
|
if not(hasattr(element, "tag")):
|
|
element = element.getroot()
|
|
if element.tag == "<!DOCTYPE>":
|
|
if element.get("publicId") or element.get("systemId"):
|
|
publicId = element.get("publicId") or ""
|
|
systemId = element.get("systemId") or ""
|
|
rv.append("""<!DOCTYPE %s "%s" "%s">""" %
|
|
(element.text, publicId, systemId))
|
|
else:
|
|
rv.append("<!DOCTYPE %s>" % (element.text,))
|
|
elif element.tag == "DOCUMENT_ROOT":
|
|
rv.append("#document")
|
|
if element.text is not None:
|
|
rv.append("|%s\"%s\"" % (' ' * (indent + 2), element.text))
|
|
if element.tail is not None:
|
|
raise TypeError("Document node cannot have tail")
|
|
if hasattr(element, "attrib") and len(element.attrib):
|
|
raise TypeError("Document node cannot have attributes")
|
|
elif element.tag == ElementTreeCommentType:
|
|
rv.append("|%s<!-- %s -->" % (' ' * indent, element.text))
|
|
else:
|
|
assert isinstance(element.tag, text_type), \
|
|
"Expected unicode, got %s, %s" % (type(element.tag), element.tag)
|
|
nsmatch = tag_regexp.match(element.tag)
|
|
|
|
if nsmatch is None:
|
|
name = element.tag
|
|
else:
|
|
ns, name = nsmatch.groups()
|
|
prefix = constants.prefixes[ns]
|
|
name = "%s %s" % (prefix, name)
|
|
rv.append("|%s<%s>" % (' ' * indent, name))
|
|
|
|
if hasattr(element, "attrib"):
|
|
attributes = []
|
|
for name, value in element.attrib.items():
|
|
nsmatch = tag_regexp.match(name)
|
|
if nsmatch is not None:
|
|
ns, name = nsmatch.groups()
|
|
prefix = constants.prefixes[ns]
|
|
attr_string = "%s %s" % (prefix, name)
|
|
else:
|
|
attr_string = name
|
|
attributes.append((attr_string, value))
|
|
|
|
for name, value in sorted(attributes):
|
|
rv.append('|%s%s="%s"' % (' ' * (indent + 2), name, value))
|
|
if element.text:
|
|
rv.append("|%s\"%s\"" % (' ' * (indent + 2), element.text))
|
|
indent += 2
|
|
for child in element:
|
|
serializeElement(child, indent)
|
|
if element.tail:
|
|
rv.append("|%s\"%s\"" % (' ' * (indent - 2), element.tail))
|
|
serializeElement(element, 0)
|
|
|
|
return "\n".join(rv)
|
|
|
|
def tostring(element):
|
|
"""Serialize an element and its child nodes to a string"""
|
|
rv = []
|
|
filter = ihatexml.InfosetFilter()
|
|
|
|
def serializeElement(element):
|
|
if isinstance(element, ElementTree.ElementTree):
|
|
element = element.getroot()
|
|
|
|
if element.tag == "<!DOCTYPE>":
|
|
if element.get("publicId") or element.get("systemId"):
|
|
publicId = element.get("publicId") or ""
|
|
systemId = element.get("systemId") or ""
|
|
rv.append("""<!DOCTYPE %s PUBLIC "%s" "%s">""" %
|
|
(element.text, publicId, systemId))
|
|
else:
|
|
rv.append("<!DOCTYPE %s>" % (element.text,))
|
|
elif element.tag == "DOCUMENT_ROOT":
|
|
if element.text is not None:
|
|
rv.append(element.text)
|
|
if element.tail is not None:
|
|
raise TypeError("Document node cannot have tail")
|
|
if hasattr(element, "attrib") and len(element.attrib):
|
|
raise TypeError("Document node cannot have attributes")
|
|
|
|
for child in element:
|
|
serializeElement(child)
|
|
|
|
elif element.tag == ElementTreeCommentType:
|
|
rv.append("<!--%s-->" % (element.text,))
|
|
else:
|
|
# This is assumed to be an ordinary element
|
|
if not element.attrib:
|
|
rv.append("<%s>" % (filter.fromXmlName(element.tag),))
|
|
else:
|
|
attr = " ".join(["%s=\"%s\"" % (
|
|
filter.fromXmlName(name), value)
|
|
for name, value in element.attrib.items()])
|
|
rv.append("<%s %s>" % (element.tag, attr))
|
|
if element.text:
|
|
rv.append(element.text)
|
|
|
|
for child in element:
|
|
serializeElement(child)
|
|
|
|
rv.append("</%s>" % (element.tag,))
|
|
|
|
if element.tail:
|
|
rv.append(element.tail)
|
|
|
|
serializeElement(element)
|
|
|
|
return "".join(rv)
|
|
|
|
class TreeBuilder(_base.TreeBuilder):
|
|
documentClass = Document
|
|
doctypeClass = DocumentType
|
|
elementClass = Element
|
|
commentClass = Comment
|
|
fragmentClass = DocumentFragment
|
|
implementation = ElementTreeImplementation
|
|
|
|
def testSerializer(self, element):
|
|
return testSerializer(element)
|
|
|
|
def getDocument(self):
|
|
if fullTree:
|
|
return self.document._element
|
|
else:
|
|
if self.defaultNamespace is not None:
|
|
return self.document._element.find(
|
|
"{%s}html" % self.defaultNamespace)
|
|
else:
|
|
return self.document._element.find("html")
|
|
|
|
def getFragment(self):
|
|
return _base.TreeBuilder.getFragment(self)._element
|
|
|
|
return locals()
|
|
|
|
|
|
getETreeModule = moduleFactoryFactory(getETreeBuilder)
|