mirror of
https://github.com/KeksPirates/SoftwareManager.git
synced 2026-08-03 17:39:42 +02:00
Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e950d2f90d | |||
| 1a59c45ba4 | |||
| 293c1c3ad1 | |||
| f30cc5da5e | |||
| 004c7ad416 | |||
| 65f7c04936 | |||
| 9d0d5332bf | |||
| 6d9991188a | |||
| f397bc18ce | |||
| 59e45efbb6 | |||
| 27f0a3f71d | |||
| 1d500305f7 | |||
| 20a310264f | |||
| aee8f16353 | |||
| 2354adb349 | |||
| 4c69a17f0e | |||
| 2bab6c3321 | |||
| 91482760b6 | |||
| 83fa23df58 | |||
| 41d28095c2 | |||
| de2e49e60b | |||
| dd6d16d833 | |||
| 7d0c8d16ec | |||
| 80750f010f | |||
| c93f6345c8 | |||
| a7132ed1cd | |||
| a2cff15d0d | |||
| 2eb384f7cf | |||
| 05a5b04507 | |||
| 87c974f124 | |||
| 95a687ddd9 | |||
| 813d2b31c4 | |||
| deb5ce5b88 | |||
| a11c1d7129 | |||
| dcc78ade44 | |||
| 10e80ab4b6 | |||
| 3290ff3fac | |||
| d5d99d6a0e | |||
| 3960612f94 | |||
| a37a47b65b | |||
| 76b26664f2 | |||
| c8a1e72107 | |||
| 7c505f1402 | |||
| 55e4f86a39 | |||
| bd71b0e645 | |||
| 2a86e4b774 | |||
| d17b4bd44a | |||
| 10c935e852 | |||
| bd8320a26c | |||
| c99ce339e0 | |||
| 1822a150d7 | |||
| 7e5e3033e6 | |||
| 1cab671a13 | |||
| 5637e80657 | |||
| 2f78b812ba | |||
| 5af9e79ecc | |||
| 4dadc9ba50 | |||
| 719c4e86e8 | |||
| 999c8e9ba4 | |||
| 3f6889da56 | |||
| 69ac739be6 | |||
| 141cc8fadc | |||
| 80c6ece72e |
+60
-32
@@ -35,6 +35,9 @@ jobs:
|
||||
asset_name: SoftwareManager-dev-${{ github.sha }}-macos
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
short_sha: ${{ steps.version.outputs.short_sha }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -66,8 +69,54 @@ jobs:
|
||||
pip install -r requirements.txt
|
||||
pip install pyinstaller
|
||||
|
||||
- name: Build executable with PyInstaller
|
||||
run: pyinstaller --onefile --windowed --name ${{ env.APP_NAME }} main.py
|
||||
- name: Generate version
|
||||
id: version
|
||||
shell: bash
|
||||
run: |
|
||||
COMMIT_SHA=${{ github.sha }}
|
||||
SHORT_SHA=${COMMIT_SHA:0:7}
|
||||
VERSION="${SHORT_SHA}-dev"
|
||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
|
||||
echo "Generated version: $VERSION"
|
||||
|
||||
- name: Inject commit hash into code
|
||||
shell: bash
|
||||
run: |
|
||||
cat > build_info.json << EOF
|
||||
{
|
||||
"version": "${{ env.VERSION }}"
|
||||
}
|
||||
EOF
|
||||
|
||||
- name: Download aria2c (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
mkdir -p deps
|
||||
C:\msys64\usr\bin\wget.exe https://github.com/aria2/aria2/releases/download/release-1.37.0/aria2-1.37.0-win-64bit-build1.zip
|
||||
unzip -j aria2-1.37.0-win-64bit-build1.zip aria2-1.37.0-win-64bit-build1/aria2c.exe -d deps/
|
||||
|
||||
- name: Build executable with PyInstaller (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
pyinstaller --onefile --windowed \
|
||||
--add-data "build_info.json;." \
|
||||
--add-data "deps/aria2c.exe;." \
|
||||
--add-data "core/interface/assets;core/interface/assets" \
|
||||
--name ${{ env.APP_NAME }} \
|
||||
main.py
|
||||
|
||||
- name: Build executable with PyInstaller (Unix)
|
||||
if: runner.os != 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
pyinstaller --onefile --windowed \
|
||||
--add-data "build_info.json:." \
|
||||
--add-data "core/interface/assets:core/interface/assets" \
|
||||
--name ${{ env.APP_NAME }} \
|
||||
main.py
|
||||
|
||||
- name: Make executable (Unix)
|
||||
if: runner.os != 'Windows'
|
||||
@@ -94,50 +143,29 @@ jobs:
|
||||
if: success()
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: Display downloaded artifacts
|
||||
run: |
|
||||
echo "Downloaded artifacts:"
|
||||
find artifacts -type f
|
||||
ls -la artifacts/
|
||||
|
||||
- name: Generate version
|
||||
id: version
|
||||
run: |
|
||||
COMMIT_SHA=${{ github.sha }}
|
||||
SHORT_SHA=${COMMIT_SHA:0:7}
|
||||
VERSION="${SHORT_SHA}-dev"
|
||||
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
||||
echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT
|
||||
echo "Generated version: ${VERSION}"
|
||||
|
||||
- name: Prepare release files
|
||||
run: |
|
||||
mkdir -p release
|
||||
SHORT_SHA="${{ steps.version.outputs.short_sha }}"
|
||||
SHORT_SHA="${{ needs.build.outputs.short_sha }}"
|
||||
cp artifacts/ubuntu-latest-build/SoftwareManager-dev-*-linux release/SoftwareManager-dev-${SHORT_SHA}-linux
|
||||
cp artifacts/windows-latest-build/SoftwareManager-dev-*-windows.exe release/SoftwareManager-dev-${SHORT_SHA}-windows.exe
|
||||
cp artifacts/macos-latest-build/SoftwareManager-dev-*-macos release/SoftwareManager-dev-${SHORT_SHA}-macos
|
||||
chmod +x release/SoftwareManager-dev-*-linux release/SoftwareManager-dev-*-macos
|
||||
ls -la release/
|
||||
|
||||
- name: Create Prerelease (Public)
|
||||
- name: Create Release (Public)
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ steps.version.outputs.version }}
|
||||
name: ${{ steps.version.outputs.version }}
|
||||
draft: false
|
||||
prerelease: true
|
||||
tag_name: ${{ needs.build.outputs.version }}
|
||||
name: ${{ needs.build.outputs.version }}
|
||||
files: |
|
||||
release/SoftwareManager-dev-${{ steps.version.outputs.short_sha }}-linux
|
||||
release/SoftwareManager-dev-${{ steps.version.outputs.short_sha }}-windows.exe
|
||||
release/SoftwareManager-dev-${{ steps.version.outputs.short_sha }}-macos
|
||||
release/SoftwareManager-dev-${{ needs.build.outputs.short_sha }}-linux
|
||||
release/SoftwareManager-dev-${{ needs.build.outputs.short_sha }}-windows.exe
|
||||
release/SoftwareManager-dev-${{ needs.build.outputs.short_sha }}-macos
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -27,6 +27,17 @@ SoftwareManager is a Python-based GUI/TUI tool that simplifies downloading and m
|
||||
- If you want to host your own server, clone the server branch of this repo, enter your cookie for rutracker.org and run server.py. Detailed Instructions on how to get your rutracker cookie are in the README of its branch.
|
||||
- This tool is for educational purposes only. Use it responsibly.
|
||||
|
||||
|
||||
## Star History
|
||||
|
||||
<a href="https://www.star-history.com/#KeksPirates/SoftwareManager&type=date&legend=top-left">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=KeksPirates/SoftwareManager&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=KeksPirates/SoftwareManager&type=date&legend=top-left" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=KeksPirates/SoftwareManager&type=date&legend=top-left" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
---
|
||||
|
||||
**Disclaimer:** SoftwareManager is intended for legal and ethical use only. Ensure compliance with applicable laws and regulations when using this tool.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"Version": "dev"
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
from bs4 import BeautifulSoup
|
||||
import requests
|
||||
|
||||
def scrape_monkrus_telegram(query):
|
||||
post = 100
|
||||
max_posts = 250
|
||||
posts: list[dict] = []
|
||||
added = set()
|
||||
|
||||
while post <= max_posts:
|
||||
url = f"https://t.me/s/real_monkrus/{post}"
|
||||
response = requests.get(url)
|
||||
soup = BeautifulSoup(response.text, "html.parser")
|
||||
bubbles = soup.find_all("div", class_="tgme_widget_message_bubble")
|
||||
|
||||
if not bubbles:
|
||||
break
|
||||
|
||||
for bubble in bubbles:
|
||||
post_txt = bubble.find("div", class_="tgme_widget_message_text js-message_text")
|
||||
if not post_txt or not post_txt.b:
|
||||
continue
|
||||
|
||||
title = post_txt.b.text
|
||||
link = bubble.find("a", href=lambda x: x and x.startswith("https://uztracker.net"))
|
||||
|
||||
if query.lower() in title.lower() and link:
|
||||
post_url = link["href"]
|
||||
if post_url not in added:
|
||||
added.add(post_url)
|
||||
posts.append(dict(
|
||||
author="m0nkrus",
|
||||
id=len(posts) + 1,
|
||||
title=title,
|
||||
url=post_url
|
||||
))
|
||||
|
||||
post += 22
|
||||
|
||||
posts.reverse()
|
||||
return(posts)
|
||||
@@ -4,6 +4,8 @@ from core.utils.data.state import state
|
||||
|
||||
def scrape_rutracker(search_text):
|
||||
search = requests.get(f"{state.api_url}/search/{search_text}")
|
||||
if state.debug:
|
||||
print("Sent request to server")
|
||||
if search:
|
||||
try:
|
||||
return search.text
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import requests
|
||||
import asyncio
|
||||
import aiohttp
|
||||
from bs4 import BeautifulSoup
|
||||
from core.utils.data.state import state
|
||||
|
||||
@@ -17,62 +16,56 @@ async def init_uztracker():
|
||||
up = True
|
||||
else:
|
||||
up = False
|
||||
print(f"Uztracker seems down, status code: {response.status_code}")
|
||||
if state.debug:
|
||||
print(f"Uztracker seems down, status code: {response.status_code}")
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"\nRequest Exception on {url_uztracker}:")
|
||||
print(e)
|
||||
print("\nIs the Site down?")
|
||||
if state.debug:
|
||||
print(f"\nRequest Exception on {url_uztracker}:")
|
||||
print(e)
|
||||
print("\nIs the Site down?")
|
||||
up = False
|
||||
|
||||
asyncio.run(init_uztracker())
|
||||
|
||||
async def scrape_uztracker(search, max_results=450):
|
||||
def scrape_uztracker(search):
|
||||
if up:
|
||||
search_url = url_uztracker + search
|
||||
if state.debug:
|
||||
print(search_url)
|
||||
result = False
|
||||
global results
|
||||
global resulttitles
|
||||
results = []
|
||||
results = []
|
||||
resulttitles = []
|
||||
|
||||
per_page = 50
|
||||
try:
|
||||
resultCount = 0
|
||||
response = requests.get(search_url)
|
||||
response.raise_for_status()
|
||||
soup = BeautifulSoup(response.text, 'html.parser')
|
||||
links = soup.find_all('a', class_="genmed tLink", href=lambda x: x and x.startswith('./viewtopic'))
|
||||
for link in links:
|
||||
if link.b:
|
||||
resultCount += 1
|
||||
results.append(link['href'])
|
||||
resulttitles.append(link.b.text)
|
||||
result = True
|
||||
|
||||
for start in range(0, max_results, per_page):
|
||||
search_url = url_uztracker + search + f"&start={start}"
|
||||
if state.debug:
|
||||
print(search_url)
|
||||
if result:
|
||||
return resulttitles, results
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
try:
|
||||
async with session.get(search_url) as response:
|
||||
response.raise_for_status()
|
||||
text = await response.text()
|
||||
soup = BeautifulSoup(text, 'html.parser')
|
||||
links = soup.find_all('a', class_="genmed tLink", href=lambda x: x and x.startswith('./viewtopic'))
|
||||
resultCount = 0
|
||||
for link in links:
|
||||
if link.b:
|
||||
resultCount += 1
|
||||
results.append(link['href'])
|
||||
resulttitles.append(link.b.text)
|
||||
result = True
|
||||
|
||||
if resultCount == 0:
|
||||
# add popup in gui for no result
|
||||
break
|
||||
|
||||
except aiohttp.ClientError as e:
|
||||
if result:
|
||||
print(f"Failed to fetch {search_url}: {e}")
|
||||
return None
|
||||
if not result:
|
||||
# add popup in gui for no result
|
||||
return None
|
||||
|
||||
else:
|
||||
except requests.RequestException as e:
|
||||
if result and state.debug:
|
||||
print(f"Failed to fetch {search_url}: {e}")
|
||||
return None
|
||||
else:
|
||||
if state.debug:
|
||||
print("Error: Uztracker down")
|
||||
return None, None
|
||||
|
||||
if not results:
|
||||
return None, None
|
||||
|
||||
return resulttitles, results
|
||||
return None, None
|
||||
|
||||
def get_post_title(post_url):
|
||||
if up:
|
||||
@@ -85,7 +78,8 @@ def get_post_title(post_url):
|
||||
if state.debug:
|
||||
print("Program not found")
|
||||
else:
|
||||
print("Error: Uztracker down")
|
||||
if state.debug:
|
||||
print("Error: Uztracker down")
|
||||
return None
|
||||
|
||||
|
||||
|
||||
@@ -10,12 +10,10 @@ from PySide6.QtWidgets import (
|
||||
QLabel,
|
||||
QHBoxLayout,
|
||||
QSpinBox,
|
||||
QCheckBox,
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def settings_dialog(self):
|
||||
|
||||
if state.debug:
|
||||
@@ -29,7 +27,20 @@ def settings_dialog(self):
|
||||
|
||||
def close_settings():
|
||||
dialog.reject()
|
||||
|
||||
|
||||
checkbox_container = QWidget()
|
||||
checkbox_layout = QHBoxLayout()
|
||||
|
||||
# ignore updates checkbox
|
||||
checkbox = QCheckBox()
|
||||
checkbox_container.setLayout(checkbox_layout)
|
||||
checkbox_layout.addWidget(QLabel("Ignore Updates: "))
|
||||
checkbox_layout.addStretch()
|
||||
checkbox.setChecked(state.ignore_updates)
|
||||
checkbox.toggled.connect(lambda checked: setattr(state, 'ignore_updates', checked))
|
||||
checkbox_layout.addWidget(checkbox)
|
||||
dialog.layout().addWidget(checkbox_container)
|
||||
|
||||
##################
|
||||
# THREAD SETTING #
|
||||
##################
|
||||
@@ -103,6 +114,21 @@ def settings_dialog(self):
|
||||
speed_limit.setFixedHeight(30)
|
||||
dialog.layout().addWidget(speed_limit_container)
|
||||
|
||||
###############
|
||||
# IMAGE PATH #
|
||||
###############
|
||||
|
||||
image_path_container = QWidget()
|
||||
image_path_layout = QHBoxLayout()
|
||||
|
||||
image_path = QLineEdit()
|
||||
image_path_layout.addWidget(QLabel("Image Path (requires restart):"))
|
||||
image_path_layout.addWidget(image_path)
|
||||
image_path_container.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
||||
image_path_container.setLayout(image_path_layout)
|
||||
image_path.setText(state.image_path)
|
||||
dialog.layout().addWidget(image_path_container)
|
||||
|
||||
###############
|
||||
# SAVE/CANCEL #
|
||||
###############
|
||||
@@ -111,13 +137,12 @@ def settings_dialog(self):
|
||||
|
||||
save_btn = QPushButton("Save")
|
||||
cancel_btn = QPushButton("Cancel")
|
||||
save_btn.clicked.connect(lambda: save_settings(thread_box.value(), close_settings, api_url.text(), download_path.text(), speed_limit.value()))
|
||||
|
||||
save_btn.clicked.connect(lambda: save_settings(thread_box.value(), close_settings, api_url.text(), download_path.text(), speed_limit.value(), image_path.text()))
|
||||
|
||||
cancel_btn.clicked.connect(dialog.reject)
|
||||
print(thread_box.value())
|
||||
layout.addWidget(cancel_btn)
|
||||
layout.addWidget(save_btn)
|
||||
|
||||
dialog.layout().addLayout(layout)
|
||||
|
||||
dialog.exec()
|
||||
dialog.exec()
|
||||
|
||||
+135
-36
@@ -1,26 +1,37 @@
|
||||
from PySide6 import QtWidgets
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtCore import Qt, QTimer, QModelIndex, QAbstractTableModel
|
||||
from PySide6.QtCore import Qt, QTimer, QModelIndex, QAbstractTableModel, Signal
|
||||
from PySide6.QtWidgets import (
|
||||
QLineEdit,
|
||||
QTableView,
|
||||
QWidget,
|
||||
QVBoxLayout,
|
||||
QListWidget,
|
||||
QToolBar,
|
||||
QLabel,
|
||||
QTableView,
|
||||
QWidget,
|
||||
QVBoxLayout,
|
||||
QListWidget,
|
||||
QToolBar,
|
||||
QLabel,
|
||||
QHBoxLayout,
|
||||
QComboBox,
|
||||
QComboBox,
|
||||
QTabWidget,
|
||||
QProgressBar,
|
||||
QHeaderView,
|
||||
QMessageBox,
|
||||
QTableWidget,
|
||||
)
|
||||
from PySide6.QtGui import QIcon, QAction, QCloseEvent
|
||||
|
||||
from PySide6.QtGui import QIcon, QAction, QCloseEvent, QImage, QPixmap
|
||||
import darkdetect
|
||||
import threading
|
||||
import platform
|
||||
import requests as r
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
import sys
|
||||
import json
|
||||
from core.utils.general.wrappers import run_thread
|
||||
from core.utils.data.state import state
|
||||
from core.utils.network.download import download_selected
|
||||
from core.utils.network.update_checker import check_for_updates
|
||||
from core.utils.general.shutdown import closehelper
|
||||
from core.interface.utils.tabhelper import create_tab
|
||||
from core.interface.utils.searchhelper import return_pressed
|
||||
@@ -28,11 +39,57 @@ from core.interface.dialogs.settings import settings_dialog
|
||||
from core.network.aria2_integration import dlprogress
|
||||
|
||||
|
||||
def download_update(latest_version):
|
||||
new_filename = f"SoftwareManager-dev-{latest_version.replace('-dev', '')}-windows.exe"
|
||||
url = f"https://github.com/KeksPirates/SoftwareManager/releases/latest/download/SoftwareManager-dev-{latest_version.replace('-dev', '')}-windows.exe"
|
||||
|
||||
print("Downloading update...")
|
||||
response = r.get(url, allow_redirects=True)
|
||||
with open(new_filename, "wb") as f:
|
||||
f.write(response.content)
|
||||
if not os.path.exists(new_filename):
|
||||
raise FileNotFoundError("Executable not found")
|
||||
subprocess.Popen([new_filename], shell=True)
|
||||
time.sleep(0.5)
|
||||
|
||||
msg = QMessageBox()
|
||||
msg.setIcon(QMessageBox.Information)
|
||||
msg.setWindowTitle("New Version")
|
||||
msg.setText("New version installed.")
|
||||
msg.setInformativeText("Please remove the old exe.")
|
||||
msg.setStandardButtons(QMessageBox.Ok)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
class MainWindow(QtWidgets.QMainWindow, QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
searchresults = []
|
||||
|
||||
build_info_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "build_info.json")
|
||||
if os.path.exists(build_info_path):
|
||||
with open(build_info_path, "r") as f:
|
||||
build_info = json.load(f)
|
||||
state.version = build_info.get("version")
|
||||
|
||||
# Check for updates on Windows
|
||||
if state.ignore_updates is False:
|
||||
if platform.system() == "Windows":
|
||||
result = check_for_updates()
|
||||
if result != (None, None):
|
||||
assets, latest_version = result
|
||||
|
||||
if assets:
|
||||
msg = QMessageBox()
|
||||
msg.setIcon(QMessageBox.Information)
|
||||
msg.setWindowTitle("Update Available")
|
||||
msg.setText("A new version is available.")
|
||||
msg.setInformativeText("Press Ok to download the update.")
|
||||
msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Ignore)
|
||||
|
||||
response = msg.exec_()
|
||||
if response == QMessageBox.Ok:
|
||||
download_update(latest_version)
|
||||
|
||||
self.setWindowTitle("Software Manager")
|
||||
self.setGeometry(100, 100, 800, 600)
|
||||
@@ -46,42 +103,56 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
||||
self.searchbar.setClearButtonEnabled(True)
|
||||
self.searchbar.setMinimumHeight(30)
|
||||
self.searchbar.returnPressed.connect(lambda: run_thread(threading.Thread(target=return_pressed, args=(self,)))) # Triggers data function thread on enter
|
||||
|
||||
|
||||
self.dlbutton = QtWidgets.QPushButton("Download")
|
||||
self.softwareList = QListWidget()
|
||||
self.post_author_list = QListWidget()
|
||||
self.libraryList = QListWidget()
|
||||
|
||||
self.emptyResults = QLabel("No Results")
|
||||
self.emptyResults.setAlignment(Qt.AlignCenter)
|
||||
self.emptyResults.hide()
|
||||
|
||||
|
||||
self.download_model = None
|
||||
self.downloadList = QTableView()
|
||||
self.emptyLibrary = QLabel("No items in library.")
|
||||
self.emptyDownload = QLabel("No items in downloads.")
|
||||
self.progressbar = QProgressBar()
|
||||
|
||||
# Table Widget for Item List
|
||||
self.qtablewidget = QTableWidget()
|
||||
|
||||
self.qtablewidget.setColumnCount(2)
|
||||
self.qtablewidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||
self.qtablewidget.verticalHeader().setVisible(False)
|
||||
self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"])
|
||||
|
||||
header = self.qtablewidget.horizontalHeader()
|
||||
header.setSectionResizeMode(0, QHeaderView.Stretch)
|
||||
header.setSectionResizeMode(1, QHeaderView.Fixed)
|
||||
header.setStretchLastSection(False)
|
||||
|
||||
self.qtablewidget.setAttribute(Qt.WA_TranslucentBackground)
|
||||
self.qtablewidget.viewport().setAttribute(Qt.WA_TranslucentBackground)
|
||||
|
||||
header = self.qtablewidget.horizontalHeader()
|
||||
header.setSectionResizeMode(1, QHeaderView.Fixed)
|
||||
header.resizeSection(1, 500)
|
||||
|
||||
container = QWidget()
|
||||
containerLayout = QVBoxLayout()
|
||||
containerLayout.addWidget(self.searchbar)
|
||||
|
||||
containerLayout.addWidget(self.softwareList)
|
||||
containerLayout.addWidget(self.post_author_list)
|
||||
self.softwareList.addItems(searchresults)
|
||||
containerLayout.addWidget(self.qtablewidget)
|
||||
|
||||
class DownloadModel(QAbstractTableModel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.headers = ["Name", "Status", "Progress", "Speed", "Size", "Total Size"]
|
||||
|
||||
|
||||
def rowCount(self, parent=QModelIndex()):
|
||||
return len(state.downloads)
|
||||
|
||||
def columnCount(self, parent=QModelIndex()):
|
||||
return len(self.headers)
|
||||
|
||||
|
||||
def headerData(self, section, orientation, role=Qt.DisplayRole):
|
||||
if role == Qt.DisplayRole and orientation == Qt.Horizontal:
|
||||
return self.headers[section]
|
||||
@@ -91,7 +162,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
||||
if role == Qt.DisplayRole:
|
||||
download = state.downloads[index.row()]
|
||||
col = index.column()
|
||||
|
||||
|
||||
if col == 0:
|
||||
return download.name
|
||||
elif col == 1:
|
||||
@@ -111,9 +182,11 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
||||
self.downloadList.horizontalHeader().setStretchLastSection(False)
|
||||
self.downloadList.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
|
||||
self.downloadList.setSelectionBehavior(QTableView.SelectRows)
|
||||
self.downloadList.setAttribute(Qt.WA_TranslucentBackground)
|
||||
self.downloadList.viewport().setAttribute(Qt.WA_TranslucentBackground)
|
||||
|
||||
# download button triggers
|
||||
self.dlbutton.clicked.connect(lambda: run_thread(threading.Thread(target=download_selected, args=(self.softwareList.currentItem(), state.posts, state.post_titles))))
|
||||
self.dlbutton.clicked.connect(lambda: run_thread(threading.Thread(target=download_selected, args=(self.qtablewidget.currentItem(), state.posts, state.post_titles))))
|
||||
|
||||
container.setLayout(containerLayout)
|
||||
self.setCentralWidget(container)
|
||||
@@ -122,31 +195,43 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
||||
self.tabs = QTabWidget()
|
||||
|
||||
self.horizontal_layout = QHBoxLayout()
|
||||
self.horizontal_layout.addWidget(self.softwareList, stretch=3)
|
||||
self.horizontal_layout.addWidget(self.emptyResults, stretch=3)
|
||||
self.horizontal_layout.addWidget(self.post_author_list)
|
||||
|
||||
self.tab1 = create_tab("Search", self.searchbar, self.softwareList, self.tabs, self.dlbutton, self.horizontal_layout)
|
||||
self.horizontal_layout.addWidget(self.qtablewidget)
|
||||
|
||||
self.tab1 = create_tab("Search", self.searchbar, self.qtablewidget, self.tabs, self.dlbutton, self.horizontal_layout)
|
||||
self.tab2 = create_tab("Library", self.emptyLibrary, self.libraryList, self.tabs, None, None)
|
||||
self.tab3 = create_tab("Downloads", self.emptyDownload, self.downloadList, self.tabs, None, None)
|
||||
|
||||
self.image = QImage(state.image_path)
|
||||
self.pixmap = QPixmap.fromImage(self.image)
|
||||
self.overlay_label = QLabel(self)
|
||||
self.overlay_label.setPixmap(self.pixmap)
|
||||
self.overlay_label.adjustSize()
|
||||
self.overlay_label.raise_()
|
||||
|
||||
offset_x = -1350
|
||||
offset_y = -550
|
||||
x = self.width() - self.overlay_label.width() - offset_x
|
||||
y = self.height() - self.overlay_label.height() - offset_y
|
||||
self.overlay_label.move(x, y)
|
||||
|
||||
# temporarily disabled
|
||||
# state.image_changed.connect(self.update_image_overlay)
|
||||
|
||||
containerLayout.addWidget(self.tabs)
|
||||
|
||||
containerLayout.setAlignment(Qt.AlignmentFlag.AlignBottom)
|
||||
|
||||
toolbar = QToolBar("Main Toolbar")
|
||||
self.addToolBar(toolbar)
|
||||
toolbar.setLayoutDirection(Qt.RightToLeft)
|
||||
|
||||
|
||||
self.tracker_list = QComboBox()
|
||||
self.tracker_list.addItems(["rutracker", "uztracker"])
|
||||
self.tracker_list.addItems(["rutracker", "uztracker", "m0nkrus"])
|
||||
self.tracker_list.activated.connect(self.set_tracker)
|
||||
|
||||
|
||||
if darkdetect.isDark():
|
||||
settings_action = QAction(QIcon("core/interface/assets/settings_dark.png"), "Settings", self)
|
||||
else:
|
||||
settings_action = QAction(QIcon("core/interface/assets/settings.png"), "Settings", self)
|
||||
settings_action = QAction(QIcon("core/interface/assets/settings_black.png"), "Settings", self)
|
||||
settings_action = QAction(QIcon("core/interface/assets/settings_white.png"), "Settings", self)
|
||||
|
||||
settings_action.triggered.connect(lambda: settings_dialog(self))
|
||||
toolbar.addAction(settings_action)
|
||||
@@ -156,7 +241,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
||||
|
||||
self.progressbar.setValue(0)
|
||||
containerLayout.addWidget(self.progressbar)
|
||||
|
||||
|
||||
self.progress_timer = QTimer()
|
||||
self.progress_timer.timeout.connect(lambda: run_thread(threading.Thread(target=self.update_progress)))
|
||||
self.progress_timer.start(1000)
|
||||
@@ -165,6 +250,13 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
||||
self.download_timer.timeout.connect(lambda: run_thread(threading.Thread(target=self.download_list_update)))
|
||||
self.download_timer.start(500)
|
||||
|
||||
def update_image_overlay(self, new_image_path):
|
||||
self.image = QImage(new_image_path)
|
||||
self.pixmap = QPixmap.fromImage(self.image)
|
||||
|
||||
self.overlay_label.setPixmap(self.pixmap)
|
||||
self.overlay_label.adjustSize()
|
||||
|
||||
def download_list_update(self):
|
||||
if self.download_model:
|
||||
self.download_model.layoutAboutToBeChanged.emit()
|
||||
@@ -183,8 +275,15 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
||||
|
||||
def show_empty_results(self, show: bool):
|
||||
if show:
|
||||
self.softwareList.hide()
|
||||
self.qtablewidget.hide()
|
||||
self.emptyResults.show()
|
||||
else:
|
||||
self.emptyResults.hide()
|
||||
self.softwareList.show()
|
||||
self.qtablewidget.show()
|
||||
|
||||
|
||||
# thank you claude
|
||||
def resizeEvent(self, event):
|
||||
super().resizeEvent(event)
|
||||
table_width = self.qtablewidget.viewport().width()
|
||||
self.qtablewidget.setColumnWidth(1, int(table_width * 0.3))
|
||||
@@ -1,42 +1,85 @@
|
||||
import asyncio
|
||||
from PySide6.QtWidgets import QTableWidgetItem
|
||||
from core.data.scrapers.uztracker import scrape_uztracker
|
||||
from core.data.scrapers.rutracker import scrape_rutracker
|
||||
from core.data.scrapers.monkrus import scrape_monkrus_telegram
|
||||
from core.utils.network.jsonhandler import split_data, format_data
|
||||
from core.utils.data.state import state
|
||||
|
||||
def return_pressed(self):
|
||||
search_text = self.searchbar.text()
|
||||
if search_text == "":
|
||||
if state.debug:
|
||||
print("Error: Can't search for nothing")
|
||||
return
|
||||
search_text = self.searchbar.text()
|
||||
if search_text == "":
|
||||
if state.debug:
|
||||
print("User searched for:", search_text)
|
||||
if state.tracker == "uztracker":
|
||||
response = asyncio.run(scrape_uztracker(search_text))
|
||||
if state.tracker == "rutracker":
|
||||
response = scrape_rutracker(search_text)
|
||||
print("Error: Can't search for nothing")
|
||||
return
|
||||
if state.debug:
|
||||
print("User searched for:", search_text)
|
||||
|
||||
if state.tracker == "uztracker":
|
||||
response = scrape_uztracker(search_text)
|
||||
if response:
|
||||
if state.tracker == "uztracker":
|
||||
state.post_titles, state.post_urls, = response
|
||||
self.softwareList.clear()
|
||||
if state.post_titles:
|
||||
self.softwareList.addItems(state.post_titles)
|
||||
self.post_author_list.addItems(state.post_author)
|
||||
if state.tracker == "rutracker":
|
||||
_, state.posts, _, _, cached = split_data(response)
|
||||
state.post_titles, state.post_urls = response
|
||||
self.qtablewidget.clear()
|
||||
if state.post_titles and len(state.post_titles) > 0:
|
||||
self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"])
|
||||
self.qtablewidget.setRowCount(len(state.post_titles))
|
||||
for i, author in enumerate(state.post_author):
|
||||
self.qtablewidget.setItem(i, 1, QTableWidgetItem(author))
|
||||
for i, title in enumerate(state.post_titles):
|
||||
self.qtablewidget.setItem(i, 0, QTableWidgetItem(title))
|
||||
self.show_empty_results(False)
|
||||
else:
|
||||
if state.debug:
|
||||
print(f"No Results for {search_text}")
|
||||
self.qtablewidget.clear()
|
||||
self.show_empty_results(True)
|
||||
else:
|
||||
if state.debug:
|
||||
print(f"No response from uztracker")
|
||||
self.qtablewidget.clear()
|
||||
self.show_empty_results(True)
|
||||
|
||||
elif state.tracker == "rutracker":
|
||||
response = scrape_rutracker(search_text)
|
||||
if response:
|
||||
_, state.posts, _, _, cached = split_data(response)
|
||||
if state.posts == []:
|
||||
if state.debug:
|
||||
print(f"No Results found for \"{search_text}\"")
|
||||
self.softwareList.clear()
|
||||
print(f"No Results for {search_text}")
|
||||
self.qtablewidget.clear()
|
||||
self.show_empty_results(True)
|
||||
else:
|
||||
state.post_titles, _, state.post_author = format_data(state.posts)
|
||||
self.show_empty_results(False)
|
||||
self.post_author_list.clear()
|
||||
self.post_author_list.addItems(state.post_author)
|
||||
self.softwareList.clear()
|
||||
self.softwareList.addItems(state.post_titles)
|
||||
self.qtablewidget.clear()
|
||||
self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"])
|
||||
self.qtablewidget.setRowCount(len(state.post_titles))
|
||||
for i, author in enumerate(state.post_author):
|
||||
self.qtablewidget.setItem(i, 1, QTableWidgetItem(author))
|
||||
for i, title in enumerate(state.post_titles):
|
||||
self.qtablewidget.setItem(i, 0, QTableWidgetItem(title))
|
||||
if state.debug == True:
|
||||
print(f"Cached: {cached}")
|
||||
print(f"Response Cached: {cached}")
|
||||
else:
|
||||
if state.debug:
|
||||
print(f"No response from rutracker")
|
||||
self.qtablewidget.clear()
|
||||
self.show_empty_results(True)
|
||||
|
||||
elif state.tracker == "m0nkrus":
|
||||
state.posts = scrape_monkrus_telegram(search_text)
|
||||
|
||||
if state.posts == []:
|
||||
if state.debug:
|
||||
print(f"No Results for {search_text}")
|
||||
self.qtablewidget.clear()
|
||||
self.show_empty_results(True)
|
||||
else:
|
||||
state.post_titles, _, state.post_author = format_data(state.posts)
|
||||
self.show_empty_results(False)
|
||||
self.qtablewidget.clear()
|
||||
self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"])
|
||||
self.qtablewidget.setRowCount(len(state.post_titles))
|
||||
for i, author in enumerate(state.post_author):
|
||||
self.qtablewidget.setItem(i, 1, QTableWidgetItem(author))
|
||||
for i, title in enumerate(state.post_titles):
|
||||
self.qtablewidget.setItem(i, 0, QTableWidgetItem(title))
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
from .aria2_integration import run_aria2p
|
||||
from core.utils.data.state import state
|
||||
|
||||
def start_client():
|
||||
global aria2
|
||||
aria2 = run_aria2p()
|
||||
if state.debug:
|
||||
print("Started Aria2p")
|
||||
|
||||
def add_magnet(uri):
|
||||
aria2.add_magnet(uri)
|
||||
if state.debug:
|
||||
print("Magnet URI added to Aria2")
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ import platform
|
||||
import configparser
|
||||
from core.utils.data.state import state
|
||||
|
||||
|
||||
def create_config():
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
config["General"] = {"debug": True, "api_url": f"{state.api_url}", "aria2_threads": f"{state.aria2_threads}", "download_path": f"{state.download_path}", f"speed_limit": f"{state.speed_limit}"}
|
||||
config["General"] = {"debug": True, "api_url": f"{state.api_url}", "aria2_threads": f"{state.aria2_threads}", "download_path": f"{state.download_path}", f"speed_limit": f"{state.speed_limit}",
|
||||
"ignore_updates": f"{state.ignore_updates}", "image_path": f"{state.image_path}"}
|
||||
|
||||
if platform.system() == "Windows":
|
||||
config_dir = os.environ.get("APPDATA", os.path.expanduser("~\\AppData\\Roaming"))
|
||||
@@ -37,9 +37,17 @@ def read_config():
|
||||
|
||||
config.read(config_file)
|
||||
|
||||
state.debug = config.getboolean("General", "debug")
|
||||
state.api_url = config.get("General", "api_url")
|
||||
state.aria2_threads = config.getint("General", "aria2_threads")
|
||||
state.download_path = config.get("General", "download_path")
|
||||
state.speed_limit = config.getint("General", "speed_limit")
|
||||
|
||||
if state.debug is not None:
|
||||
state.debug = config.getboolean("General", "debug")
|
||||
if state.api_url is not None:
|
||||
state.api_url = config.get("General", "api_url")
|
||||
if state.aria2_threads is not None:
|
||||
state.aria2_threads = config.getint("General", "aria2_threads")
|
||||
if state.download_path is not None:
|
||||
state.download_path = config.get("General", "download_path")
|
||||
if state.speed_limit is not None:
|
||||
state.speed_limit = config.getint("General", "speed_limit")
|
||||
if state.ignore_updates is not None:
|
||||
state.ignore_updates = config.getboolean("General", "ignore_updates")
|
||||
if state.image_path is not None:
|
||||
state.image_path = config.get("General", "image_path")
|
||||
|
||||
@@ -1,22 +1,29 @@
|
||||
from core.utils.data.state import state
|
||||
from core.utils.general.shutdown import kill_aria2server
|
||||
from .config import create_config
|
||||
|
||||
def restart_aria2c():
|
||||
import main # had to do this because of circle import :(
|
||||
import signal
|
||||
import atexit
|
||||
main.kill_aria2server()
|
||||
kill_aria2server()
|
||||
state.aria2process.wait()
|
||||
state.aria2process = main.run_aria2server()
|
||||
signal.signal(signal.SIGINT, main.keyboardinterrupthandler)
|
||||
atexit.unregister(main.kill_aria2server)
|
||||
atexit.register(main.kill_aria2server)
|
||||
atexit.unregister(kill_aria2server)
|
||||
atexit.register(kill_aria2server)
|
||||
|
||||
def save_settings(thread_count, close, apiurl, download_path, speed_limit):
|
||||
state.aria2_threads = thread_count
|
||||
state.api_url = apiurl
|
||||
state.download_path = download_path
|
||||
state.speed_limit = speed_limit
|
||||
def save_settings(thread_count=None, close=lambda: None, apiurl=None, download_path=None, speed_limit=None, image_path=None):
|
||||
if thread_count is not None:
|
||||
state.aria2_threads = thread_count
|
||||
if apiurl is not None:
|
||||
state.api_url = apiurl
|
||||
if download_path is not None:
|
||||
state.download_path = download_path
|
||||
if speed_limit is not None:
|
||||
state.speed_limit = speed_limit
|
||||
if image_path is not None:
|
||||
state.image_path = image_path
|
||||
|
||||
create_config()
|
||||
restart_aria2c()
|
||||
|
||||
+34
-18
@@ -1,22 +1,38 @@
|
||||
from dataclasses import dataclass
|
||||
from PySide6.QtCore import QObject, Signal
|
||||
from typing import Optional, Any, List, Dict
|
||||
from pathlib import Path
|
||||
|
||||
@dataclass
|
||||
class AppState:
|
||||
posts: List[Dict[str, Any]]
|
||||
post_titles: List[str]
|
||||
post_urls: List[str]
|
||||
post_author: List[str]
|
||||
downloads: List[str]
|
||||
debug: bool = False
|
||||
tracker: str = "rutracker"
|
||||
api_url: str = "https://api.michijackson.xyz"
|
||||
download_path: str = str(Path.home() / "Downloads")
|
||||
speed_limit: int = 0
|
||||
aria2process: Optional[Any] = None
|
||||
aria2: Any = None
|
||||
aria2p: Any = None
|
||||
aria2_threads: int = 4
|
||||
class AppState(QObject):
|
||||
image_changed = Signal(str)
|
||||
|
||||
state = AppState(posts=[], post_titles=[], post_urls=[], post_author=[], downloads=[], download_path="")
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.posts: List[Dict[str, Any]] = []
|
||||
self.post_titles: List[str] = []
|
||||
self.post_urls: List[str] = []
|
||||
self.post_author: List[str] = []
|
||||
self.downloads: List[str] = []
|
||||
self.version: str = "dev"
|
||||
self._image_path: str = ""
|
||||
self.ignore_updates: bool = False
|
||||
self.debug: bool = False
|
||||
self.tracker: str = "rutracker"
|
||||
self.api_url: str = "https://api.michijackson.xyz"
|
||||
self.download_path: str = str(Path.home() / "Downloads")
|
||||
self.speed_limit: int = 0
|
||||
self.aria2process: Optional[Any] = None
|
||||
self.aria2: Any = None
|
||||
self.aria2p: Any = None
|
||||
self.aria2_threads: int = 4
|
||||
|
||||
@property
|
||||
def image_path(self) -> str:
|
||||
return self._image_path
|
||||
|
||||
@image_path.setter
|
||||
def image_path(self, new_path: str):
|
||||
if new_path != self._image_path:
|
||||
self._image_path = new_path
|
||||
self.image_changed.emit(new_path)
|
||||
|
||||
state = AppState()
|
||||
|
||||
@@ -5,16 +5,27 @@ from core.utils.network.jsonhandler import format_data
|
||||
|
||||
|
||||
|
||||
def get_item_url(item, posts, post_titles): # oftwarelist currentitem, post list (dict), post titles list
|
||||
def get_item_url(item, posts, post_titles): # softwarelist currentitem, post list (dict), post titles list
|
||||
post_index = post_titles.index(item)
|
||||
if 0 <= post_index < len(post_titles):
|
||||
if state.tracker == "uztracker":
|
||||
item = "https://uztracker.net/" + state.post_urls[post_index].lstrip("./")
|
||||
if state.debug:
|
||||
print("Found post URL: ", item)
|
||||
return item
|
||||
if state.tracker == "rutracker":
|
||||
item_dict = posts[post_index]
|
||||
_, post_links, _ = format_data([item_dict])
|
||||
if state.debug:
|
||||
print("Found post URL: ", post_links[0])
|
||||
return post_links[0]
|
||||
if state.tracker == "m0nkrus":
|
||||
item_dict = posts[post_index]
|
||||
_, post_links, _ = format_data([item_dict])
|
||||
if state.debug:
|
||||
print("Found post URL: ", post_links[0])
|
||||
return post_links[0]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@@ -22,6 +33,8 @@ def get_item_url(item, posts, post_titles): # oftwarelist currentitem, post list
|
||||
def get_magnet_link(post_url):
|
||||
try:
|
||||
response = requests.get(post_url) # eventually impl. cloudscraper
|
||||
if state.debug:
|
||||
print("Sent Request to retrieve Magnet Link...")
|
||||
response.raise_for_status()
|
||||
soup = BeautifulSoup(response.text, 'html.parser')
|
||||
magnet_link = soup.find('a', href=lambda x: x and x.startswith('magnet:'))
|
||||
|
||||
@@ -22,7 +22,7 @@ def kill_aria2server():
|
||||
if proc.name() == process:
|
||||
proc.kill()
|
||||
if state.debug:
|
||||
print("Killed Aria2c")
|
||||
print(f"Killed Aria2c (PID {proc.pid})")
|
||||
except psutil.NoSuchProcess:
|
||||
pass
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import threading
|
||||
def download_selected(item, posts, post_titles):
|
||||
if item is not None:
|
||||
if state.debug:
|
||||
print(f"network {item.text()}")
|
||||
print(f"Downloading {item.text()}")
|
||||
run_thread(threading.Thread(target=run_download, args=(item.text(), posts, post_titles)))
|
||||
|
||||
else:
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import requests
|
||||
from core.utils.data.state import state
|
||||
|
||||
def check_for_updates():
|
||||
url = f"https://api.github.com/repos/KeksPirates/SoftwareManager/releases"
|
||||
response = requests.get(url)
|
||||
|
||||
if response.status_code != 200:
|
||||
print(f"Failed to fetch releases: {response.status_code}")
|
||||
exit(1)
|
||||
|
||||
releases = response.json()
|
||||
releases.sort(key=lambda r: r["published_at"], reverse=True)
|
||||
|
||||
latest_release = releases[0]
|
||||
latest_version = latest_release["name"]
|
||||
assets = latest_release["assets"]
|
||||
|
||||
|
||||
if state.version == "dev":
|
||||
if state.debug:
|
||||
print("Dev release detected, skipping version check")
|
||||
return None, None
|
||||
elif latest_version != state.version:
|
||||
if state.debug:
|
||||
print(f"New release available: {latest_version}")
|
||||
if assets:
|
||||
if state.debug:
|
||||
print("Assets:")
|
||||
for asset in assets:
|
||||
print(f" - {asset['name']}: {asset['browser_download_url']}")
|
||||
return assets, latest_version
|
||||
else:
|
||||
return None, None
|
||||
else:
|
||||
if state.debug:
|
||||
print("Already up-to-date.")
|
||||
return None, None
|
||||
@@ -2,7 +2,7 @@ from core.interface.gui import MainWindow
|
||||
from core.utils.data.state import state
|
||||
from core.network.aria2_integration import aria2server
|
||||
from core.network.aria2_integration import send_notification
|
||||
from core.utils.general.shutdown import kill_aria2server, closehelper, shutdown_event
|
||||
from core.utils.general.shutdown import closehelper, shutdown_event
|
||||
from core.utils.general.wrappers import run_thread
|
||||
from core.utils.config.config import read_config
|
||||
from PySide6 import QtWidgets
|
||||
@@ -10,12 +10,13 @@ import qdarktheme
|
||||
import darkdetect
|
||||
import threading
|
||||
import signal
|
||||
import atexit
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
# Debug Output
|
||||
state.debug = True
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--debug", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
def run_gui():
|
||||
app = QtWidgets.QApplication([])
|
||||
@@ -36,6 +37,7 @@ def keyboardinterrupthandler(signum, frame):
|
||||
|
||||
if __name__ == "__main__":
|
||||
read_config()
|
||||
state.debug = args.debug # override of read_config
|
||||
if state.debug:
|
||||
print("Starting Aria2 Server")
|
||||
state.aria2process = run_aria2server()
|
||||
|
||||
Reference in New Issue
Block a user