From a681abb854368bbed5e14f40260c1e5317e81a64 Mon Sep 17 00:00:00 2001 From: Timmy Welch Date: Sat, 27 Apr 2024 00:12:06 -0400 Subject: [PATCH] Consolidate preparing metadata for save --- comictaggerlib/autotagmatchwindow.py | 13 +++- comictaggerlib/cbltransformer.py | 2 +- comictaggerlib/cli.py | 100 ++++++++++----------------- comictaggerlib/md.py | 34 +++++++++ comictaggerlib/renamewindow.py | 2 +- comictaggerlib/taggerwindow.py | 88 ++++++++--------------- comictalker/talker_utils.py | 2 + 7 files changed, 115 insertions(+), 126 deletions(-) create mode 100644 comictaggerlib/md.py diff --git a/comictaggerlib/autotagmatchwindow.py b/comictaggerlib/autotagmatchwindow.py index fc49c20..fe62cbe 100644 --- a/comictaggerlib/autotagmatchwindow.py +++ b/comictaggerlib/autotagmatchwindow.py @@ -29,7 +29,7 @@ from comictaggerlib.ctsettings import ct_ns from comictaggerlib.resulttypes import IssueResult, Result from comictaggerlib.ui import ui_path from comictaggerlib.ui.qtutils import reduce_widget_font_size -from comictalker.comictalker import ComicTalker +from comictalker.comictalker import ComicTalker, TalkerError logger = logging.getLogger(__name__) @@ -240,8 +240,15 @@ class AutoTagMatchWindow(QtWidgets.QDialog): ) # now get the particular issue data - self.current_match_set.md = ct_md = self.fetch_func(match) - if ct_md is None: + + try: + self.current_match_set.md = ct_md = self.fetch_func(match) + except TalkerError as e: + QtWidgets.QApplication.restoreOverrideCursor() + QtWidgets.QMessageBox.critical(self, f"{e.source} {e.code_name} Error", f"{e}") + return + + if ct_md is None or ct_md.is_empty: QtWidgets.QMessageBox.critical(self, "Network Issue", "Could not retrieve issue details!") return diff --git a/comictaggerlib/cbltransformer.py b/comictaggerlib/cbltransformer.py index 0fe3256..79cbe4e 100644 --- a/comictaggerlib/cbltransformer.py +++ b/comictaggerlib/cbltransformer.py @@ -26,7 +26,7 @@ logger = logging.getLogger(__name__) class CBLTransformer: def __init__(self, metadata: GenericMetadata, config: ct_ns) -> None: - self.metadata = metadata + self.metadata = metadata.copy() self.config = config def apply(self) -> GenericMetadata: diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py index 508669a..d52034c 100644 --- a/comictaggerlib/cli.py +++ b/comictaggerlib/cli.py @@ -24,22 +24,20 @@ import os import pathlib import sys from collections.abc import Collection -from datetime import datetime from typing import Any, TextIO from comicapi import utils from comicapi.comicarchive import ComicArchive from comicapi.comicarchive import metadata_styles as md_styles from comicapi.genericmetadata import GenericMetadata -from comictaggerlib import ctversion from comictaggerlib.cbltransformer import CBLTransformer from comictaggerlib.ctsettings import ct_ns from comictaggerlib.filerenamer import FileRenamer, get_rename_dir from comictaggerlib.graphics import graphics_path from comictaggerlib.issueidentifier import IssueIdentifier +from comictaggerlib.md import prepare_metadata from comictaggerlib.resulttypes import Action, IssueResult, MatchStatus, OnlineMatchResults, Result, Status from comictalker.comictalker import ComicTalker, TalkerError -from comictalker.talker_utils import cleanup_html logger = logging.getLogger(__name__) @@ -104,7 +102,8 @@ class CLI: self.batch_mode = len(self.config.Runtime_Options__files) > 1 for f in self.config.Runtime_Options__files: - results.append(self.process_file_cli(self.config.Commands__command, f, match_results)) + res, match_results = self.process_file_cli(self.config.Commands__command, f, match_results) + results.append(res) if results[-1].status != Status.success: return_code = 3 if self.config.Runtime_Options__json: @@ -180,19 +179,8 @@ class CLI: ca = ComicArchive(match_set.original_path) md = self.create_local_metadata(ca) ct_md = self.actual_issue_data_fetch(match_set.online_results[int(i) - 1].issue_id) - if self.config.Issue_Identifier__clear_metadata: - md = ct_md - else: - notes = ( - f"Tagged with ComicTagger {ctversion.version} using info from {self.current_talker().name} on" - f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {ct_md.issue_id}]" - ) - md.overlay(ct_md.replace(notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger"))) - if self.config.Issue_Identifier__auto_imprint: - md.fix_publisher() - - match_set.md = md + match_set.md = prepare_metadata(md, ct_md, self.config) self.actual_metadata_save(ca, md) @@ -387,16 +375,19 @@ class CLI: res.status = status return res - def save(self, ca: ComicArchive, match_results: OnlineMatchResults) -> Result: + def save(self, ca: ComicArchive, match_results: OnlineMatchResults) -> tuple[Result, OnlineMatchResults]: if not self.config.Runtime_Options__overwrite: for style in self.config.Runtime_Options__type: if ca.has_metadata(style): self.output(f"{ca.path}: Already has {md_styles[style].name()} tags. Not overwriting.") - return Result( - Action.save, - original_path=ca.path, - status=Status.existing_tags, - tags_written=self.config.Runtime_Options__type, + return ( + Result( + Action.save, + original_path=ca.path, + status=Status.existing_tags, + tags_written=self.config.Runtime_Options__type, + ), + match_results, ) if self.batch_mode: @@ -409,6 +400,8 @@ class CLI: matches: list[IssueResult] = [] # now, search online + + ct_md = GenericMetadata() if self.config.Runtime_Options__online: if self.config.Runtime_Options__issue_id is not None: # we were given the actual issue ID to search with @@ -423,9 +416,9 @@ class CLI: tags_written=self.config.Runtime_Options__type, ) match_results.fetch_data_failures.append(res) - return res + return res, match_results - if ct_md is None: + if ct_md is None or ct_md.is_empty: logger.error("No match for ID %s was found.", self.config.Runtime_Options__issue_id) res = Result( Action.save, @@ -435,10 +428,8 @@ class CLI: tags_written=self.config.Runtime_Options__type, ) match_results.no_matches.append(res) - return res + return res, match_results - if self.config.Comic_Book_Lover__apply_transform_on_import: - ct_md = CBLTransformer(ct_md, self.config).apply() else: if md is None or md.is_empty: logger.error("No metadata given to search online with!") @@ -450,7 +441,7 @@ class CLI: tags_written=self.config.Runtime_Options__type, ) match_results.no_matches.append(res) - return res + return res, match_results ii = IssueIdentifier(ca, self.config, self.current_talker()) @@ -493,7 +484,7 @@ class CLI: tags_written=self.config.Runtime_Options__type, ) match_results.low_confidence_matches.append(res) - return res + return res, match_results logger.error("Online search: Multiple good matches. Save aborted") res = Result( @@ -505,7 +496,7 @@ class CLI: tags_written=self.config.Runtime_Options__type, ) match_results.multiple_matches.append(res) - return res + return res, match_results if low_confidence and self.config.Runtime_Options__abort_on_low_confidence: logger.error("Online search: Low confidence match. Save aborted") res = Result( @@ -517,7 +508,7 @@ class CLI: tags_written=self.config.Runtime_Options__type, ) match_results.low_confidence_matches.append(res) - return res + return res, match_results if not found_match: logger.error("Online search: No match found. Save aborted") res = Result( @@ -529,7 +520,7 @@ class CLI: tags_written=self.config.Runtime_Options__type, ) match_results.no_matches.append(res) - return res + return res, match_results # we got here, so we have a single match @@ -545,24 +536,7 @@ class CLI: tags_written=self.config.Runtime_Options__type, ) match_results.fetch_data_failures.append(res) - return res - - if self.config.Issue_Identifier__clear_metadata: - md = GenericMetadata() - - notes = ( - f"Tagged with ComicTagger {ctversion.version} using info from {self.current_talker().name} on" - + f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {ct_md.issue_id}]" - ) - md.overlay( - ct_md.replace( - notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger"), - description=cleanup_html(ct_md.description, self.config.Sources__remove_html_tables), - ) - ) - - if self.config.Issue_Identifier__auto_imprint: - md.fix_publisher() + return res, match_results res = Result( Action.save, @@ -570,7 +544,7 @@ class CLI: original_path=ca.path, online_results=matches, match_status=MatchStatus.good_match, - md=md, + md=prepare_metadata(md, ct_md, self.config), tags_written=self.config.Runtime_Options__type, ) # ok, done building our metadata. time to save @@ -579,7 +553,7 @@ class CLI: else: res.status = Status.write_failure match_results.write_failures.append(res) - return res + return res, match_results def rename(self, ca: ComicArchive) -> Result: original_path = ca.path @@ -697,36 +671,38 @@ class CLI: return Result(Action.export, Status.success, ca.path, new_file) - def process_file_cli(self, command: Action, filename: str, match_results: OnlineMatchResults) -> Result: + def process_file_cli( + self, command: Action, filename: str, match_results: OnlineMatchResults + ) -> tuple[Result, OnlineMatchResults]: if not os.path.lexists(filename): logger.error("Cannot find %s", filename) - return Result(command, Status.read_failure, pathlib.Path(filename)) + return Result(command, Status.read_failure, pathlib.Path(filename)), match_results ca = ComicArchive(filename, str(graphics_path / "nocover.png")) if not ca.seems_to_be_a_comic_archive(): logger.error("Sorry, but %s is not a comic archive!", filename) - return Result(Action.rename, Status.read_failure, ca.path) + return Result(Action.rename, Status.read_failure, ca.path), match_results if not ca.is_writable() and (command in (Action.delete, Action.copy, Action.save, Action.rename)): logger.error("This archive is not writable") - return Result(command, Status.write_permission_failure, ca.path) + return Result(command, Status.write_permission_failure, ca.path), match_results if command == Action.print: - return self.print(ca) + return self.print(ca), match_results elif command == Action.delete: - return self.delete(ca) + return self.delete(ca), match_results elif command == Action.copy is not None: - return self.copy(ca) + return self.copy(ca), match_results elif command == Action.save: return self.save(ca, match_results) elif command == Action.rename: - return self.rename(ca) + return self.rename(ca), match_results elif command == Action.export: - return self.export(ca) - return Result(None, Status.read_failure, ca.path) # type: ignore[arg-type] + return self.export(ca), match_results + return Result(None, Status.read_failure, ca.path), match_results # type: ignore[arg-type] diff --git a/comictaggerlib/md.py b/comictaggerlib/md.py new file mode 100644 index 0000000..4f3363a --- /dev/null +++ b/comictaggerlib/md.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +from datetime import datetime + +from comicapi import utils +from comicapi.genericmetadata import GenericMetadata +from comictaggerlib import ctversion +from comictaggerlib.cbltransformer import CBLTransformer +from comictaggerlib.ctsettings.settngs_namespace import SettngsNS +from comictalker.talker_utils import cleanup_html + + +def prepare_metadata(md: GenericMetadata, new_md: GenericMetadata, opts: SettngsNS) -> GenericMetadata: + if opts.Comic_Book_Lover__apply_transform_on_import: + new_md = CBLTransformer(new_md, opts).apply() + + final_md = md.copy() + if opts.Issue_Identifier__clear_metadata: + final_md = GenericMetadata() + + final_md.overlay(new_md) + assert final_md.tag_origin + notes = ( + f"Tagged with ComicTagger {ctversion.version} using info from {final_md.tag_origin.name} on" + f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {final_md.issue_id}]" + ) + final_md.replace( + notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger"), + description=cleanup_html(md.description, opts.Sources__remove_html_tables), + ) + + if opts.Issue_Identifier__auto_imprint: + md.fix_publisher() + return final_md diff --git a/comictaggerlib/renamewindow.py b/comictaggerlib/renamewindow.py index a7353d7..35fc734 100644 --- a/comictaggerlib/renamewindow.py +++ b/comictaggerlib/renamewindow.py @@ -80,7 +80,7 @@ class RenameWindow(QtWidgets.QDialog): if self.config[0].File_Rename__auto_extension: new_ext = ca.extension() - if md is None: + if md is None or md.is_empty: md = ca.read_metadata(self.data_style) if md.is_empty: md = ca.metadata_from_filename( diff --git a/comictaggerlib/taggerwindow.py b/comictaggerlib/taggerwindow.py index 3672591..431f1ba 100644 --- a/comictaggerlib/taggerwindow.py +++ b/comictaggerlib/taggerwindow.py @@ -25,8 +25,7 @@ import platform import re import sys import webbrowser -from datetime import datetime -from typing import Any, Callable +from typing import Any, Callable, cast import natsort import settngs @@ -52,18 +51,18 @@ from comictaggerlib.fileselectionlist import FileSelectionList from comictaggerlib.graphics import graphics_path from comictaggerlib.issueidentifier import IssueIdentifier from comictaggerlib.logwindow import LogWindow +from comictaggerlib.md import prepare_metadata from comictaggerlib.optionalmsgdialog import OptionalMessageDialog from comictaggerlib.pagebrowser import PageBrowserWindow from comictaggerlib.pagelisteditor import PageListEditor from comictaggerlib.renamewindow import RenameWindow -from comictaggerlib.resulttypes import Action, IssueResult, MatchStatus, OnlineMatchResults, Result, Status +from comictaggerlib.resulttypes import Action, MatchStatus, OnlineMatchResults, Result, Status from comictaggerlib.seriesselectionwindow import SeriesSelectionWindow from comictaggerlib.settingswindow import SettingsWindow from comictaggerlib.ui import ui_path from comictaggerlib.ui.qtutils import center_window_on_parent, enable_widget, reduce_widget_font_size from comictaggerlib.versionchecker import VersionChecker from comictalker.comictalker import ComicTalker, TalkerError -from comictalker.talker_utils import cleanup_html logger = logging.getLogger(__name__) @@ -1148,33 +1147,18 @@ class TaggerWindow(QtWidgets.QMainWindow): except TalkerError as e: QtWidgets.QApplication.restoreOverrideCursor() QtWidgets.QMessageBox.critical(self, f"{e.source} {e.code_name} Error", f"{e}") - else: - QtWidgets.QApplication.restoreOverrideCursor() - if new_metadata is not None: - if self.config[0].Comic_Book_Lover__apply_transform_on_import: - new_metadata = CBLTransformer(new_metadata, self.config[0]).apply() + return + QtWidgets.QApplication.restoreOverrideCursor() - if self.config[0].Issue_Identifier__clear_metadata: - self.clear_form() + if new_metadata is None or new_metadata.is_empty: + QtWidgets.QMessageBox.critical( + self, "Search", f"Could not find an issue {selector.issue_number} for that series" + ) + return - notes = ( - f"Tagged with ComicTagger {ctversion.version} using info from {self.current_talker().name} on" - f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {new_metadata.issue_id}]" - ) - self.metadata.overlay( - new_metadata.replace( - notes=utils.combine_notes(self.metadata.notes, notes, "Tagged with ComicTagger"), - description=cleanup_html( - new_metadata.description, self.config[0].Sources__remove_html_tables - ), - ) - ) - # Now push the new combined data into the edit controls - self.metadata_to_form() - else: - QtWidgets.QMessageBox.critical( - self, "Search", f"Could not find an issue {selector.issue_number} for that series" - ) + self.metadata = prepare_metadata(self.metadata, new_metadata, self.config[0]) + # Now push the new combined data into the edit controls + self.metadata_to_form() def commit_metadata(self) -> None: if self.metadata is not None and self.comic_archive is not None: @@ -1716,24 +1700,6 @@ class TaggerWindow(QtWidgets.QMainWindow): dlg.setWindowTitle("Tag Copy Summary") dlg.exec() - def actual_issue_data_fetch(self, match: IssueResult) -> GenericMetadata: - # now get the particular issue data OR series data - ct_md = GenericMetadata() - QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor)) - - try: - ct_md = self.current_talker().fetch_comic_data(match.issue_id) - except TalkerError: - logger.exception("Save aborted.") - - if not ct_md.is_empty: - if self.config[0].Comic_Book_Lover__apply_transform_on_import: - ct_md = CBLTransformer(ct_md, self.config[0]).apply() - - QtWidgets.QApplication.restoreOverrideCursor() - - return ct_md - def auto_tag_log(self, text: str) -> None: if self.atprogdialog is not None: self.atprogdialog.textEdit.append(text.rstrip()) @@ -1871,8 +1837,18 @@ class TaggerWindow(QtWidgets.QMainWindow): self.auto_tag_log("Online search: Low confidence match, but saving anyways, as indicated...\n") # now get the particular issue data - ct_md = self.actual_issue_data_fetch(matches[0]) - if ct_md is None: + QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor)) + + try: + + ct_md = self.current_talker().fetch_comic_data(matches[0].issue_id) + except TalkerError: + logger.exception("Save aborted.") + return False, match_results + + QtWidgets.QApplication.restoreOverrideCursor() + + if ct_md is None or ct_md.is_empty: match_results.fetch_data_failures.append( Result( Action.save, @@ -1884,17 +1860,11 @@ class TaggerWindow(QtWidgets.QMainWindow): ) if ct_md is not None: + temp_opts = cast(ct_ns, settngs.get_namespace(self.config, True, True, True, False)[0]) if dlg.cbxRemoveMetadata.isChecked(): - md = ct_md - else: - notes = ( - f"Tagged with ComicTagger {ctversion.version} using info from {self.current_talker().name} on" - f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {ct_md.issue_id}]" - ) - md.overlay(ct_md.replace(notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger"))) + temp_opts.Issue_Identifier__clear_metadata - if self.config[0].Issue_Identifier__auto_imprint: - md.fix_publisher() + md = prepare_metadata(md, ct_md, temp_opts) res = Result( Action.save, @@ -2036,7 +2006,7 @@ class TaggerWindow(QtWidgets.QMainWindow): self, match_results.multiple_matches, styles, - self.actual_issue_data_fetch, + lambda match: self.current_talker().fetch_comic_data(match.issue_id), self.config[0], self.current_talker(), ) diff --git a/comictalker/talker_utils.py b/comictalker/talker_utils.py index 02bf2c9..8f623d8 100644 --- a/comictalker/talker_utils.py +++ b/comictalker/talker_utils.py @@ -37,6 +37,8 @@ def cleanup_html(string: str | None, remove_html_tables: bool = False) -> str: """Cleans HTML code from any text. Will remove any HTML tables with remove_html_tables""" if string is None: return "" + if "<" not in string: + return string from bs4 import BeautifulSoup # find any tables