"""A class to encapsulate ComicRack's ComicInfo.xml data""" # Copyright 2012-2014 Anthony Beville # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import xml.etree.ElementTree as ET from . import utils from .genericmetadata import GenericMetadata from .issuestring import IssueString # from datetime import datetime # from pprint import pprint # import zipfile class ComicInfoXml: writer_synonyms = ["writer", "plotter", "scripter"] penciller_synonyms = ["artist", "penciller", "penciler", "breakdowns"] inker_synonyms = ["inker", "artist", "finishes"] colorist_synonyms = ["colorist", "colourist", "colorer", "colourer"] letterer_synonyms = ["letterer"] cover_synonyms = ["cover", "covers", "coverartist", "cover artist"] editor_synonyms = ["editor"] def getParseableCredits(self): parsable_credits = [] parsable_credits.extend(self.writer_synonyms) parsable_credits.extend(self.penciller_synonyms) parsable_credits.extend(self.inker_synonyms) parsable_credits.extend(self.colorist_synonyms) parsable_credits.extend(self.letterer_synonyms) parsable_credits.extend(self.cover_synonyms) parsable_credits.extend(self.editor_synonyms) return parsable_credits def metadataFromString(self, string): tree = ET.ElementTree(ET.fromstring(string)) return self.convertXMLToMetadata(tree) def stringFromMetadata(self, metadata): header = '\n' tree = self.convertMetadataToXML(self, metadata) tree_str = ET.tostring(tree.getroot()).decode() return header + tree_str def indent(self, elem, level=0): # for making the XML output readable 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: self.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 def convertMetadataToXML(self, filename, metadata): # shorthand for the metadata md = metadata # build a tree structure root = ET.Element("ComicInfo") root.attrib["xmlns:xsi"] = "http://www.w3.org/2001/XMLSchema-instance" root.attrib["xmlns:xsd"] = "http://www.w3.org/2001/XMLSchema" # helper func def assign(cix_entry, md_entry): if md_entry is not None: ET.SubElement(root, cix_entry).text = "{0}".format(md_entry) assign("Title", md.title) assign("Series", md.series) assign("Number", md.issue) assign("Count", md.issueCount) assign("Volume", md.volume) assign("AlternateSeries", md.alternateSeries) assign("AlternateNumber", md.alternateNumber) assign("StoryArc", md.storyArc) assign("SeriesGroup", md.seriesGroup) assign("AlternateCount", md.alternateCount) assign("Summary", md.comments) assign("Notes", md.notes) assign("Year", md.year) assign("Month", md.month) assign("Day", md.day) assign("SeriesYear", md.seriesYear) # need to specially process the credits, since they are structured # differently than CIX credit_writer_list = list() credit_penciller_list = list() credit_inker_list = list() credit_colorist_list = list() credit_letterer_list = list() credit_cover_list = list() credit_editor_list = list() # first, loop thru credits, and build a list for each role that CIX # supports for credit in metadata.credits: if credit["role"].lower() in set(self.writer_synonyms): credit_writer_list.append(credit["person"].replace(",", "")) if credit["role"].lower() in set(self.penciller_synonyms): credit_penciller_list.append(credit["person"].replace(",", "")) if credit["role"].lower() in set(self.inker_synonyms): credit_inker_list.append(credit["person"].replace(",", "")) if credit["role"].lower() in set(self.colorist_synonyms): credit_colorist_list.append(credit["person"].replace(",", "")) if credit["role"].lower() in set(self.letterer_synonyms): credit_letterer_list.append(credit["person"].replace(",", "")) if credit["role"].lower() in set(self.cover_synonyms): credit_cover_list.append(credit["person"].replace(",", "")) if credit["role"].lower() in set(self.editor_synonyms): credit_editor_list.append(credit["person"].replace(",", "")) # second, convert each list to string, and add to XML struct if len(credit_writer_list) > 0: node = ET.SubElement(root, "Writer") node.text = utils.listToString(credit_writer_list) if len(credit_penciller_list) > 0: node = ET.SubElement(root, "Penciller") node.text = utils.listToString(credit_penciller_list) if len(credit_inker_list) > 0: node = ET.SubElement(root, "Inker") node.text = utils.listToString(credit_inker_list) if len(credit_colorist_list) > 0: node = ET.SubElement(root, "Colorist") node.text = utils.listToString(credit_colorist_list) if len(credit_letterer_list) > 0: node = ET.SubElement(root, "Letterer") node.text = utils.listToString(credit_letterer_list) if len(credit_cover_list) > 0: node = ET.SubElement(root, "CoverArtist") node.text = utils.listToString(credit_cover_list) if len(credit_editor_list) > 0: node = ET.SubElement(root, "Editor") node.text = utils.listToString(credit_editor_list) assign("Publisher", md.publisher) assign("Imprint", md.imprint) assign("Genre", md.genre) assign("Web", md.webLink) assign("PageCount", md.pageCount) assign("LanguageISO", md.language) assign("Format", md.format) assign("AgeRating", md.maturityRating) if md.blackAndWhite is not None and md.blackAndWhite: ET.SubElement(root, "BlackAndWhite").text = "Yes" assign("Manga", md.manga) assign("Characters", md.characters) assign("Teams", md.teams) assign("Locations", md.locations) assign("ScanInformation", md.scanInfo) # loop and add the page entries under pages node if len(md.pages) > 0: pages_node = ET.SubElement(root, "Pages") for page_dict in md.pages: page_node = ET.SubElement(pages_node, "Page") page_node.attrib = page_dict # self pretty-print self.indent(root) # wrap it in an ElementTree instance, and save as XML tree = ET.ElementTree(root) return tree def convertXMLToMetadata(self, tree): root = tree.getroot() if root.tag != "ComicInfo": raise 1 return None def get(name): tag = root.find(name) if tag is None: return None return tag.text md = GenericMetadata() md.series = utils.xlate(get("Series")) md.title = utils.xlate(get("Title")) md.issue = IssueString(utils.xlate(get("Number"))).asString() md.issueCount = utils.xlate(get("Count"), True) md.volume = utils.xlate(get("Volume"), True) md.alternateSeries = utils.xlate(get("AlternateSeries")) md.alternateNumber = IssueString(utils.xlate(get("AlternateNumber"))).asString() md.alternateCount = utils.xlate(get("AlternateCount"), True) md.comments = utils.xlate(get("Summary")) md.notes = utils.xlate(get("Notes")) md.year = utils.xlate(get("Year"), True) md.month = utils.xlate(get("Month"), True) md.day = utils.xlate(get("Day"), True) md.seriesYear = utils.xlate(get("SeriesYear"), True) md.publisher = utils.xlate(get("Publisher")) md.imprint = utils.xlate(get("Imprint")) md.genre = utils.xlate(get("Genre")) md.webLink = utils.xlate(get("Web")) md.language = utils.xlate(get("LanguageISO")) md.format = utils.xlate(get("Format")) md.manga = utils.xlate(get("Manga")) md.characters = utils.xlate(get("Characters")) md.teams = utils.xlate(get("Teams")) md.locations = utils.xlate(get("Locations")) md.pageCount = utils.xlate(get("PageCount"), True) md.scanInfo = utils.xlate(get("ScanInformation")) md.storyArc = utils.xlate(get("StoryArc")) md.seriesGroup = utils.xlate(get("SeriesGroup")) md.maturityRating = utils.xlate(get("AgeRating")) tmp = utils.xlate(get("BlackAndWhite")) if tmp is not None and tmp.lower() in ["yes", "true", "1"]: md.blackAndWhite = True # Now extract the credit info for n in root: if n.tag == "Writer" or n.tag == "Penciller" or n.tag == "Inker" or n.tag == "Colorist" or n.tag == "Letterer" or n.tag == "Editor": if n.text is not None: for name in n.text.split(","): md.addCredit(name.strip(), n.tag) if n.tag == "CoverArtist": if n.text is not None: for name in n.text.split(","): md.addCredit(name.strip(), "Cover") # parse page data now pages_node = root.find("Pages") if pages_node is not None: for page in pages_node: md.pages.append(page.attrib) # print page.attrib md.isEmpty = False return md def writeToExternalFile(self, filename, metadata): tree = self.convertMetadataToXML(self, metadata) # ET.dump(tree) tree.write(filename, encoding="utf-8") def readFromExternalFile(self, filename): tree = ET.parse(filename) return self.convertXMLToMetadata(tree)