From b4f6820f56021e2dc23ebf0cd60959ef2023a7c3 Mon Sep 17 00:00:00 2001 From: Mizaki Date: Thu, 3 Nov 2022 23:32:35 +0000 Subject: [PATCH] remove_fetch_alternate_cover_urls.patch --- comictaggerlib/autotagmatchwindow.py | 5 +- comictaggerlib/coverimagewidget.py | 68 +------- comictaggerlib/imagefetcher.py | 4 +- comictaggerlib/issueidentifier.py | 22 ++- comictaggerlib/issueselectionwindow.py | 2 +- comictaggerlib/matchselectionwindow.py | 5 +- comictaggerlib/resulttypes.py | 2 +- comictaggerlib/volumeselectionwindow.py | 211 ++++++++++++------------ comictalker/comiccacher.py | 27 +-- comictalker/resulttypes.py | 16 +- comictalker/talkerbase.py | 4 - comictalker/talkers/comicvine.py | 38 +---- testing/comicdata.py | 4 +- tests/comicvinetalker_test.py | 2 +- 14 files changed, 172 insertions(+), 238 deletions(-) diff --git a/comictaggerlib/autotagmatchwindow.py b/comictaggerlib/autotagmatchwindow.py index 26f0f9d..516dc04 100644 --- a/comictaggerlib/autotagmatchwindow.py +++ b/comictaggerlib/autotagmatchwindow.py @@ -180,7 +180,10 @@ class AutoTagMatchWindow(QtWidgets.QDialog): if prev is not None and prev.row() == curr.row(): return None - self.altCoverWidget.set_issue_details(self.current_match()["issue_id"], self.current_match()["image_url"]) + self.altCoverWidget.set_issue_details( + self.current_match()["issue_id"], + [self.current_match()["image_url"], *self.current_match()["alt_image_urls"]], + ) if self.current_match()["description"] is None: self.teDescription.setText("") else: diff --git a/comictaggerlib/coverimagewidget.py b/comictaggerlib/coverimagewidget.py index a723782..6c7ab3c 100644 --- a/comictaggerlib/coverimagewidget.py +++ b/comictaggerlib/coverimagewidget.py @@ -20,11 +20,9 @@ TODO: This should be re-factored using subclasses! from __future__ import annotations import logging -from typing import Callable from PyQt5 import QtCore, QtGui, QtWidgets, uic -from comicapi import utils from comicapi.comicarchive import ComicArchive from comictaggerlib.graphics import graphics_path from comictaggerlib.imagefetcher import ImageFetcher @@ -56,38 +54,14 @@ def clickable(widget: QtWidgets.QWidget) -> QtCore.pyqtBoundSignal: return flt.dblclicked -class Signal(QtCore.QObject): - alt_url_list_fetch_complete = QtCore.pyqtSignal(list) - url_fetch_complete = QtCore.pyqtSignal(str, str) - image_fetch_complete = QtCore.pyqtSignal(QtCore.QByteArray) - - def __init__( - self, - list_fetch: Callable[[list[str]], None], - url_fetch: Callable[[str, str], None], - image_fetch: Callable[[bytes], None], - ) -> None: - super().__init__() - self.alt_url_list_fetch_complete.connect(list_fetch) - self.url_fetch_complete.connect(url_fetch) - self.image_fetch_complete.connect(image_fetch) - - def emit_list(self, url_list: list[str]) -> None: - self.alt_url_list_fetch_complete.emit(url_list) - - def emit_url(self, image_url: str, thumb_url: str | None) -> None: - self.url_fetch_complete.emit(image_url, thumb_url) - - def emit_image(self, image_data: bytes | QtCore.QByteArray) -> None: - self.image_fetch_complete.emit(image_data) - - class CoverImageWidget(QtWidgets.QWidget): ArchiveMode = 0 AltCoverMode = 1 URLMode = 1 DataMode = 3 + image_fetch_complete = QtCore.pyqtSignal(QtCore.QByteArray) + def __init__( self, parent: QtWidgets.QWidget, talker_api: ComicTalker, mode: int, expand_on_click: bool = True ) -> None: @@ -98,10 +72,6 @@ class CoverImageWidget(QtWidgets.QWidget): reduce_widget_font_size(self.label) - self.sig = Signal( - self.alt_cover_url_list_fetch_complete, self.primary_url_fetch_complete, self.cover_remote_fetch_complete - ) - self.talker_api = talker_api self.mode: int = mode self.page_loader: PageLoader | None = None @@ -125,6 +95,7 @@ class CoverImageWidget(QtWidgets.QWidget): self.btnLeft.clicked.connect(self.decrement_image) self.btnRight.clicked.connect(self.increment_image) + self.image_fetch_complete.connect(self.cover_remote_fetch_complete) if expand_on_click: clickable(self.lblImage).connect(self.show_popup) else: @@ -178,13 +149,13 @@ class CoverImageWidget(QtWidgets.QWidget): self.imageCount = 1 self.update_content() - def set_issue_details(self, issue_id: int, image_url: str) -> None: + def set_issue_details(self, issue_id: int, url_list: list[str]) -> None: if self.mode == CoverImageWidget.AltCoverMode: self.reset_widget() self.update_content() self.issue_id = issue_id - self.sig.emit_url(image_url, None) + self.set_url_list(url_list) def set_image_data(self, image_data: bytes) -> None: if self.mode == CoverImageWidget.DataMode: @@ -198,32 +169,11 @@ class CoverImageWidget(QtWidgets.QWidget): self.update_content() - def primary_url_fetch_complete(self, primary_url: str, thumb_url: str | None = None) -> None: - self.url_list.append(str(primary_url)) + def set_url_list(self, url_list: list[str]) -> None: + self.url_list = url_list self.imageIndex = 0 self.imageCount = len(self.url_list) self.update_content() - - # No need to search for alt covers as they are now in ComicIssue data - if self.talker_api.static_options.has_alt_covers: - QtCore.QTimer.singleShot(1, self.start_alt_cover_search) - - def start_alt_cover_search(self) -> None: - - if self.issue_id is not None: - # now we need to get the list of alt cover URLs - self.label.setText("Searching for alt. covers...") - - url_list = self.talker_api.fetch_alternate_cover_urls(utils.xlate(self.issue_id)) - if url_list: - self.url_list.extend(url_list) - self.imageCount = len(self.url_list) - self.update_controls() - - def alt_cover_url_list_fetch_complete(self, url_list: list[str]) -> None: - if url_list: - self.url_list.extend(url_list) - self.imageCount = len(self.url_list) self.update_controls() def set_page(self, pagenum: int) -> None: @@ -273,10 +223,10 @@ class CoverImageWidget(QtWidgets.QWidget): def load_url(self) -> None: self.load_default() self.cover_fetcher = ImageFetcher() - ImageFetcher.image_fetch_complete = self.sig.emit_image + ImageFetcher.image_fetch_complete = self.image_fetch_complete.emit self.cover_fetcher.fetch(self.url_list[self.imageIndex]) # TODO: Figure out async - self.sig.emit_image(self.cover_fetcher.fetch(self.url_list[self.imageIndex])) + # self.sig.emit_image(self.cover_fetcher.fetch(self.url_list[self.imageIndex])) # called when the image is done loading from internet def cover_remote_fetch_complete(self, image_data: bytes) -> None: diff --git a/comictaggerlib/imagefetcher.py b/comictaggerlib/imagefetcher.py index 6214d2b..214f2b8 100644 --- a/comictaggerlib/imagefetcher.py +++ b/comictaggerlib/imagefetcher.py @@ -81,8 +81,8 @@ class ImageFetcher: # first look in the DB image_data = self.get_image_from_cache(url) - # TODO: figure out async - if True: # if blocking or not qt_available: + # Async for retrieving covers seems to work well + if blocking: # if blocking or not qt_available: if not image_data: try: image_data = requests.get(url, headers={"user-agent": "comictagger/" + ctversion.version}).content diff --git a/comictaggerlib/issueidentifier.py b/comictaggerlib/issueidentifier.py index 833a76e..e2f702d 100644 --- a/comictaggerlib/issueidentifier.py +++ b/comictaggerlib/issueidentifier.py @@ -243,7 +243,7 @@ class IssueIdentifier: issue_id: int, primary_img_url: str, primary_thumb_url: str, - page_url: str, + alt_urls: list[str], local_cover_hash_list: list[int], use_remote_alternates: bool = False, use_log: bool = True, @@ -270,8 +270,7 @@ class IssueIdentifier: raise IssueIdentifierCancelled if use_remote_alternates: - alt_img_url_list = self.talker_api.fetch_alternate_cover_urls(issue_id) - for alt_url in alt_img_url_list: + for alt_url in alt_urls: try: alt_url_image_data = ImageFetcher().fetch(alt_url, blocking=True) except ImageFetcherException as e: @@ -395,10 +394,7 @@ class IssueIdentifier: if int(keys["year"]) < int(item["start_year"]): date_approved = False - aliases = [] - if item["aliases"]: - aliases = item["aliases"].split("\n") - for name in [item["name"], *aliases]: + for name in [item["name"], *item["aliases"]]: if utils.titles_match(keys["series"], name, self.series_match_thresh): length_approved = True break @@ -429,13 +425,12 @@ class IssueIdentifier: try: image_url = series["image_url"] thumb_url = series["image_url"] - page_url = "" score_item = self.get_issue_cover_match_score( series["id"], image_url, thumb_url, - page_url, # Only required for alt covers + [], # Only required for alt covers hash_list, use_remote_alternates=False, ) @@ -457,7 +452,7 @@ class IssueIdentifier: "publisher": series["publisher"], "image_url": image_url, "thumb_url": thumb_url, - "page_url": page_url, + "alt_image_urls": [], "description": series["description"], } @@ -525,12 +520,13 @@ class IssueIdentifier: image_url = issue["image_url"] thumb_url = issue["image_thumb_url"] page_url = issue["site_detail_url"] + alt_urls = issue["alt_image_urls"] score_item = self.get_issue_cover_match_score( issue["id"], image_url, thumb_url, - page_url, + alt_urls, hash_list, use_remote_alternates=False, ) @@ -553,6 +549,7 @@ class IssueIdentifier: "image_url": image_url, "thumb_url": thumb_url, "page_url": page_url, + "alt_image_urls": alt_urls, "description": issue["description"], } if series["publisher"] is not None: @@ -617,11 +614,12 @@ class IssueIdentifier: m["issue_id"], m["image_url"], m["thumb_url"], - m["page_url"], + m["alt_image_urls"], hash_list, use_remote_alternates=True, ) except Exception: + logger.exception("failed examining alt covers") self.match_list = [] return self.match_list self.log_msg(f"--->{score_item['score']}") diff --git a/comictaggerlib/issueselectionwindow.py b/comictaggerlib/issueselectionwindow.py index e2b271b..e1b6e0c 100644 --- a/comictaggerlib/issueselectionwindow.py +++ b/comictaggerlib/issueselectionwindow.py @@ -180,7 +180,7 @@ class IssueSelectionWindow(QtWidgets.QDialog): for record in self.issue_list: if record["id"] == self.issue_id: self.issue_number = record["issue_number"] - self.coverWidget.set_issue_details(self.issue_id, record["image_url"]) + self.coverWidget.set_issue_details(self.issue_id, [record["image_url"], *record["alt_image_urls"]]) if record["description"] is None: self.teDescription.setText("") else: diff --git a/comictaggerlib/matchselectionwindow.py b/comictaggerlib/matchselectionwindow.py index f04c387..330318c 100644 --- a/comictaggerlib/matchselectionwindow.py +++ b/comictaggerlib/matchselectionwindow.py @@ -149,7 +149,10 @@ class MatchSelectionWindow(QtWidgets.QDialog): if prev is not None and prev.row() == curr.row(): return - self.altCoverWidget.set_issue_details(self.current_match()["issue_id"], self.current_match()["image_url"]) + self.altCoverWidget.set_issue_details( + self.current_match()["issue_id"], + [self.current_match()["image_url"], *self.current_match()["alt_image_urls"]], + ) if self.current_match()["description"] is None: self.teDescription.setText("") else: diff --git a/comictaggerlib/resulttypes.py b/comictaggerlib/resulttypes.py index 8fe1d33..4261fa5 100644 --- a/comictaggerlib/resulttypes.py +++ b/comictaggerlib/resulttypes.py @@ -19,7 +19,7 @@ class IssueResult(TypedDict): publisher: str | None image_url: str thumb_url: str - page_url: str + alt_image_urls: list[str] description: str diff --git a/comictaggerlib/volumeselectionwindow.py b/comictaggerlib/volumeselectionwindow.py index bb534dc..ffc8a27 100644 --- a/comictaggerlib/volumeselectionwindow.py +++ b/comictaggerlib/volumeselectionwindow.py @@ -170,7 +170,6 @@ class VolumeSelectionWindow(QtWidgets.QDialog): self.cbxFilter.toggled.connect(self.filter_toggled) self.update_buttons() - self.perform_query() self.twList.selectRow(0) def update_buttons(self) -> None: @@ -331,18 +330,23 @@ class VolumeSelectionWindow(QtWidgets.QDialog): def perform_query(self, refresh: bool = False) -> None: - self.progdialog = QtWidgets.QProgressDialog("Searching Online", "Cancel", 0, 100, self) - self.progdialog.setWindowTitle("Online Search") - self.progdialog.canceled.connect(self.search_canceled) - self.progdialog.setModal(True) - self.progdialog.setMinimumDuration(300) self.search_thread = SearchThread( self.talker_api, self.series_name, refresh, self.literal, self.settings.id_series_match_search_thresh ) self.search_thread.searchComplete.connect(self.search_complete) self.search_thread.progressUpdate.connect(self.search_progress_update) self.search_thread.start() - self.progdialog.exec() + + self.progdialog = QtWidgets.QProgressDialog("Searching Online", "Cancel", 0, 100, self) + self.progdialog.setWindowTitle("Online Search") + self.progdialog.canceled.connect(self.search_canceled) + self.progdialog.setModal(True) + self.progdialog.setMinimumDuration(300) + + if refresh or self.search_thread.isRunning(): + self.progdialog.exec() + else: + self.progdialog = None def search_canceled(self) -> None: if self.progdialog is not None: @@ -365,122 +369,123 @@ class VolumeSelectionWindow(QtWidgets.QDialog): def search_complete(self) -> None: if self.progdialog is not None: self.progdialog.accept() - del self.progdialog - if self.search_thread is not None and self.search_thread.ct_error: - # TODO Currently still opens the window - QtWidgets.QMessageBox.critical( - self, - f"{self.search_thread.error_e.source} {self.search_thread.error_e.code_name} Error", - f"{self.search_thread.error_e}", - ) - return + self.progdialog = None + if self.search_thread is not None and self.search_thread.ct_error: + # TODO Currently still opens the window + QtWidgets.QMessageBox.critical( + self, + f"{self.search_thread.error_e.source} {self.search_thread.error_e.code_name} Error", + f"{self.search_thread.error_e}", + ) + return - self.ct_search_results = self.search_thread.ct_search_results if self.search_thread is not None else [] - # filter the publishers if enabled set - if self.use_filter: - try: - publisher_filter = {s.strip().casefold() for s in self.settings.id_publisher_filter.split(",")} - # use '' as publisher name if None - self.ct_search_results = list( - filter( - lambda d: ("" if d["publisher"] is None else str(d["publisher"]).casefold()) - not in publisher_filter, - self.ct_search_results, - ) - ) - except Exception: - logger.exception("bad data error filtering publishers") - - # pre sort the data - so that we can put exact matches first afterwards - # compare as str in case extra chars ie. '1976?' - # - missing (none) values being converted to 'None' - consistent with prior behaviour in v1.2.3 - # sort by start_year if set - if self.settings.sort_series_by_year: - try: - self.ct_search_results = sorted( + self.ct_search_results = self.search_thread.ct_search_results if self.search_thread is not None else [] + # filter the publishers if enabled set + if self.use_filter: + try: + publisher_filter = {s.strip().casefold() for s in self.settings.id_publisher_filter.split(",")} + # use '' as publisher name if None + self.ct_search_results = list( + filter( + lambda d: ("" if d["publisher"] is None else str(d["publisher"]).casefold()) + not in publisher_filter, self.ct_search_results, - key=lambda i: (str(i["start_year"]), str(i["count_of_issues"])), - reverse=True, ) - except Exception: - logger.exception("bad data error sorting results by start_year,count_of_issues") - else: - try: - self.ct_search_results = sorted( - self.ct_search_results, key=lambda i: str(i["count_of_issues"]), reverse=True - ) - except Exception: - logger.exception("bad data error sorting results by count_of_issues") + ) + except Exception: + logger.exception("bad data error filtering publishers") - # move sanitized matches to the front - if self.settings.exact_series_matches_first: - try: - sanitized = utils.sanitize_title(self.series_name, False).casefold() - sanitized_no_articles = utils.sanitize_title(self.series_name, True).casefold() + # pre sort the data - so that we can put exact matches first afterwards + # compare as str in case extra chars ie. '1976?' + # - missing (none) values being converted to 'None' - consistent with prior behaviour in v1.2.3 + # sort by start_year if set + if self.settings.sort_series_by_year: + try: + self.ct_search_results = sorted( + self.ct_search_results, + key=lambda i: (str(i["start_year"]), str(i["count_of_issues"])), + reverse=True, + ) + except Exception: + logger.exception("bad data error sorting results by start_year,count_of_issues") + else: + try: + self.ct_search_results = sorted( + self.ct_search_results, key=lambda i: str(i["count_of_issues"]), reverse=True + ) + except Exception: + logger.exception("bad data error sorting results by count_of_issues") - deques: list[deque[ComicVolume]] = [deque(), deque(), deque()] + # move sanitized matches to the front + if self.settings.exact_series_matches_first: + try: + sanitized = utils.sanitize_title(self.series_name, False).casefold() + sanitized_no_articles = utils.sanitize_title(self.series_name, True).casefold() - def categorize(result: ComicVolume) -> int: - # We don't remove anything on this one so that we only get exact matches - if utils.sanitize_title(result["name"], True).casefold() == sanitized_no_articles: - return 0 + deques: list[deque[ComicVolume]] = [deque(), deque(), deque()] - # this ensures that 'The Joker' is near the top even if you search 'Joker' - if utils.sanitize_title(result["name"], False).casefold() in sanitized: - return 1 - return 2 + def categorize(result: ComicVolume) -> int: + # We don't remove anything on this one so that we only get exact matches + if utils.sanitize_title(result["name"], True).casefold() == sanitized_no_articles: + return 0 - for comic in self.ct_search_results: - deques[categorize(comic)].append(comic) - logger.info("Length: %d, %d, %d", len(deques[0]), len(deques[1]), len(deques[2])) - self.ct_search_results = list(itertools.chain.from_iterable(deques)) - except Exception: - logger.exception("bad data error filtering exact/near matches") + # this ensures that 'The Joker' is near the top even if you search 'Joker' + if utils.sanitize_title(result["name"], False).casefold() in sanitized: + return 1 + return 2 - self.update_buttons() + for comic in self.ct_search_results: + deques[categorize(comic)].append(comic) + logger.info("Length: %d, %d, %d", len(deques[0]), len(deques[1]), len(deques[2])) + self.ct_search_results = list(itertools.chain.from_iterable(deques)) + except Exception: + logger.exception("bad data error filtering exact/near matches") - self.twList.setSortingEnabled(False) + self.update_buttons() - self.twList.setRowCount(0) + self.twList.setSortingEnabled(False) - row = 0 - for record in self.ct_search_results: - self.twList.insertRow(row) + self.twList.setRowCount(0) - item_text = record["name"] - item = QtWidgets.QTableWidgetItem(item_text) + row = 0 + for record in self.ct_search_results: + self.twList.insertRow(row) + + item_text = record["name"] + item = QtWidgets.QTableWidgetItem(item_text) + item.setData(QtCore.Qt.ItemDataRole.ToolTipRole, item_text) + item.setData(QtCore.Qt.ItemDataRole.UserRole, record["id"]) + item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled) + self.twList.setItem(row, 0, item) + + item_text = str(record["start_year"]) + item = QtWidgets.QTableWidgetItem(item_text) + item.setData(QtCore.Qt.ItemDataRole.ToolTipRole, item_text) + item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled) + self.twList.setItem(row, 1, item) + + item_text = str(record["count_of_issues"]) + item = QtWidgets.QTableWidgetItem(item_text) + item.setData(QtCore.Qt.ItemDataRole.ToolTipRole, item_text) + item.setData(QtCore.Qt.ItemDataRole.DisplayRole, record["count_of_issues"]) + item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled) + self.twList.setItem(row, 2, item) + + if record["publisher"] is not None: + item_text = record["publisher"] item.setData(QtCore.Qt.ItemDataRole.ToolTipRole, item_text) - item.setData(QtCore.Qt.ItemDataRole.UserRole, record["id"]) - item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled) - self.twList.setItem(row, 0, item) - - item_text = str(record["start_year"]) item = QtWidgets.QTableWidgetItem(item_text) - item.setData(QtCore.Qt.ItemDataRole.ToolTipRole, item_text) item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled) - self.twList.setItem(row, 1, item) + self.twList.setItem(row, 3, item) - item_text = str(record["count_of_issues"]) - item = QtWidgets.QTableWidgetItem(item_text) - item.setData(QtCore.Qt.ItemDataRole.ToolTipRole, item_text) - item.setData(QtCore.Qt.ItemDataRole.DisplayRole, record["count_of_issues"]) - item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled) - self.twList.setItem(row, 2, item) + row += 1 - if record["publisher"] is not None: - item_text = record["publisher"] - item.setData(QtCore.Qt.ItemDataRole.ToolTipRole, item_text) - item = QtWidgets.QTableWidgetItem(item_text) - item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled) - self.twList.setItem(row, 3, item) - - row += 1 - - self.twList.setSortingEnabled(True) - self.twList.selectRow(0) - self.twList.resizeColumnsToContents() + self.twList.setSortingEnabled(True) + self.twList.selectRow(0) + self.twList.resizeColumnsToContents() def showEvent(self, event: QtGui.QShowEvent) -> None: + self.perform_query() if not self.ct_search_results: QtCore.QCoreApplication.processEvents() QtWidgets.QMessageBox.information(self, "Search Result", "No matches found!") diff --git a/comictalker/comiccacher.py b/comictalker/comiccacher.py index c6b3030..18a33c5 100644 --- a/comictalker/comiccacher.py +++ b/comictalker/comiccacher.py @@ -111,7 +111,7 @@ class ComicCacher: + "timestamp DATE DEFAULT (datetime('now','localtime')), " + "source_name TEXT NOT NULL," + "aliases TEXT," # Newline separated - + "alt_images_url TEXT," # Comma separated URLs + + "alt_image_urls TEXT," # Newline separated URLs + "characters TEXT," # Newline separated + "locations TEXT," # Newline separated + "credits TEXT," # JSON: "{"name": "Bob Shakespeare", "role": "Writer"}" @@ -146,10 +146,17 @@ class ComicCacher: ) data = { + "id": record["id"], "source_name": source_name, + "name": record["name"], + "publisher": record["publisher"], + "count_of_issues": record["count_of_issues"], + "image_url": record["image_url"], + "start_year": record["start_year"], + "description": record["description"], "timestamp": datetime.datetime.now(), + "aliases": "\n".join(record["aliases"]), } - data.update(record) self.upsert(cur, "volumes", data) def get_search_results(self, source_name: str, search_term: str) -> list[ComicVolume]: @@ -176,7 +183,7 @@ class ComicCacher: count_of_issues=record[7], start_year=record[8], image_url=record[9], - aliases=record[10], + aliases=record[10].split("\n"), description=record[11], ) @@ -201,7 +208,7 @@ class ComicCacher: "count_of_issues": volume_record["count_of_issues"], "start_year": volume_record["start_year"], "timestamp": timestamp, - "aliases": volume_record["aliases"], + "aliases": "\n".join(volume_record["aliases"]), } self.upsert(cur, "volumes", data) @@ -228,8 +235,8 @@ class ComicCacher: "thumb_url": issue["image_thumb_url"], "description": issue["description"], "timestamp": timestamp, - "aliases": issue["aliases"], - "alt_images_url": issue["alt_images_url"], + "aliases": "\n".join(issue["aliases"]), + "alt_image_urls": "\n".join(issue["alt_image_urls"]), "characters": "\n".join(issue["characters"]), "locations": "\n".join(issue["locations"]), "teams": "\n".join(issue["teams"]), @@ -295,7 +302,7 @@ class ComicCacher: cur.execute( ( - "SELECT source_name,id,name,issue_number,site_detail_url,cover_date,image_url,thumb_url,description,aliases,alt_images_url,characters,locations,credits,teams,story_arcs,complete" + "SELECT source_name,id,name,issue_number,site_detail_url,cover_date,image_url,thumb_url,description,aliases,alt_image_urls,characters,locations,credits,teams,story_arcs,complete" " FROM Issues WHERE volume_id=? AND source_name=?" ), [volume_id, source_name], @@ -314,7 +321,7 @@ class ComicCacher: description=row[8], volume=volume, aliases=row[9], - alt_images_url=row[10], + alt_image_urls=row[10].split("\n"), characters=row[11].split("\n"), locations=row[12].split("\n"), credits=json.loads(row[13]), @@ -340,7 +347,7 @@ class ComicCacher: cur.execute( ( - "SELECT source_name,id,name,issue_number,site_detail_url,cover_date,image_url,thumb_url,description,aliases,volume_id,alt_images_url,characters,locations,credits,teams,story_arcs,complete" + "SELECT source_name,id,name,issue_number,site_detail_url,cover_date,image_url,thumb_url,description,aliases,volume_id,alt_image_urls,characters,locations,credits,teams,story_arcs,complete" " FROM Issues WHERE id=? AND source_name=?" ), [issue_id, source_name], @@ -366,7 +373,7 @@ class ComicCacher: description=row[8], volume=volume, aliases=row[9], - alt_images_url=row[11], + alt_image_urls=row[11].split("\n"), characters=row[12].split("\n"), locations=row[13].split("\n"), credits=json.loads(row[14]), diff --git a/comictalker/resulttypes.py b/comictalker/resulttypes.py index d034c73..ca2b013 100644 --- a/comictalker/resulttypes.py +++ b/comictalker/resulttypes.py @@ -9,7 +9,7 @@ class Credits(TypedDict): class ComicVolume(TypedDict, total=False): - aliases: str # Newline separated + aliases: list[str] count_of_issues: int description: str id: Required[int] @@ -20,7 +20,7 @@ class ComicVolume(TypedDict, total=False): class ComicIssue(TypedDict, total=False): - aliases: str # Newline separated + aliases: list[str] cover_date: str description: str id: int @@ -30,10 +30,10 @@ class ComicIssue(TypedDict, total=False): name: Required[str] site_detail_url: str volume: ComicVolume - alt_images_url: str # Comma separated URLs - characters: list - locations: list + alt_image_urls: list[str] + characters: list[str] + locations: list[str] credits: list[Credits] - teams: list - story_arcs: list - complete: bool # Is the data complete? Includes characters, locations, credits. + teams: list[str] + story_arcs: list[str] + complete: bool # Is this a complete ComicIssue? or is there more data to fetch diff --git a/comictalker/talkerbase.py b/comictalker/talkerbase.py index 85f0a4e..b9385dd 100644 --- a/comictalker/talkerbase.py +++ b/comictalker/talkerbase.py @@ -186,10 +186,6 @@ class ComicTalker: 3. Only issue_id. Retrieve the ISSUE information.""" raise NotImplementedError - # TODO Should be able to remove with alt cover rework - def fetch_alternate_cover_urls(self, issue_id: int) -> list[str]: - raise NotImplementedError - def fetch_issues_by_volume_issue_num_and_year( self, volume_id_list: list[int], issue_number: str, year: str | int | None ) -> list[ComicIssue]: diff --git a/comictalker/talkers/comicvine.py b/comictalker/talkers/comicvine.py index bf91e2e..35aa3ec 100644 --- a/comictalker/talkers/comicvine.py +++ b/comictalker/talkers/comicvine.py @@ -376,10 +376,11 @@ class ComicVineTalker(ComicTalker): start_year = 0 else: start_year = utils.xlate(record["start_year"], True) + aliases = record["aliases"] or "" formatted_results.append( ComicVolume( - aliases=record["aliases"], + aliases=aliases.split("\n"), count_of_issues=record.get("count_of_issues", 0), description=record.get("description", ""), id=record["id"], @@ -410,7 +411,7 @@ class ComicVineTalker(ComicTalker): # Convert to comma separated alt_images_list.append(alt["original_url"]) - alt_images_url = ",".join(alt_images_list) + alt_image_urls = alt_images_list character_list = [] if record.get("character_credits"): @@ -439,7 +440,7 @@ class ComicVineTalker(ComicTalker): formatted_results.append( ComicIssue( - aliases=record["aliases"], + aliases=record["aliases"].split("\n") if record["aliases"] else [], cover_date=record.get("cover_date", ""), description=record.get("description", ""), id=record["id"], @@ -449,7 +450,7 @@ class ComicVineTalker(ComicTalker): name=record["name"], site_detail_url=record.get("site_detail_url", ""), volume=cast(ComicVolume, record["volume"]), - alt_images_url=alt_images_url, + alt_image_urls=alt_image_urls, characters=character_list, locations=location_list, teams=teams_list, @@ -957,35 +958,6 @@ class ComicVineTalker(ComicTalker): return newstring - def fetch_alternate_cover_urls(self, issue_id: int) -> list[str]: - # Should always be in cache - cvc = ComicCacher() - issue = cvc.get_issue_info(issue_id, self.source_name) - - if issue: - url_list = [x for x in issue["alt_images_url"].split(",") if x] # Prevent an empty string producing [''] - else: - # On the off chance it is not in cache - params = { - "api_key": self.api_key, - "filter": f"id:{issue_id}", - "format": "json", - "field_list": "id,volume,issue_number,name,image,cover_date,site_detail_url,description,aliases,associated_images", - "offset": 0, - } - cv_response = self.get_cv_content(urljoin(self.api_base_url, "issues/"), params) - - issue_result = cast(list[CVIssueDetailResults], cv_response["results"]) - - # Format to expected output - formatted_volume_issues_result = self.format_issue_results(issue_result) - - cvc.add_volume_issues_info(self.source_name, formatted_volume_issues_result) - - url_list = [x for x in formatted_volume_issues_result[0]["alt_images_url"].split(",") if x] - - return url_list - def repair_urls(self, issue_list: list[CVIssueDetailResults]) -> None: # make sure there are URLs for the image fields for issue in issue_list: diff --git a/testing/comicdata.py b/testing/comicdata.py index 09a1bea..4340ba7 100644 --- a/testing/comicdata.py +++ b/testing/comicdata.py @@ -13,7 +13,7 @@ search_results = [ name="test", publisher="test", start_year=0, - aliases="", + aliases=[], ), comictalker.resulttypes.ComicVolume( count_of_issues=1, @@ -23,7 +23,7 @@ search_results = [ name="test 2", publisher="test", start_year=0, - aliases="", + aliases=[], ), ] diff --git a/tests/comicvinetalker_test.py b/tests/comicvinetalker_test.py index f972740..5333218 100644 --- a/tests/comicvinetalker_test.py +++ b/tests/comicvinetalker_test.py @@ -62,7 +62,7 @@ def test_fetch_issues_by_volume_issue_num_and_year(comicvine_api): for r, e in zip(results, [cv_expected]): del r["image_thumb_url"] del r["image_url"] - del r["alt_images_url"] + del r["alt_image_urls"] del r["characters"] del r["credits"] del r["locations"]