Compare commits

..

9 Commits

12 changed files with 197 additions and 108 deletions
+2
View File
@@ -17,6 +17,8 @@ def scrape_uztracker(query):
for link in links: for link in links:
theme_link = link.find('a', class_="genmed tLink", href=lambda x: x and x.startswith('./viewtopic')) theme_link = link.find('a', class_="genmed tLink", href=lambda x: x and x.startswith('./viewtopic'))
if not theme_link or not theme_link.b:
continue
url = urljoin(base_url, theme_link['href']) url = urljoin(base_url, theme_link['href'])
title = theme_link.b.text title = theme_link.b.text
author_link = link.find('a', class_="med") author_link = link.find('a', class_="med")
+45 -21
View File
@@ -4,8 +4,9 @@ from core.utils.logging.logs import consoleLog
from core.interface.utils.tabhelper import general_tab from core.interface.utils.tabhelper import general_tab
from core.interface.utils.tabhelper import paths_tab from core.interface.utils.tabhelper import paths_tab
from core.interface.utils.tabhelper import network_tab from core.interface.utils.tabhelper import network_tab
from core.interface.utils.svghelper import svg_icon
from PySide6 import QtWidgets from PySide6 import QtWidgets
from PySide6.QtCore import Qt from PySide6.QtCore import Qt, QSize
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QLineEdit, QLineEdit,
QPushButton, QPushButton,
@@ -22,6 +23,8 @@ from PySide6.QtWidgets import (
) )
import platform import platform
SVG_FOLDER = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M2 6c0-1.1.9-2 2-2h5l2 2h7c1.1 0 2 .9 2 2v10c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6z" fill="{color}"/></svg>'
def settings_dialog(self): def settings_dialog(self):
@@ -113,7 +116,19 @@ def settings_dialog(self):
if dir_path: if dir_path:
download_path.setText(dir_path) download_path.setText(dir_path)
browse_button = QPushButton("📁") browse_button = QPushButton()
browse_button.setFixedSize(36, 36)
browse_button.setIconSize(QSize(24, 24))
browse_button.setIcon(svg_icon(SVG_FOLDER, 24))
browse_button.setCursor(Qt.CursorShape.PointingHandCursor)
browse_button.setStyleSheet("""
QPushButton {
border: none;
background: transparent;
padding: 0px;
}
""")
download_path_layout.addWidget(browse_button) download_path_layout.addWidget(browse_button)
browse_button.clicked.connect(browse_download_path) browse_button.clicked.connect(browse_download_path)
@@ -121,24 +136,35 @@ def settings_dialog(self):
# IMAGE PATH # # IMAGE PATH #
############### ###############
# image_path_container = QWidget() image_path_container = QWidget()
# image_path_layout = QHBoxLayout() image_path_layout = QHBoxLayout()
# image_path = QLineEdit() image_path = QLineEdit()
# image_path_layout.addWidget(QLabel("Image Path (requires restart):")) image_path_layout.addWidget(QLabel("Image Path (requires restart):"))
# image_path_layout.addWidget(image_path) image_path_layout.addWidget(image_path)
# image_path_container.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) image_path_container.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
# image_path_container.setLayout(image_path_layout) image_path_container.setLayout(image_path_layout)
# image_path.setText(state.image_path) image_path.setText(state.image_path)
# def browse_image_path(): def browse_image_path():
# file_path = QFileDialog.getOpenFileName(dialog, "Select Image File", state.image_path, "Image Files (*.png *.jpg)")[0] file_path = QFileDialog.getOpenFileName(dialog, "Select Image File", state.image_path, "Image Files (*.png *.jpg)")[0]
# if file_path: if file_path:
# image_path.setText(file_path) image_path.setText(file_path)
# browse_button = QPushButton("📁") browse_button = QPushButton()
# image_path_layout.addWidget(browse_button) browse_button.setFixedSize(36, 36)
# browse_button.clicked.connect(browse_image_path) browse_button.setIconSize(QSize(24, 24))
browse_button.setIcon(svg_icon(SVG_FOLDER, 24))
browse_button.setCursor(Qt.CursorShape.PointingHandCursor)
browse_button.setStyleSheet("""
QPushButton {
border: none;
background: transparent;
padding: 0px;
}
""")
image_path_layout.addWidget(browse_button)
browse_button.clicked.connect(browse_image_path)
################## ##################
# SPEED LIMITING # # SPEED LIMITING #
@@ -224,8 +250,6 @@ def settings_dialog(self):
else: else:
interface_select.setCurrentIndex(0) interface_select.setCurrentIndex(0)
interface_select.setFixedWidth(180)
interface_select.setFixedHeight(30)
interface_select.setFixedWidth(180) interface_select.setFixedWidth(180)
interface_select.setFixedHeight(30) interface_select.setFixedHeight(30)
interface_layout.addWidget(interface_select) interface_layout.addWidget(interface_select)
@@ -246,7 +270,7 @@ def settings_dialog(self):
download_path.text(), download_path.text(),
down_speed_limit.value(), down_speed_limit.value(),
up_speed_limit.value(), up_speed_limit.value(),
# image_path.text(), image_path.text(),
autoresume_checkbox.isChecked(), autoresume_checkbox.isChecked(),
max_connections.value(), max_connections.value(),
max_downloads.value(), max_downloads.value(),
@@ -258,7 +282,7 @@ def settings_dialog(self):
self.tabs = QTabWidget() self.tabs = QTabWidget()
self.tab1 = general_tab("General", autoresume_container, update_checkbox_container, transparent_window_container, self.tabs) self.tab1 = general_tab("General", autoresume_container, update_checkbox_container, transparent_window_container, self.tabs)
self.tab2 = paths_tab("Paths", download_path_container, self.tabs) self.tab2 = paths_tab("Paths", download_path_container, image_path_container, self.tabs)
self.tab3 = network_tab("Network", interface_container, max_connections_container, max_downloads_container, up_speed_limit_container, down_speed_limit_container, api_url_container, self.tabs) self.tab3 = network_tab("Network", interface_container, max_connections_container, max_downloads_container, up_speed_limit_container, down_speed_limit_container, api_url_container, self.tabs)
dialog.layout().addWidget(self.tabs) dialog.layout().addWidget(self.tabs)
+70 -57
View File
@@ -19,7 +19,6 @@ from PySide6.QtWidgets import (
) )
from PySide6.QtGui import QIcon, QCloseEvent, QImage, QPixmap, QContextMenuEvent, QGuiApplication, QColor, QBrush from PySide6.QtGui import QIcon, QCloseEvent, QImage, QPixmap, QContextMenuEvent, QGuiApplication, QColor, QBrush
from PySide6.QtSvg import QSvgRenderer
import darkdetect import darkdetect
import threading import threading
import platform import platform
@@ -30,14 +29,14 @@ import libtorrent as lt
import sys import sys
import json import json
import base64 import base64
import winreg
from core.utils.logging.logs import consoleLog, remove_download_log, flush_log_buffer from core.utils.logging.logs import consoleLog, remove_download_log, flush_log_buffer
from core.utils.general.wrappers import run_thread from core.utils.general.wrappers import run_thread
from core.utils.data.state import state from core.utils.data.state import state
from core.utils.network.download import download_selected from core.utils.network.download import download_selected
from core.utils.network.update_checker import check_for_updates from core.utils.network.update_checker import get_updates
from core.interface.utils.tabhelper import create_tab from core.interface.utils.tabhelper import create_tab
from core.interface.utils.searchhelper import return_pressed from core.interface.utils.searchhelper import return_pressed
from core.interface.utils.svghelper import svg_icon
from core.interface.dialogs.settings import settings_dialog from core.interface.dialogs.settings import settings_dialog
from core.interface.assets.base64_icons import settings_black_base64 from core.interface.assets.base64_icons import settings_black_base64
from core.interface.assets.base64_icons import settings_white_base64 from core.interface.assets.base64_icons import settings_white_base64
@@ -85,10 +84,19 @@ def _table_stylesheet(view_type="QTableWidget"):
{view_type}::item {{ {view_type}::item {{
border-bottom: 1px solid {c["border"]}; border-bottom: 1px solid {c["border"]};
padding: 6px 14px; padding: 6px 14px;
outline: none;
{color_rule} {color_rule}
}} }}
{view_type}::item:selected {{ {view_type}::item:selected {{
background: {c["selected"]}; background: {c["selected"]};
outline: none;
border: none;
border-bottom: 1px solid {c["border"]};
}}
{view_type}::item:focus {{
outline: none;
border: none;
border-bottom: 1px solid {c["border"]};
}} }}
QHeaderView {{ QHeaderView {{
background: transparent; background: transparent;
@@ -109,38 +117,37 @@ def _table_stylesheet(view_type="QTableWidget"):
""" """
def _svg_icon(svg_str, size=20):
app = QtWidgets.QApplication.instance()
if app:
if _is_dark_mode():
color = "white"
else:
color = "#555555"
else:
color = "white"
svg = svg_str.replace("{color}", color)
renderer = QSvgRenderer(QByteArray(svg.encode()))
pixmap = QPixmap(size, size)
pixmap.fill(Qt.GlobalColor.transparent)
from PySide6.QtGui import QPainter
painter = QPainter(pixmap)
renderer.render(painter)
painter.end()
return QIcon(pixmap)
SVG_PLAY = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><polygon points="6,3 20,12 6,21" fill="{color}"/></svg>' SVG_PLAY = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><polygon points="6,3 20,12 6,21" fill="{color}"/></svg>'
SVG_PAUSE = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect x="5" y="3" width="4" height="18" rx="1" fill="{color}"/><rect x="15" y="3" width="4" height="18" rx="1" fill="{color}"/></svg>' SVG_PAUSE = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect x="5" y="3" width="4" height="18" rx="1" fill="{color}"/><rect x="15" y="3" width="4" height="18" rx="1" fill="{color}"/></svg>'
SVG_FOLDER = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M2 6c0-1.1.9-2 2-2h5l2 2h7c1.1 0 2 .9 2 2v10c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6z" fill="{color}"/></svg>' SVG_FOLDER = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M2 6c0-1.1.9-2 2-2h5l2 2h7c1.1 0 2 .9 2 2v10c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6z" fill="{color}"/></svg>'
def _verify_hash(file_path, expected_hash):
import hashlib
sha256 = hashlib.sha256()
def download_update(latest_version): with open(file_path, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
sha256.update(chunk)
return f"sha256:{sha256.hexdigest()}" == expected_hash
def _download_update(assets):
import tempfile import tempfile
import time import time
new_filename = f"SoftwareManager-dev-{latest_version.replace('-dev', '')}-windows-setup.exe"
url = f"https://github.com/KeksPirates/SoftwareManager/releases/latest/download/SoftwareManager-dev-{latest_version.replace('-dev', '')}-windows-setup.exe" for asset in assets:
installer_path = os.path.join(tempfile.gettempdir(), new_filename) if "-windows-setup.exe" in asset["name"]:
filename = asset["name"]
setup_hash = asset["hash"]
url = asset["url"]
break
if not filename:
consoleLog("Error: No Windows installer found in release assets")
return
installer_path = os.path.join(tempfile.gettempdir(), filename)
progress = QtWidgets.QProgressDialog("Downloading installer...", None, 0, 0) progress = QtWidgets.QProgressDialog("Downloading installer...", None, 0, 0)
progress.setWindowTitle("Updating") progress.setWindowTitle("Updating")
@@ -157,22 +164,24 @@ def download_update(latest_version):
response = r.get(url, allow_redirects=True, stream=True) response = r.get(url, allow_redirects=True, stream=True)
total = int(response.headers.get("content-length", 0)) total = int(response.headers.get("content-length", 0))
downloaded = 0 downloaded = 0
chunks = []
for chunk in response.iter_content(chunk_size=65536):
chunks.append(chunk)
downloaded += len(chunk)
if total > 0:
progress.setValue(int(downloaded * 100 / total))
QtWidgets.QApplication.processEvents()
with open(installer_path, "wb") as f: with open(installer_path, "wb") as f:
for chunk in chunks: for chunk in response.iter_content(chunk_size=65536):
f.write(chunk) f.write(chunk)
downloaded += len(chunk)
if total > 0:
progress.setValue(int(downloaded * 100 / total))
QtWidgets.QApplication.processEvents()
if not os.path.exists(installer_path): if not os.path.exists(installer_path):
progress.close() progress.close()
raise FileNotFoundError("Executable not found") raise FileNotFoundError("Executable not found")
if _verify_hash(installer_path, setup_hash):
consoleLog(f"Sucessfully validated installer hash ({setup_hash})")
else:
consoleLog("Error: Invalid Filehash, file may be corrupted")
sys.exit(0)
progress.setLabelText("Installing update...") progress.setLabelText("Installing update...")
progress.setValue(100) progress.setValue(100)
QtWidgets.QApplication.processEvents() QtWidgets.QApplication.processEvents()
@@ -216,21 +225,19 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
# Check for updates on Windows # Check for updates on Windows
if state.ignore_updates is False and platform.system() == "Windows": if state.ignore_updates is False and platform.system() == "Windows":
result = check_for_updates() assets = get_updates()
if result != (None, None): if assets != None:
assets, latest_version = result
if assets: msg = QMessageBox()
msg = QMessageBox() msg.setIcon(QMessageBox.Icon.Information)
msg.setIcon(QMessageBox.Icon.Information) msg.setWindowTitle("Update Available")
msg.setWindowTitle("Update Available") msg.setText("A new version is available.")
msg.setText("A new version is available.") msg.setInformativeText("Press Ok to download the update.")
msg.setInformativeText("Press Ok to download the update.") msg.setStandardButtons(QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Ignore)
msg.setStandardButtons(QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Ignore)
response = msg.exec_() response = msg.exec_()
if response == QMessageBox.StandardButton.Ok: if response == QMessageBox.StandardButton.Ok:
download_update(latest_version) _download_update(assets)
self.setWindowTitle("Software Manager") self.setWindowTitle("Software Manager")
self.setGeometry(100, 100, 800, 600) self.setGeometry(100, 100, 800, 600)
@@ -454,21 +461,27 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
class HoverRowDelegate(QStyledItemDelegate): class HoverRowDelegate(QStyledItemDelegate):
def paint(self, painter, option, index): def paint(self, painter, option, index):
opt = QtWidgets.QStyleOptionViewItem(option)
opt.state &= ~QtWidgets.QStyle.StateFlag.State_HasFocus
opt.state &= ~QtWidgets.QStyle.StateFlag.State_Selected
if index.row() == MainWindow._instance._hovered_row: if index.row() == MainWindow._instance._hovered_row:
painter.save() painter.save()
painter.fillRect(option.rect, _theme_colors()["hover"]) painter.fillRect(opt.rect, _theme_colors()["hover"])
painter.restore() painter.restore()
super().paint(painter, option, index) super().paint(painter, opt, index)
class PauseResumeDelegate(QStyledItemDelegate): class PauseResumeDelegate(QStyledItemDelegate):
clicked = Signal(int) clicked = Signal(int)
def paint(self, painter, option, index): def paint(self, painter, option, index):
opt = QtWidgets.QStyleOptionViewItem(option)
opt.state &= ~QtWidgets.QStyle.StateFlag.State_HasFocus
opt.state &= ~QtWidgets.QStyle.StateFlag.State_Selected
if index.row() == MainWindow._instance._hovered_row: if index.row() == MainWindow._instance._hovered_row:
painter.save() painter.save()
painter.fillRect(option.rect, _theme_colors()["hover"]) painter.fillRect(opt.rect, _theme_colors()["hover"])
painter.restore() painter.restore()
super().paint(painter, option, index) super().paint(painter, opt, index)
def setEditorData(self, editor, index): def setEditorData(self, editor, index):
button = editor.findChild(QtWidgets.QPushButton) button = editor.findChild(QtWidgets.QPushButton)
@@ -479,11 +492,11 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
if button: if button:
if status.state == lt.torrent_status.seeding: if status.state == lt.torrent_status.seeding:
button.setIcon(_svg_icon(SVG_FOLDER, 18)) button.setIcon(svg_icon(SVG_FOLDER, 18))
button.setText("") button.setText("")
else: else:
is_user_paused = status.paused and not bool(magnetdl.flags() & lt.torrent_flags.auto_managed) is_user_paused = status.paused and not bool(magnetdl.flags() & lt.torrent_flags.auto_managed)
button.setIcon(_svg_icon(SVG_PLAY if is_user_paused else SVG_PAUSE, 18)) button.setIcon(svg_icon(SVG_PLAY if is_user_paused else SVG_PAUSE, 18))
button.setText("") button.setText("")
def createEditor(self, parent, option, index): def createEditor(self, parent, option, index):
@@ -497,11 +510,11 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
layout.setContentsMargins(0, 0, 0, 0) layout.setContentsMargins(0, 0, 0, 0)
if status.state == lt.torrent_status.seeding: if status.state == lt.torrent_status.seeding:
btnPause = QtWidgets.QPushButton() btnPause = QtWidgets.QPushButton()
btnPause.setIcon(_svg_icon(SVG_FOLDER, 18)) btnPause.setIcon(svg_icon(SVG_FOLDER, 18))
else: else:
btnPause = QtWidgets.QPushButton() btnPause = QtWidgets.QPushButton()
is_user_paused = status.paused and not bool(magnetdl.flags() & lt.torrent_flags.auto_managed) is_user_paused = status.paused and not bool(magnetdl.flags() & lt.torrent_flags.auto_managed)
btnPause.setIcon(_svg_icon(SVG_PLAY if is_user_paused else SVG_PAUSE, 18)) btnPause.setIcon(svg_icon(SVG_PLAY if is_user_paused else SVG_PAUSE, 18))
btnPause.setIconSize(QSize(18, 18)) btnPause.setIconSize(QSize(18, 18))
btnPause.setFixedSize(30, 30) btnPause.setFixedSize(30, 30)
btnPause.setCursor(Qt.CursorShape.PointingHandCursor) btnPause.setCursor(Qt.CursorShape.PointingHandCursor)
+1 -1
View File
@@ -50,7 +50,7 @@ def return_pressed(self):
elif state.tracker is not None: elif state.tracker is not None:
state.posts = scrapers[state.tracker](search_text) state.posts = scrapers[state.tracker](search_text)
if state.posts == []: if not state.posts:
consoleLog(f"No Results for {search_text}") consoleLog(f"No Results for {search_text}")
state.tracker_list[state.tracker].clear() state.tracker_list[state.tracker].clear()
self.show_empty_results(True) self.show_empty_results(True)
+30
View File
@@ -0,0 +1,30 @@
from PySide6 import QtWidgets
from PySide6.QtCore import Qt, QByteArray
from PySide6.QtGui import QIcon, QPixmap, QPainter
from PySide6.QtSvg import QSvgRenderer
import darkdetect
def _is_dark_mode():
return darkdetect.isDark()
def svg_icon(svg_str, size=20):
app = QtWidgets.QApplication.instance()
if app:
if _is_dark_mode():
color = "white"
else:
color = "#555555"
else:
color = "white"
svg = svg_str.replace("{color}", color)
renderer = QSvgRenderer(QByteArray(svg.encode()))
pixmap = QPixmap(size, size)
pixmap.fill(Qt.GlobalColor.transparent)
painter = QPainter(pixmap)
renderer.render(painter)
painter.end()
return QIcon(pixmap)
+2 -2
View File
@@ -30,11 +30,11 @@ def general_tab(title, autoresume, update_checkbox, transparent_window, tabs):
tabs.addTab(tab, title) tabs.addTab(tab, title)
return tab return tab
def paths_tab(title, download_path, tabs): def paths_tab(title, download_path, image_path, tabs):
tab = QWidget() tab = QWidget()
layout = QVBoxLayout() layout = QVBoxLayout()
layout.addWidget(download_path) layout.addWidget(download_path)
# layout.addWidget(image_path) layout.addWidget(image_path)
layout.addStretch() layout.addStretch()
tab.setLayout(layout) tab.setLayout(layout)
tabs.addTab(tab, title) tabs.addTab(tab, title)
+14 -8
View File
@@ -74,10 +74,13 @@ def add_download(magnet_uri, dl_path=state.download_path):
consoleLog("Skipping Downloading, download already running... ") consoleLog("Skipping Downloading, download already running... ")
return False return False
magnetdl = lt.parse_magnet_uri(magnet_uri) try:
magnetdl.save_path = dl_path magnetdl = lt.parse_magnet_uri(magnet_uri)
magnetdl.save_path = dl_path
download = state.dl_session.add_torrent(magnetdl) download = state.dl_session.add_torrent(magnetdl)
except Exception as e:
consoleLog(f"Failed to add torrent: {e}")
return False
state.active_downloads[magnet_uri] = download state.active_downloads[magnet_uri] = download
consoleLog(f"Added {magnet_uri} to downloads") consoleLog(f"Added {magnet_uri} to downloads")
@@ -95,10 +98,13 @@ def add_seed(magnet_uri, file_path):
consoleLog("Already seeding this torrent") consoleLog("Already seeding this torrent")
return False return False
magnetdl = lt.parse_magnet_uri(magnet_uri) try:
magnetdl.save_path = os.path.dirname(file_path) magnetdl = lt.parse_magnet_uri(magnet_uri)
magnetdl.save_path = os.path.dirname(file_path)
handle = state.dl_session.add_torrent(magnetdl) handle = state.dl_session.add_torrent(magnetdl)
except Exception as e:
consoleLog(f"Failed to add seed: {e}")
return False
state.active_downloads[magnet_uri] = handle state.active_downloads[magnet_uri] = handle
state.seeded_magnets.add(magnet_uri) state.seeded_magnets.add(magnet_uri)
return True return True
+1 -1
View File
@@ -30,7 +30,7 @@ def get_item_url(item, posts, post_titles): # softwarelist currentitem, post lis
def get_magnet_link(post_url): def get_magnet_link(post_url):
try: try:
response = requests.get(post_url) # eventually impl. cloudscraper response = requests.get(post_url, timeout=15) # eventually impl. cloudscraper
consoleLog("Sent Request to retrieve Magnet Link...") consoleLog("Sent Request to retrieve Magnet Link...")
response.raise_for_status() response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser') soup = BeautifulSoup(response.text, 'html.parser')
+4 -1
View File
@@ -22,7 +22,10 @@ def check_downloads(downloads):
for download in downloads: for download in downloads:
if os.path.exists(download.path): if os.path.exists(download.path):
consoleLog(f"Existing Download: {download.title}") consoleLog(f"Existing Download: {download.title}")
seed_magnet(download.magnet_uri, download.path) try:
seed_magnet(download.magnet_uri, download.path)
except Exception as e:
consoleLog(f"Failed to seed {download.title}: {e}")
else: else:
consoleLog(f"Inexistent Download: {download.title}") consoleLog(f"Inexistent Download: {download.title}")
remove_download_log(download.magnet_uri) remove_download_log(download.magnet_uri)
+1 -1
View File
@@ -89,7 +89,7 @@ def _remove_download_log_inner(magnet_uri) -> DownloadList:
magnet_link = (magnet_uri or "").strip() magnet_link = (magnet_uri or "").strip()
title = next((getattr(d, 'title', 'Unknown') for d in downloads if (getattr(d, 'magnet_uri', None) or '').strip() == magnet_link or (getattr(d, 'url', None) or '').strip() == magnet_link)) title = next((getattr(d, 'title', 'Unknown') for d in downloads if (getattr(d, 'magnet_uri', None) or '').strip() == magnet_link or (getattr(d, 'url', None) or '').strip() == magnet_link), 'Unknown')
downloads = [d for d in downloads if (getattr(d, 'magnet_uri', None) or "").strip() != magnet_link and (getattr(d, 'url', None) or "").strip() != magnet_link] downloads = [d for d in downloads if (getattr(d, 'magnet_uri', None) or "").strip() != magnet_link and (getattr(d, 'url', None) or "").strip() != magnet_link]
consoleLog(f"Removed {title} from Log File") consoleLog(f"Removed {title} from Log File")
+23 -13
View File
@@ -2,30 +2,40 @@ import requests
from core.utils.data.state import state from core.utils.data.state import state
from core.utils.logging.logs import consoleLog from core.utils.logging.logs import consoleLog
def check_for_updates(): def get_updates():
url = f"https://api.github.com/repos/KeksPirates/SoftwareManager/releases" url = f"https://api.github.com/repos/KeksPirates/SoftwareManager/releases/latest"
response = requests.get(url)
try:
response = requests.get(url, timeout=15)
except requests.RequestException as e:
consoleLog(f"Failed to fetch releases: {e}")
return None
if response.status_code != 200: if response.status_code != 200:
consoleLog(f"Failed to fetch releases: {response.status_code}") consoleLog(f"Failed to fetch releases: {response.status_code}")
exit(1) return None
releases = response.json() release = response.json()
releases.sort(key=lambda r: r["published_at"], reverse=True)
latest_release = releases[0] latest_version = release["name"]
latest_version = latest_release["name"] assets = release["assets"]
assets = latest_release["assets"]
release_assets = []
if latest_version != state.version: if latest_version != state.version:
consoleLog(f"New release available: {latest_version}") consoleLog(f"New release available: {latest_version}")
if assets: if assets:
consoleLog("Assets:") consoleLog("Assets:")
for asset in assets: for asset in assets:
consoleLog(f" - {asset['name']}: {asset['browser_download_url']}") consoleLog(f"{asset['name']}")
return assets, latest_version release_assets.append(dict(
name=asset['name'],
url=asset['browser_download_url'],
hash=asset['digest']
))
return release_assets
else: else:
return None, None return None
else: else:
consoleLog("Already up-to-date.") consoleLog("Already up-to-date.")
return None, None return None
+2 -1
View File
@@ -56,7 +56,8 @@ def main():
consoleLog("Started Thread: check_completed") consoleLog("Started Thread: check_completed")
run_thread(threading.Thread(target=check_deleted_files, args=(shutdown_event,), daemon=True)) run_thread(threading.Thread(target=check_deleted_files, args=(shutdown_event,), daemon=True))
consoleLog("Started Thread: check_deleted_files") consoleLog("Started Thread: check_deleted_files")
check_downloads(downloads) run_thread(threading.Thread(target=check_downloads, args=(downloads,)))
consoleLog("Started Thread: check_downloads")
elapsed = time.perf_counter() - start_time elapsed = time.perf_counter() - start_time
consoleLog(f"Initialization completed in {elapsed:.2f}s. Launching GUI") consoleLog(f"Initialization completed in {elapsed:.2f}s. Launching GUI")
run_gui() run_gui()