Consolidate preparing metadata for save

This commit is contained in:
Timmy Welch 2024-04-27 00:12:06 -04:00
parent 996397b9d5
commit a681abb854
7 changed files with 115 additions and 126 deletions

View File

@ -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

View File

@ -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:

View File

@ -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]

34
comictaggerlib/md.py Normal file
View File

@ -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

View File

@ -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(

View File

@ -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(),
)

View File

@ -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