Merge pull request #51 from KeksPirates/refactor/improve-code-quality

Refactor/improve code quality
This commit is contained in:
shayaa
2026-03-21 02:50:07 +01:00
committed by GitHub
59 changed files with 524 additions and 606 deletions
+1 -1
View File
@@ -106,7 +106,7 @@ jobs:
run: | run: |
python -c " python -c "
from PIL import Image from PIL import Image
img = Image.open('src/core/interface/assets/logo.png') img = Image.open('src/interface/assets/logo.png')
img.save('app_icon.ico', format='ICO', sizes=[(256,256),(128,128),(64,64),(48,48),(32,32),(16,16)]) img.save('app_icon.ico', format='ICO', sizes=[(256,256),(128,128),(64,64),(48,48),(32,32),(16,16)])
print('Icon converted successfully') print('Icon converted successfully')
" "
View File
View File
-87
View File
@@ -1,87 +0,0 @@
from core.utils.data.tracker import get_magnet_link
from core.utils.data.state import state
from bs4 import BeautifulSoup
from typing import Dict
import requests
import time
cache = {
"data": [],
"last_fetched": 0
}
cache_expiry = 300
def _get_telegram_posts():
post = 100
max_posts = 250
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, timeout=10)
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 post_txt is None or post_txt.b is None:
continue
title = post_txt.b.text
link = bubble.find("a", href=lambda x: x and x.startswith("https://uztracker.net"))
if link:
post_url = link["href"]
if post_url not in added:
added.add(post_url)
posts.append(dict(
title=title,
author="m0nkrus",
id=len(posts) + 1,
url=post_url
))
post += 22
posts.reverse()
cache["data"] = posts
cache["last_fetched"] = current_time
return(posts)
def scrape_m0nkrus(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
def get_magnet(post: Dict):
return get_magnet_link(post["url"])
Metadata = {
"name" : "m0nkrus",
"headers" : ["Post Title", "Author"],
"scrapeFunc" : scrape_m0nkrus,
"linkFunc" : get_magnet,
"isMagnet" : True,
}
def init_m0nkrus():
state.trackers.update({Metadata["name"] : Metadata})
-52
View File
@@ -1,52 +0,0 @@
from core.utils.network.jsonhandler import split_data, format_data
from core.utils.data.tracker import get_magnet_link
from core.utils.logging.logs import consoleLog
from core.utils.data.state import state
from typing import Dict
import requests
def scrape_rutracker(query):
search = requests.get(f"{state.api_url}/search?q={query}", timeout=15)
consoleLog("Sent request to server")
if search:
_, data, _, success, cached = split_data(search.text)
if cached:
consoleLog("Server response cached")
if success:
sorted_data = []
for entry in data:
sorted_data.append(
{
"title" : entry["title"],
"author" : entry["author"],
"seeders" : entry["seeders"],
"leechers" : entry["leechers"],
"url" : entry["url"],
"id" : entry["id"],
}
)
return sorted_data
else:
consoleLog("Scraping failed server-side, unable to fetch posts from rutracker")
return []
else:
consoleLog("No response from server, returning nothing")
return []
def get_magnet(post: Dict):
_, post_links, _, _, _ = format_data([post])
return get_magnet_link(post_links[0])
Metadata = {
"name" : "rutracker",
"headers" : ["Post Title", "Author", "Seeders", "Leechers"],
"scrapeFunc" : scrape_rutracker,
"linkFunc" : get_magnet,
"isMagnet" : True,
}
def init_rutracker():
state.trackers.update({Metadata["name"] : Metadata})
-122
View File
@@ -1,122 +0,0 @@
from core.data.scrapers.provider.buzzheavier import scrape_buzzheavier
from core.data.scrapers.provider.gofile import scrape_gofile
from core.utils.logging.logs import consoleLog
from core.utils.data.state import state
from bs4 import BeautifulSoup
from typing import Dict
import webbrowser
import requests
import time
import re
cache = {
"data": [],
"last_fetched": 0
}
cache_expiry = 300
def get_Metadata():
return Metadata
def scrape_steamrip_links():
current_time = time.time()
if cache["data"] != [] and (current_time - cache["last_fetched"] < cache_expiry):
return cache["data"]
url = f"https://steamrip.com/games-list-page/"
response = requests.get(url)
text = response.text
soup = BeautifulSoup(text, "html.parser")
games = soup.find_all("li", class_="az-list-item")
if len(games) == 0:
return []
links = []
names = []
for gamehtml in games:
link = gamehtml.find("a", href=lambda x: x and x.startswith("/"))
links += re.findall(r'(?<=href=")[^"]*', link.__str__())
name = gamehtml.find("a", href=lambda x: x and x.startswith("/"))
names += re.findall(r'(?<=\/">)[^<]*', name.__str__())
# construct the list[dict[str,str]]
ret = []
for i in range(len(names)):
ret.append({"title" : names[i], "url" : links[i]})
return ret
def scrape_steamrip_game_downloads(gamelink):
url = "https://steamrip.com" + gamelink
response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser")
download_link_elements = soup.find_all("a",class_="shortc-button")
download_links = ["buzzheavier", "gofile"]
for download_link in download_link_elements:
pure = download_link.attrs.get("href")
if pure[2] == "b":
download_links[0] = "https:" + pure
if pure[2] == "g":
download_links[1] = "https:" + pure
ret = []
for link in download_links:
if len(link) != 1:
ret.append(link)
cache["data"] = ret
return ret
def get_download_link(post: Dict):
url = post["url"]
links = scrape_steamrip_game_downloads(url)
best = ""
for link in links:
if link[0] == "h":
best = link
break
if links.index(best) == 0:
link = scrape_buzzheavier(best)
return link
elif links.index(best) == 1:
link, headers = scrape_gofile(best)
return link, headers
else:
consoleLog("Unable to retrieve download link due to captcha, launching browser...")
webbrowser.open(best)
return None, None
def filter_steamrip(query: str):
games = scrape_steamrip_links()
filtered_games = [
game for game in games
if query.lower() in game["title"].lower()
]
return filtered_games
Metadata = {
"name" : "steamrip",
"headers" : ["Game"],
"scrapeFunc" : filter_steamrip,
"linkFunc" : get_download_link,
"isMagnet" : False,
}
def init_steamrip():
state.trackers.update({Metadata["name"] : Metadata})
-55
View File
@@ -1,55 +0,0 @@
from core.utils.data.tracker import get_magnet_link
from core.utils.logging.logs import consoleLog
from core.utils.data.state import state
from urllib.parse import urljoin
from bs4 import BeautifulSoup
from typing import Dict
import requests
def scrape_uztracker(query):
base_url="https://uztracker.net/"
search_url = f"{base_url.rstrip('/')}/tracker.php?nm={query}"
consoleLog(search_url)
posts = []
try:
response = requests.get(search_url)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser')
links = soup.find_all('tr', class_="tCenter hl-tr", id=lambda x: x and x.startswith('tor_'))
for link in links:
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'])
title = theme_link.b.text
author_link = link.find('a', class_="med")
author = author_link.text.strip() if author_link else "Unknown"
posts.append(dict(
title=title,
author=author,
url=url,
))
return posts
except requests.RequestException as e:
consoleLog(f"Failed to fetch {search_url}: {e}")
return None
def get_magnet(post: Dict):
return get_magnet_link(post["url"])
Metadata = {
"name" : "uztracker",
"headers" : ["Post Title", "Author"],
"scrapeFunc" : scrape_uztracker,
"linkFunc" : get_magnet,
"isMagnet" : True,
}
def init_uztracker():
state.trackers.update({Metadata["name"] : Metadata})
-26
View File
@@ -1,26 +0,0 @@
from core.data.scrapers.rutracker import init_rutracker
from core.data.scrapers.uztracker import init_uztracker
from core.data.scrapers.steamrip import init_steamrip
from core.data.scrapers.monkrus import init_m0nkrus
from core.utils.logging.logs import consoleLog
from core.utils.data.state import state
init_rutracker()
init_uztracker()
init_m0nkrus()
init_steamrip()
def return_pressed(self):
self.show_empty_results(False)
search_text = self.searchbar.text()
if search_text == "":
consoleLog("Error: Can't search for nothing")
return
consoleLog(f"User searched for: {search_text}")
tracker = state.trackers[state.currenttracker]
scrapefunc = tracker["scrapeFunc"]
state.posts = scrapefunc(search_text)
from core.interface.gui import MainWindow
MainWindow._instance.search_results_signal.emit(tracker["headers"])
-56
View File
@@ -1,56 +0,0 @@
from PySide6.QtWidgets import QWidget, QVBoxLayout
def create_tab(title, searchbar, software_list, tabs, dlbutton, layout2):
tab = QWidget()
layout = QVBoxLayout()
if searchbar:
layout.addWidget(searchbar)
if layout2:
layout.addLayout(layout2)
else:
layout.addWidget(software_list)
if dlbutton:
layout.addWidget(dlbutton)
tab.setLayout(layout)
tabs.addTab(tab, title)
return tab
#################
# SETTINGS TABS #
#################
def general_tab(title, autoresume, update_checkbox, transparent_window, tabs):
tab = QWidget()
layout = QVBoxLayout()
layout.addWidget(autoresume)
layout.addWidget(update_checkbox)
layout.addWidget(transparent_window)
layout.addStretch()
tab.setLayout(layout)
tabs.addTab(tab, title)
return tab
def paths_tab(title, download_path, image_path, tabs):
tab = QWidget()
layout = QVBoxLayout()
layout.addWidget(download_path)
layout.addWidget(image_path)
layout.addStretch()
tab.setLayout(layout)
tabs.addTab(tab, title)
return tab
def network_tab(title, network_interface, max_connections, max_downloads, up_speed_limit, down_speed_limit, api_url, tabs):
tab = QWidget()
layout = QVBoxLayout()
layout.addWidget(network_interface)
layout.addWidget(max_connections)
layout.addWidget(max_downloads)
layout.addWidget(up_speed_limit)
layout.addWidget(down_speed_limit)
layout.addWidget(api_url)
layout.addStretch()
tab.setLayout(layout)
tabs.addTab(tab, title)
return tab
View File
View File
@@ -1,4 +1,4 @@
from core.utils.logging.logs import consoleLog from utils.logging.logs import consoleLog
import requests import requests
import re import re
+12
View File
@@ -0,0 +1,12 @@
from data.sources.rutracker import RutrackerScraper
from data.sources.uztracker import UztrackerScraper
from data.sources.steamrip import SteamripScraper
from data.sources.monkrus import MonkrusScraper
# Register Scrapers here
SCRAPERS = [
RutrackerScraper(),
SteamripScraper(),
MonkrusScraper(),
UztrackerScraper(),
]
+78
View File
@@ -0,0 +1,78 @@
from utils.data.tracker import get_magnet_link
from bs4 import BeautifulSoup
from typing import Dict
import requests
import time
class MonkrusScraper:
name = "m0nkrus"
headers = ["Post Title", "Author"]
is_magnet = True
def __init__(self):
self.cache = { "data": [], "last_fetched": 0 }
self.cache_expiry = 300
def _get_telegram_posts(self):
post = 100
max_posts = 250
posts = []
added = set()
current_time = time.time()
if self.cache["data"] and (current_time - self.cache["last_fetched"] < self.cache_expiry):
return self.cache["data"]
while post <= max_posts:
url = f"https://t.me/s/real_monkrus/{post}"
response = requests.get(url, timeout=10)
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 post_txt is None or post_txt.b is None:
continue
title = post_txt.b.text
link = bubble.find("a", href=lambda x: x and x.startswith("https://uztracker.net"))
if link:
post_url = link["href"]
if post_url not in added:
added.add(post_url)
posts.append(dict(
title=title,
author="m0nkrus",
id=len(posts) + 1,
url=post_url
))
post += 22
posts.reverse()
self.cache["data"] = posts
self.cache["last_fetched"] = current_time
return(posts)
def search(self, query):
posts = self._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
def get_download_link(self, post: Dict):
return get_magnet_link(post["url"])
+46
View File
@@ -0,0 +1,46 @@
from utils.network.jsonhandler import split_data, format_data
from utils.data.tracker import get_magnet_link
from utils.logging.logs import consoleLog
from utils.data.state import state
from typing import Dict
import requests
class RutrackerScraper:
name = "rutracker"
headers = ["Post Title", "Author", "Seeders", "Leechers"]
is_magnet = True
def search(self, query):
search = requests.get(f"{state.api_url}/search?q={query}", timeout=15)
consoleLog("Sent request to server")
if search:
_, data, _, success, cached = split_data(search.text)
if cached:
consoleLog("Server response cached")
if success:
sorted_data = []
for entry in data:
sorted_data.append(
{
"title" : entry["title"],
"author" : entry["author"],
"seeders" : entry["seeders"],
"leechers" : entry["leechers"],
"url" : entry["url"],
"id" : entry["id"],
}
)
return sorted_data
else:
consoleLog("Scraping failed server-side, unable to fetch posts from rutracker")
return []
else:
consoleLog("No response from server, returning nothing")
return []
def get_download_link(self, post: Dict):
_, post_links, _, _, _ = format_data([post])
return get_magnet_link(post_links[0])
+107
View File
@@ -0,0 +1,107 @@
from data.hosts.buzzheavier import scrape_buzzheavier
from data.hosts.gofile import scrape_gofile
from utils.logging.logs import consoleLog
from bs4 import BeautifulSoup
from typing import Dict
import webbrowser
import requests
import time
import re
class SteamripScraper:
name = "steamrip"
headers = ["Game"]
is_magnet = False
def __init__(self):
self.cache = { "data": [], "last_fetched": 0 }
self.cache_expiry = 300
def scrape_steamrip_links(self):
current_time = time.time()
if self.cache["data"] != [] and (current_time - self.cache["last_fetched"] < self.cache_expiry):
return self.cache["data"]
url = f"https://steamrip.com/games-list-page/"
response = requests.get(url)
text = response.text
soup = BeautifulSoup(text, "html.parser")
games = soup.find_all("li", class_="az-list-item")
if len(games) == 0:
return []
links = []
names = []
for gamehtml in games:
link = gamehtml.find("a", href=lambda x: x and x.startswith("/"))
links += re.findall(r'(?<=href=")[^"]*', link.__str__())
name = gamehtml.find("a", href=lambda x: x and x.startswith("/"))
names += re.findall(r'(?<=\/">)[^<]*', name.__str__())
# construct the list[dict[str,str]]
ret = []
for i in range(len(names)):
ret.append({"title" : names[i], "url" : links[i]})
return ret
def scrape_steamrip_game_downloads(self, gamelink):
url = "https://steamrip.com" + gamelink
response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser")
download_link_elements = soup.find_all("a",class_="shortc-button")
download_links = ["buzzheavier", "gofile"]
for download_link in download_link_elements:
pure = download_link.attrs.get("href")
if pure[2] == "b":
download_links[0] = "https:" + pure
if pure[2] == "g":
download_links[1] = "https:" + pure
ret = []
for link in download_links:
if len(link) != 1:
ret.append(link)
self.cache["data"] = ret
return ret
def get_download_link(self, post: Dict):
url = post["url"]
links = self.scrape_steamrip_game_downloads(url)
best = ""
for link in links:
if link[0] == "h":
best = link
break
if links.index(best) == 0:
link = scrape_buzzheavier(best)
return link
elif links.index(best) == 1:
link, headers = scrape_gofile(best)
return link, headers
else:
consoleLog("Unable to retrieve download link due to captcha, launching browser...")
webbrowser.open(best)
return None, None
def search(self, query: str):
games = self.scrape_steamrip_links()
filtered_games = [
game for game in games
if query.lower() in game["title"].lower()
]
return filtered_games
+50
View File
@@ -0,0 +1,50 @@
from utils.data.tracker import get_magnet_link
from utils.logging.logs import consoleLog
from utils.data.state import state
from urllib.parse import urljoin
from bs4 import BeautifulSoup
from typing import Dict
import requests
class UztrackerScraper:
name = "uztracker"
headers = ["Post Title", "Author"]
is_magnet = True
def search(self, query):
base_url="https://uztracker.net/"
search_url = f"{base_url.rstrip('/')}/tracker.php?nm={query}"
consoleLog(search_url)
posts = []
try:
response = requests.get(search_url)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser')
links = soup.find_all('tr', class_="tCenter hl-tr", id=lambda x: x and x.startswith('tor_'))
for link in links:
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'])
title = theme_link.b.text
author_link = link.find('a', class_="med")
author = author_link.text.strip() if author_link else "Unknown"
posts.append(dict(
title=title,
author=author,
url=url,
))
return posts
except requests.RequestException as e:
consoleLog(f"Failed to fetch {search_url}: {e}")
return None
def get_download_link(self, post: Dict):
return get_magnet_link(post["url"])

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

@@ -1,7 +1,7 @@
from core.utils.logging.logs import consoleLog, remove_download_log from utils.logging.logs import consoleLog, remove_download_log
from PySide6.QtWidgets import QMessageBox from PySide6.QtWidgets import QMessageBox
from core.utils.data.state import state
from PySide6.QtCore import Qt, QPoint from PySide6.QtCore import Qt, QPoint
from utils.data.state import state
from PySide6 import QtWidgets from PySide6 import QtWidgets
import subprocess import subprocess
import platform import platform
@@ -1,9 +1,9 @@
from core.interface.dialogs.pauseresumedelegate import PauseResumeDelegate from interface.dialogs.pauseresumedelegate import PauseResumeDelegate
from core.interface.dialogs.hoverrowdelegate import HoverRowDelegate from interface.dialogs.hoverrowdelegate import HoverRowDelegate
from core.interface.dialogs.downloadmodel import DownloadModel from interface.dialogs.downloadmodel import DownloadModel
from core.interface.dialogs.theme import _table_stylesheet from interface.dialogs.theme import _table_stylesheet
from PySide6.QtWidgets import QTableView, QHeaderView from PySide6.QtWidgets import QTableView, QHeaderView
from core.utils.logging.logs import consoleLog from utils.logging.logs import consoleLog
from PySide6 import QtWidgets from PySide6 import QtWidgets
from PySide6.QtCore import Qt from PySide6.QtCore import Qt
@@ -1,6 +1,6 @@
from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt
from core.utils.logging.logs import consoleLog from utils.logging.logs import consoleLog
from core.utils.data.state import state from utils.data.state import state
import libtorrent as lt import libtorrent as lt
import subprocess import subprocess
import platform import platform
@@ -1,4 +1,4 @@
from core.interface.dialogs.theme import _theme_colors from interface.dialogs.theme import _theme_colors
from PySide6.QtWidgets import QStyledItemDelegate from PySide6.QtWidgets import QStyledItemDelegate
from PySide6.QtCore import Qt from PySide6.QtCore import Qt
@@ -1,5 +1,5 @@
from core.utils.logging.logs import consoleLog from utils.logging.logs import consoleLog
from core.utils.data.state import state from utils.data.state import state
from PySide6.QtCore import QEvent from PySide6.QtCore import QEvent
def eventFilter(self, obj, event): def eventFilter(self, obj, event):
@@ -1,11 +1,11 @@
from core.interface.dialogs.theme import _theme_colors from interface.dialogs.theme import _theme_colors
from PySide6.QtWidgets import QStyledItemDelegate from PySide6.QtWidgets import QStyledItemDelegate
from PySide6 import QtWidgets from PySide6 import QtWidgets
class HoverRowDelegate(QStyledItemDelegate): class HoverRowDelegate(QStyledItemDelegate):
def paint(self, painter, option, index): def paint(self, painter, option, index):
from core.interface.gui import MainWindow from interface.gui import MainWindow
opt = QtWidgets.QStyleOptionViewItem(option) opt = QtWidgets.QStyleOptionViewItem(option)
option.state &= ~QtWidgets.QStyle.StateFlag.State_HasFocus option.state &= ~QtWidgets.QStyle.StateFlag.State_HasFocus
@@ -1,6 +1,6 @@
from PySide6.QtGui import QImage, QPixmap from PySide6.QtGui import QImage, QPixmap
from core.utils.data.state import state
from PySide6.QtWidgets import QLabel from PySide6.QtWidgets import QLabel
from utils.data.state import state
from PySide6.QtCore import Qt from PySide6.QtCore import Qt
import os import os
@@ -1,8 +1,8 @@
from core.interface.dialogs.theme import _theme_colors, SVG_PLAY, SVG_PAUSE, SVG_FOLDER from interface.dialogs.theme import _theme_colors, SVG_PLAY, SVG_PAUSE, SVG_FOLDER
from PySide6.QtWidgets import QStyledItemDelegate, QWidget, QHBoxLayout from PySide6.QtWidgets import QStyledItemDelegate, QWidget, QHBoxLayout
from core.interface.utils.svghelper import svg_icon from interface.utils.svghelper import svg_icon
from PySide6.QtCore import Qt, QSize, Signal from PySide6.QtCore import Qt, QSize, Signal
from core.utils.data.state import state from utils.data.state import state
from PySide6 import QtWidgets from PySide6 import QtWidgets
import libtorrent as lt import libtorrent as lt
@@ -10,7 +10,7 @@ class PauseResumeDelegate(QStyledItemDelegate):
clicked = Signal(int) clicked = Signal(int)
def paint(self, painter, option, index): def paint(self, painter, option, index):
from core.interface.gui import MainWindow from interface.gui import MainWindow
opt = QtWidgets.QStyleOptionViewItem(option) opt = QtWidgets.QStyleOptionViewItem(option)
option.state &= ~QtWidgets.QStyle.StateFlag.State_HasFocus option.state &= ~QtWidgets.QStyle.StateFlag.State_HasFocus
@@ -1,11 +1,9 @@
from core.interface.utils.tabhelper import general_tab from interface.utils.tabhelper import create_tab
from core.interface.utils.tabhelper import network_tab from utils.config.settings import save_settings
from core.utils.config.settings import save_settings from interface.utils.svghelper import svg_icon
from core.interface.utils.tabhelper import paths_tab from utils.logging.logs import consoleLog
from core.interface.utils.svghelper import svg_icon
from core.utils.logging.logs import consoleLog
from core.utils.data.state import state
from PySide6.QtCore import Qt, QSize from PySide6.QtCore import Qt, QSize
from utils.data.state import state
from PySide6 import QtWidgets from PySide6 import QtWidgets
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QLineEdit, QLineEdit,
@@ -19,7 +17,6 @@ from PySide6.QtWidgets import (
QCheckBox, QCheckBox,
QFileDialog, QFileDialog,
QComboBox, QComboBox,
QTabWidget,
) )
import platform import platform
@@ -46,7 +43,7 @@ def settings_dialog(self):
update_checkbox_container = QWidget() update_checkbox_container = QWidget()
update_checkbox_layout = QHBoxLayout() update_checkbox_layout = QHBoxLayout()
# ignore updates checkbox # Ignore updates checkbox
if platform.system() == "Windows": if platform.system() == "Windows":
update_checkbox = QCheckBox() update_checkbox = QCheckBox()
@@ -57,7 +54,7 @@ def settings_dialog(self):
update_checkbox.toggled.connect(lambda checked: setattr(state, 'ignore_updates', checked)) update_checkbox.toggled.connect(lambda checked: setattr(state, 'ignore_updates', checked))
update_checkbox_layout.addWidget(update_checkbox) update_checkbox_layout.addWidget(update_checkbox)
# auto-resume downloads checkbox # Auto-resume downloads checkbox
autoresume_container = QWidget() autoresume_container = QWidget()
autoresume_layout = QHBoxLayout() autoresume_layout = QHBoxLayout()
@@ -71,7 +68,7 @@ def settings_dialog(self):
autoresume_layout.addWidget(autoresume_checkbox) autoresume_layout.addWidget(autoresume_checkbox)
# transparent window checkbox # Transparent window checkbox
transparent_window_container = QWidget() transparent_window_container = QWidget()
transparent_window_layout = QHBoxLayout() transparent_window_layout = QHBoxLayout()
@@ -84,10 +81,8 @@ def settings_dialog(self):
transparent_window_checkbox.toggled.connect(lambda checked: setattr(state, 'window_transparency', checked)) transparent_window_checkbox.toggled.connect(lambda checked: setattr(state, 'window_transparency', checked))
transparent_window_layout.addWidget(transparent_window_checkbox) transparent_window_layout.addWidget(transparent_window_checkbox)
##################
# SERVER SETTING #
##################
# API Server Setting
api_url_container = QWidget() api_url_container = QWidget()
api_url_layout = QHBoxLayout() api_url_layout = QHBoxLayout()
@@ -98,10 +93,8 @@ def settings_dialog(self):
api_url_container.setLayout(api_url_layout) api_url_container.setLayout(api_url_layout)
api_url.setText(state.api_url) api_url.setText(state.api_url)
#################
# DOWNLOAD PATH #
#################
# Download Path
download_path_container = QWidget() download_path_container = QWidget()
download_path_layout = QHBoxLayout() download_path_layout = QHBoxLayout()
@@ -133,10 +126,8 @@ def settings_dialog(self):
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)
###############
# IMAGE PATH #
###############
# Image Path
image_path_container = QWidget() image_path_container = QWidget()
image_path_layout = QHBoxLayout() image_path_layout = QHBoxLayout()
@@ -167,10 +158,8 @@ def settings_dialog(self):
image_path_layout.addWidget(browse_button) image_path_layout.addWidget(browse_button)
browse_button.clicked.connect(browse_image_path) browse_button.clicked.connect(browse_image_path)
##################
# SPEED LIMITING #
##################
# Speed Limiting
down_speed_limit_container = QWidget() down_speed_limit_container = QWidget()
down_speed_limit_layout = QHBoxLayout() down_speed_limit_layout = QHBoxLayout()
@@ -197,10 +186,8 @@ def settings_dialog(self):
up_speed_limit.setFixedWidth(180) up_speed_limit.setFixedWidth(180)
up_speed_limit.setFixedHeight(30) up_speed_limit.setFixedHeight(30)
######################
# CONNECTION CONFIGS # # Connection Configs
######################
max_connections_container = QWidget() max_connections_container = QWidget()
max_connections_layout = QHBoxLayout() max_connections_layout = QHBoxLayout()
@@ -214,10 +201,8 @@ def settings_dialog(self):
max_connections.setFixedWidth(180) max_connections.setFixedWidth(180)
max_connections.setFixedHeight(30) max_connections.setFixedHeight(30)
####################
# DOWNLOAD CONFIGS #
####################
# Download Configs
max_downloads_container = QWidget() max_downloads_container = QWidget()
max_downloads_layout = QHBoxLayout() max_downloads_layout = QHBoxLayout()
@@ -231,10 +216,8 @@ def settings_dialog(self):
max_downloads.setFixedWidth(180) max_downloads.setFixedWidth(180)
max_downloads.setFixedHeight(30) max_downloads.setFixedHeight(30)
#####################
# INTERFACE BINDING #
#####################
# Interface Binding
interface_container = QWidget() interface_container = QWidget()
interface_layout = QHBoxLayout() interface_layout = QHBoxLayout()
@@ -257,10 +240,7 @@ def settings_dialog(self):
interface_container.setLayout(interface_layout) interface_container.setLayout(interface_layout)
############### # Save / Cancel buttons
# SAVE/CANCEL #
###############
layout = QHBoxLayout() layout = QHBoxLayout()
save_btn = QPushButton("Save") save_btn = QPushButton("Save")
@@ -281,12 +261,12 @@ def settings_dialog(self):
layout.addWidget(cancel_btn) layout.addWidget(cancel_btn)
layout.addWidget(save_btn) layout.addWidget(save_btn)
self.tabs = QTabWidget() tabs = QtWidgets.QTabWidget()
self.tab1 = general_tab("General", autoresume_container, update_checkbox_container, transparent_window_container, self.tabs) create_tab("General", [autoresume_container, update_checkbox_container, transparent_window_container], tabs=tabs, stretch=True)
self.tab2 = paths_tab("Paths", download_path_container, image_path_container, self.tabs) create_tab("Paths", [download_path_container, image_path_container], tabs=tabs, stretch=True)
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) create_tab("Network", [interface_container, max_connections_container, max_downloads_container, up_speed_limit_container, down_speed_limit_container, api_url_container], tabs=tabs, stretch=True)
dialog_layout.addWidget(self.tabs) dialog_layout.addWidget(tabs)
dialog_layout.addLayout(layout) dialog_layout.addLayout(layout)
dialog.exec() dialog.exec()
@@ -1,4 +1,4 @@
from core.interface.dialogs.theme import _theme_colors from interface.dialogs.theme import _theme_colors
from PySide6.QtWidgets import QStyledItemDelegate from PySide6.QtWidgets import QStyledItemDelegate
class TrackerHoverDelegate(QStyledItemDelegate): class TrackerHoverDelegate(QStyledItemDelegate):
@@ -1,4 +1,4 @@
from core.interface.dialogs.theme import _table_stylesheet from interface.dialogs.theme import _table_stylesheet
from PySide6.QtWidgets import QTableWidget from PySide6.QtWidgets import QTableWidget
from PySide6 import QtWidgets from PySide6 import QtWidgets
from PySide6.QtCore import Qt from PySide6.QtCore import Qt
@@ -1,8 +1,8 @@
from core.utils.network.update_checker import get_updates from utils.network.update_checker import get_updates
from core.utils.network.updater import download_update
from PySide6.QtWidgets import QDialog, QMessageBox from PySide6.QtWidgets import QDialog, QMessageBox
from core.utils.logging.logs import consoleLog from utils.network.updater import download_update
from core.utils.data.state import state from utils.logging.logs import consoleLog
from utils.data.state import state
from pathlib import Path from pathlib import Path
import platform import platform
import json import json
@@ -24,7 +24,7 @@ def _get_build_info_path() -> (Path | None):
def get_version() -> None: def get_version() -> None:
# Get build info filepath # Get build info filepath
build_info_path = _get_build_info_path() build_info_path = _get_build_info_path()
if os.path.exists(build_info_path): if build_info_path and os.path.exists(build_info_path):
with open(build_info_path, "r") as f: with open(build_info_path, "r") as f:
build_info = json.load(f) build_info = json.load(f)
state.version = build_info.get("version") state.version = build_info.get("version")
@@ -46,3 +46,4 @@ class UpdateDialog(QDialog):
response = msg.exec_() response = msg.exec_()
if response == QMessageBox.StandardButton.Ok: if response == QMessageBox.StandardButton.Ok:
download_update(assets) download_update(assets)
@@ -1,25 +1,25 @@
from core.interface.dialogs.trackerhoverdelegate import TrackerHoverDelegate from interface.dialogs.trackerhoverdelegate import TrackerHoverDelegate
from core.interface.dialogs.elideditemdelegate import ElidedItemDelegate from interface.dialogs.elideditemdelegate import ElidedItemDelegate
from core.interface.dialogs.trackertable import _create_tracker_table from interface.dialogs.trackertable import _create_tracker_table
from core.interface.dialogs.downloadlist import _create_download_list from interface.dialogs.downloadlist import _create_download_list
from core.interface.assets.base64_icons import settings_white_base64 from interface.assets.base64_icons import settings_white_base64
from core.interface.assets.base64_icons import settings_black_base64 from interface.assets.base64_icons import settings_black_base64
from core.interface.dialogs.downloadlist import download_list_update from interface.dialogs.downloadlist import download_list_update
from core.interface.dialogs.update import get_version, UpdateDialog from interface.dialogs.update import get_version, UpdateDialog
from core.utils.logging.logs import consoleLog, flush_log_buffer from utils.logging.logs import consoleLog, flush_log_buffer
from core.interface.dialogs.downloadmodel import DownloadModel from interface.dialogs.downloadmodel import DownloadModel
from core.interface.utils.searchhelper import return_pressed from interface.dialogs.settings import settings_dialog
from core.interface.dialogs.settings import settings_dialog from interface.assets.base64_icons import logo_base64
from core.interface.assets.base64_icons import logo_base64 from interface.dialogs.eventfilter import eventFilter
from core.interface.dialogs.eventfilter import eventFilter from utils.network.download import download_selected
from core.utils.network.download import download_selected from interface.utils.searchhelper import run_search
from core.interface.utils.tabhelper import create_tab from interface.utils.tabhelper import create_tab
from core.utils.general.shutdown import closehelper from utils.general.shutdown import closehelper
from core.utils.general.wrappers import run_thread from utils.general.wrappers import run_thread
from core.interface.dialogs.image import Image from interface.dialogs.image import Image
from core.utils.data.state import state from utils.data.state import state
import core.interface.dialogs.contextmenu import interface.dialogs.contextmenu
from PySide6 import QtWidgets from PySide6 import QtWidgets
from PySide6.QtCore import ( from PySide6.QtCore import (
@@ -86,7 +86,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
consoleLog(f"Could not set app ID: {e}") consoleLog(f"Could not set app ID: {e}")
# Get current version # Get current version
# core.interface.dialogs.update # interface.dialogs.update
get_version() get_version()
UpdateDialog() UpdateDialog()
@@ -137,7 +137,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
containerLayout.addWidget(self.searchbar) containerLayout.addWidget(self.searchbar)
containerLayout.addWidget(state.trackertable) containerLayout.addWidget(state.trackertable)
# core.interface.dialogs.hoverrowdelegate # interface.dialogs.hoverrowdelegate
self.download_model = DownloadModel() self.download_model = DownloadModel()
self.downloadList = _create_download_list(self) self.downloadList = _create_download_list(self)
self.downloadList.viewport().installEventFilter(self) self.downloadList.viewport().installEventFilter(self)
@@ -164,8 +164,8 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
self.horizontal_layout.addWidget(state.trackertable) self.horizontal_layout.addWidget(state.trackertable)
# Tabs # Tabs
self.tab1 = create_tab("Search", self.searchbar, state.trackertable, self.tabs, self.dlbutton, self.horizontal_layout) create_tab("Search", [self.searchbar, self.horizontal_layout, self.dlbutton], tabs=self.tabs, stretch=False)
self.tab2 = create_tab("Downloads", self.emptyDownload, self.downloadList, self.tabs, None, None) create_tab("Downloads", [self.emptyDownload, self.downloadList], tabs=self.tabs, stretch=False)
# Corner Widget (Settings Button, Tracker list, Tab button container) # Corner Widget (Settings Button, Tracker list, Tab button container)
self.corner_widget = QWidget() self.corner_widget = QWidget()
@@ -232,7 +232,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
self.statusbar.addPermanentWidget(self.version, Qt.AlignmentFlag.AlignLeft) self.statusbar.addPermanentWidget(self.version, Qt.AlignmentFlag.AlignLeft)
# (\u2193) Arrow down, (\u2191) Arrow up # (\u2193) Arrow down, (\u2191) Arrow up
self.speed_label = QLabel("\u2193 0.0 kB/s \u2191 0.0 kB/s") self.speed_label = QLabel(" 0.0 kB/s 0.0 kB/s")
self.speed_label.setStyleSheet("padding-right: 6px;") self.speed_label.setStyleSheet("padding-right: 6px;")
self.statusbar.addPermanentWidget(self.speed_label) self.statusbar.addPermanentWidget(self.speed_label)
@@ -246,7 +246,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
self.active_timer.timeout.connect(self.show_empty_downloads) self.active_timer.timeout.connect(self.show_empty_downloads)
self.active_timer.start(500) self.active_timer.start(500)
self._context_menu = core.interface.dialogs.contextmenu.ContextMenu(self) self._context_menu = interface.dialogs.contextmenu.ContextMenu(self)
self.image_overlay = Image(self) self.image_overlay = Image(self)
def _apply_default_headers(self, table): def _apply_default_headers(self, table):
@@ -292,10 +292,9 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
table.setItemDelegateForColumn(i, self._tracker_hover_delegate) table.setItemDelegateForColumn(i, self._tracker_hover_delegate)
def _start_search(self): def _start_search(self):
self.searchbar.setEnabled(False)
def _search_thread(): def _search_thread():
try: try:
return_pressed(self) run_search(self)
except Exception as e: except Exception as e:
consoleLog(f"Search error: {e}", True) consoleLog(f"Search error: {e}", True)
self.search_results_signal.emit([]) self.search_results_signal.emit([])
@@ -384,7 +383,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
def closeEvent(self, event: QCloseEvent): def closeEvent(self, event: QCloseEvent):
closehelper() closehelper()
event.accept() event.accept()
from core.utils.general.shutdown import force_exit from utils.general.shutdown import force_exit
force_exit() force_exit()
def _invalidate_hover_row(self, row): def _invalidate_hover_row(self, row):
+27
View File
@@ -0,0 +1,27 @@
from utils.logging.logs import consoleLog
from utils.data.state import state
from data.sources import SCRAPERS
for scraper in SCRAPERS:
state.trackers[scraper.name] = scraper
def run_search(self) -> None:
state.trackertable.setRowCount(0)
self.show_empty_results(False)
search_text = self.searchbar.text()
# Check for empty search
if search_text == "":
consoleLog("Error: Can't search for nothing")
return
state.posts = []
self.searchbar.setEnabled(False)
consoleLog(f"User searched for: {search_text}")
# Get current tracker and call its search function
scraper = state.trackers[state.currenttracker]
state.posts = scraper.search(search_text)
# Update GUI headers to match the current scraper
self.search_results_signal.emit(scraper.headers)
@@ -1,16 +1,13 @@
from PySide6 import QtWidgets
from PySide6.QtCore import Qt, QByteArray
from PySide6.QtGui import QIcon, QPixmap, QPainter from PySide6.QtGui import QIcon, QPixmap, QPainter
from PySide6.QtCore import Qt, QByteArray
from PySide6.QtSvg import QSvgRenderer from PySide6.QtSvg import QSvgRenderer
from PySide6 import QtWidgets
import darkdetect import darkdetect
# Check if darkmode is enabled
def _is_dark_mode(): def _is_dark_mode():
return darkdetect.isDark() return darkdetect.isDark()
def svg_icon(svg_str, size=20): def svg_icon(svg_str, size=20):
app = QtWidgets.QApplication.instance() app = QtWidgets.QApplication.instance()
if app: if app:
+22
View File
@@ -0,0 +1,22 @@
from PySide6.QtWidgets import QWidget, QVBoxLayout, QLayout
# Tab creation helper function
def create_tab(title, items, tabs, stretch):
tab = QWidget()
layout = QVBoxLayout()
for item in items:
if item is None:
continue
if isinstance(item, QLayout):
layout.addLayout(item)
else:
layout.addWidget(item)
if stretch:
layout.addStretch()
tab.setLayout(layout)
tabs.addTab(tab, title)
return tab
+13 -13
View File
@@ -1,16 +1,16 @@
from core.network.libtorrent_misc import send_notification, update_log, check_deleted_files from network.libtorrent_misc import send_notification, update_log, check_deleted_files
from core.utils.logging.loghandler import split_data, check_completed, check_downloads from utils.logging.loghandler import split_data, check_completed, check_downloads
from core.network.interface import list_interfaces, init_interfaces from network.interface import list_interfaces, init_interfaces
from core.utils.logging.logs import get_download_logs from utils.logging.logs import get_download_logs
from core.utils.logging.logs import set_main_window from utils.logging.logs import set_main_window
from core.network.libtorrent_int import check_space from network.libtorrent_int import check_space
from core.utils.general.shutdown import closehelper from utils.general.shutdown import closehelper
from core.utils.general.wrappers import run_thread from utils.general.wrappers import run_thread
from core.utils.general.shutdown import force_exit from utils.general.shutdown import force_exit
from core.utils.config.config import read_config from utils.config.config import read_config
from core.utils.logging.logs import consoleLog from utils.logging.logs import consoleLog
from core.interface.gui import MainWindow from interface.gui import MainWindow
from core.utils.data.state import state from utils.data.state import state
from PySide6 import QtWidgets from PySide6 import QtWidgets
from PySide6.QtCore import Qt from PySide6.QtCore import Qt
import qdarktheme import qdarktheme
@@ -1,9 +1,10 @@
from utils.logging.logs import consoleLog, add_download_log
from utils.data.state import state
from typing import Optional from typing import Optional
from core.utils.data.state import state
from core.utils.logging.logs import consoleLog, add_download_log
from .handle import DirectDownloadHandle
from .utils import ( from network.direct_download.handle import DirectDownloadHandle
from network.direct_download.utils import (
sanitize_filename, sanitize_filename,
extract_filename_from_url, extract_filename_from_url,
detect_filename_from_headers, detect_filename_from_headers,
@@ -1,8 +1,8 @@
from core.utils.logging.logs import consoleLog, update_download_completed from network.direct_download.status import DirectDownloadStatus, ChunkSpec
from utils.logging.logs import consoleLog, update_download_completed
from concurrent.futures import ThreadPoolExecutor, as_completed from concurrent.futures import ThreadPoolExecutor, as_completed
from .status import DirectDownloadStatus, ChunkSpec from network.direct_download.utils import format_size
from core.utils.data.state import state from utils.data.state import state
from .utils import format_size
from typing import Optional from typing import Optional
import libtorrent as lt import libtorrent as lt
import threading import threading
@@ -1,5 +1,5 @@
from core.utils.logging.logs import consoleLog
from urllib.parse import urlparse, unquote from urllib.parse import urlparse, unquote
from utils.logging.logs import consoleLog
from typing import Optional from typing import Optional
import requests import requests
import os import os
@@ -1,5 +1,5 @@
from core.utils.logging.logs import consoleLog from utils.logging.logs import consoleLog
from core.utils.data.state import state from utils.data.state import state
import psutil import psutil
def get_net_interfaces(): def get_net_interfaces():
@@ -1,7 +1,7 @@
from core.network.interface import get_interface_ip from network.interface import get_interface_ip
from core.utils.general.wrappers import run_thread from utils.general.wrappers import run_thread
from core.utils.logging.logs import consoleLog from utils.logging.logs import consoleLog
from core.utils.data.state import state from utils.data.state import state
import libtorrent as lt import libtorrent as lt
import threading import threading
import platform import platform
@@ -23,7 +23,7 @@ def get_free_space_mb(dirname):
def check_space(): def check_space():
while not state.shutdown_event.is_set(): while not state.shutdown_event.is_set():
for magnet_uri, magnetdl in list(state.active_downloads.items()): for _, magnetdl in list(state.active_downloads.items()):
try: try:
status = magnetdl.status() status = magnetdl.status()
except RuntimeError: except RuntimeError:
@@ -79,7 +79,6 @@ def add_download(magnet_uri):
state.active_downloads = {} state.active_downloads = {}
init_session() init_session()
free_space = get_free_space_mb(state.download_path) free_space = get_free_space_mb(state.download_path)
if magnet_uri in state.active_downloads: if magnet_uri in state.active_downloads:
@@ -110,7 +109,6 @@ def add_download(magnet_uri):
params.save_path = state.download_path params.save_path = state.download_path
handle = state.dl_session.add_torrent(params) handle = state.dl_session.add_torrent(params)
while not handle.has_metadata(): while not handle.has_metadata():
time.sleep(1) time.sleep(1)
@@ -1,6 +1,6 @@
from core.utils.logging.logs import update_download_completed_by_hash from utils.logging.logs import update_download_completed_by_hash
from core.utils.logging.logs import consoleLog from utils.logging.logs import consoleLog
from core.utils.data.state import state from utils.data.state import state
from plyer import notification from plyer import notification
import libtorrent as lt import libtorrent as lt
import time import time
@@ -17,9 +17,6 @@ def cleanup_session():
state.dl_session = None state.dl_session = None
state.active_downloads.clear() state.active_downloads.clear()
def send_notification(shutdown_event): def send_notification(shutdown_event):
notified = set() notified = set()
while not shutdown_event.is_set(): while not shutdown_event.is_set():
@@ -1,5 +1,5 @@
from core.network.libtorrent_int import add_download from network.libtorrent_int import add_download
from core.utils.logging.logs import consoleLog from utils.logging.logs import consoleLog
def add_magnet(uri): def add_magnet(uri):
@@ -1,4 +1,4 @@
from core.utils.data.state import state from utils.data.state import state
import configparser import configparser
import platform import platform
import os import os
@@ -1,7 +1,8 @@
from core.network.libtorrent_int import update_settings from network.libtorrent_int import update_settings
from core.utils.logging.logs import consoleLog from utils.config.config import create_config
from core.utils.data.state import state from utils.logging.logs import consoleLog
from .config import create_config from utils.data.state import state
def save_settings(close=lambda: None, apiurl=None, download_path=None, down_speed_limit=None, up_speed_limit=None, image_path=None, autoresume=None, max_connections=None, max_downloads=None, bound_interface=None): def save_settings(close=lambda: None, apiurl=None, download_path=None, down_speed_limit=None, up_speed_limit=None, image_path=None, autoresume=None, max_connections=None, max_downloads=None, bound_interface=None):
@@ -9,43 +9,44 @@ class AppState(QObject):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.posts: list[Dict[str,str]] | None = None # titles, urls, author, seederm leecher
self.version: str = "dev"
self._image_path: str = ""
self.ignore_updates: bool = False
self.debug: bool = False
self.autoresume: bool = True
self.currenttracker: str = "rutracker" # General
self.trackertable: QTableWidget self.version: str = "Unknown"
self.trackers: Dict[str,Dict[str,Any]] = {} # each tracker should add itself here self.debug: bool = False
''' self.main_window: Any = None
an example: self.loop_running: bool = False
"rutracker" : { self.shutdown_event = threading.Event()
"name" : "rutracker", # name of the tracker self.log_buffer: List[str] = []
"headers" : ["author", "title"], # keys shown in the table
"scrapeFunc" : function, # Paths
} self.settings_path: str = ""
''' self._image_path: str = ""
self.api_url: str = "https://api.michijackson.xyz"
self.seeded_magnets: set = set()
self.download_path: str = str(Path.home() / "Downloads") self.download_path: str = str(Path.home() / "Downloads")
# GUI
self.window_transparency: bool = False
self.trackertable: QTableWidget
self.interfaces: List = []
self.active_interfaces: List = []
self.bound_interface: Any = None
# Trackers / Scraping
self.posts: list[Dict[str,str]] | None = None # titles, urls, author, seeders, leechers
self.currenttracker: str = "rutracker"
self.api_url: str = "https://api.michijackson.xyz"
self.trackers: Dict[str,Dict[str,Any]] = {}
# LibTorrent / Download related stuff
self.dl_session: Any = None
self.active_downloads: Dict = {}
self.seeded_magnets: set = set()
self.ignore_updates: bool = False
self.autoresume: bool = True
self.up_speed_limit: int = 0 self.up_speed_limit: int = 0
self.down_speed_limit: int = 0 self.down_speed_limit: int = 0
self.max_connections: int = 200 self.max_connections: int = 200
self.max_downloads: int = 10 self.max_downloads: int = 10
self.settings_path: str = ""
self.dl_session: Any = None
self.active_downloads: Dict = {}
self.window_transparency: bool = False
self.interfaces: List = []
self.active_interfaces: List = []
self.bound_interface: Any = None
self.log_buffer: List[str] = []
self.downloads_lock = threading.RLock() self.downloads_lock = threading.RLock()
self.main_window: Any = None
self.loop_running: bool = False
self.shutdown_event = threading.Event()
@property @property
def image_path(self) -> str: def image_path(self) -> str:
@@ -1,4 +1,4 @@
from core.utils.logging.logs import consoleLog from utils.logging.logs import consoleLog
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
import requests import requests
@@ -1,6 +1,6 @@
from core.network.libtorrent_misc import cleanup_session from network.libtorrent_misc import cleanup_session
from core.utils.logging.logs import consoleLog from utils.logging.logs import consoleLog
from core.utils.data.state import state from utils.data.state import state
import os import os
def closehelper(): def closehelper():
@@ -1,4 +1,4 @@
from core.utils.logging.logs import consoleLog from utils.logging.logs import consoleLog
def run_thread(thread): def run_thread(thread):
target_name = thread._target.__name__ target_name = thread._target.__name__
@@ -1,5 +1,5 @@
from core.utils.network.download import run_download_direct, seed_magnet from utils.network.download import run_download_direct, seed_magnet
from core.utils.logging.logs import consoleLog, remove_download_log from utils.logging.logs import consoleLog, remove_download_log
import os import os
def split_data(data): def split_data(data):
@@ -18,7 +18,7 @@ def check_completed(downloads, resume):
run_download_direct(download.magnet_uri, download.title) run_download_direct(download.magnet_uri, download.title)
consoleLog(f"Resuming Magnet: {download.title}") consoleLog(f"Resuming Magnet: {download.title}")
elif download.url: elif download.url:
from core.network.direct_download import add_direct_download from network.direct_download import add_direct_download
add_direct_download(download.url, download.title) add_direct_download(download.url, download.title)
consoleLog(f"Resuming Direct Download: {download.title}") consoleLog(f"Resuming Direct Download: {download.title}")
@@ -1,5 +1,5 @@
from core.utils.data.models import Download, DownloadList from utils.data.models import Download, DownloadList
from core.utils.data.state import state from utils.data.state import state
from dataclasses import asdict from dataclasses import asdict
from datetime import datetime from datetime import datetime
import json import json
@@ -243,7 +243,7 @@ def set_main_window(window):
def flush_log_buffer(): # credits to claude def flush_log_buffer(): # credits to claude
if state.log_buffer: if state.log_buffer:
try: try:
from core.interface.gui import MainWindow from interface.gui import MainWindow
for log_entry in state.log_buffer: for log_entry in state.log_buffer:
MainWindow.add_log(log_entry) MainWindow.add_log(log_entry)
state.log_buffer = [] state.log_buffer = []
@@ -256,7 +256,7 @@ def consoleLog(text, printAnyways = False):
formatted_text = f"[{current_time}] {text}" formatted_text = f"[{current_time}] {text}"
try: try:
from core.interface.gui import MainWindow from interface.gui import MainWindow
if not MainWindow.add_log(formatted_text): if not MainWindow.add_log(formatted_text):
state.log_buffer.append(formatted_text) state.log_buffer.append(formatted_text)
except Exception: except Exception:
@@ -1,11 +1,11 @@
from core.network.direct_download import add_direct_download from network.direct_download import add_direct_download
from core.network.libtorrent_wrapper import add_magnet from network.libtorrent_wrapper import add_magnet
from core.utils.logging.logs import add_download_log
from core.utils.general.wrappers import run_thread
from core.network.libtorrent_int import add_seed
from PySide6.QtWidgets import QTableWidgetItem from PySide6.QtWidgets import QTableWidgetItem
from core.utils.logging.logs import consoleLog from utils.logging.logs import add_download_log
from core.utils.data.state import state from utils.general.wrappers import run_thread
from network.libtorrent_int import add_seed
from utils.logging.logs import consoleLog
from utils.data.state import state
from PySide6.QtCore import Qt from PySide6.QtCore import Qt
from typing import Optional from typing import Optional
import threading import threading
@@ -31,9 +31,9 @@ def download_selected(items: list[QTableWidgetItem]):
run_thread(threading.Thread(target=run_download, args=(post,))) run_thread(threading.Thread(target=run_download, args=(post,)))
def run_download(post, headers: Optional[dict] = None): def run_download(post, headers: Optional[dict] = None):
linkfunc = state.trackers[state.currenttracker]["linkFunc"] link_method = state.trackers[state.currenttracker].get_download_link
ismagnet = state.trackers[state.currenttracker]["isMagnet"] ismagnet = state.trackers[state.currenttracker].is_magnet
result = linkfunc(post) result = link_method(post)
if ismagnet: if ismagnet:
link = result link = result
@@ -1,9 +1,8 @@
from core.utils.logging.logs import consoleLog from utils.logging.logs import consoleLog
from core.utils.data.state import state from utils.data.state import state
import requests import requests
def get_updates(): def get_updates():
url = f"https://api.github.com/repos/KeksPirates/SoftwareManager/releases/latest" url = f"https://api.github.com/repos/KeksPirates/SoftwareManager/releases/latest"
try: try:
@@ -1,6 +1,6 @@
from core.network.direct_download.handle import DirectDownloadHandle from network.direct_download.handle import DirectDownloadHandle
from core.utils.logging.logs import consoleLog from utils.logging.logs import consoleLog
from core.utils.data.state import state from utils.data.state import state
from PySide6.QtCore import Qt from PySide6.QtCore import Qt
from PySide6 import QtWidgets from PySide6 import QtWidgets
import libtorrent as lt import libtorrent as lt