Lgndr: Transfer what DownloadThread does to install_game_real
(not used yet)
DownloadThread: Refactor names to match Lgndr
This commit is contained in:
parent
dffd768934
commit
c40fef0595
|
@ -123,6 +123,7 @@ class DownloadsTab(QWidget, Ui_DownloadsTab):
|
|||
|
||||
def stop_download(self):
|
||||
self.thread.kill()
|
||||
self.kill_button.setEnabled(False)
|
||||
|
||||
def install_game(self, queue_item: InstallQueueItemModel):
|
||||
if self.active_game is None:
|
||||
|
@ -137,7 +138,7 @@ class DownloadsTab(QWidget, Ui_DownloadsTab):
|
|||
self.queue_widget.update_queue(self.dl_queue)
|
||||
self.active_game = queue_item.download.game
|
||||
self.thread = DownloadThread(self.core, queue_item)
|
||||
self.thread.exit_status.connect(self.status)
|
||||
self.thread.ret_status.connect(self.status)
|
||||
self.thread.ui_update.connect(self.progress_update)
|
||||
self.thread.start()
|
||||
self.kill_button.setDisabled(False)
|
||||
|
@ -146,9 +147,9 @@ class DownloadsTab(QWidget, Ui_DownloadsTab):
|
|||
|
||||
self.signals.installation_started.emit(self.active_game.app_name)
|
||||
|
||||
@pyqtSlot(DownloadThread.ExitStatus)
|
||||
def status(self, result: DownloadThread.ExitStatus):
|
||||
if result.exit_code == DownloadThread.ExitCode.FINISHED:
|
||||
@pyqtSlot(DownloadThread.ReturnStatus)
|
||||
def status(self, result: DownloadThread.ReturnStatus):
|
||||
if result.ret_code == result.ReturnCode.FINISHED:
|
||||
if result.shortcuts:
|
||||
if not create_desktop_link(result.app_name, self.core, "desktop"):
|
||||
# maybe add it to download summary, to show in finished downloads
|
||||
|
@ -190,10 +191,10 @@ class DownloadsTab(QWidget, Ui_DownloadsTab):
|
|||
else:
|
||||
self.queue_widget.update_queue(self.dl_queue)
|
||||
|
||||
elif result.exit_code == DownloadThread.ExitCode.ERROR:
|
||||
elif result.ret_code == result.ReturnCode.ERROR:
|
||||
QMessageBox.warning(self, self.tr("Error"), f"Download error: {result.message}")
|
||||
|
||||
elif result.exit_code == DownloadThread.ExitCode.STOPPED:
|
||||
elif result.ret_code == result.ReturnCode.STOPPED:
|
||||
self.reset_infos()
|
||||
if w := self.update_widgets.get(self.active_game.app_name):
|
||||
w.update_button.setDisabled(False)
|
||||
|
|
|
@ -20,21 +20,22 @@ logger = getLogger("DownloadThread")
|
|||
|
||||
|
||||
class DownloadThread(QThread):
|
||||
class ExitCode(IntEnum):
|
||||
ERROR = 1
|
||||
STOPPED = 2
|
||||
FINISHED = 3
|
||||
|
||||
@dataclass
|
||||
class ExitStatus:
|
||||
class ReturnStatus:
|
||||
class ReturnCode(IntEnum):
|
||||
ERROR = 1
|
||||
STOPPED = 2
|
||||
FINISHED = 3
|
||||
|
||||
app_name: str
|
||||
exit_code: int
|
||||
ret_code: ReturnCode = ReturnCode.ERROR
|
||||
message: str = ""
|
||||
dlcs: Optional[List[Dict]] = None
|
||||
sync_saves: bool = False
|
||||
tip_url: str = ""
|
||||
shortcuts: bool = False
|
||||
|
||||
exit_status = pyqtSignal(ExitStatus)
|
||||
ret_status = pyqtSignal(ReturnStatus)
|
||||
ui_update = pyqtSignal(UIUpdate)
|
||||
|
||||
def __init__(self, core: LegendaryCore, item: InstallQueueItemModel):
|
||||
|
@ -48,7 +49,7 @@ class DownloadThread(QThread):
|
|||
cli = LegendaryCLI(self.core)
|
||||
self.item.download.dlm.logging_queue = cli.logging_queue
|
||||
self.item.download.dlm.proc_debug = ArgumentsSingleton().debug
|
||||
exit_status = DownloadThread.ExitStatus(self.item.download.game.app_name, DownloadThread.ExitCode.ERROR)
|
||||
ret = DownloadThread.ReturnStatus(self.item.download.game.app_name)
|
||||
start_t = time.time()
|
||||
try:
|
||||
self.item.download.dlm.start()
|
||||
|
@ -69,25 +70,25 @@ class DownloadThread(QThread):
|
|||
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)
|
||||
ret.ret_code = ret.ReturnCode.ERROR
|
||||
ret.message = f"{e!r}"
|
||||
self.ret_status.emit(ret)
|
||||
return
|
||||
else:
|
||||
end_t = time.time()
|
||||
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)
|
||||
ret.ret_code = ret.ReturnCode.STOPPED
|
||||
self.ret_status.emit(ret)
|
||||
return
|
||||
logger.info(f"Download finished in {end_t - start_t:.02f} seconds.")
|
||||
|
||||
exit_status.exit_code = DownloadThread.ExitCode.FINISHED
|
||||
ret.ret_code = ret.ReturnCode.FINISHED
|
||||
|
||||
if self.item.options.overlay:
|
||||
self.signals.overlay_installation_finished.emit()
|
||||
self.core.finish_overlay_install(self.item.download.igame)
|
||||
self.exit_status.emit(exit_status)
|
||||
self.ret_status.emit(ret)
|
||||
return
|
||||
|
||||
if not self.item.options.no_install:
|
||||
|
@ -104,12 +105,23 @@ class DownloadThread(QThread):
|
|||
dlcs = self.core.get_dlc_for_game(self.item.download.igame.app_name)
|
||||
if dlcs and not self.item.options.skip_dlcs:
|
||||
for dlc in dlcs:
|
||||
exit_status.dlcs.append(
|
||||
{"app_name": dlc.app_name, "app_title": dlc.app_title, "app_version": dlc.app_version}
|
||||
ret.dlcs.append(
|
||||
{
|
||||
"app_name": dlc.app_name,
|
||||
"app_title": dlc.app_title,
|
||||
"app_version": dlc.app_version(self.item.options.platform),
|
||||
}
|
||||
)
|
||||
|
||||
if self.item.download.game.supports_cloud_saves and not self.item.download.game.is_dlc:
|
||||
exit_status.sync_saves = True
|
||||
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
|
||||
|
||||
LegendaryCLI(self.core).install_game_cleanup(
|
||||
self.item.download.game,
|
||||
|
@ -119,19 +131,19 @@ class DownloadThread(QThread):
|
|||
)
|
||||
|
||||
if not self.item.options.update and self.item.options.create_shortcut:
|
||||
exit_status.shortcuts = True
|
||||
ret.shortcuts = True
|
||||
|
||||
self.exit_status.emit(exit_status)
|
||||
self.ret_status.emit(ret)
|
||||
|
||||
def _handle_postinstall(self, postinstall, igame):
|
||||
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":
|
||||
if not self.item.options.install_preqs:
|
||||
logger.info('Marking prerequisites as installed...')
|
||||
logger.info("Marking prerequisites as installed...")
|
||||
self.core.prereq_installed(self.item.download.igame.app_name)
|
||||
else:
|
||||
logger.info('Launching prerequisite executable..')
|
||||
logger.info("Launching prerequisite executable..")
|
||||
self.core.prereq_installed(igame.app_name)
|
||||
req_path, req_exec = os.path.split(postinstall["path"])
|
||||
work_dir = os.path.join(igame.install_path, req_path)
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Callable, List, Optional
|
||||
from enum import IntEnum
|
||||
from typing import Callable, List, Optional, Dict
|
||||
|
||||
from .api_monkeys import LgndrIndirectStatus, GetBooleanChoiceProtocol, get_boolean_choice, verify_stdout
|
||||
from .api_monkeys import (
|
||||
LgndrIndirectStatus,
|
||||
GetBooleanChoiceProtocol,
|
||||
get_boolean_choice,
|
||||
verify_stdout,
|
||||
DLManagerSignals
|
||||
)
|
||||
from .downloading import UIUpdate
|
||||
|
||||
"""
|
||||
@dataclass(kw_only=True)
|
||||
|
@ -104,7 +112,25 @@ class LgndrInstallGameRealArgs:
|
|||
skip_dlcs: bool = False
|
||||
with_dlcs: bool = False
|
||||
dlm_debug: bool = False
|
||||
yes: bool = True
|
||||
yes: bool = False
|
||||
# Rare: Extra arguments
|
||||
install_preqs: bool = False
|
||||
indirect_status: LgndrIndirectStatus = LgndrIndirectStatus()
|
||||
get_boolean_choice: GetBooleanChoiceProtocol = get_boolean_choice
|
||||
ui_update: Callable[[UIUpdate], None] = lambda ui: None
|
||||
dlm_signals: DLManagerSignals = DLManagerSignals()
|
||||
|
||||
|
||||
@dataclass
|
||||
class LgndrInstallGameRealRet:
|
||||
class ReturnCode(IntEnum):
|
||||
ERROR = 1
|
||||
STOPPED = 2
|
||||
FINISHED = 3
|
||||
|
||||
app_name: str
|
||||
ret_code: ReturnCode = ReturnCode.ERROR
|
||||
message: str = ""
|
||||
dlcs: Optional[List[Dict]] = None
|
||||
sync_saves: bool = False
|
||||
tip_url: str = ""
|
||||
shortcuts: bool = False
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import logging
|
||||
import os
|
||||
import queue
|
||||
import subprocess
|
||||
import time
|
||||
from typing import Optional, Union, Tuple
|
||||
|
@ -16,6 +17,7 @@ from .api_arguments import (
|
|||
LgndrVerifyGameArgs,
|
||||
LgndrUninstallGameArgs,
|
||||
LgndrInstallGameRealArgs,
|
||||
LgndrInstallGameRealRet,
|
||||
)
|
||||
from .api_monkeys import LgndrIndirectStatus, LgndrIndirectLogger
|
||||
from .core import LegendaryCore
|
||||
|
@ -198,10 +200,10 @@ class LegendaryCLI(LegendaryCLIReal):
|
|||
return dlm, analysis, igame, game, args.repair_mode, repair_file, res
|
||||
|
||||
# Rare: This is currently handled in DownloadThread, this is a trial
|
||||
def install_game_real(self, args: LgndrInstallGameRealArgs, dlm: DLManager, game: Game, igame: InstalledGame) -> None:
|
||||
def install_game_real(self, args: LgndrInstallGameRealArgs, dlm: DLManager, game: Game, igame: InstalledGame) -> LgndrInstallGameRealRet:
|
||||
# Override logger for the local context to use message as part of the indirect return value
|
||||
logger = LgndrIndirectLogger(args.indirect_status, self.logger)
|
||||
get_boolean_choice = args.get_boolean_choice
|
||||
ret = LgndrInstallGameRealRet(game.app_name)
|
||||
|
||||
start_t = time.time()
|
||||
|
||||
|
@ -211,6 +213,17 @@ class LegendaryCLI(LegendaryCLIReal):
|
|||
dlm.proc_debug = args.dlm_debug
|
||||
|
||||
dlm.start()
|
||||
while dlm.is_alive():
|
||||
try:
|
||||
args.ui_update(dlm.status_queue.get(timeout=1.0))
|
||||
except queue.Empty:
|
||||
pass
|
||||
if args.dlm_signals.update:
|
||||
try:
|
||||
dlm.signals_queue.put(args.dlm_signals, block=False, timeout=1.0)
|
||||
except queue.Full:
|
||||
pass
|
||||
time.sleep(dlm.update_interval / 10)
|
||||
dlm.join()
|
||||
except Exception as e:
|
||||
end_t = time.time()
|
||||
|
@ -218,8 +231,16 @@ class LegendaryCLI(LegendaryCLIReal):
|
|||
logger.warning(f'The following exception occurred while waiting for the downloader to finish: {e!r}. '
|
||||
f'Try restarting the process, the resume file will be used to start where it failed. '
|
||||
f'If it continues to fail please open an issue on GitHub.')
|
||||
ret.ret_code = ret.ReturnCode.ERROR
|
||||
ret.message = f"{e!r}"
|
||||
return ret
|
||||
else:
|
||||
end_t = time.time()
|
||||
if args.dlm_signals.kill is True:
|
||||
logger.info(f"Download stopped after {end_t - start_t:.02f} seconds.")
|
||||
ret.exit_code = ret.ReturnCode.STOPPED
|
||||
return ret
|
||||
logger.info(f"Download finished in {end_t - start_t:.02f} seconds.")
|
||||
if not args.no_install:
|
||||
# Allow setting savegame directory at install time so sync-saves will work immediately
|
||||
if (game.supports_cloud_saves or game.supports_mac_cloud_saves) and args.save_path:
|
||||
|
@ -227,30 +248,20 @@ class LegendaryCLI(LegendaryCLIReal):
|
|||
|
||||
postinstall = self.core.install_game(igame)
|
||||
if postinstall:
|
||||
self._handle_postinstall(postinstall, igame, yes=args.yes)
|
||||
self._handle_postinstall(postinstall, igame, yes=args.yes, choice=args.install_preqs)
|
||||
|
||||
dlcs = self.core.get_dlc_for_game(game.app_name)
|
||||
if dlcs and not args.skip_dlcs:
|
||||
print('\nThe following DLCs are available for this game:')
|
||||
for dlc in dlcs:
|
||||
print(f' - {dlc.app_title} (App name: {dlc.app_name}, version: '
|
||||
f'{dlc.app_version(args.platform)})')
|
||||
print('\nYou can manually install these later by running this command with the DLC\'s app name.')
|
||||
ret.dlcs.append(
|
||||
{
|
||||
"app_name": dlc.app_name,
|
||||
"app_title": dlc.app_title,
|
||||
"app_version": dlc.app_version(args.platform)
|
||||
}
|
||||
)
|
||||
|
||||
install_dlcs = not args.skip_dlcs
|
||||
if not args.yes and not args.with_dlcs and not args.skip_dlcs:
|
||||
if not get_boolean_choice(f'Do you wish to automatically install DLCs?'):
|
||||
install_dlcs = False
|
||||
|
||||
if install_dlcs:
|
||||
_yes, _app_name = args.yes, args.app_name
|
||||
args.yes = True
|
||||
for dlc in dlcs:
|
||||
args.app_name = dlc.app_name
|
||||
self.install_game(args)
|
||||
args.yes, args.app_name = _yes, _app_name
|
||||
else:
|
||||
print('')
|
||||
# Rare: We do not install DLCs automatically, we offer to do so through our downloads tab
|
||||
|
||||
if (game.supports_cloud_saves or game.supports_mac_cloud_saves) and not game.is_dlc:
|
||||
# todo option to automatically download saves after the installation
|
||||
|
@ -258,15 +269,18 @@ class LegendaryCLI(LegendaryCLIReal):
|
|||
# not sure how to solve that elegantly.
|
||||
logger.info(f'This game supports cloud saves, syncing is handled by the "sync-saves" command. '
|
||||
f'To download saves for this game run "legendary sync-saves {args.app_name}"')
|
||||
ret.sync_saves = True
|
||||
|
||||
# show tip again after installation finishes so users hopefully actually see it
|
||||
if tip_url := self.core.get_game_tip(igame.app_name):
|
||||
print(f'\nThis game may require additional setup, see: {tip_url}\n')
|
||||
ret.tip_url = tip_url
|
||||
|
||||
self.install_game_cleanup(game, igame, args.repair_mode, args.repair_file)
|
||||
|
||||
logger.info(f'Finished installation process in {end_t - start_t:.02f} seconds.')
|
||||
|
||||
return ret
|
||||
|
||||
def install_game_cleanup(self, game: Game, igame: InstalledGame, repair_mode: bool = False, repair_file: str = '') -> None:
|
||||
# Override logger for the local context to use message as part of the indirect return value
|
||||
logger = LgndrIndirectLogger(LgndrIndirectStatus(), self.logger)
|
||||
|
|
|
@ -128,8 +128,8 @@ class DLManager(DLManagerReal):
|
|||
hours = minutes = seconds = 0
|
||||
rt_hours = rt_minutes = rt_seconds = 0
|
||||
|
||||
log_level = self.log.level
|
||||
# Rare: Disable up to INFO logging level for the segment below
|
||||
log_level = self.log.level
|
||||
self.log.setLevel(logging.ERROR)
|
||||
self.log.info(f'= Progress: {perc:.02f}% ({processed_chunks}/{num_chunk_tasks}), '
|
||||
f'Running for {rt_hours:02d}:{rt_minutes:02d}:{rt_seconds:02d}, '
|
||||
|
|
Loading…
Reference in a new issue