Re-enable cover/metadata scraping for themes and DLC
This commit is contained in:
@@ -309,9 +309,10 @@ Requirements & behaviour:
|
|||||||
> CFW already ship it). The desktop app only downloads and decrypts; it doesn't
|
> CFW already ship it). The desktop app only downloads and decrypts; it doesn't
|
||||||
> strip DRM.
|
> strip DRM.
|
||||||
>
|
>
|
||||||
> **Covers/metadata:** themes and DLC have no cover art or game metadata, so
|
> **Covers/metadata:** themes and DLC have no cover art or game metadata of
|
||||||
> the app never scrapes IGDB/libretro/etc. for them (their Library rows show a
|
> their own — the app scrapes them like games (IGDB/libretro/etc.), which can
|
||||||
> placeholder icon instead of a mis-matched game cover).
|
> return unrelated results. Downloaded themes keep their embedded preview as
|
||||||
|
> the primary cover (used instead of a scraped image when available).
|
||||||
|
|
||||||
## Server setup
|
## Server setup
|
||||||
|
|
||||||
|
|||||||
@@ -30,15 +30,6 @@ _PLATFORMS = {
|
|||||||
"ps1": {"libretro_repo": "Sony_-_PlayStation", "igdb_id": 7, "ss_id": "57"},
|
"ps1": {"libretro_repo": "Sony_-_PlayStation", "igdb_id": 7, "ss_id": "57"},
|
||||||
}
|
}
|
||||||
|
|
||||||
# Content types that have no meaningful cover/metadata (IGDB/etc. lookups
|
|
||||||
# return unrelated games, so scraping is disabled for them).
|
|
||||||
NON_COVER_SYSTEMS = {"theme", "dlc"}
|
|
||||||
|
|
||||||
|
|
||||||
def is_scrapable(system):
|
|
||||||
"""Whether cover/metadata scraping makes sense for this item type."""
|
|
||||||
return (system or "games") not in NON_COVER_SYSTEMS
|
|
||||||
|
|
||||||
|
|
||||||
def _platform(system):
|
def _platform(system):
|
||||||
"""Platform parameters (libretro repo, IGDB id, ScreenScraper id)."""
|
"""Platform parameters (libretro repo, IGDB id, ScreenScraper id)."""
|
||||||
@@ -1151,8 +1142,6 @@ def fetch_cover(name, release_id, system, timeout=10, quick=False):
|
|||||||
``quick`` limits the game fallback to libretro only (index-driven and fast),
|
``quick`` limits the game fallback to libretro only (index-driven and fast),
|
||||||
for bulk store loading; details views use the full IGDB/ScreenScraper path.
|
for bulk store loading; details views use the full IGDB/ScreenScraper path.
|
||||||
"""
|
"""
|
||||||
if not is_scrapable(system):
|
|
||||||
return None
|
|
||||||
dest = cover_path(release_id, system)
|
dest = cover_path(release_id, system)
|
||||||
if dest.exists():
|
if dest.exists():
|
||||||
if file_is_image(dest):
|
if file_is_image(dest):
|
||||||
|
|||||||
+6
-40
@@ -123,11 +123,10 @@ class CoverLoader(QObject):
|
|||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
|
||||||
pool = ThreadPoolExecutor(max_workers=8)
|
pool = ThreadPoolExecutor(max_workers=8)
|
||||||
futures = {}
|
futures = {
|
||||||
for i, it in enumerate(self.items):
|
pool.submit(load_cover, it): i
|
||||||
if not covers.is_scrapable(it.get("system")):
|
for i, it in enumerate(self.items)
|
||||||
continue # themes/DLC have no covers; keep the placeholder
|
}
|
||||||
futures[pool.submit(load_cover, it)] = i
|
|
||||||
try:
|
try:
|
||||||
for fut in as_completed(futures):
|
for fut in as_completed(futures):
|
||||||
if self._cancel.is_set():
|
if self._cancel.is_set():
|
||||||
@@ -212,8 +211,6 @@ def load_cover(item, timeout=8, quick=False):
|
|||||||
name = item["name"]
|
name = item["name"]
|
||||||
rid = item["release_id"]
|
rid = item["release_id"]
|
||||||
system = item["system"]
|
system = item["system"]
|
||||||
if not covers.is_scrapable(system):
|
|
||||||
return None # themes/DLC have no covers
|
|
||||||
if system == "videos":
|
if system == "videos":
|
||||||
return _load_video_cover(name, rid, system, timeout)
|
return _load_video_cover(name, rid, system, timeout)
|
||||||
return covers.fetch_cover(name, rid, system, timeout, quick=quick)
|
return covers.fetch_cover(name, rid, system, timeout, quick=quick)
|
||||||
@@ -377,13 +374,6 @@ class CoverShowcase(QWidget):
|
|||||||
item.setTextAlignment(Qt.AlignHCenter | Qt.AlignTop)
|
item.setTextAlignment(Qt.AlignHCenter | Qt.AlignTop)
|
||||||
item.setToolTip(it["name"])
|
item.setToolTip(it["name"])
|
||||||
item.setData(Qt.UserRole, it)
|
item.setData(Qt.UserRole, it)
|
||||||
# Themes/DLC show their locally-extracted preview (no scraping).
|
|
||||||
if not covers.is_scrapable(it.get("system")):
|
|
||||||
rid = it.get("release_id")
|
|
||||||
system = it.get("system")
|
|
||||||
path = covers.cover_path(rid, system)
|
|
||||||
if path is not None and path.exists() and covers.file_is_image(path):
|
|
||||||
item.setIcon(self._fit_icon(QPixmap(str(path))))
|
|
||||||
self.list.addItem(item)
|
self.list.addItem(item)
|
||||||
|
|
||||||
self._start_loader(gen)
|
self._start_loader(gen)
|
||||||
@@ -484,7 +474,6 @@ class CoverShowcase(QWidget):
|
|||||||
if it.get("on_psp"):
|
if it.get("on_psp"):
|
||||||
menu.addAction("Delete from PSP", lambda: self.delete_psp_requested.emit(it))
|
menu.addAction("Delete from PSP", lambda: self.delete_psp_requested.emit(it))
|
||||||
menu.addAction("Delete…", lambda: self.delete_requested.emit(it))
|
menu.addAction("Delete…", lambda: self.delete_requested.emit(it))
|
||||||
if it is not None and covers.is_scrapable(it.get("system")):
|
|
||||||
menu.addAction("Scrape…", lambda: self.scrape_requested.emit(it))
|
menu.addAction("Scrape…", lambda: self.scrape_requested.emit(it))
|
||||||
menu.exec(self.list.mapToGlobal(pos))
|
menu.exec(self.list.mapToGlobal(pos))
|
||||||
|
|
||||||
@@ -2032,13 +2021,6 @@ class StorePage(QWidget):
|
|||||||
rid, system = lc
|
rid, system = lc
|
||||||
else:
|
else:
|
||||||
rid, system = covers.stable_key(it["name"]), it.get("system") or "games"
|
rid, system = covers.stable_key(it["name"]), it.get("system") or "games"
|
||||||
if not covers.is_scrapable(system):
|
|
||||||
# Themes/DLC aren't scraped, but a downloaded theme's preview is
|
|
||||||
# cached locally — show it when available.
|
|
||||||
p = covers.cover_path(rid, system)
|
|
||||||
if p.exists() and covers.file_is_image(p):
|
|
||||||
item.setIcon(QIcon(_fit_pixmap(QPixmap(str(p)), self.ICON_W, self.ICON_H)))
|
|
||||||
return
|
|
||||||
p = covers.cover_path(rid, system)
|
p = covers.cover_path(rid, system)
|
||||||
if p.exists() and covers.file_is_image(p):
|
if p.exists() and covers.file_is_image(p):
|
||||||
item.setIcon(QIcon(_fit_pixmap(QPixmap(str(p)), self.ICON_W, self.ICON_H)))
|
item.setIcon(QIcon(_fit_pixmap(QPixmap(str(p)), self.ICON_W, self.ICON_H)))
|
||||||
@@ -2205,9 +2187,6 @@ class StorePage(QWidget):
|
|||||||
rid, system = lc
|
rid, system = lc
|
||||||
else:
|
else:
|
||||||
rid, system = covers.stable_key(it["name"]), it.get("system") or "games"
|
rid, system = covers.stable_key(it["name"]), it.get("system") or "games"
|
||||||
if not covers.is_scrapable(system):
|
|
||||||
self.details.set_metadata({})
|
|
||||||
return
|
|
||||||
cached = info.load(str(rid), system)
|
cached = info.load(str(rid), system)
|
||||||
if cached is not None:
|
if cached is not None:
|
||||||
self.details.set_metadata(cached)
|
self.details.set_metadata(cached)
|
||||||
@@ -2239,8 +2218,6 @@ class StorePage(QWidget):
|
|||||||
return
|
return
|
||||||
lc = item.get("_lib_cover")
|
lc = item.get("_lib_cover")
|
||||||
rid, system = lc if lc is not None else (covers.stable_key(item["name"]), item.get("system") or "games")
|
rid, system = lc if lc is not None else (covers.stable_key(item["name"]), item.get("system") or "games")
|
||||||
if not covers.is_scrapable(system):
|
|
||||||
return
|
|
||||||
dlg = ScrapeDialog(item, self, rid=rid, system=system)
|
dlg = ScrapeDialog(item, self, rid=rid, system=system)
|
||||||
if dlg.exec() != QDialog.Accepted:
|
if dlg.exec() != QDialog.Accepted:
|
||||||
return
|
return
|
||||||
@@ -2264,8 +2241,7 @@ class StorePage(QWidget):
|
|||||||
menu.addAction("Download", lambda: self.download_requested.emit([it]))
|
menu.addAction("Download", lambda: self.download_requested.emit([it]))
|
||||||
if it.get("path"):
|
if it.get("path"):
|
||||||
menu.addAction("Open containing folder", lambda: open_containing_folder(it.get("path")))
|
menu.addAction("Open containing folder", lambda: open_containing_folder(it.get("path")))
|
||||||
if covers.is_scrapable(it.get("system")):
|
menu.addAction("Scrape…", lambda: self._search_cover(it))
|
||||||
menu.addAction("Scrape…", lambda: self._search_cover(it))
|
|
||||||
menu.exec(self.grid.mapToGlobal(pos))
|
menu.exec(self.grid.mapToGlobal(pos))
|
||||||
|
|
||||||
def _on_table_menu(self, pos):
|
def _on_table_menu(self, pos):
|
||||||
@@ -2280,14 +2256,11 @@ class StorePage(QWidget):
|
|||||||
menu.addAction("Download", lambda: self.download_requested.emit([it]))
|
menu.addAction("Download", lambda: self.download_requested.emit([it]))
|
||||||
if it.get("path"):
|
if it.get("path"):
|
||||||
menu.addAction("Open containing folder", lambda: open_containing_folder(it.get("path")))
|
menu.addAction("Open containing folder", lambda: open_containing_folder(it.get("path")))
|
||||||
if covers.is_scrapable(it.get("system")):
|
menu.addAction("Scrape…", lambda: self._search_cover(it))
|
||||||
menu.addAction("Scrape…", lambda: self._search_cover(it))
|
|
||||||
menu.exec(self.table.viewport().mapToGlobal(pos))
|
menu.exec(self.table.viewport().mapToGlobal(pos))
|
||||||
|
|
||||||
def _search_cover(self, it):
|
def _search_cover(self, it):
|
||||||
system = it.get("system") or "games"
|
system = it.get("system") or "games"
|
||||||
if not covers.is_scrapable(system):
|
|
||||||
return
|
|
||||||
rid = covers.stable_key(it["name"])
|
rid = covers.stable_key(it["name"])
|
||||||
dlg = ScrapeDialog({"name": it["name"], "system": system}, self, rid=rid, system=system)
|
dlg = ScrapeDialog({"name": it["name"], "system": system}, self, rid=rid, system=system)
|
||||||
if dlg.exec() == QDialog.Accepted:
|
if dlg.exec() == QDialog.Accepted:
|
||||||
@@ -2324,8 +2297,6 @@ class StorePage(QWidget):
|
|||||||
rid, system = lc
|
rid, system = lc
|
||||||
else:
|
else:
|
||||||
rid, system = covers.stable_key(it["name"]), it.get("system") or "games"
|
rid, system = covers.stable_key(it["name"]), it.get("system") or "games"
|
||||||
if not covers.is_scrapable(system):
|
|
||||||
continue # themes/DLC have no covers/metadata
|
|
||||||
key = (str(rid), system)
|
key = (str(rid), system)
|
||||||
if key in seen:
|
if key in seen:
|
||||||
continue
|
continue
|
||||||
@@ -4445,9 +4416,6 @@ class MainWindow(QMainWindow):
|
|||||||
self.details_panel.set_item(item)
|
self.details_panel.set_item(item)
|
||||||
rid = str(item["release_id"])
|
rid = str(item["release_id"])
|
||||||
system = item.get("system")
|
system = item.get("system")
|
||||||
if not covers.is_scrapable(system):
|
|
||||||
self.details_panel.set_metadata({})
|
|
||||||
return
|
|
||||||
cached = info.load(rid, system)
|
cached = info.load(rid, system)
|
||||||
if cached is not None:
|
if cached is not None:
|
||||||
self.details_panel.set_metadata(cached)
|
self.details_panel.set_metadata(cached)
|
||||||
@@ -4508,8 +4476,6 @@ class MainWindow(QMainWindow):
|
|||||||
item = getattr(self, "_current_item", None)
|
item = getattr(self, "_current_item", None)
|
||||||
if item is None:
|
if item is None:
|
||||||
return
|
return
|
||||||
if not covers.is_scrapable(item.get("system")):
|
|
||||||
return # themes/DLC have no metadata/cover
|
|
||||||
rid = str(item.get("release_id") or covers.stable_key(item.get("name") or ""))
|
rid = str(item.get("release_id") or covers.stable_key(item.get("name") or ""))
|
||||||
system = item.get("system") or "games"
|
system = item.get("system") or "games"
|
||||||
dlg = ScrapeDialog(item, self, rid=rid, system=system)
|
dlg = ScrapeDialog(item, self, rid=rid, system=system)
|
||||||
|
|||||||
@@ -182,8 +182,6 @@ def cached_release_dates():
|
|||||||
|
|
||||||
def get_or_fetch(release_id, system, fetch_title):
|
def get_or_fetch(release_id, system, fetch_title):
|
||||||
"""Return cached info (override or fetched) or fetch-and-cache it."""
|
"""Return cached info (override or fetched) or fetch-and-cache it."""
|
||||||
if not covers.is_scrapable(system):
|
|
||||||
return None
|
|
||||||
info = load(release_id, system)
|
info = load(release_id, system)
|
||||||
if info is not None:
|
if info is not None:
|
||||||
return info
|
return info
|
||||||
|
|||||||
Reference in New Issue
Block a user