def overlay_metadata(self): # make sure we have the metadata self.get_comic_metadata_from_file() # make the metadata generic, if none exists now if self.comic_metadata is None: self.comic_metadata = GenericMetadata() self.convert_calibre_md_to_comic_md() self.comic_metadata.overlay(self.calibre_md_in_comic_format)
def _get_combined_metadata(self): ''' Combines the metadata from both sources ''' self.comic_metadata = GenericMetadata() if self.cbi_metadata is not None: self.comic_metadata.overlay(self.cbi_metadata, False) if self.cix_metadata is not None: self.comic_metadata.overlay(self.cix_metadata, False) if self.cbi_metadata is None and self.cix_metadata is None: self.comic_metadata = None
def convert_calibre_md_to_comic_md(self): ''' Maps the entries in the calibre metadata to comictagger metadata ''' from calibre.utils.html2text import html2text from calibre.utils.date import UNDEFINED_DATE from calibre.utils.localization import lang_as_iso639_1 if self.calibre_md_in_comic_format: return self.calibre_md_in_comic_format = GenericMetadata() mi = self.calibre_metadata # shorten some functions role = partial(set_role, credits=self.calibre_md_in_comic_format.credits) update_field = partial(update_comic_field, target=self.calibre_md_in_comic_format) # update the fields of comic metadata update_field("title", mi.title) role("Writer", mi.authors) update_field("series", mi.series) update_field("issue", mi.series_index) update_field("tags", mi.tags) update_field("publisher", mi.publisher) update_field("criticalRating", mi.rating) # need to check for None if mi.comments: update_field("comments", html2text(mi.comments)) if mi.language: update_field("language", lang_as_iso639_1(mi.language)) if mi.pubdate != UNDEFINED_DATE: update_field("year", mi.pubdate.year) update_field("month", mi.pubdate.month) update_field("day", mi.pubdate.day) # custom columns field = partial(self.db.field_for, book_id=self.book_id) # artists role("Penciller", field(prefs['penciller_column'])) role("Inker", field(prefs['inker_column'])) role("Colorist", field(prefs['colorist_column'])) role("Letterer", field(prefs['letterer_column'])) role("CoverArtist", field(prefs['cover_artist_column'])) role("Editor", field(prefs['editor_column'])) # others update_field("storyArc", field(prefs['storyarc_column'])) update_field("characters", field(prefs['characters_column'])) update_field("teams", field(prefs['teams_column'])) update_field("locations", field(prefs['locations_column'])) update_field("volume", field(prefs['volume_column'])) update_field("genre", field(prefs['genre_column'])) update_field("issueCount", field(prefs['count_column'])) update_field("pageCount", field(prefs['pages_column'])) update_field("webLink", get_link(field(prefs['comicvine_column']))) update_field("manga", field(prefs['manga_column']))
def overlay_metadata(self): # make sure we have the metadata self.get_comic_metadata_from_file() # make the metadata generic, if none exists now if self.comic_metadata is None: self.comic_metadata = GenericMetadata() self.convert_calibre_md_to_comic_md() self.comic_metadata.overlay(self.calibre_md_in_comic_format)
def _get_combined_metadata(self): ''' Combines the metadata from both sources ''' self.comic_metadata = GenericMetadata() if self.cbi_metadata is not None: self.comic_metadata.overlay(self.cbi_metadata, False) if self.cix_metadata is not None: self.comic_metadata.overlay(self.cix_metadata, False) if self.cbi_metadata is None and self.cix_metadata is None: self.comic_metadata = None
def convertXMLToMetadata(self, tree): root = tree.getroot() if root.tag != 'ComicInfo': raise 1 return None metadata = GenericMetadata() md = metadata # Helper function def xlate(tag): node = root.find(tag) if node is not None: return node.text else: return None md.series = xlate('Series') md.title = xlate('Title') md.issue = xlate('Number') md.issueCount = xlate('Count') md.volume = xlate('Volume') md.alternateSeries = xlate('AlternateSeries') md.alternateNumber = xlate('AlternateNumber') md.alternateCount = xlate('AlternateCount') md.comments = xlate('Summary') md.notes = xlate('Notes') md.year = xlate('Year') md.month = xlate('Month') md.day = xlate('Day') md.publisher = xlate('Publisher') md.imprint = xlate('Imprint') md.genre = xlate('Genre') md.webLink = xlate('Web') md.language = xlate('LanguageISO') md.format = xlate('Format') md.manga = xlate('Manga') md.characters = xlate('Characters') md.teams = xlate('Teams') md.locations = xlate('Locations') md.pageCount = xlate('PageCount') md.scanInfo = xlate('ScanInformation') md.storyArc = xlate('StoryArc') md.seriesGroup = xlate('SeriesGroup') md.maturityRating = xlate('AgeRating') tmp = xlate('BlackAndWhite') md.blackAndWhite = False 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(','): metadata.addCredit(name.strip(), n.tag) if n.tag == 'CoverArtist': if n.text is not None: for name in n.text.split(','): metadata.addCredit(name.strip(), "Cover") # parse page data now pages_node = root.find("Pages") if pages_node is not None: for page in pages_node: metadata.pages.append(page.attrib) # print page.attrib metadata.isEmpty = False return metadata
def convertXMLToMetadata(self, tree): root = tree.getroot() if root.tag != 'ComicInfo': raise 1 return None metadata = GenericMetadata() md = metadata # Helper function def xlate(tag): node = root.find(tag) if node is not None: return node.text else: return None md.series = xlate('Series') md.title = xlate('Title') md.issue = xlate('Number') md.issueCount = xlate('Count') md.volume = xlate('Volume') md.alternateSeries = xlate('AlternateSeries') md.alternateNumber = xlate('AlternateNumber') md.alternateCount = xlate('AlternateCount') md.comments = xlate('Summary') md.notes = xlate('Notes') md.year = xlate('Year') md.month = xlate('Month') md.day = xlate('Day') md.publisher = xlate('Publisher') md.imprint = xlate('Imprint') md.genre = xlate('Genre') md.webLink = xlate('Web') md.language = xlate('LanguageISO') md.format = xlate('Format') md.manga = xlate('Manga') md.characters = xlate('Characters') md.teams = xlate('Teams') md.locations = xlate('Locations') md.pageCount = xlate('PageCount') md.scanInfo = xlate('ScanInformation') md.storyArc = xlate('StoryArc') md.seriesGroup = xlate('SeriesGroup') md.maturityRating = xlate('AgeRating') tmp = xlate('BlackAndWhite') md.blackAndWhite = False 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(','): metadata.addCredit(name.strip(), n.tag) if n.tag == 'CoverArtist': if n.text is not None: for name in n.text.split(','): metadata.addCredit(name.strip(), "Cover") # parse page data now pages_node = root.find("Pages") if pages_node is not None: for page in pages_node: metadata.pages.append(page.attrib) # print page.attrib metadata.isEmpty = False return metadata
class ComicMetadata: ''' An object for calibre to interact with comic metadata. ''' def __init__(self, book_id, ia): # initialize the attributes self.book_id = book_id self.ia = ia self.db = ia.gui.current_db.new_api self.calibre_metadata = self.db.get_metadata(book_id) self.cbi_metadata = None self.cix_metadata = None self.calibre_md_in_comic_format = None self.comic_md_in_calibre_format = None self.comic_metadata = None self.checked_for_metadata = False self.file = None self.zipinfo = None # get the comic formats if self.db.has_format(book_id, "cbz"): self.format = "cbz" elif self.db.has_format(book_id, "cbr"): self.format = "cbr" elif self.db.has_format(book_id, "zip"): self.format = "zip" elif self.db.has_format(book_id, "rar"): self.format = "rar" else: self.format = None # generate a string with the books info, to show in the completion dialog self.info = "{} - {}".format(self.calibre_metadata.title, self.calibre_metadata.authors[0]) if self.calibre_metadata.series: self.info = "{}: {} - ".format( self.calibre_metadata.series, self.calibre_metadata.series_index) + self.info def __del__(self): delete_temp_file(self.file) def get_comic_metadata_from_file(self): if self.checked_for_metadata: return if self.format == "cbz": self.get_comic_metadata_from_cbz() elif self.format == "cbr": self.get_comic_metadata_from_cbr() self.checked_for_metadata = True def add_updated_comic_to_calibre(self): self.db.add_format(self.book_id, "cbz", self.file) def import_comic_metadata_to_calibre(self, comic_metadata): self.convert_comic_md_to_calibre_md(comic_metadata) self.db.set_metadata(self.book_id, self.comic_md_in_calibre_format) def overlay_metadata(self): # make sure we have the metadata self.get_comic_metadata_from_file() # make the metadata generic, if none exists now if self.comic_metadata is None: self.comic_metadata = GenericMetadata() self.convert_calibre_md_to_comic_md() self.comic_metadata.overlay(self.calibre_md_in_comic_format) def embed_cix_metadata(self): ''' Embeds the cix_metadata ''' from io import StringIO cix_string = ComicInfoXml().stringFromMetadata(self.comic_metadata) # ensure we have a temp file self.make_temp_cbz_file() if not python3: cix_string = cix_string.decode('utf-8', 'ignore') # use the safe_replace function from calibre to prevent coruption if self.zipinfo is not None: with open(self.file, 'r+b') as zf: safe_replace(zf, self.zipinfo, StringIO(cix_string)) # save the metadata in the file else: zf = ZipFile(self.file, "a") zf.writestr("ComicInfo.xml", cix_string) zf.close() def embed_cbi_metadata(self): ''' Embeds the cbi_metadata ''' cbi_string = ComicBookInfo().stringFromMetadata(self.comic_metadata) # ensure we have a temp file self.make_temp_cbz_file() # save the metadata in the comment zf = ZipFile(self.file, 'a') zf.comment = cbi_string.encode("utf-8") zf._didModify = True zf.close() def convert_calibre_md_to_comic_md(self): ''' Maps the entries in the calibre metadata to comictagger metadata ''' from calibre.utils.html2text import html2text from calibre.utils.date import UNDEFINED_DATE from calibre.utils.localization import lang_as_iso639_1 if self.calibre_md_in_comic_format: return self.calibre_md_in_comic_format = GenericMetadata() mi = self.calibre_metadata # shorten some functions role = partial(set_role, credits=self.calibre_md_in_comic_format.credits) update_field = partial(update_comic_field, target=self.calibre_md_in_comic_format) # update the fields of comic metadata update_field("title", mi.title) role("Writer", mi.authors) update_field("series", mi.series) update_field("issue", mi.series_index) update_field("tags", mi.tags) update_field("publisher", mi.publisher) update_field("criticalRating", mi.rating) # need to check for None if mi.comments: update_field("comments", html2text(mi.comments)) if mi.language: update_field("language", lang_as_iso639_1(mi.language)) if mi.pubdate != UNDEFINED_DATE: update_field("year", mi.pubdate.year) update_field("month", mi.pubdate.month) update_field("day", mi.pubdate.day) # custom columns field = partial(self.db.field_for, book_id=self.book_id) # artists role("Penciller", field(prefs['penciller_column'])) role("Inker", field(prefs['inker_column'])) role("Colorist", field(prefs['colorist_column'])) role("Letterer", field(prefs['letterer_column'])) role("CoverArtist", field(prefs['cover_artist_column'])) role("Editor", field(prefs['editor_column'])) # others update_field("storyArc", field(prefs['storyarc_column'])) update_field("characters", field(prefs['characters_column'])) update_field("teams", field(prefs['teams_column'])) update_field("locations", field(prefs['locations_column'])) update_field("volume", field(prefs['volume_column'])) update_field("genre", field(prefs['genre_column'])) update_field("issueCount", field(prefs['count_column'])) update_field("pageCount", field(prefs['pages_column'])) update_field("webLink", get_link(field(prefs['comicvine_column']))) update_field("manga", field(prefs['manga_column'])) def convert_comic_md_to_calibre_md(self, comic_metadata): ''' Maps the entries in the comic_metadata to calibre metadata ''' import unicodedata from calibre.ebooks.metadata import MetaInformation from calibre.utils.date import parse_only_date from datetime import date from calibre.utils.localization import calibre_langcode_to_name if self.comic_md_in_calibre_format: return # start with a fresh calibre metadata mi = MetaInformation(None, None) co = comic_metadata # shorten some functions role = partial(get_role, credits=co.credits) update_field = partial(update_calibre_field, target=mi) # Get title, if no title, try to assign series infos if co.title: mi.title = co.title elif co.series: mi.title = co.series if co.issue: mi.title += " " + str(co.issue) else: mi.title = "" # tags if co.tags != [] and prefs['import_tags']: if prefs['overwrite_calibre_tags']: mi.tags = co.tags else: mi.tags = list(set(self.calibre_metadata.tags + co.tags)) # simple metadata update_field("authors", role(WRITER)) update_field("series", co.series) update_field("rating", co.criticalRating) update_field("publisher", co.publisher) # special cases if co.language: update_field("language", calibre_langcode_to_name(co.language)) if co.comments: update_field("comments", co.comments.strip()) # issue if co.issue: try: if not python3 and isinstance(co.issue, unicode): mi.series_index = unicodedata.numeric(co.issue) else: mi.series_index = float(co.issue) except ValueError: pass # pub date puby = co.year pubm = co.month if puby is not None: try: dt = date(int(puby), 6 if pubm is None else int(pubm), 15) dt = parse_only_date(str(dt)) mi.pubdate = dt except: pass # custom columns update_column = partial( update_custom_column, calibre_metadata=mi, custom_cols=self.db.field_metadata.custom_field_metadata()) # artists update_column(prefs['penciller_column'], role(PENCILLER)) update_column(prefs['inker_column'], role(INKER)) update_column(prefs['colorist_column'], role(COLORIST)) update_column(prefs['letterer_column'], role(LETTERER)) update_column(prefs['cover_artist_column'], role(COVER_ARTIST)) update_column(prefs['editor_column'], role(EDITOR)) # others update_column(prefs['storyarc_column'], co.storyArc) update_column(prefs['characters_column'], co.characters) update_column(prefs['teams_column'], co.teams) update_column(prefs['locations_column'], co.locations) update_column(prefs['genre_column'], co.genre) ensure_int(co.issueCount, update_column, prefs['count_column'], co.issueCount) ensure_int(co.volume, update_column, prefs['volume_column'], co.volume) if prefs['auto_count_pages']: update_column(prefs['pages_column'], self.count_pages()) else: update_column(prefs['pages_column'], co.pageCount) if prefs['get_image_sizes']: update_column(prefs['image_size_column'], self.get_picture_size()) update_column(prefs['comicvine_column'], '<a href="{}">Comic Vine</a>'.format(co.webLink)) update_column(prefs['manga_column'], co.manga) self.comic_md_in_calibre_format = mi def make_temp_cbz_file(self): if not self.file and self.format == "cbz": self.file = self.db.format(self.book_id, "cbz", as_path=True) def convert_cbr_to_cbz(self): ''' Converts a rar or cbr-comic to a cbz-comic ''' from calibre.utils.unrar import extract, comment with TemporaryDirectory('_cbr2cbz') as tdir: # extract the rar file ffile = self.db.format(self.book_id, self.format, as_path=True) extract(ffile, tdir) comments = comment(ffile) delete_temp_file(ffile) # make the cbz file with TemporaryFile("comic.cbz") as tf: zf = ZipFile(tf, "w") zf.add_dir(tdir) if comments: zf.comment = comments.encode("utf-8") zf.close() # add the cbz format to calibres library self.db.add_format(self.book_id, "cbz", tf) self.format = "cbz" def convert_zip_to_cbz(self): import os zf = self.db.format(self.book_id, "zip", as_path=True) new_fname = os.path.splitext(zf)[0] + ".cbz" os.rename(zf, new_fname) self.db.add_format(self.book_id, "cbz", new_fname) delete_temp_file(new_fname) self.format = "cbz" def update_cover(self): # get the calibre cover cover_path = self.db.cover(self.book_id, as_path=True) fmt = cover_path.rpartition('.')[-1] new_cover_name = "00000000_cover." + fmt self.make_temp_cbz_file() # search for a previously embeded cover zf = ZipFile(self.file) cover_info = "" for name in zf.namelist(): if name.rsplit(".", 1)[0] == "00000000_cover": cover_info = name break zf.close() # delete previous cover if cover_info != "": with open(self.file, 'r+b') as zf, open(cover_path, 'r+b') as cp: safe_replace(zf, cover_info, cp) # save the cover in the file else: zf = ZipFile(self.file, "a") zf.write(cover_path, new_cover_name) zf.close() delete_temp_file(cover_path) def count_pages(self): self.make_temp_cbz_file() zf = ZipFile(self.file) pages = 0 for name in zf.namelist(): if name.lower().rpartition('.')[-1] in IMG_EXTENSIONS: pages += 1 zf.close() return pages def action_count_pages(self): pages = self.count_pages() if pages == 0: return False update_custom_column(prefs['pages_column'], str(pages), self.calibre_metadata, self.db.field_metadata.custom_field_metadata()) self.db.set_metadata(self.book_id, self.calibre_metadata) return True def get_picture_size(self): from calibre.utils.magick import Image self.make_temp_cbz_file() zf = ZipFile(self.file) files = zf.namelist() size_x, size_y = 0, 0 index = 1 while index < 10 and index < len(files): fname = files[index] if fname.lower().rpartition('.')[-1] in IMG_EXTENSIONS: with zf.open(fname) as ffile: img = Image() try: img.open(ffile) size_x, size_y = img.size except: pass if size_x < size_y: break index += 1 zf.close() size = round(size_x * size_y / 1000000, 2) return size def action_picture_size(self): size = self.get_picture_size() if not size: return False update_custom_column(prefs['image_size_column'], size, self.calibre_metadata, self.db.field_metadata.custom_field_metadata()) self.db.set_metadata(self.book_id, self.calibre_metadata) return True def get_comic_metadata_from_cbz(self): ''' Reads the comic metadata from the comic cbz file as comictagger metadata ''' self.make_temp_cbz_file() # open the zipfile zf = ZipFile(self.file) # get cix metadata for name in zf.namelist(): if name.lower() == "comicinfo.xml": self.cix_metadata = ComicInfoXml().metadataFromString( zf.read(name)) self.zipinfo = name break # get the cbi metadata if ComicBookInfo().validateString(zf.comment): self.cbi_metadata = ComicBookInfo().metadataFromString(zf.comment) zf.close() # get combined metadata self._get_combined_metadata() def get_comic_metadata_from_cbr(self): ''' Reads the comic metadata from the comic cbr file as comictagger metadata and returns the metadata depending on do_action ''' from calibre.utils.unrar import extract_member, names, comment ffile = self.db.format(self.book_id, "cbr", as_path=True) with open(ffile, 'rb') as stream: # get the cix metadata fnames = list(names(stream)) for name in fnames: if name.lower() == "comicinfo.xml": self.cix_metadata = extract_member(stream, match=None, name=name)[1] self.cix_metadata = ComicInfoXml().metadataFromString( self.cix_metadata) break # get the cbi metadata comments = comment(ffile) if ComicBookInfo().validateString(comments): self.cbi_metadata = ComicBookInfo().metadataFromString( comments) delete_temp_file(ffile) self._get_combined_metadata() def _get_combined_metadata(self): ''' Combines the metadata from both sources ''' self.comic_metadata = GenericMetadata() if self.cbi_metadata is not None: self.comic_metadata.overlay(self.cbi_metadata, False) if self.cix_metadata is not None: self.comic_metadata.overlay(self.cix_metadata, False) if self.cbi_metadata is None and self.cix_metadata is None: self.comic_metadata = None
class ComicMetadata: ''' An object for calibre to interact with comic metadata. ''' def __init__(self, book_id, ia): # initialize the attributes self.book_id = book_id self.ia = ia self.db = ia.gui.current_db.new_api self.calibre_metadata = self.db.get_metadata(book_id) self.cbi_metadata = None self.cix_metadata = None self.calibre_md_in_comic_format = None self.comic_md_in_calibre_format = None self.comic_metadata = None self.checked_for_metadata = False self.file = None self.zipinfo = None # get the comic formats if self.db.has_format(book_id, "cbz"): self.format = "cbz" elif self.db.has_format(book_id, "cbr"): self.format = "cbr" else: self.format = None # generate a string with the books info, to show in the completion dialog self.info = "{} - {}".format(self.calibre_metadata.title, self.calibre_metadata.authors[0]) if self.calibre_metadata.series: self.info = "{}: {} - ".format(self.calibre_metadata.series, self.calibre_metadata.series_index) + self.info def __del__(self): delete_temp_file(self.file) def get_comic_metadata_from_file(self): if self.checked_for_metadata: return if self.format == "cbz": self.get_comic_metadata_from_cbz() elif self.format == "cbr": self.get_comic_metadata_from_cbr() self.checked_for_metadata = True def add_updated_comic_to_calibre(self): self.db.add_format(self.book_id, "cbz", self.file) def import_comic_metadata_to_calibre(self, comic_metadata): self.convert_comic_md_to_calibre_md(comic_metadata) self.db.set_metadata(self.book_id, self.comic_md_in_calibre_format) def overlay_metadata(self): # make sure we have the metadata self.get_comic_metadata_from_file() # make the metadata generic, if none exists now if self.comic_metadata is None: self.comic_metadata = GenericMetadata() self.convert_calibre_md_to_comic_md() self.comic_metadata.overlay(self.calibre_md_in_comic_format) def embed_cix_metadata(self): ''' Embeds the cix_metadata ''' from io import StringIO cix_string = ComicInfoXml().stringFromMetadata(self.comic_metadata) # ensure we have a temp file self.make_temp_cbz_file() # make a new cbz if a metadata file is already there, to prevent corruption if self.zipinfo is not None: with open(self.file, 'r+b') as zf: safe_replace(zf, self.zipinfo, StringIO(cix_string.decode('utf-8', 'ignore'))) else: # save the metadata in the file zf = ZipFile(self.file, "a") zf.writestr("ComicInfo.xml", cix_string.decode('utf-8', 'ignore')) zf.close() def embed_cbi_metadata(self): ''' Embeds the cbi_metadata ''' cbi_string = ComicBookInfo().stringFromMetadata(self.comic_metadata) # ensure we have a temp file self.make_temp_cbz_file() # save the metadata in the comment writeZipComment(self.file, cbi_string) def convert_calibre_md_to_comic_md(self): ''' Maps the entries in the calibre metadata to comictagger metadata ''' from calibre.utils.html2text import html2text from calibre.utils.date import UNDEFINED_DATE from calibre.utils.localization import lang_as_iso639_1 if self.calibre_md_in_comic_format: return self.calibre_md_in_comic_format = GenericMetadata() mi = self.calibre_metadata # shorten some functions role = partial(set_role, credits=self.calibre_md_in_comic_format.credits) update_field = partial(update_comic_field, target=self.calibre_md_in_comic_format) # update the fields of comic metadata update_field("title", mi.title) role("Writer", mi.authors) update_field("series", mi.series) update_field("issue", mi.series_index) update_field("tags", mi.tags) update_field("publisher", mi.publisher) update_field("criticalRating", mi.rating) # need to check for None if mi.comments: update_field("comments", html2text(mi.comments)) if mi.language: update_field("language", lang_as_iso639_1(mi.language)) if mi.pubdate != UNDEFINED_DATE: update_field("year", mi.pubdate.year) update_field("month", mi.pubdate.month) update_field("day", mi.pubdate.day) # custom columns field = partial(self.db.field_for, book_id=self.book_id) # artists role("Penciller", field(prefs['penciller_column'])) role("Inker", field(prefs['inker_column'])) role("Colorist", field(prefs['colorist_column'])) role("Letterer", field(prefs['letterer_column'])) role("CoverArtist", field(prefs['cover_artist_column'])) role("Editor", field(prefs['editor_column'])) # others update_field("storyArc", field(prefs['storyarc_column'])) update_field("characters", field(prefs['characters_column'])) update_field("teams", field(prefs['teams_column'])) update_field("locations", field(prefs['locations_column'])) update_field("volume", field(prefs['volume_column'])) update_field("genre", field(prefs['genre_column'])) def convert_comic_md_to_calibre_md(self, comic_metadata): ''' Maps the entries in the comic_metadata to calibre metadata ''' import unicodedata from calibre.ebooks.metadata import MetaInformation from calibre.utils.date import parse_only_date from datetime import date from calibre.utils.localization import calibre_langcode_to_name if self.comic_md_in_calibre_format: return # synonyms for artists WRITER = ['writer', 'plotter', 'scripter'] PENCILLER = ['artist', 'penciller', 'penciler', 'breakdowns'] INKER = ['inker', 'artist', 'finishes'] COLORIST = ['colorist', 'colourist', 'colorer', 'colourer'] LETTERER = ['letterer'] COVER_ARTIST = ['cover', 'covers', 'coverartist', 'cover artist'] EDITOR = ['editor'] # start with a fresh calibre metadata mi = MetaInformation(None, None) co = comic_metadata # shorten some functions role = partial(get_role, credits=co.credits) update_field = partial(update_calibre_field, target=mi) # Get title, if no title, try to assign series infos if co.title: mi.title = co.title elif co.series: mi.title = co.series if co.issue: mi.title += " " + str(co.issue) else: mi.title = "" # tags if co.tags != [] and prefs['import_tags']: if prefs['overwrite_calibre_tags']: mi.tags = co.tags else: mi.tags = list(set(self.calibre_metadata.tags + co.tags)) # simple metadata update_field("authors", role(WRITER)) update_field("series", co.series) update_field("rating", co.criticalRating) update_field("publisher", co.publisher) # special cases if co.language: update_field("language", calibre_langcode_to_name(co.language)) if co.comments: update_field("comments", co.comments.strip()) # issue if co.issue: if isinstance(co.issue, unicode): mi.series_index = unicodedata.numeric(co.issue) else: mi.series_index = float(co.issue) # pub date puby = co.year pubm = co.month if puby is not None: try: dt = date(int(puby), 6 if pubm is None else int(pubm), 15) dt = parse_only_date(str(dt)) mi.pubdate = dt except: pass # custom columns custom_cols = self.db.field_metadata.custom_field_metadata() update_column = partial(update_custom_column, calibre_metadata=mi, custom_cols=custom_cols) # artists update_column(prefs['penciller_column'], role(PENCILLER)) update_column(prefs['inker_column'], role(INKER)) update_column(prefs['colorist_column'], role(COLORIST)) update_column(prefs['letterer_column'], role(LETTERER)) update_column(prefs['cover_artist_column'], role(COVER_ARTIST)) update_column(prefs['editor_column'], role(EDITOR)) # others update_column(prefs['storyarc_column'], co.storyArc) update_column(prefs['characters_column'], co.characters) update_column(prefs['teams_column'], co.teams) update_column(prefs['locations_column'], co.locations) update_column(prefs['volume_column'], co.volume) update_column(prefs['genre_column'], co.genre) self.comic_md_in_calibre_format = mi def make_temp_cbz_file(self): if not self.file and self.format == "cbz": self.file = self.db.format(self.book_id, "cbz", as_path=True) def convert_to_cbz(self): ''' Converts a cbr-comic to a cbz-comic ''' from calibre.utils.unrar import RARFile, extract with TemporaryDirectory('_cbr2cbz') as tdir: # extract the rar file ffile = self.db.format(self.book_id, "cbr", as_path=True) extract(ffile, tdir) # get the comment with open(ffile, 'rb') as stream: zr = RARFile(stream, get_comment=True) comment = zr.comment delete_temp_file(ffile) # make the cbz file with TemporaryFile("comic.cbz") as tf: zf = ZipFile(tf, "w") zf.add_dir(tdir) zf.close() # write comment if comment: writeZipComment(tf, comment) # add the cbz format to calibres library self.db.add_format(self.book_id, "cbz", tf) self.format = "cbz" def update_cover(self): # get the calibre cover cover_path = self.db.cover(self.book_id, as_path=True) fmt = cover_path.rpartition('.')[-1] new_cover_name = "00000000_cover." + fmt self.make_temp_cbz_file() # search for a previously embeded cover zf = ZipFile(self.file) cover_info = "" for name in zf.namelist(): if name.rsplit(".", 1)[0] == "00000000_cover": cover_info = name break # delete previous cover if cover_info != "": with open(self.file, 'r+b') as zf, open(cover_path, 'r+b') as cp: safe_replace(zf, cover_info, cp) # save the cover in the file else: zf = ZipFile(self.file, "a") zf.write(cover_path, new_cover_name) zf.close() delete_temp_file(cover_path) def get_comic_metadata_from_cbz(self): ''' Reads the comic metadata from the comic cbz file as comictagger metadata ''' self.make_temp_cbz_file() # open the zipfile zf = ZipFile(self.file) # get cix metadata for name in zf.namelist(): if name.lower() == "comicinfo.xml": self.cix_metadata = ComicInfoXml().metadataFromString(zf.read(name)) self.zipinfo = name break # get the cbi metadata if ComicBookInfo().validateString(zf.comment): self.cbi_metadata = ComicBookInfo().metadataFromString(zf.comment) zf.close() # get combined metadata self._get_combined_metadata() def get_comic_metadata_from_cbr(self): ''' Reads the comic metadata from the comic cbr file as comictagger metadata and returns the metadata depending on do_action ''' from calibre.utils.unrar import RARFile, extract_member, names ffile = self.db.format(self.book_id, "cbr", as_path=True) with open(ffile, 'rb') as stream: # get the cix metadata fnames = list(names(stream)) for name in fnames: if name.lower() == "comicinfo.xml": self.cix_metadata = extract_member(stream, match=None, name=name)[1] self.cix_metadata = ComicInfoXml().metadataFromString(self.cix_metadata) break # get the cbi metadata zr = RARFile(stream, get_comment=True) comment = zr.comment if ComicBookInfo().validateString(comment): self.cbi_metadata = ComicBookInfo().metadataFromString(comment) delete_temp_file(ffile) self._get_combined_metadata() def _get_combined_metadata(self): ''' Combines the metadata from both sources ''' self.comic_metadata = GenericMetadata() if self.cbi_metadata is not None: self.comic_metadata.overlay(self.cbi_metadata, False) if self.cix_metadata is not None: self.comic_metadata.overlay(self.cix_metadata, False) if self.cbi_metadata is None and self.cix_metadata is None: self.comic_metadata = None
def metadataFromString(self, string): cbi_container = json.loads(unicode(string, 'utf-8')) metadata = GenericMetadata() cbi = cbi_container['ComicBookInfo/1.0'] # helper func # If item is not in CBI, return None def xlate(cbi_entry): if cbi_entry in cbi: return cbi[cbi_entry] else: return None metadata.series = xlate('series') metadata.title = xlate('title') metadata.issue = xlate('issue') metadata.publisher = xlate('publisher') metadata.month = xlate('publicationMonth') metadata.year = xlate('publicationYear') metadata.issueCount = xlate('numberOfIssues') metadata.comments = xlate('comments') metadata.credits = xlate('credits') metadata.genre = xlate('genre') metadata.volume = xlate('volume') metadata.volumeCount = xlate('numberOfVolumes') metadata.language = xlate('language') metadata.country = xlate('country') metadata.criticalRating = xlate('rating') metadata.tags = xlate('tags') # make sure credits and tags are at least empty lists and not None if metadata.credits is None: metadata.credits = [] if metadata.tags is None: metadata.tags = [] # need to massage the language string to be ISO # modified to use a calibre function if metadata.language is not None: metadata.language = lang_as_iso639_1(metadata.language) metadata.isEmpty = False return metadata
def metadataFromString(self, string): cbi_container = json.loads(unicode(string, 'utf-8')) metadata = GenericMetadata() cbi = cbi_container['ComicBookInfo/1.0'] # helper func # If item is not in CBI, return None def xlate(cbi_entry): if cbi_entry in cbi: return cbi[cbi_entry] else: return None metadata.series = xlate('series') metadata.title = xlate('title') metadata.issue = xlate('issue') metadata.publisher = xlate('publisher') metadata.month = xlate('publicationMonth') metadata.year = xlate('publicationYear') metadata.issueCount = xlate('numberOfIssues') metadata.comments = xlate('comments') metadata.credits = xlate('credits') metadata.genre = xlate('genre') metadata.volume = xlate('volume') metadata.volumeCount = xlate('numberOfVolumes') metadata.language = xlate('language') metadata.country = xlate('country') metadata.criticalRating = xlate('rating') metadata.tags = xlate('tags') # make sure credits and tags are at least empty lists and not None if metadata.credits is None: metadata.credits = [] if metadata.tags is None: metadata.tags = [] # need to massage the language string to be ISO # modified to use a calibre function if metadata.language is not None: metadata.language = lang_as_iso639_1(metadata.language) metadata.isEmpty = False return metadata