diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index ddaec180..640f145a 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -88,7 +88,6 @@ jobs:
python-version: '3.9'
- name: Dependencies
run: |
- pip3 install certifi==2022.6.15
pip3 install -r requirements.txt
pip3 install -r requirements-presence.txt
- name: Build Dependencies
diff --git a/rare/components/dialogs/launch_dialog.py b/rare/components/dialogs/launch_dialog.py
index 9b2ea92b..557b59dc 100644
--- a/rare/components/dialogs/launch_dialog.py
+++ b/rare/components/dialogs/launch_dialog.py
@@ -2,8 +2,9 @@ import os
import platform
from logging import getLogger
-from PyQt5.QtCore import Qt, pyqtSignal, QRunnable, QObject, QThreadPool, QSettings, pyqtSlot, QCoreApplication
+from PyQt5.QtCore import Qt, pyqtSignal, QRunnable, QObject, QThreadPool, QSettings, pyqtSlot
from PyQt5.QtWidgets import QDialog, QApplication
+from legendary.models.game import Game
from requests.exceptions import ConnectionError, HTTPError
from rare.components.dialogs.login import LoginDialog
@@ -29,19 +30,41 @@ class LaunchWorker(QRunnable):
self.signals = LaunchWorker.Signals()
self.core = LegendaryCoreSingleton()
- def run(self):
+ def run_real(self):
pass
+ def run(self):
+ self.run_real()
+ self.signals.deleteLater()
+
class ImageWorker(LaunchWorker):
+ # FIXME: this is a middle-ground solution for concurrent downloads
+ class DownloadSlot(QObject):
+ def __init__(self, signals: LaunchWorker.Signals):
+ super(ImageWorker.DownloadSlot, self).__init__()
+ self.signals = signals
+ self.counter = 0
+ self.length = 0
+
+ @pyqtSlot(object)
+ def counter_inc(self, game: Game):
+ self.signals.progress.emit(
+ int(self.counter / self.length * 50),
+ self.tr("Downloading image for {}").format(game.app_title)
+ )
+ self.counter += 1
+
def __init__(self):
super(ImageWorker, self).__init__()
+ # FIXME: this is a middle-ground solution for concurrent downloads
+ self.dl_slot = ImageWorker.DownloadSlot(self.signals)
self.image_manager = ImageManagerSingleton()
def tr(self, t) -> str:
- return QCoreApplication.translate(self.__class__.__name__, t)
+ return QApplication.instance().translate(self.__class__.__name__, t)
- def run(self):
+ def run_real(self):
# Download Images
games, dlcs = self.core.get_game_and_dlc_list(update_assets=True, skip_ue=False)
self.signals.result.emit((games, dlcs), "gamelist")
@@ -53,20 +76,21 @@ class ImageWorker(LaunchWorker):
game_list = games + dlc_list + na_games + na_dlc_list
+ self.dl_slot.length = len(game_list)
for i, game in enumerate(game_list):
if game.app_title == "Unreal Engine":
game.app_title += f" {game.app_name.split('_')[-1]}"
self.core.lgd.set_game_meta(game.app_name, game)
- self.signals.progress.emit(
- int(i / len(game_list) * 50),
- self.tr("Downloading image for {}").format(game.app_title)
- )
- self.image_manager.download_image_blocking(game)
+ # self.image_manager.download_image_blocking(game)
+ self.image_manager.download_image(game, self.dl_slot.counter_inc, priority=0)
+ # FIXME: this is a middle-ground solution for concurrent downloads
+ while self.dl_slot.counter < len(game_list):
+ QApplication.instance().processEvents()
+ self.dl_slot.deleteLater()
igame_list = self.core.get_installed_list(include_dlc=True)
# FIXME: incorporate installed game status checking here for now, still slow
- # if igame := self.core.get_installed_game(game.app_name, skip_sync=True):
for i, igame in enumerate(igame_list):
self.signals.progress.emit(
int(i / len(igame_list) * 50) + 50,
@@ -100,7 +124,7 @@ class ApiRequestWorker(LaunchWorker):
super(ApiRequestWorker, self).__init__()
self.settings = QSettings()
- def run(self) -> None:
+ def run_real(self) -> None:
if self.settings.value("mac_meta", platform.system() == "Darwin", bool):
try:
result = self.core.get_game_and_dlc_list(update_assets=False, platform="Mac")
diff --git a/rare/shared/image_manager.py b/rare/shared/image_manager.py
index 1412a96e..7e3054b8 100644
--- a/rare/shared/image_manager.py
+++ b/rare/shared/image_manager.py
@@ -1,9 +1,8 @@
-from __future__ import annotations
-
import hashlib
import json
import pickle
import zlib
+# from concurrent import futures
from logging import getLogger
from pathlib import Path
from typing import TYPE_CHECKING
@@ -30,6 +29,8 @@ from rare.lgndr.core import LegendaryCore
from rare.models.signals import GlobalSignals
from rare.utils.paths import image_dir, resources_path
+# from requests_futures.sessions import FuturesSession
+
if TYPE_CHECKING:
pass
@@ -86,8 +87,8 @@ class ImageSize:
class ImageManager(QObject):
class Worker(QRunnable):
class Signals(QObject):
- # str: app_name
- completed = pyqtSignal(str)
+ # object: Game
+ completed = pyqtSignal(object)
def __init__(self, func: Callable, updates: List, json_data: Dict, game: Game):
super(ImageManager.Worker, self).__init__()
@@ -101,7 +102,7 @@ class ImageManager(QObject):
def run(self):
self.func(self.updates, self.json_data, self.game)
logger.debug(f" Emitting singal for game {self.game.app_name} - {self.game.app_title}")
- self.signals.completed.emit(self.game.app_name)
+ self.signals.completed.emit(self.game)
def __init__(self, signals: GlobalSignals, core: LegendaryCore):
# lk: the ordering in __img_types matters for the order of fallbacks
@@ -179,7 +180,7 @@ class ImageManager(QObject):
return updates, json_data
- def __download(self, updates, json_data, game) -> bool:
+ def __download(self, updates, json_data, game, use_async: bool = False) -> bool:
# Decompress existing image.cache
if not self.__img_cache(game.app_name).is_file():
cache_data = dict(zip(self.__img_types, [None] * len(self.__img_types)))
@@ -193,6 +194,22 @@ class ImageManager(QObject):
if cache_data[image["type"]] is None or json_data[image["type"]] != image["md5"]
]
+ # Download
+ # # lk: Keep this so I don't have to go looking for it again,
+ # # lk: it might be useful in the future.
+ # if use_async and len(updates) > 1:
+ # session = FuturesSession(max_workers=len(self.__img_types))
+ # image_requests = []
+ # for image in updates:
+ # logger.info(f"Downloading {image['type']} for {game.app_title}")
+ # json_data[image["type"]] = image["md5"]
+ # payload = {"resize": 1, "w": ImageSize.Image.size.width(), "h": ImageSize.Image.size.height()}
+ # req = session.get(image["url"], params=payload)
+ # req.image_type = image["type"]
+ # image_requests.append(req)
+ # for req in futures.as_completed(image_requests):
+ # cache_data[req.image_type] = req.result().content
+ # else:
for image in updates:
logger.info(f"Downloading {image['type']} for {game.app_title}")
json_data[image["type"]] = image["md5"]
@@ -291,18 +308,18 @@ class ImageManager(QObject):
return data
def download_image(
- self, game: Game, load_callback: Callable[[], None], priority: int, force: bool = False
+ self, game: Game, load_callback: Callable[[Game], None], priority: int, force: bool = False
) -> None:
updates, json_data = self.__prepare_download(game, force)
if not updates:
- load_callback()
+ load_callback(game)
return
if updates and game.app_name not in self.__worker_app_names:
image_worker = ImageManager.Worker(self.__download, updates, json_data, game)
self.__worker_app_names.append(game.app_name)
image_worker.signals.completed.connect(load_callback)
- image_worker.signals.completed.connect(lambda app_name: self.__worker_app_names.remove(app_name))
+ image_worker.signals.completed.connect(lambda g: self.__worker_app_names.remove(g.app_name))
self.threadpool.start(image_worker, priority)
def download_image_blocking(self, game: Game, force: bool = False) -> None:
@@ -310,7 +327,7 @@ class ImageManager(QObject):
if not updates:
return
if updates:
- self.__download(updates, json_data, game)
+ self.__download(updates, json_data, game, use_async=True)
def __get_cover(
self, container: Union[Type[QPixmap], Type[QImage]], app_name: str, color: bool = True