1
0
Fork 0
mirror of synced 2024-05-28 00:10:07 +12:00
Rare/rare/components/tabs/downloads/download_thread.py

166 lines
6.7 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, ArgumentsSingleton
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
@dataclass
class ReturnStatus:
class ReturnCode(IntEnum):
ERROR = 1
STOPPED = 2
FINISHED = 3
2022-08-03 11:33:50 +12:00
app_name: str
ret_code: ReturnCode = ReturnCode.ERROR
2022-08-03 11:33:50 +12:00
message: str = ""
dlcs: Optional[List[Dict]] = None
sync_saves: bool = False
tip_url: str = ""
2022-08-03 11:33:50 +12:00
shortcuts: bool = False
ret_status = pyqtSignal(ReturnStatus)
ui_update = pyqtSignal(UIUpdate)
2021-04-08 02:50:36 +12:00
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):
cli = LegendaryCLI(self.core)
self.item.download.dlm.logging_queue = cli.logging_queue
self.item.download.dlm.proc_debug = ArgumentsSingleton().debug
ret = DownloadThread.ReturnStatus(self.item.download.game.app_name)
2022-08-03 11:33:50 +12:00
start_t = time.time()
2021-04-08 02:50:36 +12:00
try:
self.item.download.dlm.start()
2021-04-08 02:50:36 +12:00
time.sleep(1)
while self.item.download.dlm.is_alive():
2021-04-08 02:50:36 +12:00
try:
self.ui_update.emit(self.item.download.dlm.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.dlm.signals_queue.put(self.dlm_signals, block=False, timeout=1.0)
2022-08-03 11:33:50 +12:00
except queue.Full:
pass
time.sleep(self.item.download.dlm.update_interval / 10)
self.item.download.dlm.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}.")
ret.ret_code = ret.ReturnCode.ERROR
ret.message = f"{e!r}"
self.ret_status.emit(ret)
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.")
ret.ret_code = ret.ReturnCode.STOPPED
self.ret_status.emit(ret)
2022-08-03 11:33:50 +12:00
return
logger.info(f"Download finished in {end_t - start_t:.02f} seconds.")
ret.ret_code = ret.ReturnCode.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)
self.ret_status.emit(ret)
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)
if dlcs and not self.item.options.skip_dlcs:
2022-08-14 03:53:00 +12:00
ret.dlcs = []
2021-05-21 09:00:38 +12:00
for dlc in dlcs:
ret.dlcs.append(
{
"app_name": dlc.app_name,
"app_title": dlc.app_title,
"app_version": dlc.app_version(self.item.options.platform),
}
2021-12-24 22:09:50 +13:00
)
2021-05-21 09:00:38 +12:00
if (
self.item.download.game.supports_cloud_saves
or self.item.download.game.supports_mac_cloud_saves
) and not self.item.download.game.is_dlc:
ret.sync_saves = True
# show tip again after installation finishes so users hopefully actually see it
if tip_url := self.core.get_game_tip(self.item.download.igame.app_name):
ret.tip_url = tip_url
2022-08-03 11:33:50 +12:00
LegendaryCLI(self.core).install_game_cleanup(
2022-08-03 11:33:50 +12:00
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:
ret.shortcuts = True
2022-08-03 11:33:50 +12:00
self.ret_status.emit(ret)
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...")
2022-08-03 11:33:50 +12:00
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