From aefe778b36aacc58e92916643c61a7819bd0ebe5 Mon Sep 17 00:00:00 2001 From: lordwelch Date: Sat, 7 Aug 2021 21:50:45 -0700 Subject: [PATCH] Add publisher and imprint handling Imprint handling has been added to utils and uses a subclassed dict to return a tuple for imprint matching, this may not be the best idea but it works for now. Add settings option auto_imprint Add cli flag -a, --auto-import --- comicapi/genericmetadata.py | 18 +++ comicapi/utils.py | 167 ++++++++++++++++++++++++ comictaggerlib/autotagstartwindow.py | 4 + comictaggerlib/cli.py | 9 ++ comictaggerlib/options.py | 3 + comictaggerlib/settings.py | 4 + comictaggerlib/taggerwindow.py | 15 +++ comictaggerlib/ui/autotagstartwindow.ui | 16 ++- comictaggerlib/ui/taggerwindow.ui | 9 ++ 9 files changed, 242 insertions(+), 3 deletions(-) diff --git a/comicapi/genericmetadata.py b/comicapi/genericmetadata.py index b3679a2..4195c43 100644 --- a/comicapi/genericmetadata.py +++ b/comicapi/genericmetadata.py @@ -319,3 +319,21 @@ class GenericMetadata: outstr += fmt_str.format(i[0] + ":", i[1]) return outstr + + def fixPublisher(self): + if self.publisher is None: + return + if self.imprint is None: + self.imprint = "" + + imprint, publisher = utils.getPublisher(self.publisher) + + self.publisher = publisher + + if self.imprint.lower() in publisher.lower(): + self.imprint = None + + if self.imprint is None or self.imprint == "": + self.imprint = imprint + elif self.imprint.lower() in imprint.lower(): + self.imprint = imprint diff --git a/comicapi/utils.py b/comicapi/utils.py index 05bacc2..152833f 100644 --- a/comicapi/utils.py +++ b/comicapi/utils.py @@ -615,3 +615,170 @@ def getLanguageFromISO(iso): return None else: return lang_dict[iso] + + +def getPublisher(publisher): + if publisher is None: + return ("", "") + imprint = "" + + for pub in publishers: + imprint, publisher, ok = pub[publisher] + if ok: + break + + return (imprint, publisher) + + +class ImprintDict(dict): + ''' + ImprintDict takes a publisher and a dict or mapping of lowercased + imprint names to the proper imprint name. Retreiving a value from an + ImprintDict returns a tuple of (imprint, publisher, keyExists). + if the key does not exist the key is returned as the publisher unchanged + ''' + def __init__(self, publisher, mapping=(), **kwargs): + super().__init__(mapping, **kwargs) + self.publisher = publisher + + def __missing__(self, key): + return None + + def __getitem__(self, k): + item = super().__getitem__(k.lower()) + if item is None: + return ("", k, False) + else: + return (item, self.publisher, True) + +Marvel = ImprintDict("Marvel", { + "marvel comics": "", + "marvel": "", + "aircel comics": "Aircel Comics", + "aircel": "Aircel Comics", + "atlas comics": "Atlas Comics", + "atlas": "Atlas Comics", + "crossgen comics": "CrossGen comics", + "crossgen": "CrossGen comics", + "curtis magazines": "Curtis Magazines", + "disney books group": "Disney Books Group", + "disney books": "Disney Books Group", + "disney kingdoms": "Disney Kingdoms", + "epic comics": "Epic Comics", + "epic": "Epic Comics", + "epic comics group": "Epic Comics", + "eternity comics": "Eternity Comics", + "humorama": "Humorama", + "icon comics": "Icon Comics", + "infinite comics": "Infinite Comics", + "malibu comics": "Malibu Comics", + "malibu": "Malibu Comics", + "marvel 2099": "Marvel 2099", + "marvel absurd": "Marvel Absurd", + "marvel adventures": "Marvel Adventures", + "marvel age": "Marvel Age", + "marvel books": "Marvel Books", + "marvel comics 2": "Marvel Comics 2", + "marvel edge": "Marvel Edge", + "marvel frontier": "Marvel Frontier", + "marvel illustrated": "Marvel Illustrated", + "marvel knights": "Marvel Knights", + "marvel digital comics unlimited": "Marvel Unlimited", + "marvel magazine group": "Marvel Magazine Group", + "marvel mangaverse": "Marvel Mangaverse", + "marvel monsters group": "Marvel Monsters Group", + "marvel music": "Marvel Music", + "marvel next": "Marvel Next", + "marvel noir": "Marvel Noir", + "marvel press": "Marvel Press", + "marvel uk": "Marvel UK", + "marvel unlimited": "Marvel Unlimited", + "max": "MAX", + "mc2": "Marvel Comics 2", + "new universe": "New Universe", + "non-pareil publishing corp.": "Non-Pareil Publishing Corp.", + "paramount comics": "Paramount Comics", + "power comics": "Power Comics", + "razorline": "Razorline", + "star comics": "Star Comics", + "timely comics": "Timely Comics", + "timely": "Timely Comics", + "tsunami": "Tsunami", + "ultimate comics": "Ultimate Comics", + "ultimate marvel": "Ultimate Marvel", + "vital publications, inc.": "Vital Publications, Inc." +}) + +DC_Comics = ImprintDict("DC Comics", { + "dc comics": "", + "dc_comics": "", + "dc": "", + "tangent comics": "Tangent Comics", + "dccomics": "", + "all star dc": "All-Star", + "all star": "All-Star", + "all-star dc": "All-Star", + "all-star": "All-Star", + "america's best comics": "America's Best Comics", + "black label": "DC Black Label", + "cliffhanger": "Cliffhanger", + "cmx manga": "CMX Manga", + "dc black label": "DC Black Label", + "dc focus": "DC Focus", + "dc ink": "DC Ink", + "dc zoom": "DC Zoom", + "earth m": "Earth M", + "earth one": "Earth One", + "earth-m": "Earth M", + "elseworlds": "Elseworlds", + "eo": "Earth One", + "first wave": "First Wave", + "focus": "DC Focus", + "helix": "Helix", + "homage comics": "Homage Comics", + "impact comics": "Impact Comics", + "impact! comics": "Impact Comics", + "!mpact comics": "Impact Comics", + "johnny dc": "Johnny DC", + "mad": "Mad", + "minx": "Minx", + "paradox press": "Paradox Press", + "piranha press": "Piranha Press", + "sandman universe": "Sandman Universe", + "tsr": "TSR", + "vertigo": "Vertigo", + "wildstorm productions": "WildStorm Productions", + "wildstorm signature": "WildStorm Productions", + "wildstorm": "WildStorm Productions", + "wonder comics": "Wonder Comics", + "young animal": "Young Animal", + "zuda comics": "Zuda Comics", + "zuda": "Zuda Comics", +}) + +Dark_Horse_Comics = ImprintDict("Dark Horse Comics", { + "legend": "Legend", + "comics' greatest world": "Dark Horse Heroes", + "dark horse heroes": "Dark Horse Heroes", + "dark horse manga": "Dark Horse Manga", + "maverick": "Maverick", + "dh press": "DH Press", + "m press": "M Press", + "dark horse digital": "Dark Horse Digital", + "dh deluxe": "DH Deluxe", + "kitchen sink books": "Kitchen Sink Books", + "berger books": "Berger Books", +}) + +Archie_Comics = ImprintDict("Archie Comics", { + "Archie Action": "Archie Action", + "Archie Horror": "Archie Horror", + "Dark Circle Comics": "Dark Circle Comics", + "Dark Circle": "Dark Circle Comics", + "Red Circle Comics": "Dark Circle Comics", + "Red Circle": "Dark Circle Comics", + "Archie Adventure Series": "Archie Adventure Series", + "Radio Comics": "Mighty Comics Group", + "Mighty Comics Group": "Mighty Comics Group", +}) +publishers = [Marvel, DC_Comics, Dark_Horse_Comics, Archie_Comics] diff --git a/comictaggerlib/autotagstartwindow.py b/comictaggerlib/autotagstartwindow.py index 653c369..97ac301 100644 --- a/comictaggerlib/autotagstartwindow.py +++ b/comictaggerlib/autotagstartwindow.py @@ -45,6 +45,7 @@ class AutoTagStartWindow(QtWidgets.QDialog): QtCore.Qt.Unchecked) self.cbxRemoveAfterSuccess.setCheckState(QtCore.Qt.Unchecked) self.cbxSpecifySearchString.setCheckState(QtCore.Qt.Unchecked) + self.cbxAutoImprint.setCheckState(QtCore.Qt.Unchecked) self.leNameLengthMatchTolerance.setText( str(self.settings.id_length_delta_thresh)) self.leSearchString.setEnabled(False) @@ -62,6 +63,9 @@ class AutoTagStartWindow(QtWidgets.QDialog): self.cbxRemoveAfterSuccess.setCheckState(QtCore.Qt.Checked) if self.settings.wait_and_retry_on_rate_limit: self.cbxWaitForRateLimit.setCheckState(QtCore.Qt.Checked) + if self.settings.auto_imprint: + self.cbxAutoImprint.setCheckState(QtCore.Qt.Checked) + nlmtTip = ( """ The Name Length Match Tolerance is for eliminating automatic diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py index 688907d..0298de6 100644 --- a/comictaggerlib/cli.py +++ b/comictaggerlib/cli.py @@ -132,6 +132,10 @@ def display_match_set_for_choice(label, match_set, opts, settings): cv_md = actual_issue_data_fetch( match_set.matches[int(i)], settings, opts) md.overlay(cv_md) + + if settings.auto_imprint: + md.fixPublisher() + actual_metadata_save(ca, opts, md) @@ -219,6 +223,8 @@ def process_file_cli(filename, opts, settings, match_results): batch_mode = len(opts.file_list) > 1 + settings.auto_imprint = opts.auto_imprint + ca = ComicArchive( filename, settings.rar_exe_path, @@ -473,6 +479,9 @@ def process_file_cli(filename, opts, settings, match_results): md.overlay(cv_md) + if settings.auto_imprint: + md.fixPublisher() + # ok, done building our metadata. time to save if not actual_metadata_save(ca, opts, md): match_results.writeFailures.append(filename) diff --git a/comictaggerlib/options.py b/comictaggerlib/options.py index eff0c78..5bf0632 100644 --- a/comictaggerlib/options.py +++ b/comictaggerlib/options.py @@ -113,6 +113,7 @@ For more help visit the wiki at: https://github.com/comictagger/comictagger/wiki self.filename = None self.verbose = False self.terse = False + self.auto_imprint = False self.metadata = None self.print_tags = False self.copy_tags = False @@ -291,6 +292,8 @@ For more help visit the wiki at: https://github.com/comictagger/comictagger/wiki self.delete_tags = True if o in ("-i", "--interactive"): self.interactive = True + if o in ("-a", "--auto-imprint"): + self.auto_imprint = True if o in ("-c", "--copy"): self.copy_tags = True if a.lower() == "cr": diff --git a/comictaggerlib/settings.py b/comictaggerlib/settings.py index 8808788..00133ee 100644 --- a/comictaggerlib/settings.py +++ b/comictaggerlib/settings.py @@ -94,6 +94,7 @@ class ComicTaggerSettings: self.clear_form_before_populating_from_cv = False self.remove_html_tables = False self.cv_api_key = "" + self.auto_imprint = False # CBL Tranform settings @@ -323,6 +324,8 @@ class ComicTaggerSettings: if self.config.has_option('autotag', 'wait_and_retry_on_rate_limit'): self.wait_and_retry_on_rate_limit = self.config.getboolean( 'autotag', 'wait_and_retry_on_rate_limit') + if self.config.has_option('autotag', 'auto_imprint'): + self.auto_imprint = self.config.getboolean('autotag', 'auto_imprint') def save(self): @@ -483,6 +486,7 @@ class ComicTaggerSettings: 'autotag', 'wait_and_retry_on_rate_limit', self.wait_and_retry_on_rate_limit) + self.config.set('autotag', 'auto_imprint', self.auto_imprint) with codecs.open(self.settings_file, 'wb', 'utf8') as configfile: self.config.write(configfile) diff --git a/comictaggerlib/taggerwindow.py b/comictaggerlib/taggerwindow.py index be4ab8f..6684fe2 100644 --- a/comictaggerlib/taggerwindow.py +++ b/comictaggerlib/taggerwindow.py @@ -248,6 +248,8 @@ class TaggerWindow(QtWidgets.QMainWindow): self.fileSelectionList.addAppAction(self.actionRemoveAuto) self.fileSelectionList.addAppAction(self.actionRepackage) + self.btnAutoImprint.clicked.connect(self.autoImprint) + if len(file_list) != 0: self.fileSelectionList.addPathList(file_list) @@ -384,6 +386,8 @@ class TaggerWindow(QtWidgets.QMainWindow): self.actionSearchOnline.setStatusTip('Search online for tags') self.actionSearchOnline.triggered.connect(self.queryOnline) + self.actionAutoImprint.triggered.connect(self.autoImprint) + self.actionAutoIdentify.setShortcut('Ctrl+I') self.actionAutoIdentify.triggered.connect(self.autoIdentifySearch) @@ -425,6 +429,8 @@ class TaggerWindow(QtWidgets.QMainWindow): QtGui.QIcon(ComicTaggerSettings.getGraphic('auto.png'))) self.actionAutoTag.setIcon( QtGui.QIcon(ComicTaggerSettings.getGraphic('autotag.png'))) + self.actionAutoImprint.setIcon( + QtGui.QIcon(ComicTaggerSettings.getGraphic('autotag.png'))) self.actionClearEntryForm.setIcon( QtGui.QIcon(ComicTaggerSettings.getGraphic('clear.png'))) self.actionPageBrowser.setIcon( @@ -438,6 +444,7 @@ class TaggerWindow(QtWidgets.QMainWindow): self.toolBar.addAction(self.actionAutoTag) self.toolBar.addAction(self.actionClearEntryForm) self.toolBar.addAction(self.actionPageBrowser) + self.toolBar.addAction(self.actionAutoImprint) def repackageArchive(self): ca_list = self.fileSelectionList.getSelectedArchiveList() @@ -1816,6 +1823,9 @@ class TaggerWindow(QtWidgets.QMainWindow): if cv_md is not None: md.overlay(cv_md) + if self.settings.auto_imprint: + md.fixPublisher() + if not ca.writeMetadata(md, self.save_data_style): match_results.writeFailures.append(ca.path) self.autoTagLog("Save failed ;-(\n") @@ -2167,3 +2177,8 @@ class TaggerWindow(QtWidgets.QMainWindow): # self.show() self.setWindowFlags(flags) self.show() + + def autoImprint(self): + self.formToMetadata() + self.metadata.fixPublisher() + self.metadataToForm() diff --git a/comictaggerlib/ui/autotagstartwindow.ui b/comictaggerlib/ui/autotagstartwindow.ui index dc863d0..04d80a5 100644 --- a/comictaggerlib/ui/autotagstartwindow.ui +++ b/comictaggerlib/ui/autotagstartwindow.ui @@ -44,7 +44,7 @@ - + @@ -129,6 +129,16 @@ + + + + <html><head/><body><p>Checks the publisher against a list of imprints.</p></body></html> + + + Auto Imprint + + + @@ -145,7 +155,7 @@ - + @@ -155,7 +165,7 @@ - + diff --git a/comictaggerlib/ui/taggerwindow.ui b/comictaggerlib/ui/taggerwindow.ui index d5fcc3b..9a3d07c 100644 --- a/comictaggerlib/ui/taggerwindow.ui +++ b/comictaggerlib/ui/taggerwindow.ui @@ -1176,6 +1176,7 @@ + @@ -1373,6 +1374,14 @@ Search online for tags,auto-identify best match, and save to archive + + + Auto Imprint + + + Normalize the publisher and map imprints to their parent publisher (e.g. Vertigo is an imprint of DC Comics) + +