Compare commits

...

24 Commits

Author SHA1 Message Date
Vxrtrauter 4bf2e9af81 feat: add download existence checks and improve log removal feedback 2026-03-01 23:14:10 +01:00
Vxrtrauter a6bb879db3 feat: enhance download logging with path handling and timeout mechanism 2026-02-28 04:01:00 +01:00
Vxrtrauter 7653748599 chore: remove unused file_list utility 2026-02-28 03:34:56 +01:00
Vxrtrauter 9bec3f9189 Merge branch 'main' of https://github.com/KeksPirates/SoftwareManager 2026-02-28 03:33:13 +01:00
Vxrtrauter 04d93b870a refactor: reorganize logging functionality and remove unused log files 2026-02-28 03:33:10 +01:00
KeksNino 664e660198 fix a bunch of lsp errors 2026-02-28 03:31:17 +01:00
Vxrtrauter 60019e0d2e refactor: update log imports and restructure logging functionality 2026-02-28 03:31:05 +01:00
Vxrtrauter 8238cfbe56 Merge branch 'main' of https://github.com/KeksPirates/SoftwareManager 2026-02-28 03:05:10 +01:00
Vxrtrauter 7ccff5c5a1 Refactor monkrus scraper to implement caching and improve post fetching logic 2026-02-28 03:05:09 +01:00
KeksNino 8abe92fc4f fix: hide seeders and leechers in uztracker and m0nkrus tab 2026-02-28 02:37:35 +01:00
Vxrtrauter 0a3b5eeee2 Remove unused asyncio import in uztracker.py 2026-02-27 23:59:59 +01:00
KeksNino 7662c861ea trigger github CI 2026-02-20 01:13:13 +01:00
KeksNino 2a8f305a76 fix exe not starting on windows 2026-02-19 19:28:24 +01:00
KeksNino 12223d18de add transparency to settings dialog on linux/macos 2026-02-17 00:29:55 +01:00
KeksNino 544733ce6b fix app icon in nuitka build process and remove settings pngs 2026-02-17 00:10:30 +01:00
KeksNino 7aee2bcfe9 remove include flags for assets in github workflows 2026-02-16 23:58:34 +01:00
KeksNino d274e64841 fix assets not showing up by loading them from base64 2026-02-16 23:49:21 +01:00
KeksNino 23c74f6487 fix: wrong assets path on windows 2026-02-16 02:34:44 +01:00
KeksNino 19ea09d035 fix: build_info.json path in github CI 2026-02-11 21:28:06 +01:00
shayaa 92359d75e5 Fix asset paths in dev.yml for logo and data files 2026-02-11 21:27:01 +01:00
shayaa 315a68c000 Update asset paths in dev.yml for PyInstaller 2026-02-11 21:26:13 +01:00
shayaa 4b0ef44efd Update file paths to src/main.py in dev.yml 2026-02-11 21:23:58 +01:00
KeksNino 6dc28615a0 refactor: move main.py and core into new src folder 2026-02-11 21:19:35 +01:00
shayaa 2ea9a329d1 Update server hosting instructions in README 2026-02-11 03:49:12 +01:00
35 changed files with 214 additions and 134 deletions
+10 -15
View File
@@ -106,7 +106,7 @@ jobs:
run: |
python -c "
from PIL import Image
img = Image.open('core/interface/assets/logo.png')
img = Image.open('src/core/interface/assets/logo.png')
img.save('app_icon.ico', format='ICO', sizes=[(256,256),(128,128),(64,64),(48,48),(32,32),(16,16)])
print('Icon converted successfully')
"
@@ -126,12 +126,11 @@ jobs:
--enable-plugin=upx \
--upx-binary="${{ env.UPX_BINARY }}" \
--noinclude-qt-translations \
--include-data-dir=core/interface/assets=core/interface/assets \
--include-data-files=build_info.json=build_info.json \
--include-data-files=build_info.json=core/build_info.json \
--include-data-files=build_info.json=core/interface/build_info.json \
--include-data-files=build_info.json=src/build_info.json \
--include-data-files=build_info.json=src/core/interface/build_info.json \
--output-filename=${{ env.APP_NAME }}.exe \
main.py
src/main.py
- name: Build with PyInstaller (Linux)
if: runner.os == 'Linux'
@@ -139,10 +138,8 @@ jobs:
run: |
pyinstaller --onefile --windowed \
--add-data "build_info.json:." \
--add-data "build_info.json:core" \
--add-data "build_info.json:core/interface" \
--add-data "core/interface/assets:core/interface/assets" \
--icon "core/interface/assets/logo.png" \
--add-data "build_info.json:src/core" \
--add-data "build_info.json:src/core/interface" \
--exclude-module PyQt6 \
--exclude-module PyQt5 \
--hidden-import=backports.tarfile \
@@ -151,7 +148,7 @@ jobs:
--hidden-import=jaraco.text \
--hidden-import=jaraco.functools \
--name ${{ env.APP_NAME }} \
main.py
src/main.py
- name: Build with PyInstaller (macOS)
if: runner.os == 'macOS'
@@ -159,10 +156,8 @@ jobs:
run: |
pyinstaller --onefile \
--add-data "build_info.json:." \
--add-data "build_info.json:core" \
--add-data "build_info.json:core/interface" \
--add-data "core/interface/assets:core/interface/assets" \
--icon "core/interface/assets/logo.png" \
--add-data "build_info.json:src/core" \
--add-data "build_info.json:src/core/interface" \
--exclude-module PyQt6 \
--exclude-module PyQt5 \
--hidden-import=backports.tarfile \
@@ -171,7 +166,7 @@ jobs:
--hidden-import=jaraco.text \
--hidden-import=jaraco.functools \
--name ${{ env.APP_NAME }} \
main.py
src/main.py
- name: Rename artifact
shell: bash
+1 -1
View File
@@ -30,7 +30,7 @@ SoftwareManager is a Python-based GUI tool that simplifies searching and downloa
## Additional Information
- SoftwareManager is still in Development and most likely contains issues. Please report any bugs you find via the Issues tab. Contributions are also greatly appreciated.
- MacOS Builds haven't been properly tested - We're thankful to receive feedback.
- 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 the server branch.
- If you want to host your own server, clone our [server repository](https://github.com/KeksPirates/SoftwareManager-Server), enter your cookie for rutracker.org and run server.py. Detailed Instructions on how to get your rutracker cookie are in the README of the server repo.
## Python Dependencies
(also included in requirements.txt)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

@@ -1,16 +1,28 @@
from bs4 import BeautifulSoup
import requests
from core.utils.general.logs import consoleLog
import time
def scrape_monkrus_telegram(query):
cache = {
"data": [],
"last_fetched": 0
}
cache_expiry = 300
def _get_telegram_posts():
post = 100
max_posts = 250
posts: list[dict] = []
posts = []
added = set()
current_time = time.time()
if cache["data"] and (current_time - cache["last_fetched"] < cache_expiry):
return cache["data"]
while post <= max_posts:
url = f"https://t.me/s/real_monkrus/{post}"
response = requests.get(url)
response = requests.get(url, timeout=10)
soup = BeautifulSoup(response.text, "html.parser")
bubbles = soup.find_all("div", class_="tgme_widget_message_bubble")
@@ -25,7 +37,7 @@ def scrape_monkrus_telegram(query):
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:
if link:
post_url = link["href"]
if post_url not in added:
added.add(post_url)
@@ -39,4 +51,20 @@ def scrape_monkrus_telegram(query):
post += 22
posts.reverse()
cache["data"] = posts
cache["last_fetched"] = current_time
return(posts)
def scrape_monkrus_telegram(query):
posts = _get_telegram_posts()
filtered_posts = []
for post in posts:
if query.lower() in post["title"].lower():
filtered_post = post.copy()
filtered_post["id"] = len(filtered_posts) + 1
filtered_posts.append(filtered_post)
return filtered_posts
@@ -1,6 +1,6 @@
import requests
from core.utils.data.state import state
from core.utils.general.logs import consoleLog
from core.utils.logging.logs import consoleLog
def scrape_rutracker(search_text):
@@ -1,9 +1,8 @@
import requests
import asyncio
import threading
from bs4 import BeautifulSoup
from core.utils.data.state import state
from core.utils.general.logs import consoleLog
from core.utils.logging.logs import consoleLog
from core.utils.general.wrappers import run_thread
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

@@ -1,10 +1,11 @@
from core.utils.config.settings import save_settings
from core.utils.data.state import state
from core.utils.general.logs import consoleLog
from core.utils.logging.logs import consoleLog
from core.interface.utils.tabhelper import general_tab
from core.interface.utils.tabhelper import paths_tab
from core.interface.utils.tabhelper import network_tab
from PySide6 import QtWidgets
from PySide6.QtCore import Qt
from PySide6.QtWidgets import (
QLineEdit,
QPushButton,
@@ -31,6 +32,9 @@ def settings_dialog(self):
dialog.setLayout(QVBoxLayout())
if state.window_transparency and platform.system() != "Windows" and dialog:
dialog.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
def close_settings():
dialog.reject()
@@ -1,5 +1,5 @@
from PySide6 import QtWidgets
from PySide6.QtCore import Qt, QTimer, QModelIndex, QAbstractTableModel, Signal, QEvent, QSize
from PySide6.QtCore import QPersistentModelIndex, Qt, QTimer, QModelIndex, QAbstractTableModel, Signal, QEvent, QSize
from PySide6.QtWidgets import (
QLineEdit,
QTableView,
@@ -29,8 +29,8 @@ import libtorrent as lt
import time
import sys
import json
import asyncio
from core.utils.general.logs import consoleLog, remove_download_log, flush_log_buffer
import base64
from core.utils.logging.logs import consoleLog, remove_download_log, flush_log_buffer
from core.utils.general.wrappers import run_thread
from core.utils.data.state import state
from core.utils.network.download import download_selected
@@ -38,7 +38,9 @@ from core.utils.network.update_checker import check_for_updates
from core.interface.utils.tabhelper import create_tab
from core.interface.utils.searchhelper import return_pressed
from core.interface.dialogs.settings import settings_dialog
from core.network.libtorrent_misc import cleanup_session
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 logo_base64
from core.utils.general.shutdown import closehelper
@@ -73,21 +75,10 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
MainWindow._instance = self
self.log_signal.connect(self._on_log_signal)
def get_asset_path(filename):
base_path = os.path.dirname(os.path.abspath(__file__))
asset_path = os.path.join(base_path, 'assets', filename)
if os.path.exists(asset_path):
return asset_path
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
asset_path = os.path.join(sys._MEIPASS, 'core', 'interface', 'assets', filename)
if os.path.exists(asset_path):
return asset_path
return os.path.join('core', 'interface', 'assets', filename)
self.setWindowIcon(QIcon(get_asset_path("logo.png")))
pixmap = QPixmap()
image_data = base64.b64decode(logo_base64)
pixmap.loadFromData(image_data)
self.setWindowIcon(QIcon(pixmap))
if platform.system() == "Windows":
try:
@@ -153,51 +144,57 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
flush_log_buffer()
# Table Widget for Item List
self.qtablewidget = QTableWidget()
self.qtablewidget.setColumnCount(4)
self.qtablewidget.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers)
self.qtablewidget.verticalHeader().setVisible(False)
self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author", "Seeders", "Leechers"])
def create_tracker_table(headers):
table = QTableWidget()
table.setColumnCount(len(headers))
table.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers)
table.verticalHeader().setVisible(False)
table.setHorizontalHeaderLabels(headers)
header = table.horizontalHeader()
header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
header.setSectionResizeMode(1, QHeaderView.ResizeMode.Fixed)
header.resizeSection(1, 500)
header.setStretchLastSection(False)
header = self.qtablewidget.horizontalHeader()
header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
header.setSectionResizeMode(1, QHeaderView.ResizeMode.Fixed)
header.setStretchLastSection(False)
return table
self.qtablewidget.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
self.qtablewidget.viewport().setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
self.rutrackerlist = create_tracker_table(["Post Title", "Author", "Seeders", "Leechers"])
self.uztrackerlist = create_tracker_table(["Post Title", "Author"])
self.monkruslist = create_tracker_table(["Post Title", "Author"])
header = self.qtablewidget.horizontalHeader()
header.setSectionResizeMode(1, QHeaderView.ResizeMode.Fixed)
header.resizeSection(1, 500)
state.tracker_list.update({"rutracker": self.rutrackerlist, "uztracker": self.uztrackerlist, "m0nkrus": self.monkruslist})
self.rutrackerlist.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
self.rutrackerlist.viewport().setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
self.uztrackerlist.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
self.uztrackerlist.viewport().setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
container = QWidget()
containerLayout = QVBoxLayout()
containerLayout.addWidget(self.searchbar)
containerLayout.addWidget(self.qtablewidget)
containerLayout.addWidget(state.tracker_list[state.tracker])
class DownloadModel(QAbstractTableModel):
def __init__(self):
super().__init__()
self.headers = ["Action", "Name", "Status", "Progress", "Speed", "Size", "Total Size"]
def rowCount(self, parent=QModelIndex()):
def rowCount(self, parent: QModelIndex | QPersistentModelIndex = QModelIndex()) -> int:
return len(state.active_downloads)
def columnCount(self, parent=QModelIndex()):
def columnCount(self, parent: QModelIndex | QPersistentModelIndex = QModelIndex()):
return len(self.headers)
def headerData(self, section, orientation, role=Qt.DisplayRole):
if role == Qt.DisplayRole and orientation == Qt.Horizontal:
def headerData(self, section, orientation, role: int = Qt.ItemDataRole.DisplayRole):
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
return self.headers[section]
return None
def data(self, index, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
def data(self, index: QModelIndex | QPersistentModelIndex, role: int = Qt.ItemDataRole.DisplayRole):
if role == Qt.ItemDataRole.DisplayRole:
col = index.column()
if index.row() >= len(state.active_downloads) or index.row() < 0:
@@ -213,9 +210,9 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
elif col == 1:
return status.name if status.has_metadata else "Fetching metadata..."
elif col == 2:
if status.state == lt.torrent_status.downloading:
if status.state == lt.torrent_status.downloading:
return "Downloading"
elif status.state == lt.torrent_status.seeding:
elif status.state == lt.torrent_status.seeding:
return "Seeding"
elif status.paused:
return "Paused"
@@ -259,7 +256,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
else:
return "" if status.paused else "Stalled"
if role == Qt.UserRole and index.column() == 0:
if role == Qt.ItemDataRole.UserRole and index.column() == 0:
magnet_link = list(state.active_downloads.keys())[index.row()]
magnetdl = state.active_downloads[magnet_link]
return magnetdl.status().paused
@@ -359,7 +356,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
delegate.clicked.connect(on_pause_resume_clicked)
# download button triggers
self.dlbutton.clicked.connect(lambda: run_thread(threading.Thread(target=download_selected, args=(self.qtablewidget.currentItem(), state.posts, state.post_titles))))
self.dlbutton.clicked.connect(lambda: run_thread(threading.Thread(target=download_selected, args=(state.tracker_list[state.tracker].currentItem(), state.posts, state.post_titles))))
container.setLayout(containerLayout)
self.setCentralWidget(container)
@@ -379,15 +376,16 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
self.horizontal_layout = QHBoxLayout()
self.horizontal_layout.addWidget(self.emptyResults, stretch=3)
self.horizontal_layout.addWidget(self.qtablewidget)
self.tracker_widget = state.tracker_list[state.tracker]
self.horizontal_layout.addWidget(self.tracker_widget)
self.tab1 = create_tab("Search", self.searchbar, self.qtablewidget, self.tabs, self.dlbutton, self.horizontal_layout)
self.tab1 = create_tab("Search", self.searchbar, state.tracker_list[state.tracker], 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)
if state.image_path is not None and os.path.exists(state.image_path):
self.image = QImage(state.image_path)
self.image = self.image.scaledToWidth(300, Qt.SmoothTransformation)
self.image = self.image.scaledToWidth(300, Qt.TransformationMode.SmoothTransformation)
self.pixmap = QPixmap.fromImage(self.image)
self.overlay_label = QLabel(self)
self.overlay_label.setPixmap(self.pixmap)
@@ -418,10 +416,18 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
self.settings_btn = QtWidgets.QToolButton()
self.settings_btn.setIconSize(QSize(21, 21))
self.settings_btn.setStyleSheet("background: transparent;")
if darkdetect.isDark():
self.settings_btn.setIcon(QIcon(get_asset_path("settings_white.png")))
settings_white_pixmap = QPixmap()
settings_white = base64.b64decode(settings_white_base64)
settings_white_pixmap.loadFromData(settings_white)
self.settings_btn.setIcon(QIcon(settings_white_pixmap))
else:
self.settings_btn.setIcon(QIcon(get_asset_path("settings_black.png")))
settings_black_pixmap = QPixmap()
settings_black = base64.b64decode(settings_black_base64)
settings_black_pixmap.loadFromData(settings_black)
self.settings_btn.setIcon(QIcon(settings_black_pixmap))
self.settings_btn.setToolTip("Settings")
self.settings_btn.setCursor(Qt.CursorShape.PointingHandCursor)
self.settings_btn.clicked.connect(lambda: settings_dialog(self))
@@ -506,12 +512,20 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
def set_tracker(self, _):
state.tracker = self.tracker_list.currentText()
while self.horizontal_layout.count():
item = self.horizontal_layout.takeAt(0)
if item.widget():
item.widget().setParent(None)
tracker_widget = state.tracker_list[state.tracker]
self.horizontal_layout.addWidget(tracker_widget)
def show_empty_results(self, show: bool):
if show:
self.qtablewidget.hide()
state.tracker_list[state.tracker].hide()
self.emptyResults.show()
else:
self.qtablewidget.show()
state.tracker_list[state.tracker].show()
self.emptyResults.hide()
def show_empty_downloads(self):
@@ -525,8 +539,8 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
# 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))
table_width = state.tracker_list[state.tracker].viewport().width()
state.tracker_list[state.tracker].setColumnWidth(1, int(table_width * 0.3))
def contextMenuEvent(self, event: QContextMenuEvent):
if self.downloadList.underMouse():
@@ -1,5 +1,5 @@
from PySide6.QtWidgets import QTableWidgetItem
from core.utils.general.logs import consoleLog
from core.utils.logging.logs import consoleLog
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
@@ -17,22 +17,22 @@ def return_pressed(self):
response = scrape_uztracker(search_text)
if response:
state.post_titles, state.post_urls = response
self.qtablewidget.clear()
state.tracker_list[state.tracker].clear()
if state.post_titles and len(state.post_titles) > 0:
self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"])
self.qtablewidget.setRowCount(len(state.post_titles))
state.tracker_list[state.tracker].setHorizontalHeaderLabels(["Post Title", "Author"])
state.tracker_list[state.tracker].setRowCount(len(state.post_titles))
for i, author in enumerate(state.post_author):
self.qtablewidget.setItem(i, 1, QTableWidgetItem(author))
state.tracker_list[state.tracker].setItem(i, 1, QTableWidgetItem(author))
for i, title in enumerate(state.post_titles):
self.qtablewidget.setItem(i, 0, QTableWidgetItem(title))
state.tracker_list[state.tracker].setItem(i, 0, QTableWidgetItem(title))
self.show_empty_results(False)
else:
consoleLog(f"No Results for {search_text}")
self.qtablewidget.clear()
state.tracker_list[state.tracker].clear()
self.show_empty_results(True)
else:
consoleLog(f"No response from uztracker")
self.qtablewidget.clear()
state.tracker_list[state.tracker].clear()
self.show_empty_results(True)
elif state.tracker == "rutracker":
@@ -41,27 +41,27 @@ def return_pressed(self):
_, state.posts, _, _, cached = split_data(response)
if state.posts == []:
consoleLog(f"No Results for {search_text}")
self.qtablewidget.clear()
state.tracker_list[state.tracker].clear()
self.show_empty_results(True)
else:
state.post_titles, _, state.post_author, state.post_seeders, state.post_leechers = format_data(state.posts)
self.show_empty_results(False)
self.qtablewidget.clear()
self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author", "Seeders", "Leechers"])
self.qtablewidget.setRowCount(len(state.post_titles))
state.tracker_list[state.tracker].clear()
state.tracker_list[state.tracker].setHorizontalHeaderLabels(["Post Title", "Author", "Seeders", "Leechers"])
state.tracker_list[state.tracker].setRowCount(len(state.post_titles))
for i, author in enumerate(state.post_author):
self.qtablewidget.setItem(i, 1, QTableWidgetItem(author))
state.tracker_list[state.tracker].setItem(i, 1, QTableWidgetItem(author))
for i, title in enumerate(state.post_titles):
self.qtablewidget.setItem(i, 0, QTableWidgetItem(title))
state.tracker_list[state.tracker].setItem(i, 0, QTableWidgetItem(title))
for i, seeders in enumerate(state.post_seeders):
self.qtablewidget.setItem(i, 2, QTableWidgetItem(seeders))
state.tracker_list[state.tracker].setItem(i, 2, QTableWidgetItem(seeders))
for i, leechers in enumerate(state.post_leechers):
self.qtablewidget.setItem(i, 3, QTableWidgetItem(leechers))
state.tracker_list[state.tracker].setItem(i, 3, QTableWidgetItem(leechers))
if state.debug == True:
consoleLog(f"Response Cached: {cached}")
else:
consoleLog(f"No response from rutracker")
self.qtablewidget.clear()
state.tracker_list[state.tracker].clear()
self.show_empty_results(True)
elif state.tracker == "m0nkrus":
@@ -69,15 +69,15 @@ def return_pressed(self):
if state.posts == []:
consoleLog(f"No Results for {search_text}")
self.qtablewidget.clear()
state.tracker_list[state.tracker].clear()
self.show_empty_results(True)
else:
state.post_titles, _, state.post_author = format_data_m0nkrus(state.posts)
self.show_empty_results(False)
self.qtablewidget.clear()
self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"])
self.qtablewidget.setRowCount(len(state.post_titles))
state.tracker_list[state.tracker].clear()
state.tracker_list[state.tracker].setHorizontalHeaderLabels(["Post Title", "Author"])
state.tracker_list[state.tracker].setRowCount(len(state.post_titles))
for i, author in enumerate(state.post_author):
self.qtablewidget.setItem(i, 1, QTableWidgetItem(author))
state.tracker_list[state.tracker].setItem(i, 1, QTableWidgetItem(author))
for i, title in enumerate(state.post_titles):
self.qtablewidget.setItem(i, 0, QTableWidgetItem(title))
state.tracker_list[state.tracker].setItem(i, 0, QTableWidgetItem(title))
@@ -1,4 +1,4 @@
from core.utils.general.logs import consoleLog
from core.utils.logging.logs import consoleLog
from core.utils.data.state import state
import psutil
@@ -4,7 +4,7 @@ from core.network.interface import get_interface_ip
import threading
import libtorrent as lt
from core.utils.data.state import state
from core.utils.general.logs import consoleLog
from core.utils.logging.logs import consoleLog
global loop_running
@@ -1,6 +1,6 @@
from core.utils.data.state import state
from core.utils.general.logs import update_download_completed_by_hash
from core.utils.general.logs import consoleLog
from core.utils.logging.logs import update_download_completed_by_hash
from core.utils.logging.logs import consoleLog
from plyer import notification
import libtorrent as lt
import time
@@ -57,4 +57,4 @@ def update_log(shutdown_event):
updated.add(magnet_uri)
except Exception:
pass
time.sleep(5)
time.sleep(5)
@@ -1,4 +1,4 @@
from core.utils.general.logs import consoleLog
from core.utils.logging.logs import consoleLog
from core.network.libtorrent_int import add_download
def add_magnet(uri):
@@ -1,6 +1,6 @@
from core.network.libtorrent_int import update_settings
from core.utils.data.state import state
from core.utils.general.logs import consoleLog
from core.utils.logging.logs import consoleLog
from .config import create_config
@@ -7,6 +7,7 @@ class Download:
title: str
url: str
magnet_uri: str
path: str
completed: bool
@dataclass
@@ -1,4 +1,5 @@
from PySide6.QtCore import QObject, Signal
from PySide6.QtWidgets import QTableWidget
from typing import Optional, Any, List, Dict
from pathlib import Path
@@ -8,8 +9,8 @@ class AppState(QObject):
def __init__(self):
super().__init__()
self.posts: List[Dict[str, Any]] = []
self.post_titles: List[str] = []
self.post_urls: List[str] = []
self.post_titles: Optional[List] = None
self.post_urls: Optional[List] = None
self.post_author: List[str] = []
self.post_seeders: List[str] = []
self.post_leechers: List[str] = []
@@ -19,6 +20,7 @@ class AppState(QObject):
self.debug: bool = False
self.autoresume: bool = True
self.tracker: str = "rutracker"
self.tracker_list: dict[str, QTableWidget] = {}
self.api_url: str = "https://api.michijackson.xyz"
self.download_path: str = str(Path.home() / "Downloads")
self.up_speed_limit: int = 0
@@ -27,11 +29,11 @@ class AppState(QObject):
self.max_downloads: int = 10
self.settings_path: str = ""
self.dl_session: Any = None
self.active_downloads: List[str] = {}
self.active_downloads: Dict = {}
self.window_transparency: bool = False
self.interfaces: List = []
self.active_interfaces: List = []
self.bound_interface: str = None
self.bound_interface: Any = None
@property
def image_path(self) -> str:
@@ -1,7 +1,7 @@
import requests
from bs4 import BeautifulSoup
from core.utils.data.state import state
from core.utils.general.logs import consoleLog
from core.utils.logging.logs import consoleLog
from core.utils.network.jsonhandler import format_data
@@ -1,6 +1,8 @@
from core.utils.general.logs import consoleLog
from core.utils.logging.logs import consoleLog, remove_download_log
from core.utils.network.download import run_download_direct
import os
def split_data(data):
count = data.count
@@ -16,6 +18,11 @@ def check_completed(downloads, resume):
run_download_direct(download.magnet_uri)
consoleLog(f"Resuming {download.title}")
def check_downloads(downloads):
for download in downloads:
if os.path.exists(download.path):
consoleLog(f"Existing Download: {download.title}")
else:
consoleLog(f"Inexistent Download: {download.title}")
remove_download_log(download.magnet_uri)
@@ -5,6 +5,7 @@ from datetime import datetime
import json
import os
import re
import time
_log_buffer = []
@@ -21,7 +22,21 @@ def add_download_log(title, url, magnet_uri, completed) -> DownloadList:
downloads = []
else:
downloads = []
magnetdl = state.active_downloads.get(magnet_uri)
if magnetdl:
status = magnetdl.status()
timeout = 60
start = time.time()
while not status.has_metadata and (time.time() - start) < timeout:
time.sleep(0.5)
status = magnetdl.status()
torrent_name = status.name
save_path = status.save_path
consoleLog(f"Torrent name (has_metadata={status.has_metadata}): {torrent_name}")
path = os.path.join(save_path, torrent_name)
else:
path = os.path.join(state.download_path, title)
if any(d.magnet_uri == magnet_uri or d.url == url for d in downloads):
if magnet_uri in state.active_downloads:
@@ -37,7 +52,8 @@ def add_download_log(title, url, magnet_uri, completed) -> DownloadList:
title=title,
url=url,
magnet_uri=magnet_uri,
completed=completed,
path=path,
completed=completed
))
consoleLog(f"Added {title} to Log File")
@@ -61,10 +77,12 @@ def remove_download_log(magnet_uri) -> DownloadList:
else:
downloads = []
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))
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 {magnet_link} from Log File")
consoleLog(f"Removed {title} from Log File")
download_list = DownloadList(data=downloads, count=len(downloads))
with open(downloads_file, "w") as file:
@@ -1,10 +1,11 @@
from core.utils.data.state import state
from core.utils.general.logs import consoleLog
from core.utils.logging.logs import consoleLog
from core.utils.data.tracker import get_item_url
from core.utils.data.tracker import get_magnet_link
from core.network.libtorrent_wrapper import add_download
from core.utils.general.wrappers import run_thread
from core.utils.general.logs import add_download_log
from core.utils.logging.logs import add_download_log
import time
import threading
@@ -21,8 +22,8 @@ def run_download(item, posts, post_titles):
consoleLog(f"Selected URL: {post_url}")
magnet_uri = get_magnet_link(post_url)
add_download_log(item, post_url, magnet_uri, False)
add_download(magnet_uri)
add_download_log(item, post_url, magnet_uri, False)
def run_download_direct(magnet_uri):
add_download(magnet_uri)
@@ -32,4 +32,4 @@ def format_data_m0nkrus(data):
# https://open.spotify.com/track/70vjXI7oBou5buTtgiJeRW?si=25640cc4243c4bf3
# https://open.spotify.com/track/0F0fMcIdL2FmcC9eYqwazX
@@ -1,6 +1,6 @@
import requests
from core.utils.data.state import state
from core.utils.general.logs import consoleLog
from core.utils.logging.logs import consoleLog
def check_for_updates():
url = f"https://api.github.com/repos/KeksPirates/SoftwareManager/releases"
+5 -5
View File
@@ -1,13 +1,12 @@
from core.interface.gui import MainWindow
from core.utils.data.state import state
from core.utils.general.logs import consoleLog
from core.utils.logging.logs import consoleLog
from core.network.libtorrent_misc import send_notification, update_log
from core.utils.general.logs import get_download_logs
from core.utils.logging.logs import get_download_logs
from core.utils.general.shutdown import closehelper, shutdown_event
from core.utils.general.wrappers import run_thread
from core.utils.general.loghandler import split_data, check_completed
from core.utils.logging.loghandler import split_data, check_completed, check_downloads
from core.network.interface import list_interfaces, init_interfaces
from core.network.libtorrent_int import update_bound_interface
from core.utils.config.config import read_config
from PySide6 import QtWidgets
from PySide6.QtCore import Qt
@@ -33,7 +32,7 @@ def run_gui():
widget.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
qdarktheme.setup_theme("auto", custom_colors={"background": "#00000000"})
from core.utils.general.logs import set_main_window
from core.utils.logging.logs import set_main_window
set_main_window(widget)
widget.show()
sys.exit(app.exec())
@@ -61,6 +60,7 @@ if __name__ == "__main__":
consoleLog("Started Thread: update_log")
run_thread(threading.Thread(target=check_completed, args=(downloads, state.autoresume)))
consoleLog("Started Thread: check_completed")
check_downloads(downloads)
elapsed = time.perf_counter() - start_time
consoleLog(f"Initialization completed in {elapsed:.2f}s. Launching GUI")
run_gui()