Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b24d391ac | ||
|
|
d94ae52e14 | ||
|
|
d8a552c61e |
+5
-3
@@ -1220,9 +1220,11 @@ def fetch_cover(name, release_id, system, timeout=10, quick=False):
|
|||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Server is authoritative for covers when configured — no libretro/IGDB
|
# Server is authoritative for game/video covers when configured — no
|
||||||
# fallback; if the server is unreachable the cover just stays empty.
|
# libretro/IGDB fallback; if the server is unreachable the cover just
|
||||||
if server_client.configured():
|
# stays empty. PS1/theme art stays client-side (the server catalog only
|
||||||
|
# covers games and videos).
|
||||||
|
if server_client.configured() and system in ("games", "videos"):
|
||||||
try:
|
try:
|
||||||
data = server_client.fetch_cover(system, name)
|
data = server_client.fetch_cover(system, name)
|
||||||
except Exception as exc: # noqa: BLE001
|
except Exception as exc: # noqa: BLE001
|
||||||
|
|||||||
+45
-3
@@ -463,7 +463,9 @@ class CoverShowcase(QWidget):
|
|||||||
self.loader.loaded.connect(self._on_loaded)
|
self.loader.loaded.connect(self._on_loaded)
|
||||||
self.loader.failed.connect(self._on_failed)
|
self.loader.failed.connect(self._on_failed)
|
||||||
self.loader.finished.connect(self._on_loading_done)
|
self.loader.finished.connect(self._on_loading_done)
|
||||||
|
self.loader.finished.connect(self._on_loader_finished)
|
||||||
self.loader.finished.connect(self.thread.quit)
|
self.loader.finished.connect(self.thread.quit)
|
||||||
|
self.thread.finished.connect(self._on_thread_finished)
|
||||||
self._threads.append(self.thread)
|
self._threads.append(self.thread)
|
||||||
self._loaders.append(self.loader)
|
self._loaders.append(self.loader)
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
@@ -521,6 +523,26 @@ class CoverShowcase(QWidget):
|
|||||||
self._loading = False
|
self._loading = False
|
||||||
self._update_status()
|
self._update_status()
|
||||||
|
|
||||||
|
def _on_loader_finished(self):
|
||||||
|
"""Drop a finished cover loader from tracking and schedule its deletion."""
|
||||||
|
loader = self.sender()
|
||||||
|
if loader is None:
|
||||||
|
return
|
||||||
|
if loader in self._loaders:
|
||||||
|
self._loaders.remove(loader)
|
||||||
|
if self.loader is loader:
|
||||||
|
self.loader = None
|
||||||
|
loader.deleteLater()
|
||||||
|
|
||||||
|
def _on_thread_finished(self):
|
||||||
|
"""Delete a finished cover-loader thread."""
|
||||||
|
thread = self.sender()
|
||||||
|
if thread in self._threads:
|
||||||
|
self._threads.remove(thread)
|
||||||
|
if self.thread is thread:
|
||||||
|
self.thread = None
|
||||||
|
thread.deleteLater()
|
||||||
|
|
||||||
def _update_status(self):
|
def _update_status(self):
|
||||||
if self._closing:
|
if self._closing:
|
||||||
return
|
return
|
||||||
@@ -2044,7 +2066,6 @@ class StorePage(QWidget):
|
|||||||
self._cover_attempted.clear()
|
self._cover_attempted.clear()
|
||||||
for loader in self._loaders:
|
for loader in self._loaders:
|
||||||
loader.cancel()
|
loader.cancel()
|
||||||
self._loaders = []
|
|
||||||
self.grid.clear()
|
self.grid.clear()
|
||||||
self.table.setRowCount(0)
|
self.table.setRowCount(0)
|
||||||
self.status.setText("Loading…")
|
self.status.setText("Loading…")
|
||||||
@@ -2220,10 +2241,12 @@ class StorePage(QWidget):
|
|||||||
self._cover_attempted.clear()
|
self._cover_attempted.clear()
|
||||||
self._placeholder = QIcon(_placeholder_pixmap(self.ICON_W, self.ICON_H))
|
self._placeholder = QIcon(_placeholder_pixmap(self.ICON_W, self.ICON_H))
|
||||||
|
|
||||||
# Cancel any in-flight cover loaders (stale generation).
|
# Cancel any in-flight cover loaders (stale generation). Each loader
|
||||||
|
# removes itself from ``_loaders`` via its own ``finished`` handler once
|
||||||
|
# its thread winds down — dropping the list here would garbage-collect
|
||||||
|
# live QObjects and crash Qt.
|
||||||
for loader in self._loaders:
|
for loader in self._loaders:
|
||||||
loader.cancel()
|
loader.cancel()
|
||||||
self._loaders = []
|
|
||||||
|
|
||||||
self.grid.clear()
|
self.grid.clear()
|
||||||
self.grid.setUpdatesEnabled(False)
|
self.grid.setUpdatesEnabled(False)
|
||||||
@@ -2401,6 +2424,7 @@ class StorePage(QWidget):
|
|||||||
thread.started.connect(loader.run)
|
thread.started.connect(loader.run)
|
||||||
loader.loaded.connect(self._on_date_loaded)
|
loader.loaded.connect(self._on_date_loaded)
|
||||||
loader.finished.connect(self._on_date_batch_done)
|
loader.finished.connect(self._on_date_batch_done)
|
||||||
|
loader.finished.connect(self._on_loader_finished)
|
||||||
loader.finished.connect(thread.quit)
|
loader.finished.connect(thread.quit)
|
||||||
thread.finished.connect(self._on_thread_finished)
|
thread.finished.connect(self._on_thread_finished)
|
||||||
self._threads.append(thread)
|
self._threads.append(thread)
|
||||||
@@ -2425,6 +2449,21 @@ class StorePage(QWidget):
|
|||||||
if cell is not None:
|
if cell is not None:
|
||||||
cell.setText(date)
|
cell.setText(date)
|
||||||
|
|
||||||
|
def _on_loader_finished(self):
|
||||||
|
"""Drop a finished cover/date loader from tracking.
|
||||||
|
|
||||||
|
Loaders stay in ``_loaders`` until their thread finishes so their
|
||||||
|
QObject wrapper is never garbage-collected while its thread is still
|
||||||
|
winding down (that left a dangling thread affinity and crashed Qt on
|
||||||
|
filter/refresh).
|
||||||
|
"""
|
||||||
|
loader = self.sender()
|
||||||
|
if loader is None:
|
||||||
|
return
|
||||||
|
if loader in self._loaders:
|
||||||
|
self._loaders.remove(loader)
|
||||||
|
loader.deleteLater()
|
||||||
|
|
||||||
def _start_cover_loader(self, gen, tasks):
|
def _start_cover_loader(self, gen, tasks):
|
||||||
if self._closing or not tasks:
|
if self._closing or not tasks:
|
||||||
return
|
return
|
||||||
@@ -2434,6 +2473,7 @@ class StorePage(QWidget):
|
|||||||
thread.started.connect(loader.run)
|
thread.started.connect(loader.run)
|
||||||
loader.loaded.connect(self._on_cover_loaded)
|
loader.loaded.connect(self._on_cover_loaded)
|
||||||
loader.finished.connect(self._on_cover_batch_done)
|
loader.finished.connect(self._on_cover_batch_done)
|
||||||
|
loader.finished.connect(self._on_loader_finished)
|
||||||
loader.finished.connect(thread.quit)
|
loader.finished.connect(thread.quit)
|
||||||
thread.finished.connect(self._on_thread_finished)
|
thread.finished.connect(self._on_thread_finished)
|
||||||
self._threads.append(thread)
|
self._threads.append(thread)
|
||||||
@@ -4772,6 +4812,8 @@ class MainWindow(QMainWindow):
|
|||||||
thread.started.connect(worker.run)
|
thread.started.connect(worker.run)
|
||||||
worker.result.connect(self._on_library_loaded)
|
worker.result.connect(self._on_library_loaded)
|
||||||
worker.finished.connect(thread.quit)
|
worker.finished.connect(thread.quit)
|
||||||
|
worker.finished.connect(worker.deleteLater)
|
||||||
|
thread.finished.connect(thread.deleteLater)
|
||||||
thread.finished.connect(lambda t=thread, w=worker: self._forget_library_thread(t, w))
|
thread.finished.connect(lambda t=thread, w=worker: self._forget_library_thread(t, w))
|
||||||
self._library_threads.append(thread)
|
self._library_threads.append(thread)
|
||||||
self._library_workers.append(worker)
|
self._library_workers.append(worker)
|
||||||
|
|||||||
+5
-3
@@ -199,9 +199,11 @@ def get_or_fetch(release_id, system, fetch_title):
|
|||||||
info = load(release_id, system)
|
info = load(release_id, system)
|
||||||
if info is not None:
|
if info is not None:
|
||||||
return info
|
return info
|
||||||
# Server is authoritative for metadata when configured — no IGDB/TMDB
|
# Server is authoritative for game/video metadata when configured — no
|
||||||
# fallback; an unreachable server just leaves metadata empty.
|
# IGDB/TMDB fallback; an unreachable server just leaves metadata empty.
|
||||||
if server_client.configured():
|
# PS1 metadata stays client-side (the server catalog only covers games
|
||||||
|
# and videos).
|
||||||
|
if server_client.configured() and system in ("games", "videos"):
|
||||||
try:
|
try:
|
||||||
meta = server_client.fetch_metadata(system, fetch_title)
|
meta = server_client.fetch_metadata(system, fetch_title)
|
||||||
except Exception as exc: # noqa: BLE001
|
except Exception as exc: # noqa: BLE001
|
||||||
|
|||||||
Reference in New Issue
Block a user