ps1: preview EBOOT icon/background in the PS1 tab before converting
Selecting a pending disc in the PS1 tab now shows a live preview of the ICON0 (144x80 XMB icon) and PIC1 (480x272 launch background) that the converter would embed, fetched asynchronously so the UI stays responsive. Converted discs/EBOOTs show an 'already converted' notice, and ps1_art now falls back to a name-derived cache key when a disc has no serial.
This commit is contained in:
+2
-1
@@ -861,8 +861,9 @@ def ps1_art(title, release_id, timeout=10):
|
|||||||
sources (libretro, ScreenScraper, IGDB, Wikipedia). Returns
|
sources (libretro, ScreenScraper, IGDB, Wikipedia). Returns
|
||||||
``(None, None)`` when no art can be produced.
|
``(None, None)`` when no art can be produced.
|
||||||
"""
|
"""
|
||||||
|
key = release_id or stable_key(title)
|
||||||
try:
|
try:
|
||||||
path = fetch_cover(title, release_id, "ps1", timeout=timeout)
|
path = fetch_cover(title, key, "ps1", timeout=timeout)
|
||||||
except Exception: # noqa: BLE001
|
except Exception: # noqa: BLE001
|
||||||
return None, None
|
return None, None
|
||||||
if path is None:
|
if path is None:
|
||||||
|
|||||||
+116
-1
@@ -3365,6 +3365,27 @@ class Ps1ScanWorker(QObject):
|
|||||||
self.failed.emit(str(exc))
|
self.failed.emit(str(exc))
|
||||||
|
|
||||||
|
|
||||||
|
class Ps1PreviewWorker(QObject):
|
||||||
|
"""Fetch a disc's cover art and build the EBOOT icon/background preview."""
|
||||||
|
|
||||||
|
result = Signal(int, str, object, object) # gen, game_id, icon0 bytes, pic1 bytes
|
||||||
|
finished = Signal()
|
||||||
|
|
||||||
|
def __init__(self, gen, title, game_id):
|
||||||
|
super().__init__()
|
||||||
|
self.gen = gen
|
||||||
|
self.title = title
|
||||||
|
self.game_id = game_id
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
icon0, pic1 = covers.ps1_art(self.title, self.game_id)
|
||||||
|
except Exception: # noqa: BLE001
|
||||||
|
icon0, pic1 = None, None
|
||||||
|
self.result.emit(self.gen, self.game_id, icon0, pic1)
|
||||||
|
self.finished.emit()
|
||||||
|
|
||||||
|
|
||||||
class Ps1Page(QWidget):
|
class Ps1Page(QWidget):
|
||||||
"""PS1 tab: list raw source discs, converted EBOOTs and PSN games."""
|
"""PS1 tab: list raw source discs, converted EBOOTs and PSN games."""
|
||||||
|
|
||||||
@@ -3377,6 +3398,9 @@ class Ps1Page(QWidget):
|
|||||||
self._entries = []
|
self._entries = []
|
||||||
self._thread = None
|
self._thread = None
|
||||||
self._worker = None
|
self._worker = None
|
||||||
|
self._preview_gen = 0
|
||||||
|
self._preview_threads = []
|
||||||
|
self._preview_workers = []
|
||||||
|
|
||||||
layout = QVBoxLayout(self)
|
layout = QVBoxLayout(self)
|
||||||
bar = QHBoxLayout()
|
bar = QHBoxLayout()
|
||||||
@@ -3396,7 +3420,98 @@ class Ps1Page(QWidget):
|
|||||||
self.table.setEditTriggers(QAbstractItemView.NoEditTriggers)
|
self.table.setEditTriggers(QAbstractItemView.NoEditTriggers)
|
||||||
self.table.verticalHeader().setVisible(False)
|
self.table.verticalHeader().setVisible(False)
|
||||||
self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
|
self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
|
||||||
layout.addWidget(self.table, 1)
|
self.table.itemSelectionChanged.connect(self._on_preview_changed)
|
||||||
|
|
||||||
|
self.preview_title = QLabel("Select a disc to preview its XMB look.")
|
||||||
|
self.preview_title.setWordWrap(True)
|
||||||
|
self.preview_title.setStyleSheet("font-weight: bold;")
|
||||||
|
self.preview_icon0 = QLabel()
|
||||||
|
self.preview_icon0.setAlignment(Qt.AlignCenter)
|
||||||
|
self.preview_icon0.setFixedSize(288, 160)
|
||||||
|
self.preview_icon0.setStyleSheet("background: #14151a; border: 1px solid #333; color: #888;")
|
||||||
|
self.preview_pic1 = QLabel()
|
||||||
|
self.preview_pic1.setAlignment(Qt.AlignCenter)
|
||||||
|
self.preview_pic1.setFixedSize(480, 272)
|
||||||
|
self.preview_pic1.setStyleSheet("background: #14151a; border: 1px solid #333; color: #888;")
|
||||||
|
|
||||||
|
preview = QWidget()
|
||||||
|
pv = QVBoxLayout(preview)
|
||||||
|
pv.setContentsMargins(8, 0, 0, 0)
|
||||||
|
pv.addWidget(QLabel("<b>Preview</b>"))
|
||||||
|
pv.addWidget(self.preview_title)
|
||||||
|
pv.addWidget(QLabel("ICON0 — XMB icon"))
|
||||||
|
pv.addWidget(self.preview_icon0)
|
||||||
|
pv.addWidget(QLabel("PIC1 — launch background"))
|
||||||
|
pv.addWidget(self.preview_pic1)
|
||||||
|
pv.addStretch(1)
|
||||||
|
|
||||||
|
splitter = QSplitter(Qt.Horizontal)
|
||||||
|
splitter.addWidget(self.table)
|
||||||
|
splitter.addWidget(preview)
|
||||||
|
splitter.setStretchFactor(0, 1)
|
||||||
|
layout.addWidget(splitter, 1)
|
||||||
|
|
||||||
|
def _on_preview_changed(self):
|
||||||
|
row = self.table.currentRow()
|
||||||
|
if row < 0 or row >= len(self._entries):
|
||||||
|
self._reset_preview("Select a disc to preview its XMB look.")
|
||||||
|
return
|
||||||
|
e = self._entries[row]
|
||||||
|
if e["kind"] != "disc":
|
||||||
|
self._reset_preview("This game is already converted.")
|
||||||
|
return
|
||||||
|
if e.get("status") == "converted":
|
||||||
|
self._reset_preview("This disc has already been converted.")
|
||||||
|
return
|
||||||
|
self.preview_title.setText(e["name"])
|
||||||
|
self.preview_icon0.setPixmap(QPixmap())
|
||||||
|
self.preview_icon0.setText("Loading…")
|
||||||
|
self.preview_pic1.setPixmap(QPixmap())
|
||||||
|
self.preview_pic1.setText("Loading…")
|
||||||
|
self._preview_gen += 1
|
||||||
|
self._start_preview(self._preview_gen, e["name"], e.get("serial") or "")
|
||||||
|
|
||||||
|
def _start_preview(self, gen, title, game_id):
|
||||||
|
worker = Ps1PreviewWorker(gen, title, game_id)
|
||||||
|
thread = QThread(self)
|
||||||
|
worker.moveToThread(thread)
|
||||||
|
thread.started.connect(worker.run)
|
||||||
|
worker.result.connect(self._on_preview_result)
|
||||||
|
worker.finished.connect(thread.quit)
|
||||||
|
worker.finished.connect(worker.deleteLater)
|
||||||
|
thread.finished.connect(thread.deleteLater)
|
||||||
|
thread.start()
|
||||||
|
self._preview_threads.append(thread)
|
||||||
|
self._preview_workers.append(worker)
|
||||||
|
|
||||||
|
def _on_preview_result(self, gen, game_id, icon0, pic1):
|
||||||
|
if gen != self._preview_gen:
|
||||||
|
return
|
||||||
|
self.preview_icon0.setText("")
|
||||||
|
self.preview_pic1.setText("")
|
||||||
|
if icon0:
|
||||||
|
pm = QPixmap()
|
||||||
|
pm.loadFromData(icon0)
|
||||||
|
self.preview_icon0.setPixmap(
|
||||||
|
pm.scaled(self.preview_icon0.width(), self.preview_icon0.height(),
|
||||||
|
Qt.KeepAspectRatio, Qt.SmoothTransformation))
|
||||||
|
else:
|
||||||
|
self.preview_icon0.setText("No art found")
|
||||||
|
if pic1:
|
||||||
|
pm = QPixmap()
|
||||||
|
pm.loadFromData(pic1)
|
||||||
|
self.preview_pic1.setPixmap(
|
||||||
|
pm.scaled(self.preview_pic1.width(), self.preview_pic1.height(),
|
||||||
|
Qt.KeepAspectRatio, Qt.SmoothTransformation))
|
||||||
|
else:
|
||||||
|
self.preview_pic1.setText("No art found — the default icon will be used")
|
||||||
|
|
||||||
|
def _reset_preview(self, message):
|
||||||
|
self.preview_title.setText(message)
|
||||||
|
self.preview_icon0.setPixmap(QPixmap())
|
||||||
|
self.preview_icon0.setText("")
|
||||||
|
self.preview_pic1.setPixmap(QPixmap())
|
||||||
|
self.preview_pic1.setText("")
|
||||||
|
|
||||||
def set_dirs(self, ps1_dir, output_dir):
|
def set_dirs(self, ps1_dir, output_dir):
|
||||||
self._ps1_dir = ps1_dir
|
self._ps1_dir = ps1_dir
|
||||||
|
|||||||
Reference in New Issue
Block a user