1
0
Fork 0
mirror of synced 2024-06-18 02:24:43 +12:00
Rare/rare/components/tabs/downloads/download_thread.py

150 lines
6.1 KiB
Python
Raw Normal View History

2021-04-08 02:50:36 +12:00
import os
import platform
2021-04-08 02:50:36 +12:00
import queue
import time
2022-08-03 11:33:50 +12:00
from dataclasses import dataclass
from enum import IntEnum
2021-04-08 02:50:36 +12:00
from logging import getLogger
2022-08-03 11:33:50 +12:00
from typing import List, Optional, Dict
2021-04-08 02:50:36 +12:00
from PyQt5.QtCore import QThread, pyqtSignal, QProcess
from legendary.core import LegendaryCore
2022-08-03 11:33:50 +12:00
from rare.lgndr.api_monkeys import DLManagerSignals
from rare.lgndr.cli import LegendaryCLI
from rare.lgndr.downloading import UIUpdate
2022-08-03 11:33:50 +12:00
from rare.models.install import InstallQueueItemModel
from rare.shared import GlobalSignalsSingleton
2021-04-08 02:50:36 +12:00
logger = getLogger("DownloadThread")
2021-04-08 02:50:36 +12:00
class DownloadThread(QThread):
2022-08-03 11:33:50 +12:00
class ExitCode(IntEnum):
ERROR = 1
STOPPED = 2
FINISHED = 3
@dataclass
class ExitStatus:
app_name: str
exit_code: int
message: str = ""
dlcs: Optional[List[Dict]] = None
sync_saves: bool = False
shortcuts: bool = False
exit_status = pyqtSignal(ExitStatus)
2021-04-08 02:50:36 +12:00
statistics = pyqtSignal(UIUpdate)
def __init__(self, core: LegendaryCore, item: InstallQueueItemModel):
2021-04-08 02:50:36 +12:00
super(DownloadThread, self).__init__()
self.signals = GlobalSignalsSingleton()
self.core: LegendaryCore = core
self.item: InstallQueueItemModel = item
2022-08-03 11:33:50 +12:00
self.dlm_signals: DLManagerSignals = DLManagerSignals()
2021-04-08 02:50:36 +12:00
def run(self):
2022-08-03 11:33:50 +12:00
_exit_status = DownloadThread.ExitStatus(self.item.download.game.app_name, DownloadThread.ExitCode.ERROR)
start_t = time.time()
2021-04-08 02:50:36 +12:00
try:
self.item.download.dlmanager.start()
2021-04-08 02:50:36 +12:00
time.sleep(1)
while self.item.download.dlmanager.is_alive():
2021-04-08 02:50:36 +12:00
try:
2022-08-03 11:33:50 +12:00
self.statistics.emit(self.item.download.dlmanager.status_queue.get(timeout=1.0))
2021-04-08 02:50:36 +12:00
except queue.Empty:
pass
2022-08-03 11:33:50 +12:00
if self.dlm_signals.update:
try:
self.item.download.dlmanager.signals_queue.put(self.dlm_signals, block=False, timeout=1.0)
except queue.Full:
pass
time.sleep(self.item.download.dlmanager.update_interval/10)
self.item.download.dlmanager.join()
2021-04-08 02:50:36 +12:00
except Exception as e:
2022-08-03 11:33:50 +12:00
end_t = time.time()
logger.error(f"Installation failed after {end_t - start_t:.02f} seconds.")
logger.warning(f"The following exception occurred while waiting for the downloader to finish: {e!r}.")
_exit_status.exit_code = DownloadThread.ExitCode.ERROR
_exit_status.message = f"{e!r}"
self.exit_status.emit(_exit_status)
2021-04-08 02:50:36 +12:00
return
else:
end_t = time.time()
2022-08-03 11:33:50 +12:00
if self.dlm_signals.kill is True:
logger.info(f"Download stopped after {end_t - start_t:.02f} seconds.")
_exit_status.exit_code = DownloadThread.ExitCode.STOPPED
self.exit_status.emit(_exit_status)
return
logger.info(f"Download finished in {end_t - start_t:.02f} seconds.")
_exit_status.exit_code = DownloadThread.ExitCode.FINISHED
2021-05-21 09:00:38 +12:00
if self.item.options.overlay:
self.signals.overlay_installation_finished.emit()
self.core.finish_overlay_install(self.item.download.igame)
2022-08-03 11:33:50 +12:00
self.exit_status.emit(_exit_status)
return
if not self.item.options.no_install:
postinstall = self.core.install_game(self.item.download.igame)
2021-05-21 09:00:38 +12:00
if postinstall:
2022-08-03 11:33:50 +12:00
# LegendaryCLI(self.core)._handle_postinstall(
# postinstall,
# self.item.download.igame,
# False,
# self.item.options.install_preqs,
# )
self._handle_postinstall(postinstall, self.item.download.igame)
2021-05-21 09:00:38 +12:00
dlcs = self.core.get_dlc_for_game(self.item.download.igame.app_name)
2021-05-21 09:00:38 +12:00
if dlcs:
for dlc in dlcs:
2022-08-03 11:33:50 +12:00
_exit_status.dlcs.append(
{"app_name": dlc.app_name, "app_title": dlc.app_title, "app_version": dlc.app_version}
2021-12-24 22:09:50 +13:00
)
2021-05-21 09:00:38 +12:00
if self.item.download.game.supports_cloud_saves and not self.item.download.game.is_dlc:
2022-08-03 11:33:50 +12:00
_exit_status.sync_saves = True
LegendaryCLI(self.core).clean_post_install(
self.item.download.game,
self.item.download.igame,
self.item.download.repair,
self.item.download.repair_file,
)
2022-08-03 11:33:50 +12:00
if not self.item.options.update and self.item.options.create_shortcut:
_exit_status.shortcuts = True
self.exit_status.emit(_exit_status)
2021-04-08 02:50:36 +12:00
def _handle_postinstall(self, postinstall, igame):
2022-08-03 11:33:50 +12:00
logger.info("This game lists the following prequisites to be installed:")
logger.info(f'- {postinstall["name"]}: {" ".join((postinstall["path"], postinstall["args"]))}')
if platform.system() == "Windows":
2022-08-03 11:33:50 +12:00
if not self.item.options.install_preqs:
logger.info('Marking prerequisites as installed...')
self.core.prereq_installed(self.item.download.igame.app_name)
else:
logger.info('Launching prerequisite executable..')
2021-04-08 02:50:36 +12:00
self.core.prereq_installed(igame.app_name)
2021-12-24 22:09:50 +13:00
req_path, req_exec = os.path.split(postinstall["path"])
2021-04-08 02:50:36 +12:00
work_dir = os.path.join(igame.install_path, req_path)
fullpath = os.path.join(work_dir, req_exec)
proc = QProcess()
proc.setProcessChannelMode(QProcess.MergedChannels)
proc.readyReadStandardOutput.connect(
2022-08-03 11:33:50 +12:00
lambda: logger.debug(str(proc.readAllStandardOutput().data(), "utf-8", "ignore"))
)
proc.setNativeArguments(postinstall.get("args", []))
proc.setWorkingDirectory(work_dir)
proc.start(fullpath)
proc.waitForFinished() # wait, because it is inside the thread
2021-04-08 02:50:36 +12:00
else:
2021-12-24 22:09:50 +13:00
logger.info("Automatic installation not available on Linux.")
2021-04-08 02:50:36 +12:00
def kill(self):
2022-08-03 11:33:50 +12:00
self.dlm_signals.kill = True