Download queue; delete option
This commit is contained in:
parent
4237358909
commit
d8aa6541b9
93
Rare/Components/Tabs/Downloads/DlQueueWidget.py
Normal file
93
Rare/Components/Tabs/Downloads/DlQueueWidget.py
Normal file
|
@ -0,0 +1,93 @@
|
|||
from PyQt5.QtCore import pyqtSignal
|
||||
from PyQt5.QtWidgets import QGroupBox, QVBoxLayout, QLabel, QHBoxLayout, QPushButton, QWidget
|
||||
from qtawesome import icon
|
||||
|
||||
|
||||
class DlWidget(QWidget):
|
||||
move_up = pyqtSignal(str) # app_name
|
||||
move_down = pyqtSignal(str) # app_name
|
||||
remove = pyqtSignal(str) # app_name
|
||||
|
||||
def __init__(self, index, item):
|
||||
super(DlWidget, self).__init__()
|
||||
self.app_name = item[1].app_name
|
||||
self.layout = QHBoxLayout()
|
||||
|
||||
self.left_layout = QVBoxLayout()
|
||||
self.move_up_button = QPushButton(icon("fa.arrow-up", color="white"), "")
|
||||
if index == 0:
|
||||
self.move_up_button.setDisabled(True)
|
||||
self.move_up_button.clicked.connect(lambda: self.move_up.emit(self.app_name))
|
||||
self.move_up_button.setFixedWidth(20)
|
||||
self.left_layout.addWidget(self.move_up_button)
|
||||
|
||||
self.move_down_buttton = QPushButton(icon("fa.arrow-down", color="white"), "")
|
||||
self.move_down_buttton.clicked.connect(lambda: self.move_down.emit(self.app_name))
|
||||
self.left_layout.addWidget(self.move_down_buttton)
|
||||
self.move_down_buttton.setFixedWidth(20)
|
||||
|
||||
self.layout.addLayout(self.left_layout)
|
||||
|
||||
self.right_layout = QVBoxLayout()
|
||||
self.title = QLabel(item[1].app_title)
|
||||
self.right_layout.addWidget(self.title)
|
||||
|
||||
dl_size = item[-1].dl_size
|
||||
install_size = item[-1].install_size
|
||||
|
||||
self.size = QHBoxLayout()
|
||||
|
||||
self.size.addWidget(QLabel(self.tr("Download size: {} GB").format(dl_size/1024**3)))
|
||||
self.size.addWidget(QLabel(self.tr("Install size: {} GB").format(install_size/1024**3)))
|
||||
self.right_layout.addLayout(self.size)
|
||||
|
||||
self.delete = QPushButton(self.tr("Remove Download"))
|
||||
self.delete.clicked.connect(lambda: self.remove.emit(self.app_name))
|
||||
self.right_layout.addWidget(self.delete)
|
||||
|
||||
self.layout.addLayout(self.right_layout)
|
||||
self.setLayout(self.layout)
|
||||
|
||||
|
||||
class DlQueueWidget(QGroupBox):
|
||||
update_list = pyqtSignal(list)
|
||||
dl_queue = []
|
||||
def __init__(self):
|
||||
|
||||
super(DlQueueWidget, self).__init__()
|
||||
self.setTitle(self.tr("Download Queue"))
|
||||
self.layout = QVBoxLayout()
|
||||
self.setObjectName("group")
|
||||
self.text = QLabel(self.tr("No downloads in queue"))
|
||||
self.layout.addWidget(self.text)
|
||||
|
||||
self.setLayout(self.layout)
|
||||
|
||||
def update_queue(self, dl_queue: list):
|
||||
self.dl_queue = dl_queue
|
||||
self.setLayout(QVBoxLayout())
|
||||
QWidget().setLayout(self.layout)
|
||||
self.layout = QVBoxLayout()
|
||||
|
||||
if len(dl_queue) == 0:
|
||||
self.layout.addWidget(QLabel(self.tr("No downloads in queue")))
|
||||
self.setLayout(self.layout)
|
||||
return
|
||||
|
||||
for index, item in enumerate(dl_queue):
|
||||
widget = DlWidget(index, item)
|
||||
widget.remove.connect(self.remove)
|
||||
self.layout.addWidget(widget)
|
||||
|
||||
self.setLayout(self.layout)
|
||||
|
||||
def remove(self, app_name):
|
||||
for index, i in enumerate(self.dl_queue):
|
||||
if i[1].app_name == app_name:
|
||||
self.dl_queue.pop(index)
|
||||
break
|
||||
else:
|
||||
print("BUG! ", app_name)
|
||||
return
|
||||
self.update_list.emit(self.dl_queue)
|
||||
self.update_queue(self.dl_queue)
|
|
@ -1,19 +1,16 @@
|
|||
import datetime
|
||||
import os
|
||||
import queue
|
||||
import subprocess
|
||||
import time
|
||||
from logging import getLogger
|
||||
from multiprocessing import Queue as MPQueue
|
||||
|
||||
from PyQt5.QtCore import QThread, pyqtSignal, Qt, QCoreApplication
|
||||
from PyQt5.QtCore import QThread, pyqtSignal, Qt
|
||||
from PyQt5.QtWidgets import QWidget, QMessageBox, QVBoxLayout, QLabel, QGridLayout, QProgressBar, QPushButton, QDialog, \
|
||||
QListWidget, QHBoxLayout
|
||||
|
||||
from Rare.Components.Dialogs.InstallDialog import InstallInfoDialog, InstallDialog
|
||||
from Rare.utils.Models import InstallOptions, KillDownloadException
|
||||
from Rare.Components.Tabs.Downloads.DlQueueWidget import DlQueueWidget
|
||||
from Rare.Components.Tabs.Downloads.DownloadThread import DownloadThread
|
||||
from Rare.utils.Models import InstallOptions
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.downloader.manager import DLManager
|
||||
from custom_legendary.models.downloading import UIUpdate
|
||||
from custom_legendary.models.game import Game, InstalledGame
|
||||
from custom_legendary.utils.selective_dl import games
|
||||
|
@ -21,105 +18,6 @@ from custom_legendary.utils.selective_dl import games
|
|||
logger = getLogger("Download")
|
||||
|
||||
|
||||
class DownloadThread(QThread):
|
||||
status = pyqtSignal(str)
|
||||
statistics = pyqtSignal(UIUpdate)
|
||||
kill = False
|
||||
|
||||
def __init__(self, dlm: DLManager, core: LegendaryCore, status_queue: MPQueue, igame, repair=False,
|
||||
repair_file=None):
|
||||
super(DownloadThread, self).__init__()
|
||||
self.dlm = dlm
|
||||
self.core = core
|
||||
self.status_queue = status_queue
|
||||
self.igame = igame
|
||||
self.repair = repair
|
||||
self.repair_file = repair_file
|
||||
|
||||
def run(self):
|
||||
start_time = time.time()
|
||||
try:
|
||||
|
||||
self.dlm.start()
|
||||
time.sleep(1)
|
||||
while self.dlm.is_alive():
|
||||
if self.kill:
|
||||
# raise KillDownloadException()
|
||||
# TODO kill download queue, workers
|
||||
pass
|
||||
try:
|
||||
self.statistics.emit(self.status_queue.get(timeout=1))
|
||||
except queue.Empty:
|
||||
pass
|
||||
|
||||
self.dlm.join()
|
||||
|
||||
except KillDownloadException:
|
||||
self.status.emit("stop")
|
||||
logger.info("Downlaod can be continued later")
|
||||
self.dlm.kill()
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Installation failed after {time.time() - start_time:.02f} seconds: {e}")
|
||||
self.status.emit("error")
|
||||
return
|
||||
|
||||
else:
|
||||
self.status.emit("dl_finished")
|
||||
end_t = time.time()
|
||||
|
||||
game = self.core.get_game(self.igame.app_name)
|
||||
postinstall = self.core.install_game(self.igame)
|
||||
if postinstall:
|
||||
self._handle_postinstall(postinstall, self.igame)
|
||||
|
||||
dlcs = self.core.get_dlc_for_game(self.igame.app_name)
|
||||
if dlcs:
|
||||
print('The following DLCs are available for this game:')
|
||||
for dlc in dlcs:
|
||||
print(f' - {dlc.app_title} (App name: {dlc.app_name}, version: {dlc.app_version})')
|
||||
print('Manually installing DLCs works the same; just use the DLC app name instead.')
|
||||
|
||||
# install_dlcs = QMessageBox.question(self, "", "Do you want to install the prequisites", QMessageBox.Yes|QMessageBox.No) == QMessageBox.Yes
|
||||
# TODO
|
||||
if game.supports_cloud_saves and not game.is_dlc:
|
||||
logger.info('This game supports cloud saves, syncing is handled by the "sync-saves" command.')
|
||||
logger.info(f'To download saves for this game run "legendary sync-saves {game.app_name}"')
|
||||
old_igame = self.core.get_installed_game(game.app_name)
|
||||
if old_igame and self.repair and os.path.exists(self.repair_file):
|
||||
if old_igame.needs_verification:
|
||||
old_igame.needs_verification = False
|
||||
self.core.install_game(old_igame)
|
||||
|
||||
logger.debug('Removing repair file.')
|
||||
os.remove(self.repair_file)
|
||||
if old_igame and old_igame.install_tags != self.igame.install_tags:
|
||||
old_igame.install_tags = self.igame.install_tags
|
||||
self.logger.info('Deleting now untagged files.')
|
||||
self.core.uninstall_tag(old_igame)
|
||||
self.core.install_game(old_igame)
|
||||
|
||||
self.status.emit("finish")
|
||||
|
||||
def _handle_postinstall(self, postinstall, igame):
|
||||
print('This game lists the following prequisites to be installed:')
|
||||
print(f'- {postinstall["name"]}: {" ".join((postinstall["path"], postinstall["args"]))}')
|
||||
if os.name == 'nt':
|
||||
if QMessageBox.question(self, "", "Do you want to install the prequisites",
|
||||
QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
|
||||
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)
|
||||
fullpath = os.path.join(work_dir, req_exec)
|
||||
subprocess.call([fullpath, postinstall['args']], cwd=work_dir)
|
||||
else:
|
||||
self.core.prereq_installed(self.igame.app_name)
|
||||
|
||||
else:
|
||||
logger.info('Automatic installation not available on Linux.')
|
||||
|
||||
|
||||
class DownloadTab(QWidget):
|
||||
finished = pyqtSignal()
|
||||
thread: QThread
|
||||
|
@ -159,8 +57,9 @@ class DownloadTab(QWidget):
|
|||
|
||||
self.layout.addLayout(self.mini_layout)
|
||||
|
||||
self.queue_label = QLabel(self.tr("Download queue: Empty"))
|
||||
self.layout.addWidget(self.queue_label)
|
||||
self.queue_widget = DlQueueWidget()
|
||||
self.layout.addWidget(self.queue_widget)
|
||||
self.queue_widget.update_list.connect(self.update_dl_queue)
|
||||
|
||||
self.update_title = QLabel(f"<h2>{self.tr('Updates')}</h2>")
|
||||
self.update_title.setStyleSheet("""
|
||||
|
@ -184,6 +83,10 @@ class DownloadTab(QWidget):
|
|||
|
||||
self.setLayout(self.layout)
|
||||
|
||||
def update_dl_queue(self, dl_queue):
|
||||
self.dl_queue = dl_queue
|
||||
|
||||
|
||||
def stop_download(self):
|
||||
self.thread.kill = True
|
||||
|
||||
|
@ -232,15 +135,13 @@ class DownloadTab(QWidget):
|
|||
return
|
||||
|
||||
if self.active_game is None:
|
||||
self.start_installation(dlm, game, status_queue, igame, repair_file, options)
|
||||
self.start_installation(dlm, game, status_queue, igame, repair_file, options, analysis)
|
||||
else:
|
||||
self.dl_queue.append((dlm, game, status_queue, igame, repair_file, options))
|
||||
self.queue_label.setText(
|
||||
self.tr("Download queue: ") + ", ".join(
|
||||
i[1].app_title for i in self.dl_queue) if self.dl_queue else self.tr("Empty"))
|
||||
|
||||
def start_installation(self, dlm, game, status_queue, igame, repair_file, options: InstallOptions):
|
||||
self.dl_queue.append((dlm, game, status_queue, igame, repair_file, options, analysis))
|
||||
self.queue_widget.update_queue(self.dl_queue)
|
||||
|
||||
def start_installation(self, dlm, game, status_queue, igame, repair_file, options: InstallOptions, analysis):
|
||||
print("start installation", game.app_title)
|
||||
self.active_game = game
|
||||
self.thread = DownloadThread(dlm, self.core, status_queue, igame, options.repair, repair_file)
|
||||
self.thread.status.connect(self.status)
|
||||
|
@ -305,8 +206,10 @@ class DownloadTab(QWidget):
|
|||
# QMessageBox.information(self, "Info", "Download finished")
|
||||
logger.info("Download finished: " + self.active_game.app_title)
|
||||
|
||||
if self.dl_queue[0][1] == self.active_game.app_name:
|
||||
self.dl_queue.pop(0)
|
||||
if self.dl_queue:
|
||||
if self.dl_queue[0][1] == self.active_game.app_name:
|
||||
self.dl_queue.pop(0)
|
||||
self.queue_widget.update_queue(self.dl_queue)
|
||||
|
||||
if self.active_game.app_name in self.update_widgets.keys():
|
||||
self.update_widgets[self.active_game.app_name].setVisible(False)
|
||||
|
@ -329,7 +232,7 @@ class DownloadTab(QWidget):
|
|||
if len(self.dl_queue) != 0:
|
||||
self.start_installation(*self.dl_queue[0])
|
||||
else:
|
||||
self.queue_label.setText(self.tr("Download queue: Empty"))
|
||||
self.queue_widget.update_queue(self.dl_queue)
|
||||
|
||||
elif text == "error":
|
||||
QMessageBox.warning(self, "warn", "Download error")
|
||||
|
|
117
Rare/Components/Tabs/Downloads/DownloadThread.py
Normal file
117
Rare/Components/Tabs/Downloads/DownloadThread.py
Normal file
|
@ -0,0 +1,117 @@
|
|||
import os
|
||||
import queue
|
||||
import subprocess
|
||||
import time
|
||||
from logging import getLogger
|
||||
from multiprocessing import Queue as MPQueue
|
||||
|
||||
from PyQt5.QtCore import QThread, pyqtSignal
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
|
||||
from Rare.utils.Models import KillDownloadException
|
||||
|
||||
from custom_legendary.core import LegendaryCore
|
||||
from custom_legendary.downloader.manager import DLManager
|
||||
from custom_legendary.models.downloading import UIUpdate
|
||||
|
||||
logger = getLogger("Download")
|
||||
|
||||
|
||||
class DownloadThread(QThread):
|
||||
status = pyqtSignal(str)
|
||||
statistics = pyqtSignal(UIUpdate)
|
||||
kill = False
|
||||
|
||||
def __init__(self, dlm: DLManager, core: LegendaryCore, status_queue: MPQueue, igame, repair=False,
|
||||
repair_file=None):
|
||||
super(DownloadThread, self).__init__()
|
||||
self.dlm = dlm
|
||||
self.core = core
|
||||
self.status_queue = status_queue
|
||||
self.igame = igame
|
||||
self.repair = repair
|
||||
self.repair_file = repair_file
|
||||
|
||||
def run(self):
|
||||
start_time = time.time()
|
||||
try:
|
||||
|
||||
self.dlm.start()
|
||||
time.sleep(1)
|
||||
while self.dlm.is_alive():
|
||||
if self.kill:
|
||||
# raise KillDownloadException()
|
||||
# TODO kill download queue, workers
|
||||
pass
|
||||
try:
|
||||
self.statistics.emit(self.status_queue.get(timeout=1))
|
||||
except queue.Empty:
|
||||
pass
|
||||
|
||||
self.dlm.join()
|
||||
|
||||
except KillDownloadException:
|
||||
self.status.emit("stop")
|
||||
logger.info("Downlaod can be continued later")
|
||||
self.dlm.kill()
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Installation failed after {time.time() - start_time:.02f} seconds: {e}")
|
||||
self.status.emit("error")
|
||||
return
|
||||
|
||||
else:
|
||||
self.status.emit("dl_finished")
|
||||
end_t = time.time()
|
||||
|
||||
game = self.core.get_game(self.igame.app_name)
|
||||
postinstall = self.core.install_game(self.igame)
|
||||
if postinstall:
|
||||
self._handle_postinstall(postinstall, self.igame)
|
||||
|
||||
dlcs = self.core.get_dlc_for_game(self.igame.app_name)
|
||||
if dlcs:
|
||||
print('The following DLCs are available for this game:')
|
||||
for dlc in dlcs:
|
||||
print(f' - {dlc.app_title} (App name: {dlc.app_name}, version: {dlc.app_version})')
|
||||
print('Manually installing DLCs works the same; just use the DLC app name instead.')
|
||||
|
||||
# install_dlcs = QMessageBox.question(self, "", "Do you want to install the prequisites", QMessageBox.Yes|QMessageBox.No) == QMessageBox.Yes
|
||||
# TODO
|
||||
if game.supports_cloud_saves and not game.is_dlc:
|
||||
logger.info('This game supports cloud saves, syncing is handled by the "sync-saves" command.')
|
||||
logger.info(f'To download saves for this game run "legendary sync-saves {game.app_name}"')
|
||||
old_igame = self.core.get_installed_game(game.app_name)
|
||||
if old_igame and self.repair and os.path.exists(self.repair_file):
|
||||
if old_igame.needs_verification:
|
||||
old_igame.needs_verification = False
|
||||
self.core.install_game(old_igame)
|
||||
|
||||
logger.debug('Removing repair file.')
|
||||
os.remove(self.repair_file)
|
||||
if old_igame and old_igame.install_tags != self.igame.install_tags:
|
||||
old_igame.install_tags = self.igame.install_tags
|
||||
self.logger.info('Deleting now untagged files.')
|
||||
self.core.uninstall_tag(old_igame)
|
||||
self.core.install_game(old_igame)
|
||||
|
||||
self.status.emit("finish")
|
||||
|
||||
def _handle_postinstall(self, postinstall, igame):
|
||||
print('This game lists the following prequisites to be installed:')
|
||||
print(f'- {postinstall["name"]}: {" ".join((postinstall["path"], postinstall["args"]))}')
|
||||
if os.name == 'nt':
|
||||
if QMessageBox.question(self, "", "Do you want to install the prequisites",
|
||||
QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
|
||||
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)
|
||||
fullpath = os.path.join(work_dir, req_exec)
|
||||
subprocess.call([fullpath, postinstall['args']], cwd=work_dir)
|
||||
else:
|
||||
self.core.prereq_installed(self.igame.app_name)
|
||||
|
||||
else:
|
||||
logger.info('Automatic installation not available on Linux.')
|
||||
|
|
@ -45,7 +45,7 @@ class RareSettings(QGroupBox):
|
|||
self.select_lang.currentIndexChanged.connect(self.update_lang)
|
||||
self.layout.addWidget(self.lang_widget)
|
||||
|
||||
self.exit_to_sys_tray = QCheckBox("Hide to System Tray Icon")
|
||||
self.exit_to_sys_tray = QCheckBox(self.tr("Hide to System Tray Icon"))
|
||||
self.exit_to_sys_tray.setChecked(settings.value("sys_tray", True, bool))
|
||||
self.exit_to_sys_tray.stateChanged.connect(self.update_sys_tray)
|
||||
self.sys_tray_widget = SettingsWidget(self.tr("Exit to System Tray Icon"), self.exit_to_sys_tray)
|
||||
|
|
Loading…
Reference in a new issue