Release v1.0.14: stop passing match results through Qt signals.

Store worker results on the thread object, defer preview refresh, and log Python errors to crash.log to prevent native Qt crashes after TheTVDB match.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Bulk Renamer
2026-07-03 20:51:20 -05:00
co-authored by Cursor
parent 64d7a30328
commit 87282b0259
5 changed files with 70 additions and 16 deletions
+29 -13
View File
@@ -310,18 +310,19 @@ class EpisodeRenumberRuleWidget(QWidget):
class _TvdbSearchWorker(QThread):
finished = pyqtSignal(list)
search_done = pyqtSignal()
failed = pyqtSignal(str)
def __init__(self, query: str):
super().__init__()
self.query = query
self.results: list = []
def run(self):
try:
client = TvdbClient(TVDB_API_KEY)
results = client.search_series(self.query)
self.finished.emit(results)
self.results = client.search_series(self.query)
self.search_done.emit()
except TvdbError as e:
self.failed.emit(str(e))
except Exception as e:
@@ -329,7 +330,7 @@ class _TvdbSearchWorker(QThread):
class _TvdbMatchWorker(QThread):
finished = pyqtSignal(dict, list, list)
match_done = pyqtSignal()
failed = pyqtSignal(str)
def __init__(
@@ -350,6 +351,8 @@ class _TvdbMatchWorker(QThread):
self.all_seasons = all_seasons
self.multi_episode = multi_episode
self.language = language
self.mapping: dict[str, tuple[int, ...]] = {}
self.unmatched: list[str] = []
def run(self):
try:
@@ -437,8 +440,10 @@ class _TvdbMatchWorker(QThread):
official_episodes=official,
combined_episodes=combined,
)
safe_mapping = {path: target_to_tuple(t) for path, t in mapping.items()}
self.finished.emit(safe_mapping, unmatched, notes)
self.mapping = {path: target_to_tuple(t) for path, t in mapping.items()}
self.unmatched = list(unmatched)
del notes
self.match_done.emit()
except TvdbError as e:
self.failed.emit(str(e))
except Exception as e:
@@ -564,11 +569,12 @@ class TvdbEpisodeRenumberRuleWidget(QWidget):
self.series_combo.setEnabled(False)
self.status.setText("Searching TheTVDB…")
self._search_worker = _TvdbSearchWorker(query)
self._search_worker.finished.connect(self._on_search_finished)
self._search_worker.search_done.connect(self._on_search_finished)
self._search_worker.failed.connect(self._on_search_failed)
self._search_worker.start()
def _on_search_finished(self, results):
def _on_search_finished(self):
results = self._search_worker.results if self._search_worker else []
self._search_results = results
self.series_combo.clear()
if not results:
@@ -618,15 +624,26 @@ class TvdbEpisodeRenumberRuleWidget(QWidget):
self.multi_episode_cb.isChecked(),
self._language_value(),
)
self._match_worker.finished.connect(self._on_match_finished)
self._match_worker.match_done.connect(self._on_match_finished)
self._match_worker.failed.connect(self._on_match_failed)
self._match_worker.finished.connect(self._on_match_worker_finished)
self._match_worker.start()
def _on_match_finished(self, mapping: dict, unmatched: list, notes: list):
def _on_match_worker_finished(self):
worker = self._match_worker
if worker is not None:
worker.deleteLater()
self._match_worker = None
def _on_match_finished(self):
try:
self._episode_mapping = mapping
worker = self._match_worker
if worker is None:
return
self._episode_mapping = dict(worker.mapping)
unmatched = list(worker.unmatched)
self.match_btn.setEnabled(True)
matched = len(mapping)
matched = len(self._episode_mapping)
total = len(self._file_names)
scope = "all seasons" if self.season.value() == 0 else f"season {self.season.value()}"
msg = f"Matched {matched} of {total} file(s) ({scope}, {self._order_label()}, {self._language_label()})."
@@ -634,7 +651,6 @@ class TvdbEpisodeRenumberRuleWidget(QWidget):
msg += f" {len(unmatched)} file(s) unmatched."
self.status.setText(msg)
self.matchCompleted.emit()
self._emit()
except Exception as e:
self.match_btn.setEnabled(True)
self.status.setText(f"Match error: {e}")