1
0
Fork 0
mirror of synced 2024-05-06 13:42:52 +12:00

Lgndr: Transfer what DownloadThread does to install_game_real (not used yet)

DownloadThread: Refactor names to match Lgndr
This commit is contained in:
loathingKernel 2022-08-12 13:17:53 +03:00
parent dffd768934
commit c40fef0595
5 changed files with 110 additions and 57 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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}, '