From a9da87bff3fea39d0a1c70e25a402a66c5adf72a Mon Sep 17 00:00:00 2001 From: Timmy Welch Date: Sat, 4 Jan 2025 15:14:32 -0800 Subject: [PATCH] Improve canceling during a ratelimit --- comictaggerlib/autotagmatchwindow.py | 9 ++--- comictaggerlib/autotagprogresswindow.py | 36 +++++++++++++------ comictaggerlib/renamewindow.py | 2 +- comictaggerlib/taggerwindow.py | 1 + .../pyrate_limiter/limit_context_decorator.py | 22 ++++++------ 5 files changed, 41 insertions(+), 29 deletions(-) diff --git a/comictaggerlib/autotagmatchwindow.py b/comictaggerlib/autotagmatchwindow.py index a68f17a..c91dcc3 100644 --- a/comictaggerlib/autotagmatchwindow.py +++ b/comictaggerlib/autotagmatchwindow.py @@ -22,7 +22,6 @@ import os from PyQt5 import QtCore, QtGui, QtWidgets, uic from comicapi.comicarchive import ComicArchive, tags -from comicapi.genericmetadata import GenericMetadata from comictaggerlib.coverimagewidget import CoverImageWidget from comictaggerlib.ctsettings import ct_ns from comictaggerlib.md import prepare_metadata @@ -77,6 +76,7 @@ class AutoTagMatchWindow(QtWidgets.QDialog): self.match_set_list = match_set_list self._tags = read_tags + self.talker = talker self.current_match_set_idx = 0 @@ -86,9 +86,6 @@ class AutoTagMatchWindow(QtWidgets.QDialog): self.update_data() - def fetch(self, match: IssueResult) -> GenericMetadata: - return self.current_talker().fetch_comic_data(issue_id=match.issue_id) - def update_data(self) -> None: self.current_match_set = self.match_set_list[self.current_match_set_idx] @@ -226,7 +223,7 @@ class AutoTagMatchWindow(QtWidgets.QDialog): def save_match(self) -> None: match = self.current_match() ca = ComicArchive(self.current_match_set.original_path) - md, error = self.parent().read_selected_tags(self._tags, ca) + md, _, error = self.parent().read_selected_tags(self._tags, ca) if error is not None: logger.error("Failed to load tags for %s: %s", ca.path, error) QtWidgets.QApplication.restoreOverrideCursor() @@ -248,7 +245,7 @@ class AutoTagMatchWindow(QtWidgets.QDialog): # now get the particular issue data try: - self.current_match_set.md = ct_md = self.fetch(match) + self.current_match_set.md = ct_md = self.talker.fetch_comic_data(issue_id=match.issue_id) except TalkerError as e: QtWidgets.QApplication.restoreOverrideCursor() QtWidgets.QMessageBox.critical(self, f"{e.source} {e.code_name} Error", f"{e}") diff --git a/comictaggerlib/autotagprogresswindow.py b/comictaggerlib/autotagprogresswindow.py index 7c75ad4..5381f25 100644 --- a/comictaggerlib/autotagprogresswindow.py +++ b/comictaggerlib/autotagprogresswindow.py @@ -27,6 +27,7 @@ from comicapi.comicarchive import ComicArchive, tags from comicapi.genericmetadata import GenericMetadata from comictaggerlib.coverimagewidget import CoverImageWidget from comictaggerlib.ctsettings.settngs_namespace import SettngsNS +from comictaggerlib.issueidentifier import IssueIdentifierCancelled from comictaggerlib.md import read_selected_tags from comictaggerlib.resulttypes import Action, OnlineMatchResults, Result, Status from comictaggerlib.tag import identify_comic @@ -88,6 +89,8 @@ class AutoTagThread(QtCore.QThread): self.autoTagComplete.emit(match_results, archives_to_remove) def on_rate_limit(self, full_time: float, sleep_time: float) -> None: + if self.canceled: + raise IssueIdentifierCancelled self.log_output( f"Rate limit reached: {full_time:.0f}s until next request. Waiting {sleep_time:.0f}s for ratelimit" ) @@ -160,17 +163,28 @@ class AutoTagThread(QtCore.QThread): ), match_results, ) - res, match_results = identify_comic( - ca, - md, - tags_used, - match_results, - self.config, - self.talker, - self.log_output, - on_rate_limit=ratelimit_callback, - on_progress=on_progress, - ) + + try: + res, match_results = identify_comic( + ca, + md, + tags_used, + match_results, + self.config, + self.talker, + self.log_output, + on_rate_limit=ratelimit_callback, + on_progress=on_progress, + ) + except IssueIdentifierCancelled: + return ( + Result( + Action.save, + original_path=ca.path, + status=Status.fetch_data_failure, + ), + match_results, + ) if self.canceled: return res, match_results diff --git a/comictaggerlib/renamewindow.py b/comictaggerlib/renamewindow.py index fc3d03f..554bf5d 100644 --- a/comictaggerlib/renamewindow.py +++ b/comictaggerlib/renamewindow.py @@ -82,7 +82,7 @@ class RenameWindow(QtWidgets.QDialog): new_ext = ca.extension() if md is None or md.is_empty: - md, error = self.parent().read_selected_tags(self.read_tag_ids, ca) + md, _, error = self.parent().read_selected_tags(self.read_tag_ids, ca) if error is not None: logger.error("Failed to load tags from %s: %s", ca.path, error) QtWidgets.QMessageBox.warning( diff --git a/comictaggerlib/taggerwindow.py b/comictaggerlib/taggerwindow.py index 9f557d6..2ea3b1b 100644 --- a/comictaggerlib/taggerwindow.py +++ b/comictaggerlib/taggerwindow.py @@ -1154,6 +1154,7 @@ class TaggerWindow(QtWidgets.QMainWindow): toast = Toast(self) # Convert to milliseconds, make it end half a second before the ratelimit triggers again, make sure we have a positive time toast.setDuration(abs(int(sleep_time * 1000) - 500)) + toast.setResetDurationOnHover(False) toast.setTitle("Rate Limit Hit!") toast.setText( f"Rate limit reached: {full_time:.0f}s until next request. Waiting {sleep_time:.0f}s for ratelimit" diff --git a/comictalker/vendor/pyrate_limiter/limit_context_decorator.py b/comictalker/vendor/pyrate_limiter/limit_context_decorator.py index 10e5104..02d9b37 100644 --- a/comictalker/vendor/pyrate_limiter/limit_context_decorator.py +++ b/comictalker/vendor/pyrate_limiter/limit_context_decorator.py @@ -86,20 +86,20 @@ class LimitContextDecorator: self.try_acquire() except BucketFullException as err: delay_time = full_delay_time = self.delay_or_reraise(err) - - if self.on_rate_limit: - if self.on_rate_limit.interval > 0 and delay_time > self.on_rate_limit.interval: - delay_time = self.on_rate_limit.interval - self.on_rate_limit.callback(full_delay_time, delay_time) - logger.warning( - "Rate limit reached; %.0f seconds remaining before next request. Sleeping for %.0f seconds", - full_delay_time, - delay_time, - ) - sleep(delay_time) else: break + if self.on_rate_limit: + if self.on_rate_limit.interval > 0 and delay_time > self.on_rate_limit.interval: + delay_time = self.on_rate_limit.interval + self.on_rate_limit.callback(full_delay_time, delay_time) + logger.warning( + "Rate limit reached; %.0f seconds remaining before next request. Sleeping for %.0f seconds", + full_delay_time, + delay_time, + ) + sleep(delay_time) + async def async_delayed_acquire(self): """Delay and retry until we can successfully acquire an available bucket item""" while True: