Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
10839623c6
2
.github/GenPKG.sh
vendored
2
.github/GenPKG.sh
vendored
|
@ -1,5 +1,5 @@
|
||||||
export PYTHONPATH=$PWD
|
export PYTHONPATH=$PWD
|
||||||
version=$(python3 Rare --version)
|
version=$(python3 rare --version)
|
||||||
cd .github
|
cd .github
|
||||||
git clone https://aur.archlinux.org/rare.git
|
git clone https://aur.archlinux.org/rare.git
|
||||||
cd ..
|
cd ..
|
||||||
|
|
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
|
@ -1,12 +1,9 @@
|
||||||
# This workflow will upload a Python Package using Twine when a release
|
|
||||||
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
|
|
||||||
|
|
||||||
name: New Release
|
name: New Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [ published ]
|
types: [ published ]
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pypy-deploy:
|
pypy-deploy:
|
||||||
|
@ -61,9 +58,10 @@ jobs:
|
||||||
pip3 install pyinstaller setuptools wheel
|
pip3 install pyinstaller setuptools wheel
|
||||||
pip3 install -r requirements.txt
|
pip3 install -r requirements.txt
|
||||||
- name: Prepare
|
- name: Prepare
|
||||||
run: cp Rare/__main__.py ./
|
run: cp rare/__main__.py ./
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pyinstaller
|
run: pyinstaller
|
||||||
|
--icon=rare/styles/Logo.ico
|
||||||
--onefile
|
--onefile
|
||||||
--name Rare
|
--name Rare
|
||||||
--add-data="Rare/languages/*;Rare/languages"
|
--add-data="Rare/languages/*;Rare/languages"
|
||||||
|
@ -91,7 +89,7 @@ jobs:
|
||||||
python3-requests
|
python3-requests
|
||||||
python3-pyqt5
|
python3-pyqt5
|
||||||
python3-pil
|
python3-pil
|
||||||
python3-qtawesome-common
|
python3-qtawesome
|
||||||
python3-setuptools
|
python3-setuptools
|
||||||
python3-wheel
|
python3-wheel
|
||||||
|
|
||||||
|
@ -101,7 +99,7 @@ jobs:
|
||||||
python3 setup.py --command-packages=stdeb.command bdist_deb
|
python3 setup.py --command-packages=stdeb.command bdist_deb
|
||||||
|
|
||||||
- name: move file
|
- name: move file
|
||||||
run: mv deb_dist/*.deb rare.deb
|
run: mv deb_dist/*.deb Rare.deb
|
||||||
|
|
||||||
- name: Upload files to GitHub
|
- name: Upload files to GitHub
|
||||||
uses: svenstaro/upload-release-action@2.2.1
|
uses: svenstaro/upload-release-action@2.2.1
|
||||||
|
|
|
@ -19,7 +19,8 @@ exmples:
|
||||||
Select one Card of the project and implement it or make other changes
|
Select one Card of the project and implement it or make other changes
|
||||||
|
|
||||||
|
|
||||||
##Git crash-course
|
## Git crash-course
|
||||||
To contribute fork the repository and clone **your** repo. Then make your changes, add it to git with `git add .` and upload it to Github with `git commit -m "message"` and `git push` or with your IDE.
|
To contribute fork the repository and clone **your** repo. Then make your changes, add it to git with `git add File.xy` and upload it to GitHub with `git commit -m "message"` and `git push`.
|
||||||
|
Some IDEs can do this automatically.
|
||||||
|
|
||||||
If you uploaded your changes, create a pull request
|
If you uploaded your changes, create a pull request to dev-branch
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
include README.md
|
include README.md
|
||||||
include rare/languages/de.qm
|
include rare/languages/*.qm
|
||||||
include rare/styles/*
|
include rare/styles/*
|
|
@ -1,5 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
__version__ = "1.0_beta2"
|
__version__ = "1.0.0"
|
||||||
style_path = os.path.join(os.path.dirname(__file__), "styles/")
|
style_path = os.path.join(os.path.dirname(__file__), "styles/")
|
||||||
lang_path = os.path.join(os.path.dirname(__file__), "languages/")
|
lang_path = os.path.join(os.path.dirname(__file__), "languages/")
|
||||||
|
|
|
@ -50,9 +50,9 @@ class DownloadTab(QWidget):
|
||||||
self.mini_layout.addWidget(self.prog_bar)
|
self.mini_layout.addWidget(self.prog_bar)
|
||||||
|
|
||||||
self.kill_button = QPushButton(self.tr("Stop Download"))
|
self.kill_button = QPushButton(self.tr("Stop Download"))
|
||||||
# self.mini_layout.addWidget(self.kill_button)
|
|
||||||
self.kill_button.setDisabled(True)
|
self.kill_button.setDisabled(True)
|
||||||
self.kill_button.clicked.connect(self.stop_download)
|
self.kill_button.clicked.connect(self.stop_download)
|
||||||
|
self.mini_layout.addWidget(self.kill_button)
|
||||||
|
|
||||||
self.layout.addLayout(self.mini_layout)
|
self.layout.addLayout(self.mini_layout)
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ class DownloadTab(QWidget):
|
||||||
self.dl_queue = dl_queue
|
self.dl_queue = dl_queue
|
||||||
|
|
||||||
def stop_download(self):
|
def stop_download(self):
|
||||||
self.thread.kill = True
|
self.thread.kill()
|
||||||
|
|
||||||
def install_game(self, options: InstallOptions):
|
def install_game(self, options: InstallOptions):
|
||||||
|
|
||||||
|
@ -227,11 +227,12 @@ class DownloadTab(QWidget):
|
||||||
else:
|
else:
|
||||||
self.queue_widget.update_queue(self.dl_queue)
|
self.queue_widget.update_queue(self.dl_queue)
|
||||||
|
|
||||||
elif text == "error":
|
elif text[:5] == "error":
|
||||||
QMessageBox.warning(self, "warn", "Download error")
|
QMessageBox.warning(self, "warn", "Download error: "+text[6:])
|
||||||
|
|
||||||
elif text == "stop":
|
elif text == "stop":
|
||||||
self.reset_infos()
|
self.reset_infos()
|
||||||
|
self.active_game = None
|
||||||
|
|
||||||
def reset_infos(self):
|
def reset_infos(self):
|
||||||
self.kill_button.setDisabled(True)
|
self.kill_button.setDisabled(True)
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
import os
|
import os
|
||||||
import queue
|
import queue
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from multiprocessing import Queue as MPQueue
|
from multiprocessing import Queue as MPQueue
|
||||||
|
from queue import Empty
|
||||||
|
|
||||||
|
import psutil
|
||||||
from PyQt5.QtCore import QThread, pyqtSignal
|
from PyQt5.QtCore import QThread, pyqtSignal
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
from PyQt5.QtWidgets import QMessageBox
|
||||||
|
|
||||||
from rare.utils.models import KillDownloadException
|
|
||||||
|
|
||||||
from custom_legendary.core import LegendaryCore
|
from custom_legendary.core import LegendaryCore
|
||||||
from custom_legendary.downloader.manager import DLManager
|
from custom_legendary.downloader.manager import DLManager
|
||||||
from custom_legendary.models.downloading import UIUpdate
|
from custom_legendary.models.downloading import UIUpdate, WriterTask
|
||||||
|
|
||||||
logger = getLogger("Download")
|
logger = getLogger("Download")
|
||||||
|
|
||||||
|
@ -20,7 +21,6 @@ logger = getLogger("Download")
|
||||||
class DownloadThread(QThread):
|
class DownloadThread(QThread):
|
||||||
status = pyqtSignal(str)
|
status = pyqtSignal(str)
|
||||||
statistics = pyqtSignal(UIUpdate)
|
statistics = pyqtSignal(UIUpdate)
|
||||||
kill = False
|
|
||||||
|
|
||||||
def __init__(self, dlm: DLManager, core: LegendaryCore, status_queue: MPQueue, igame, repair=False,
|
def __init__(self, dlm: DLManager, core: LegendaryCore, status_queue: MPQueue, igame, repair=False,
|
||||||
repair_file=None):
|
repair_file=None):
|
||||||
|
@ -31,37 +31,92 @@ class DownloadThread(QThread):
|
||||||
self.igame = igame
|
self.igame = igame
|
||||||
self.repair = repair
|
self.repair = repair
|
||||||
self.repair_file = repair_file
|
self.repair_file = repair_file
|
||||||
|
self._kill = False
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
dl_stopped = False
|
||||||
try:
|
try:
|
||||||
|
|
||||||
self.dlm.start()
|
self.dlm.start()
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
while self.dlm.is_alive():
|
while self.dlm.is_alive():
|
||||||
if self.kill:
|
if self._kill:
|
||||||
# raise KillDownloadException()
|
self.status.emit("stop")
|
||||||
# TODO kill download queue, workers
|
logger.info("Download stopping...")
|
||||||
pass
|
|
||||||
|
# The code below is a temporary solution.
|
||||||
|
# It should be removed once legendary supports stopping downloads more gracefully.
|
||||||
|
|
||||||
|
self.dlm.running = False
|
||||||
|
|
||||||
|
# send conditions to unlock threads if they aren't already
|
||||||
|
for cond in self.dlm.conditions:
|
||||||
|
with cond:
|
||||||
|
cond.notify()
|
||||||
|
|
||||||
|
# make sure threads are dead.
|
||||||
|
for t in self.dlm.threads:
|
||||||
|
t.join(timeout=5.0)
|
||||||
|
if t.is_alive():
|
||||||
|
logger.warning(f'Thread did not terminate! {repr(t)}')
|
||||||
|
|
||||||
|
# clean up all the queues, otherwise this process won't terminate properly
|
||||||
|
for name, q in zip(('Download jobs', 'Writer jobs', 'Download results', 'Writer results'),
|
||||||
|
(self.dlm.dl_worker_queue, self.dlm.writer_queue, self.dlm.dl_result_q, self.dlm.writer_result_q)):
|
||||||
|
logger.debug(f'Cleaning up queue "{name}"')
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
_ = q.get_nowait()
|
||||||
|
except Empty:
|
||||||
|
q.close()
|
||||||
|
q.join_thread()
|
||||||
|
except AttributeError:
|
||||||
|
logger.warning(f'Queue {name} did not close')
|
||||||
|
|
||||||
|
if self.dlm.writer_queue:
|
||||||
|
# cancel installation
|
||||||
|
self.dlm.writer_queue.put_nowait(WriterTask('', kill=True))
|
||||||
|
|
||||||
|
# forcibly kill DL workers that are not actually dead yet
|
||||||
|
for child in self.dlm.children:
|
||||||
|
if child.exitcode is None:
|
||||||
|
child.terminate()
|
||||||
|
|
||||||
|
if self.dlm.shared_memory:
|
||||||
|
# close up shared memory
|
||||||
|
self.dlm.shared_memory.close()
|
||||||
|
self.dlm.shared_memory.unlink()
|
||||||
|
self.dlm.shared_memory = None
|
||||||
|
|
||||||
|
self.dlm.kill()
|
||||||
|
|
||||||
|
# force kill any threads that are somehow still alive
|
||||||
|
for proc in psutil.process_iter():
|
||||||
|
# check whether the process name matches
|
||||||
|
if sys.platform in ['linux', 'darwin'] and proc.name() == 'DownloadThread':
|
||||||
|
proc.kill()
|
||||||
|
elif sys.platform == 'win32' and proc.name() == 'python.exe' and proc.create_time() >= start_time:
|
||||||
|
proc.kill()
|
||||||
|
|
||||||
|
logger.info("Download stopped. It can be continued later.")
|
||||||
|
dl_stopped = True
|
||||||
try:
|
try:
|
||||||
self.statistics.emit(self.status_queue.get(timeout=1))
|
if not dl_stopped:
|
||||||
|
self.statistics.emit(self.status_queue.get(timeout=1))
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.dlm.join()
|
self.dlm.join()
|
||||||
|
|
||||||
except KillDownloadException:
|
|
||||||
self.status.emit("stop")
|
|
||||||
logger.info("Downlaod can be continued later")
|
|
||||||
self.dlm.kill()
|
|
||||||
return
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Installation failed after {time.time() - start_time:.02f} seconds: {e}")
|
logger.error(f"Installation failed after {time.time() - start_time:.02f} seconds: {e}")
|
||||||
self.status.emit("error")
|
self.status.emit("error "+str(e))
|
||||||
return
|
return
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
if dl_stopped:
|
||||||
|
return
|
||||||
self.status.emit("dl_finished")
|
self.status.emit("dl_finished")
|
||||||
end_t = time.time()
|
end_t = time.time()
|
||||||
|
|
||||||
|
@ -115,3 +170,6 @@ class DownloadThread(QThread):
|
||||||
else:
|
else:
|
||||||
logger.info('Automatic installation not available on Linux.')
|
logger.info('Automatic installation not available on Linux.')
|
||||||
|
|
||||||
|
def kill(self):
|
||||||
|
self._kill = True
|
||||||
|
|
||||||
|
|
|
@ -64,8 +64,14 @@ class RareSettings(QScrollArea):
|
||||||
self.cloud_sync.setChecked(self.settings.value("auto_sync_cloud", True, bool))
|
self.cloud_sync.setChecked(self.settings.value("auto_sync_cloud", True, bool))
|
||||||
self.cloud_sync_widget = SettingsWidget(self.tr("Auto sync with cloud"), self.cloud_sync)
|
self.cloud_sync_widget = SettingsWidget(self.tr("Auto sync with cloud"), self.cloud_sync)
|
||||||
self.layout.addWidget(self.cloud_sync_widget)
|
self.layout.addWidget(self.cloud_sync_widget)
|
||||||
self.cloud_sync.stateChanged.connect(lambda: self.settings.setValue(f"auto_sync_cloud",
|
self.cloud_sync.stateChanged.connect(
|
||||||
self.cloud_sync.isChecked()))
|
lambda: self.settings.setValue(f"auto_sync_cloud", self.cloud_sync.isChecked()))
|
||||||
|
|
||||||
|
self.save_size = QCheckBox(self.tr("Save size"))
|
||||||
|
self.save_size.setChecked(self.settings.value("save_size", False, bool))
|
||||||
|
self.save_size_widget = SettingsWidget(self.tr("Save size of window after restart"), self.save_size)
|
||||||
|
self.layout.addWidget(self.save_size_widget)
|
||||||
|
self.save_size.stateChanged.connect(self.save_window_size)
|
||||||
|
|
||||||
self.save_size = QCheckBox(self.tr("Save size"))
|
self.save_size = QCheckBox(self.tr("Save size"))
|
||||||
self.save_size.setChecked(self.settings.value("save_size", False, bool))
|
self.save_size.setChecked(self.settings.value("save_size", False, bool))
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
from PyQt5.QtCore import QCoreApplication
|
from PyQt5.QtGui import QIcon
|
||||||
from PyQt5.QtWidgets import QSystemTrayIcon, QMenu, QAction
|
from PyQt5.QtWidgets import QSystemTrayIcon, QMenu, QAction
|
||||||
from qtawesome import icon
|
from rare import style_path
|
||||||
|
|
||||||
|
|
||||||
class TrayIcon(QSystemTrayIcon):
|
class TrayIcon(QSystemTrayIcon):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super(TrayIcon, self).__init__(parent)
|
super(TrayIcon, self).__init__(parent)
|
||||||
self.setIcon(icon("ei.cogs", color="white")) # TODO change icon to logo
|
self.setIcon(QIcon(style_path+"Logo.png"))
|
||||||
self.setVisible(True)
|
self.setVisible(True)
|
||||||
self.setToolTip("Rare")
|
self.setToolTip("Rare")
|
||||||
|
|
||||||
|
|
Binary file not shown.
BIN
rare/styles/Logo.ico
Normal file
BIN
rare/styles/Logo.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 70 KiB |
|
@ -13,6 +13,3 @@ class InstallOptions:
|
||||||
self.ignore_free_space = ignore_free_space
|
self.ignore_free_space = ignore_free_space
|
||||||
self.force = force
|
self.force = force
|
||||||
|
|
||||||
|
|
||||||
class KillDownloadException(Exception):
|
|
||||||
pass
|
|
||||||
|
|
|
@ -3,3 +3,4 @@ Pillow
|
||||||
PyQt5
|
PyQt5
|
||||||
QtAwesome
|
QtAwesome
|
||||||
notify-py
|
notify-py
|
||||||
|
psutil
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -10,6 +10,7 @@ setuptools.setup(
|
||||||
version=version,
|
version=version,
|
||||||
author="Dummerle",
|
author="Dummerle",
|
||||||
license="GPL-3",
|
license="GPL-3",
|
||||||
|
description="A gui for Legendary",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
|
@ -32,5 +33,6 @@ setuptools.setup(
|
||||||
"PyQt5",
|
"PyQt5",
|
||||||
"QtAwesome",
|
"QtAwesome",
|
||||||
"notify-py",
|
"notify-py",
|
||||||
|
"psutil"
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue