[cli] use status queue to print progress

This commit is contained in:
ChemicalXandco 2021-02-06 10:32:27 +00:00
parent 1cf2d7e6e7
commit c1db2b9f07
4 changed files with 53 additions and 31 deletions

View file

@ -3,9 +3,11 @@
import argparse import argparse
import csv import csv
import datetime
import json import json
import logging import logging
import os import os
import queue
import shlex import shlex
import subprocess import subprocess
import time import time
@ -501,13 +503,13 @@ class LegendaryCLI:
logger.info(f'Launch parameters: {shlex.join(params)}') logger.info(f'Launch parameters: {shlex.join(params)}')
logger.info(f'Working directory: {cwd}') logger.info(f'Working directory: {cwd}')
if env: if env:
logger.info('Environment overrides:', env) logger.info(f'Environment overrides: {env}')
else: else:
logger.info(f'Launching {app_name}...') logger.info(f'Launching {app_name}...')
logger.debug(f'Launch parameters: {shlex.join(params)}') logger.debug(f'Launch parameters: {shlex.join(params)}')
logger.debug(f'Working directory: {cwd}') logger.debug(f'Working directory: {cwd}')
if env: if env:
logger.debug('Environment overrides:', env) logger.debug(f'Environment overrides: {env}')
subprocess.Popen(params, cwd=cwd, env=env) subprocess.Popen(params, cwd=cwd, env=env)
def install_game(self, args): def install_game(self, args):
@ -522,15 +524,15 @@ class LegendaryCLI:
logger.error(f'Update requested for "{args.app_name}", but app not installed!') logger.error(f'Update requested for "{args.app_name}", but app not installed!')
exit(1) exit(1)
status_queue = MPQueue()
logger.info('Preparing download...') logger.info('Preparing download...')
# todo use status queue to print progress from CLI
# This has become a little ridiculous hasn't it?
try: try:
dlm, analysis, game, igame, repair, repair_file = self.core.prepare_download( dlm, analysis, game, igame, repair, repair_file = self.core.prepare_download(
app_name=args.app_name, app_name=args.app_name,
base_path=args.base_path, base_path=args.base_path,
force=args.force, force=args.force,
no_install=args.no_install, no_install=args.no_install,
status_q=status_queue,
max_shm=args.shared_memory, max_shm=args.shared_memory,
max_workers=args.max_workers, max_workers=args.max_workers,
game_folder=args.game_folder, game_folder=args.game_folder,
@ -578,6 +580,22 @@ class LegendaryCLI:
dlm.proc_debug = args.dlm_debug dlm.proc_debug = args.dlm_debug
dlm.start() dlm.start()
time.sleep(1)
while dlm.is_alive():
try:
status = status_queue.get(timeout=0.1)
logger.info(f'= Progress: {status.progress:.02f}% ({status.processed_chunks}/{status.chunk_tasks}), '
f'Running for {str(datetime.timedelta(seconds=status.runtime))}, '
f'ETA: {str(datetime.timedelta(seconds=status.estimated_time_left))}')
logger.info(f' - Downloaded: {status.total_downloaded / 1024 / 1024:.02f} MiB, '
f'Written: {status.total_written / 1024 / 1024:.02f} MiB')
logger.info(f' - Cache usage: {status.cache_usage} MiB, active tasks: {status.active_tasks}')
logger.info(f' + Download\t- {status.download_speed / 1024 / 1024:.02f} MiB/s (raw) '
f'/ {status.download_decompressed_speed / 1024 / 1024:.02f} MiB/s (decompressed)')
logger.info(f' + Disk\t- {status.write_speed / 1024 / 1024:.02f} MiB/s (write) / '
f'{status.read_speed / 1024 / 1024:.02f} MiB/s (read)')
except queue.Empty:
pass
dlm.join() dlm.join()
except Exception as e: except Exception as e:
end_t = time.time() end_t = time.time()

View file

@ -24,8 +24,8 @@ from legendary.lfs.lgndry import LGDLFS
from legendary.utils.lfs import clean_filename, delete_folder, delete_filelist, validate_files from legendary.utils.lfs import clean_filename, delete_folder, delete_filelist, validate_files
from legendary.models.downloading import AnalysisResult, ConditionCheckResult from legendary.models.downloading import AnalysisResult, ConditionCheckResult
from legendary.models.egl import EGLManifest from legendary.models.egl import EGLManifest
from legendary.models.exceptions import * from legendary.models.exceptions import InvalidCredentialsError
from legendary.models.game import * from legendary.models.game import GameAsset, Game, InstalledGame, SaveGameFile, SaveGameStatus, VerifyResult
from legendary.models.json_manifest import JSONManifest from legendary.models.json_manifest import JSONManifest
from legendary.models.manifest import Manifest, ManifestMeta from legendary.models.manifest import Manifest, ManifestMeta
from legendary.models.chunk import Chunk from legendary.models.chunk import Chunk

View file

@ -695,35 +695,30 @@ class DLManager(Process):
total_avail = len(self.sms) total_avail = len(self.sms)
total_used = (num_shared_memory_segments - total_avail) * (self.analysis.biggest_chunk / 1024 / 1024) total_used = (num_shared_memory_segments - total_avail) * (self.analysis.biggest_chunk / 1024 / 1024)
if runtime and processed_chunks: try:
rt_hours, runtime = int(runtime // 3600), runtime % 3600
rt_minutes, rt_seconds = int(runtime // 60), int(runtime % 60)
average_speed = processed_chunks / runtime average_speed = processed_chunks / runtime
estimate = (num_chunk_tasks - processed_chunks) / average_speed estimate = (num_chunk_tasks - processed_chunks) / average_speed
hours, estimate = int(estimate // 3600), estimate % 3600 except ZeroDivisionError:
minutes, seconds = int(estimate // 60), int(estimate % 60) average_speed = estimate = 0
else:
hours = minutes = seconds = 0
rt_hours = rt_minutes = rt_seconds = 0
self.log.info(f'= Progress: {perc:.02f}% ({processed_chunks}/{num_chunk_tasks}), '
f'Running for {rt_hours:02d}:{rt_minutes:02d}:{rt_seconds:02d}, '
f'ETA: {hours:02d}:{minutes:02d}:{seconds:02d}')
self.log.info(f' - Downloaded: {total_dl / 1024 / 1024:.02f} MiB, '
f'Written: {total_write / 1024 / 1024:.02f} MiB')
self.log.info(f' - Cache usage: {total_used} MiB, active tasks: {self.active_tasks}')
self.log.info(f' + Download\t- {dl_speed / 1024 / 1024:.02f} MiB/s (raw) '
f'/ {dl_unc_speed / 1024 / 1024:.02f} MiB/s (decompressed)')
self.log.info(f' + Disk\t- {w_speed / 1024 / 1024:.02f} MiB/s (write) / '
f'{r_speed / 1024 / 1024:.02f} MiB/s (read)')
# TODO set current_filename argument of UIUpdate
# send status update to back to instantiator (if queue exists) # send status update to back to instantiator (if queue exists)
if self.status_queue: if self.status_queue:
try: try:
self.status_queue.put(UIUpdate( self.status_queue.put(UIUpdate(
progress=perc, download_speed=dl_unc_speed, write_speed=w_speed, read_speed=r_speed, progress=perc,
memory_usage=total_used * 1024 * 1024 runtime=round(runtime),
estimated_time_left=round(estimate),
processed_chunks=processed_chunks,
chunk_tasks=num_chunk_tasks,
total_downloaded=total_dl,
total_written=total_write,
cache_usage=total_used,
active_tasks=self.active_tasks,
download_speed=dl_speed,
download_decompressed_speed=dl_unc_speed,
write_speed=w_speed,
read_speed=r_speed,
), timeout=1.0) ), timeout=1.0)
except Exception as e: except Exception as e:
self.log.warning(f'Failed to send status update to queue: {e!r}') self.log.warning(f'Failed to send status update to queue: {e!r}')

View file

@ -70,14 +70,23 @@ class UIUpdate:
Status update object sent from the manager to the CLI/GUI to update status indicators Status update object sent from the manager to the CLI/GUI to update status indicators
""" """
def __init__(self, progress, download_speed, write_speed, read_speed, def __init__(self, progress, runtime, estimated_time_left, processed_chunks, chunk_tasks,
memory_usage, current_filename=''): total_downloaded, total_written, cache_usage, active_tasks, download_speed,
download_decompressed_speed, write_speed, read_speed, current_filename=''):
self.progress = progress self.progress = progress
self.runtime = runtime
self.estimated_time_left = estimated_time_left
self.processed_chunks = processed_chunks
self.chunk_tasks = chunk_tasks
self.total_downloaded = total_downloaded
self.total_written = total_written
self.cache_usage = cache_usage
self.active_tasks = active_tasks
self.download_speed = download_speed self.download_speed = download_speed
self.download_decompressed_speed = download_decompressed_speed
self.write_speed = write_speed self.write_speed = write_speed
self.read_speed = read_speed self.read_speed = read_speed
self.current_filename = current_filename self.current_filename = current_filename
self.memory_usage = memory_usage
class SharedMemorySegment: class SharedMemorySegment: