mirror of
https://github.com/KeksPirates/SoftwareManager.git
synced 2026-08-03 17:39:42 +02:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d6e418d045 | |||
| a7c71b37f5 | |||
| 8fb1bb9e8d | |||
| 89fd8b4ebb | |||
| 3915e114a3 | |||
| 34a50e1a1b | |||
| 90124fd9c8 | |||
| 4175f82967 | |||
| 4bf2e9af81 | |||
| a6bb879db3 | |||
| 7653748599 | |||
| 9bec3f9189 | |||
| 04d93b870a | |||
| 664e660198 | |||
| 60019e0d2e | |||
| 8238cfbe56 | |||
| 7ccff5c5a1 | |||
| 8abe92fc4f | |||
| 0a3b5eeee2 | |||
| 7662c861ea | |||
| 2a8f305a76 | |||
| 12223d18de | |||
| 544733ce6b | |||
| 7aee2bcfe9 | |||
| d274e64841 | |||
| 23c74f6487 | |||
| 19ea09d035 | |||
| 92359d75e5 | |||
| 315a68c000 | |||
| 4b0ef44efd | |||
| 6dc28615a0 | |||
| 2ea9a329d1 | |||
| 9f8111ac73 | |||
| e5b809d6d4 |
+92
-32
@@ -15,7 +15,7 @@ permissions:
|
|||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
env:
|
env:
|
||||||
PYTHON_VERSION: "3.11"
|
PYTHON_VERSION: "3.12"
|
||||||
APP_NAME: "SoftwareManager"
|
APP_NAME: "SoftwareManager"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -57,7 +57,13 @@ jobs:
|
|||||||
- name: Install Python dependencies
|
- name: Install Python dependencies
|
||||||
run: |
|
run: |
|
||||||
uv pip install -r requirements.txt
|
uv pip install -r requirements.txt
|
||||||
uv pip install pyinstaller backports.tarfile pillow
|
uv pip install Pillow
|
||||||
|
env:
|
||||||
|
UV_SYSTEM_PYTHON: 1
|
||||||
|
|
||||||
|
- name: Install Nuitka (Windows only)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
run: uv pip install nuitka
|
||||||
env:
|
env:
|
||||||
UV_SYSTEM_PYTHON: 1
|
UV_SYSTEM_PYTHON: 1
|
||||||
|
|
||||||
@@ -82,36 +88,58 @@ jobs:
|
|||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
- name: Build executable with PyInstaller (Windows)
|
- name: Install UPX (Windows)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
pyinstaller --onefile --windowed \
|
curl -sL https://github.com/upx/upx/releases/download/v5.1.0/upx-5.1.0-win64.zip -o upx.zip
|
||||||
--add-data "build_info.json;." \
|
7z x upx.zip -oupx-dir
|
||||||
--add-data "build_info.json;core" \
|
UPX_DIR="$(pwd)/upx-dir/upx-5.1.0-win64"
|
||||||
--add-data "build_info.json;core/interface" \
|
echo "$UPX_DIR" >> $GITHUB_PATH
|
||||||
--add-data "core/interface/assets;core/interface/assets" \
|
export PATH="$UPX_DIR:$PATH"
|
||||||
--icon "core/interface/assets/logo.png" \
|
echo "UPX_BINARY=$UPX_DIR/upx.exe" >> $GITHUB_ENV
|
||||||
--exclude-module PyQt6 \
|
upx --version | head -1
|
||||||
--exclude-module PyQt5 \
|
|
||||||
--hidden-import=backports.tarfile \
|
|
||||||
--hidden-import=backports \
|
|
||||||
--hidden-import=jaraco.context \
|
|
||||||
--hidden-import=jaraco.text \
|
|
||||||
--hidden-import=jaraco.functools \
|
|
||||||
--name ${{ env.APP_NAME }} \
|
|
||||||
main.py
|
|
||||||
|
|
||||||
- name: Build executable with PyInstaller (Unix)
|
- name: Convert icon for Windows
|
||||||
if: runner.os != 'Windows'
|
if: runner.os == 'Windows'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
python -c "
|
||||||
|
from PIL import Image
|
||||||
|
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')
|
||||||
|
"
|
||||||
|
|
||||||
|
- name: Build with Nuitka (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
python -m nuitka \
|
||||||
|
--onefile \
|
||||||
|
--onefile-no-compression \
|
||||||
|
--onefile-tempdir-spec="{CACHE_DIR}/${{ env.APP_NAME }}/${{ env.VERSION }}" \
|
||||||
|
--assume-yes-for-downloads \
|
||||||
|
--windows-console-mode=disable \
|
||||||
|
--windows-icon-from-ico=app_icon.ico \
|
||||||
|
--enable-plugin=pyside6 \
|
||||||
|
--enable-plugin=upx \
|
||||||
|
--upx-binary="${{ env.UPX_BINARY }}" \
|
||||||
|
--noinclude-qt-translations \
|
||||||
|
--include-data-files=build_info.json=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 \
|
||||||
|
src/main.py
|
||||||
|
|
||||||
|
- name: Build with PyInstaller (Linux)
|
||||||
|
if: runner.os == 'Linux'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
pyinstaller --onefile --windowed \
|
pyinstaller --onefile --windowed \
|
||||||
--add-data "build_info.json:." \
|
--add-data "build_info.json:." \
|
||||||
--add-data "build_info.json:core" \
|
--add-data "build_info.json:src/core" \
|
||||||
--add-data "build_info.json:core/interface" \
|
--add-data "build_info.json:src/core/interface" \
|
||||||
--add-data "core/interface/assets:core/interface/assets" \
|
|
||||||
--icon "core/interface/assets/logo.png" \
|
|
||||||
--exclude-module PyQt6 \
|
--exclude-module PyQt6 \
|
||||||
--exclude-module PyQt5 \
|
--exclude-module PyQt5 \
|
||||||
--hidden-import=backports.tarfile \
|
--hidden-import=backports.tarfile \
|
||||||
@@ -120,24 +148,43 @@ jobs:
|
|||||||
--hidden-import=jaraco.text \
|
--hidden-import=jaraco.text \
|
||||||
--hidden-import=jaraco.functools \
|
--hidden-import=jaraco.functools \
|
||||||
--name ${{ env.APP_NAME }} \
|
--name ${{ env.APP_NAME }} \
|
||||||
main.py
|
src/main.py
|
||||||
|
|
||||||
- name: Make executable (Unix)
|
- name: Build with PyInstaller (macOS)
|
||||||
if: runner.os != 'Windows'
|
if: runner.os == 'macOS'
|
||||||
run: chmod +x dist/${{ env.APP_NAME }}
|
shell: bash
|
||||||
|
run: |
|
||||||
|
pyinstaller --onefile \
|
||||||
|
--add-data "build_info.json:." \
|
||||||
|
--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 \
|
||||||
|
--hidden-import=backports \
|
||||||
|
--hidden-import=jaraco.context \
|
||||||
|
--hidden-import=jaraco.text \
|
||||||
|
--hidden-import=jaraco.functools \
|
||||||
|
--name ${{ env.APP_NAME }} \
|
||||||
|
src/main.py
|
||||||
|
|
||||||
- name: Rename artifact
|
- name: Rename artifact
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
cd dist
|
mkdir -p dist-final
|
||||||
mv ${{ env.APP_NAME }}${{ runner.os == 'Windows' && '.exe' || '' }} ${{ matrix.artifact_name }}
|
if [ "$RUNNER_OS" == "Windows" ]; then
|
||||||
ls -la
|
mv ${{ env.APP_NAME }}.exe dist-final/${{ matrix.artifact_name }}
|
||||||
|
else
|
||||||
|
chmod +x dist/${{ env.APP_NAME }}
|
||||||
|
mv dist/${{ env.APP_NAME }} dist-final/${{ matrix.artifact_name }}
|
||||||
|
fi
|
||||||
|
ls -la dist-final/
|
||||||
|
|
||||||
- name: Upload build artifact
|
- name: Upload build artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.os }}-build
|
name: ${{ matrix.os }}-build
|
||||||
path: dist/${{ matrix.artifact_name }}
|
path: dist-final/${{ matrix.artifact_name }}
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
release:
|
release:
|
||||||
@@ -162,9 +209,16 @@ jobs:
|
|||||||
chmod +x release/SoftwareManager-dev-*-linux release/SoftwareManager-dev-*-macos
|
chmod +x release/SoftwareManager-dev-*-linux release/SoftwareManager-dev-*-macos
|
||||||
ls -la release/
|
ls -la release/
|
||||||
|
|
||||||
|
- name: Generate checksums
|
||||||
|
run: |
|
||||||
|
cd release
|
||||||
|
sha256sum * > SHA256SUMS.txt
|
||||||
|
cat SHA256SUMS.txt
|
||||||
|
|
||||||
- name: Generate release notes
|
- name: Generate release notes
|
||||||
run: |
|
run: |
|
||||||
SHORT_SHA="${{ needs.build.outputs.short_sha }}"
|
SHORT_SHA="${{ needs.build.outputs.short_sha }}"
|
||||||
|
CHECKSUMS=$(cat release/SHA256SUMS.txt)
|
||||||
cat > release_notes.md << EOF
|
cat > release_notes.md << EOF
|
||||||
## Development Build
|
## Development Build
|
||||||
|
|
||||||
@@ -177,6 +231,11 @@ jobs:
|
|||||||
- Linux: \`SoftwareManager-dev-${SHORT_SHA}-linux\`
|
- Linux: \`SoftwareManager-dev-${SHORT_SHA}-linux\`
|
||||||
- Windows: \`SoftwareManager-dev-${SHORT_SHA}-windows.exe\`
|
- Windows: \`SoftwareManager-dev-${SHORT_SHA}-windows.exe\`
|
||||||
- macOS: \`SoftwareManager-dev-${SHORT_SHA}-macos\`
|
- macOS: \`SoftwareManager-dev-${SHORT_SHA}-macos\`
|
||||||
|
|
||||||
|
### SHA-256 Checksums
|
||||||
|
\`\`\`
|
||||||
|
${CHECKSUMS}
|
||||||
|
\`\`\`
|
||||||
EOF
|
EOF
|
||||||
cat release_notes.md
|
cat release_notes.md
|
||||||
|
|
||||||
@@ -190,5 +249,6 @@ jobs:
|
|||||||
release/SoftwareManager-dev-${{ needs.build.outputs.short_sha }}-linux
|
release/SoftwareManager-dev-${{ needs.build.outputs.short_sha }}-linux
|
||||||
release/SoftwareManager-dev-${{ needs.build.outputs.short_sha }}-windows.exe
|
release/SoftwareManager-dev-${{ needs.build.outputs.short_sha }}-windows.exe
|
||||||
release/SoftwareManager-dev-${{ needs.build.outputs.short_sha }}-macos
|
release/SoftwareManager-dev-${{ needs.build.outputs.short_sha }}-macos
|
||||||
|
release/SHA256SUMS.txt
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ SoftwareManager is a Python-based GUI tool that simplifies searching and downloa
|
|||||||
## Additional Information
|
## 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.
|
- 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.
|
- 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
|
## Python Dependencies
|
||||||
(also included in requirements.txt)
|
(also included in requirements.txt)
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
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.general.wrappers import run_thread
|
|
||||||
|
|
||||||
|
|
||||||
global url_uztracker
|
|
||||||
url_uztracker = "https://uztracker.net/tracker.php?nm="
|
|
||||||
|
|
||||||
def init_uztracker():
|
|
||||||
global up
|
|
||||||
global soup
|
|
||||||
try:
|
|
||||||
response = requests.get(url_uztracker, timeout=10)
|
|
||||||
if response.status_code == 200:
|
|
||||||
up = True
|
|
||||||
else:
|
|
||||||
up = False
|
|
||||||
consoleLog(f"Uztracker seems down, status code: {response.status_code}")
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
consoleLog(f"Request Exception on {url_uztracker}:")
|
|
||||||
consoleLog(str(e))
|
|
||||||
consoleLog("Is the Site down?")
|
|
||||||
up = False
|
|
||||||
|
|
||||||
run_thread(threading.Thread(target=init_uztracker))
|
|
||||||
|
|
||||||
def scrape_uztracker(search):
|
|
||||||
if up:
|
|
||||||
search_url = url_uztracker + search
|
|
||||||
consoleLog(search_url)
|
|
||||||
result = False
|
|
||||||
global results
|
|
||||||
global resulttitles
|
|
||||||
results = []
|
|
||||||
resulttitles = []
|
|
||||||
|
|
||||||
try:
|
|
||||||
resultCount = 0
|
|
||||||
response = requests.get(search_url)
|
|
||||||
response.raise_for_status()
|
|
||||||
soup = BeautifulSoup(response.text, 'html.parser')
|
|
||||||
links = soup.find_all('a', class_="genmed tLink", href=lambda x: x and x.startswith('./viewtopic'))
|
|
||||||
for link in links:
|
|
||||||
if link.b:
|
|
||||||
resultCount += 1
|
|
||||||
results.append(link['href'])
|
|
||||||
resulttitles.append(link.b.text)
|
|
||||||
result = True
|
|
||||||
|
|
||||||
if result:
|
|
||||||
return resulttitles, results
|
|
||||||
|
|
||||||
if not result:
|
|
||||||
# add popup in gui for no result
|
|
||||||
return None
|
|
||||||
|
|
||||||
except requests.RequestException as e:
|
|
||||||
if result:
|
|
||||||
consoleLog(f"Failed to fetch {search_url}: {e}")
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
consoleLog("Error: Uztracker down")
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
def get_post_title(post_url):
|
|
||||||
if up:
|
|
||||||
response = requests.get(post_url)
|
|
||||||
soup = BeautifulSoup(response.text, 'html.parser')
|
|
||||||
maintitle = soup.find(class_='tt-text')
|
|
||||||
if maintitle:
|
|
||||||
return maintitle.text
|
|
||||||
else:
|
|
||||||
consoleLog("Program not found")
|
|
||||||
else:
|
|
||||||
consoleLog("Error: Uztracker down")
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
@@ -1,83 +0,0 @@
|
|||||||
from PySide6.QtWidgets import QTableWidgetItem
|
|
||||||
from core.utils.general.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
|
|
||||||
from core.utils.network.jsonhandler import split_data, format_data, format_data_m0nkrus
|
|
||||||
from core.utils.data.state import state
|
|
||||||
|
|
||||||
def return_pressed(self):
|
|
||||||
search_text = self.searchbar.text()
|
|
||||||
if search_text == "":
|
|
||||||
consoleLog("Error: Can't search for nothing")
|
|
||||||
return
|
|
||||||
consoleLog(f"User searched for: {search_text}")
|
|
||||||
|
|
||||||
if state.tracker == "uztracker":
|
|
||||||
response = scrape_uztracker(search_text)
|
|
||||||
if response:
|
|
||||||
state.post_titles, state.post_urls = response
|
|
||||||
self.qtablewidget.clear()
|
|
||||||
if state.post_titles and len(state.post_titles) > 0:
|
|
||||||
self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author"])
|
|
||||||
self.qtablewidget.setRowCount(len(state.post_titles))
|
|
||||||
for i, author in enumerate(state.post_author):
|
|
||||||
self.qtablewidget.setItem(i, 1, QTableWidgetItem(author))
|
|
||||||
for i, title in enumerate(state.post_titles):
|
|
||||||
self.qtablewidget.setItem(i, 0, QTableWidgetItem(title))
|
|
||||||
self.show_empty_results(False)
|
|
||||||
else:
|
|
||||||
consoleLog(f"No Results for {search_text}")
|
|
||||||
self.qtablewidget.clear()
|
|
||||||
self.show_empty_results(True)
|
|
||||||
else:
|
|
||||||
consoleLog(f"No response from uztracker")
|
|
||||||
self.qtablewidget.clear()
|
|
||||||
self.show_empty_results(True)
|
|
||||||
|
|
||||||
elif state.tracker == "rutracker":
|
|
||||||
response = scrape_rutracker(search_text)
|
|
||||||
if response:
|
|
||||||
_, state.posts, _, _, cached = split_data(response)
|
|
||||||
if state.posts == []:
|
|
||||||
consoleLog(f"No Results for {search_text}")
|
|
||||||
self.qtablewidget.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))
|
|
||||||
for i, author in enumerate(state.post_author):
|
|
||||||
self.qtablewidget.setItem(i, 1, QTableWidgetItem(author))
|
|
||||||
for i, title in enumerate(state.post_titles):
|
|
||||||
self.qtablewidget.setItem(i, 0, QTableWidgetItem(title))
|
|
||||||
for i, seeders in enumerate(state.post_seeders):
|
|
||||||
self.qtablewidget.setItem(i, 2, QTableWidgetItem(seeders))
|
|
||||||
for i, leechers in enumerate(state.post_leechers):
|
|
||||||
self.qtablewidget.setItem(i, 3, QTableWidgetItem(leechers))
|
|
||||||
if state.debug == True:
|
|
||||||
consoleLog(f"Response Cached: {cached}")
|
|
||||||
else:
|
|
||||||
consoleLog(f"No response from rutracker")
|
|
||||||
self.qtablewidget.clear()
|
|
||||||
self.show_empty_results(True)
|
|
||||||
|
|
||||||
elif state.tracker == "m0nkrus":
|
|
||||||
state.posts = scrape_monkrus_telegram(search_text)
|
|
||||||
|
|
||||||
if state.posts == []:
|
|
||||||
consoleLog(f"No Results for {search_text}")
|
|
||||||
self.qtablewidget.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))
|
|
||||||
for i, author in enumerate(state.post_author):
|
|
||||||
self.qtablewidget.setItem(i, 1, QTableWidgetItem(author))
|
|
||||||
for i, title in enumerate(state.post_titles):
|
|
||||||
self.qtablewidget.setItem(i, 0, QTableWidgetItem(title))
|
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
requests==2.32.2
|
requests==2.32.2
|
||||||
PySide6==6.10.1
|
PySide6-Essentials==6.10.1
|
||||||
beautifulsoup4==4.13.5
|
beautifulsoup4==4.13.5
|
||||||
darkdetect==0.7.1
|
darkdetect==0.7.1
|
||||||
pyinstaller==6.15.0
|
pyinstaller==6.15.0
|
||||||
|
|||||||
@@ -1,16 +1,28 @@
|
|||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
import requests
|
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
|
post = 100
|
||||||
max_posts = 250
|
max_posts = 250
|
||||||
posts: list[dict] = []
|
posts = []
|
||||||
added = set()
|
added = set()
|
||||||
|
|
||||||
|
current_time = time.time()
|
||||||
|
|
||||||
|
if cache["data"] and (current_time - cache["last_fetched"] < cache_expiry):
|
||||||
|
return cache["data"]
|
||||||
|
|
||||||
while post <= max_posts:
|
while post <= max_posts:
|
||||||
url = f"https://t.me/s/real_monkrus/{post}"
|
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")
|
soup = BeautifulSoup(response.text, "html.parser")
|
||||||
bubbles = soup.find_all("div", class_="tgme_widget_message_bubble")
|
bubbles = soup.find_all("div", class_="tgme_widget_message_bubble")
|
||||||
|
|
||||||
@@ -25,7 +37,7 @@ def scrape_monkrus_telegram(query):
|
|||||||
title = post_txt.b.text
|
title = post_txt.b.text
|
||||||
link = bubble.find("a", href=lambda x: x and x.startswith("https://uztracker.net"))
|
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"]
|
post_url = link["href"]
|
||||||
if post_url not in added:
|
if post_url not in added:
|
||||||
added.add(post_url)
|
added.add(post_url)
|
||||||
@@ -39,4 +51,20 @@ def scrape_monkrus_telegram(query):
|
|||||||
post += 22
|
post += 22
|
||||||
|
|
||||||
posts.reverse()
|
posts.reverse()
|
||||||
|
|
||||||
|
cache["data"] = posts
|
||||||
|
cache["last_fetched"] = current_time
|
||||||
|
|
||||||
return(posts)
|
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
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import requests
|
import requests
|
||||||
from core.utils.data.state import state
|
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):
|
def scrape_rutracker(query):
|
||||||
search = requests.get(f"{state.api_url}/search?q={search_text}")
|
search = requests.get(f"{state.api_url}/search?q={query}")
|
||||||
consoleLog("Sent request to server")
|
consoleLog("Sent request to server")
|
||||||
if search:
|
if search:
|
||||||
try:
|
try:
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import requests
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
from urllib.parse import urljoin
|
||||||
|
from core.utils.logging.logs import consoleLog
|
||||||
|
|
||||||
|
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'))
|
||||||
|
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,
|
||||||
|
url=url,
|
||||||
|
author=author
|
||||||
|
))
|
||||||
|
|
||||||
|
return posts
|
||||||
|
|
||||||
|
except requests.RequestException as e:
|
||||||
|
consoleLog(f"Failed to fetch {search_url}: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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.config.settings import save_settings
|
||||||
from core.utils.data.state import state
|
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 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 PySide6 import QtWidgets
|
from PySide6 import QtWidgets
|
||||||
|
from PySide6.QtCore import Qt
|
||||||
from PySide6.QtWidgets import (
|
from PySide6.QtWidgets import (
|
||||||
QLineEdit,
|
QLineEdit,
|
||||||
QPushButton,
|
QPushButton,
|
||||||
@@ -31,6 +32,9 @@ def settings_dialog(self):
|
|||||||
|
|
||||||
dialog.setLayout(QVBoxLayout())
|
dialog.setLayout(QVBoxLayout())
|
||||||
|
|
||||||
|
if state.window_transparency and platform.system() != "Windows" and dialog:
|
||||||
|
dialog.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
||||||
|
|
||||||
def close_settings():
|
def close_settings():
|
||||||
dialog.reject()
|
dialog.reject()
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
from PySide6 import QtWidgets
|
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 (
|
from PySide6.QtWidgets import (
|
||||||
QLineEdit,
|
QLineEdit,
|
||||||
QTableView,
|
QTableView,
|
||||||
@@ -18,7 +18,7 @@ from PySide6.QtWidgets import (
|
|||||||
QStyledItemDelegate,
|
QStyledItemDelegate,
|
||||||
)
|
)
|
||||||
|
|
||||||
from PySide6.QtGui import QIcon, QCloseEvent, QImage, QPixmap, QContextMenuEvent
|
from PySide6.QtGui import QIcon, QCloseEvent, QImage, QPixmap, QContextMenuEvent, QGuiApplication
|
||||||
import darkdetect
|
import darkdetect
|
||||||
import threading
|
import threading
|
||||||
import platform
|
import platform
|
||||||
@@ -29,7 +29,8 @@ import libtorrent as lt
|
|||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
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.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
|
||||||
@@ -37,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.tabhelper import create_tab
|
||||||
from core.interface.utils.searchhelper import return_pressed
|
from core.interface.utils.searchhelper import return_pressed
|
||||||
from core.interface.dialogs.settings import settings_dialog
|
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
|
from core.utils.general.shutdown import closehelper
|
||||||
|
|
||||||
|
|
||||||
@@ -64,6 +67,9 @@ def download_update(latest_version):
|
|||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
def windowCloseHelper():
|
||||||
|
QGuiApplication.quit()
|
||||||
|
|
||||||
class MainWindow(QtWidgets.QMainWindow, QWidget):
|
class MainWindow(QtWidgets.QMainWindow, QWidget):
|
||||||
log_signal = Signal(str) # Thread-safe signal for logging
|
log_signal = Signal(str) # Thread-safe signal for logging
|
||||||
|
|
||||||
@@ -72,20 +78,10 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
MainWindow._instance = self
|
MainWindow._instance = self
|
||||||
self.log_signal.connect(self._on_log_signal)
|
self.log_signal.connect(self._on_log_signal)
|
||||||
|
|
||||||
def get_asset_path(filename):
|
pixmap = QPixmap()
|
||||||
if getattr(sys, 'frozen', False):
|
image_data = base64.b64decode(logo_base64)
|
||||||
base_path = sys._MEIPASS
|
pixmap.loadFromData(image_data)
|
||||||
else:
|
self.setWindowIcon(QIcon(pixmap))
|
||||||
base_path = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
asset_path = os.path.join(base_path, '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")))
|
|
||||||
|
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
try:
|
try:
|
||||||
@@ -102,23 +98,22 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
state.version = build_info.get("version")
|
state.version = build_info.get("version")
|
||||||
|
|
||||||
# Check for updates on Windows
|
# Check for updates on Windows
|
||||||
if state.ignore_updates is False:
|
if state.ignore_updates is False and platform.system() == "Windows":
|
||||||
if platform.system() == "Windows":
|
result = check_for_updates()
|
||||||
result = check_for_updates()
|
if result != (None, None):
|
||||||
if result != (None, None):
|
assets, latest_version = result
|
||||||
assets, latest_version = result
|
|
||||||
|
|
||||||
if assets:
|
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(latest_version)
|
||||||
|
|
||||||
self.setWindowTitle("Software Manager")
|
self.setWindowTitle("Software Manager")
|
||||||
self.setGeometry(100, 100, 800, 600)
|
self.setGeometry(100, 100, 800, 600)
|
||||||
@@ -152,51 +147,57 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
|
|
||||||
flush_log_buffer()
|
flush_log_buffer()
|
||||||
|
|
||||||
# Table Widget for Item List
|
def create_tracker_table(headers):
|
||||||
self.qtablewidget = QTableWidget()
|
table = QTableWidget()
|
||||||
|
table.setColumnCount(len(headers))
|
||||||
self.qtablewidget.setColumnCount(4)
|
table.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers)
|
||||||
self.qtablewidget.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers)
|
table.verticalHeader().setVisible(False)
|
||||||
self.qtablewidget.verticalHeader().setVisible(False)
|
table.setHorizontalHeaderLabels(headers)
|
||||||
self.qtablewidget.setHorizontalHeaderLabels(["Post Title", "Author", "Seeders", "Leechers"])
|
|
||||||
|
|
||||||
|
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()
|
return table
|
||||||
header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
|
|
||||||
header.setSectionResizeMode(1, QHeaderView.ResizeMode.Fixed)
|
|
||||||
header.setStretchLastSection(False)
|
|
||||||
|
|
||||||
self.qtablewidget.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
self.rutrackerlist = create_tracker_table(["Post Title", "Author", "Seeders", "Leechers"])
|
||||||
self.qtablewidget.viewport().setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
self.uztrackerlist = create_tracker_table(["Post Title", "Author"])
|
||||||
|
self.monkruslist = create_tracker_table(["Post Title", "Author"])
|
||||||
|
|
||||||
header = self.qtablewidget.horizontalHeader()
|
state.tracker_list.update({"rutracker": self.rutrackerlist, "uztracker": self.uztrackerlist, "m0nkrus": self.monkruslist})
|
||||||
header.setSectionResizeMode(1, QHeaderView.ResizeMode.Fixed)
|
|
||||||
header.resizeSection(1, 500)
|
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()
|
container = QWidget()
|
||||||
containerLayout = QVBoxLayout()
|
containerLayout = QVBoxLayout()
|
||||||
containerLayout.addWidget(self.searchbar)
|
containerLayout.addWidget(self.searchbar)
|
||||||
containerLayout.addWidget(self.qtablewidget)
|
containerLayout.addWidget(state.tracker_list[state.tracker])
|
||||||
|
|
||||||
class DownloadModel(QAbstractTableModel):
|
class DownloadModel(QAbstractTableModel):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.headers = ["Action", "Name", "Status", "Progress", "Speed", "Size", "Total Size"]
|
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)
|
return len(state.active_downloads)
|
||||||
|
|
||||||
def columnCount(self, parent=QModelIndex()):
|
def columnCount(self, parent: QModelIndex | QPersistentModelIndex = QModelIndex()):
|
||||||
return len(self.headers)
|
return len(self.headers)
|
||||||
|
|
||||||
def headerData(self, section, orientation, role=Qt.DisplayRole):
|
def headerData(self, section, orientation, role: int = Qt.ItemDataRole.DisplayRole):
|
||||||
if role == Qt.DisplayRole and orientation == Qt.Horizontal:
|
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
|
||||||
return self.headers[section]
|
return self.headers[section]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def data(self, index, role=Qt.DisplayRole):
|
def data(self, index: QModelIndex | QPersistentModelIndex, role: int = Qt.ItemDataRole.DisplayRole):
|
||||||
if role == Qt.DisplayRole:
|
if role == Qt.ItemDataRole.DisplayRole:
|
||||||
col = index.column()
|
col = index.column()
|
||||||
|
|
||||||
if index.row() >= len(state.active_downloads) or index.row() < 0:
|
if index.row() >= len(state.active_downloads) or index.row() < 0:
|
||||||
@@ -212,9 +213,9 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
elif col == 1:
|
elif col == 1:
|
||||||
return status.name if status.has_metadata else "Fetching metadata..."
|
return status.name if status.has_metadata else "Fetching metadata..."
|
||||||
elif col == 2:
|
elif col == 2:
|
||||||
if status.state == lt.torrent_status.downloading:
|
if status.state == lt.torrent_status.downloading:
|
||||||
return "Downloading"
|
return "Downloading"
|
||||||
elif status.state == lt.torrent_status.seeding:
|
elif status.state == lt.torrent_status.seeding:
|
||||||
return "Seeding"
|
return "Seeding"
|
||||||
elif status.paused:
|
elif status.paused:
|
||||||
return "Paused"
|
return "Paused"
|
||||||
@@ -258,7 +259,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
else:
|
else:
|
||||||
return "∞" if status.paused else "Stalled"
|
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()]
|
magnet_link = list(state.active_downloads.keys())[index.row()]
|
||||||
magnetdl = state.active_downloads[magnet_link]
|
magnetdl = state.active_downloads[magnet_link]
|
||||||
return magnetdl.status().paused
|
return magnetdl.status().paused
|
||||||
@@ -358,7 +359,7 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
delegate.clicked.connect(on_pause_resume_clicked)
|
delegate.clicked.connect(on_pause_resume_clicked)
|
||||||
|
|
||||||
# download button triggers
|
# 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)
|
container.setLayout(containerLayout)
|
||||||
self.setCentralWidget(container)
|
self.setCentralWidget(container)
|
||||||
@@ -378,15 +379,16 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
|
|
||||||
self.horizontal_layout = QHBoxLayout()
|
self.horizontal_layout = QHBoxLayout()
|
||||||
self.horizontal_layout.addWidget(self.emptyResults, stretch=3)
|
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.tab2 = create_tab("Library", self.emptyLibrary, self.libraryList, self.tabs, None, None)
|
||||||
self.tab3 = create_tab("Downloads", self.emptyDownload, self.downloadList, self.tabs, None, None)
|
self.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):
|
if state.image_path is not None and os.path.exists(state.image_path):
|
||||||
self.image = QImage(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.pixmap = QPixmap.fromImage(self.image)
|
||||||
self.overlay_label = QLabel(self)
|
self.overlay_label = QLabel(self)
|
||||||
self.overlay_label.setPixmap(self.pixmap)
|
self.overlay_label.setPixmap(self.pixmap)
|
||||||
@@ -417,10 +419,18 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
self.settings_btn = QtWidgets.QToolButton()
|
self.settings_btn = QtWidgets.QToolButton()
|
||||||
self.settings_btn.setIconSize(QSize(21, 21))
|
self.settings_btn.setIconSize(QSize(21, 21))
|
||||||
self.settings_btn.setStyleSheet("background: transparent;")
|
self.settings_btn.setStyleSheet("background: transparent;")
|
||||||
|
|
||||||
if darkdetect.isDark():
|
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:
|
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.setToolTip("Settings")
|
||||||
self.settings_btn.setCursor(Qt.CursorShape.PointingHandCursor)
|
self.settings_btn.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||||
self.settings_btn.clicked.connect(lambda: settings_dialog(self))
|
self.settings_btn.clicked.connect(lambda: settings_dialog(self))
|
||||||
@@ -475,10 +485,11 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
MainWindow._instance.log_signal.emit(text)
|
MainWindow._instance.log_signal.emit(text)
|
||||||
|
|
||||||
def _on_log_signal(self, text):
|
def _on_log_signal(self, text):
|
||||||
self.consoleLog.append(text)
|
if hasattr(self, 'consoleLog'):
|
||||||
self.consoleLog.verticalScrollBar().setValue(
|
self.consoleLog.append(text)
|
||||||
self.consoleLog.verticalScrollBar().maximum()
|
self.consoleLog.verticalScrollBar().setValue(
|
||||||
)
|
self.consoleLog.verticalScrollBar().maximum()
|
||||||
|
)
|
||||||
|
|
||||||
def update_image_overlay(self, new_image_path):
|
def update_image_overlay(self, new_image_path):
|
||||||
self.image = QImage(new_image_path)
|
self.image = QImage(new_image_path)
|
||||||
@@ -502,14 +513,22 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
event.accept()
|
event.accept()
|
||||||
|
|
||||||
def set_tracker(self, _):
|
def set_tracker(self, _):
|
||||||
|
old_tracker = state.tracker
|
||||||
state.tracker = self.tracker_list.currentText()
|
state.tracker = self.tracker_list.currentText()
|
||||||
|
|
||||||
|
old_widget = state.tracker_list[old_tracker]
|
||||||
|
self.horizontal_layout.removeWidget(old_widget)
|
||||||
|
old_widget.setParent(None)
|
||||||
|
|
||||||
|
tracker_widget = state.tracker_list[state.tracker]
|
||||||
|
self.horizontal_layout.addWidget(tracker_widget)
|
||||||
|
|
||||||
def show_empty_results(self, show: bool):
|
def show_empty_results(self, show: bool):
|
||||||
if show:
|
if show:
|
||||||
self.qtablewidget.hide()
|
state.tracker_list[state.tracker].hide()
|
||||||
self.emptyResults.show()
|
self.emptyResults.show()
|
||||||
else:
|
else:
|
||||||
self.qtablewidget.show()
|
state.tracker_list[state.tracker].show()
|
||||||
self.emptyResults.hide()
|
self.emptyResults.hide()
|
||||||
|
|
||||||
def show_empty_downloads(self):
|
def show_empty_downloads(self):
|
||||||
@@ -523,8 +542,8 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
|||||||
# thank you claude
|
# thank you claude
|
||||||
def resizeEvent(self, event):
|
def resizeEvent(self, event):
|
||||||
super().resizeEvent(event)
|
super().resizeEvent(event)
|
||||||
table_width = self.qtablewidget.viewport().width()
|
table_width = state.tracker_list[state.tracker].viewport().width()
|
||||||
self.qtablewidget.setColumnWidth(1, int(table_width * 0.3))
|
state.tracker_list[state.tracker].setColumnWidth(1, int(table_width * 0.3))
|
||||||
|
|
||||||
def contextMenuEvent(self, event: QContextMenuEvent):
|
def contextMenuEvent(self, event: QContextMenuEvent):
|
||||||
if self.downloadList.underMouse():
|
if self.downloadList.underMouse():
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
from PySide6.QtWidgets import QTableWidgetItem
|
||||||
|
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_m0nkrus
|
||||||
|
from core.utils.network.jsonhandler import split_data, format_data, format_data_minimal
|
||||||
|
from core.utils.data.state import state
|
||||||
|
|
||||||
|
scrapers = {
|
||||||
|
"uztracker": scrape_uztracker,
|
||||||
|
"rutracker": scrape_rutracker,
|
||||||
|
"m0nkrus": scrape_m0nkrus
|
||||||
|
}
|
||||||
|
|
||||||
|
def return_pressed(self):
|
||||||
|
search_text = self.searchbar.text()
|
||||||
|
if search_text == "":
|
||||||
|
consoleLog("Error: Can't search for nothing")
|
||||||
|
return
|
||||||
|
consoleLog(f"User searched for: {search_text}")
|
||||||
|
|
||||||
|
if state.tracker == "rutracker":
|
||||||
|
response = scrape_rutracker(search_text)
|
||||||
|
if response:
|
||||||
|
_, state.posts, _, _, cached = split_data(response)
|
||||||
|
if state.posts == []:
|
||||||
|
consoleLog(f"No Results for {search_text}")
|
||||||
|
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)
|
||||||
|
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):
|
||||||
|
state.tracker_list[state.tracker].setItem(i, 1, QTableWidgetItem(author))
|
||||||
|
for i, title in enumerate(state.post_titles):
|
||||||
|
state.tracker_list[state.tracker].setItem(i, 0, QTableWidgetItem(title))
|
||||||
|
for i, seeders in enumerate(state.post_seeders):
|
||||||
|
state.tracker_list[state.tracker].setItem(i, 2, QTableWidgetItem(seeders))
|
||||||
|
for i, leechers in enumerate(state.post_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")
|
||||||
|
state.tracker_list[state.tracker].clear()
|
||||||
|
self.show_empty_results(True)
|
||||||
|
|
||||||
|
elif state.tracker is not None:
|
||||||
|
state.posts = scrapers[state.tracker](search_text)
|
||||||
|
if state.posts == []:
|
||||||
|
consoleLog(f"No Results for {search_text}")
|
||||||
|
state.tracker_list[state.tracker].clear()
|
||||||
|
self.show_empty_results(True)
|
||||||
|
else:
|
||||||
|
state.post_author, state.post_titles, state.post_urls = format_data_minimal(state.posts)
|
||||||
|
self.show_empty_results(False)
|
||||||
|
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):
|
||||||
|
state.tracker_list[state.tracker].setItem(i, 1, QTableWidgetItem(author))
|
||||||
|
for i, title in enumerate(state.post_titles):
|
||||||
|
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
|
from core.utils.data.state import state
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ from core.network.interface import get_interface_ip
|
|||||||
import threading
|
import threading
|
||||||
import libtorrent as lt
|
import libtorrent as lt
|
||||||
from core.utils.data.state import state
|
from core.utils.data.state import state
|
||||||
from core.utils.general.logs import consoleLog
|
from core.utils.logging.logs import consoleLog
|
||||||
|
|
||||||
|
|
||||||
global loop_running
|
global loop_running
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from core.utils.data.state import state
|
from core.utils.data.state import state
|
||||||
from core.utils.general.logs import update_download_completed_by_hash
|
from core.utils.logging.logs import update_download_completed_by_hash
|
||||||
from core.utils.general.logs import consoleLog
|
from core.utils.logging.logs import consoleLog
|
||||||
from plyer import notification
|
from plyer import notification
|
||||||
import libtorrent as lt
|
import libtorrent as lt
|
||||||
import time
|
import time
|
||||||
@@ -57,4 +57,4 @@ def update_log(shutdown_event):
|
|||||||
updated.add(magnet_uri)
|
updated.add(magnet_uri)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
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
|
from core.network.libtorrent_int import add_download
|
||||||
|
|
||||||
def add_magnet(uri):
|
def add_magnet(uri):
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from core.network.libtorrent_int import update_settings
|
from core.network.libtorrent_int import update_settings
|
||||||
from core.utils.data.state import state
|
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
|
from .config import create_config
|
||||||
|
|
||||||
|
|
||||||
@@ -7,6 +7,7 @@ class Download:
|
|||||||
title: str
|
title: str
|
||||||
url: str
|
url: str
|
||||||
magnet_uri: str
|
magnet_uri: str
|
||||||
|
path: str
|
||||||
completed: bool
|
completed: bool
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
from PySide6.QtCore import QObject, Signal
|
from PySide6.QtCore import QObject, Signal
|
||||||
|
from PySide6.QtWidgets import QTableWidget
|
||||||
from typing import Optional, Any, List, Dict
|
from typing import Optional, Any, List, Dict
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -7,9 +8,9 @@ class AppState(QObject):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.posts: List[Dict[str, Any]] = []
|
self.posts: list[Any] | None = None
|
||||||
self.post_titles: List[str] = []
|
self.post_titles: Optional[List] = None
|
||||||
self.post_urls: List[str] = []
|
self.post_urls: Optional[List] = None
|
||||||
self.post_author: List[str] = []
|
self.post_author: List[str] = []
|
||||||
self.post_seeders: List[str] = []
|
self.post_seeders: List[str] = []
|
||||||
self.post_leechers: List[str] = []
|
self.post_leechers: List[str] = []
|
||||||
@@ -19,6 +20,7 @@ class AppState(QObject):
|
|||||||
self.debug: bool = False
|
self.debug: bool = False
|
||||||
self.autoresume: bool = True
|
self.autoresume: bool = True
|
||||||
self.tracker: str = "rutracker"
|
self.tracker: str = "rutracker"
|
||||||
|
self.tracker_list: dict[str, QTableWidget] = {}
|
||||||
self.api_url: str = "https://api.michijackson.xyz"
|
self.api_url: str = "https://api.michijackson.xyz"
|
||||||
self.download_path: str = str(Path.home() / "Downloads")
|
self.download_path: str = str(Path.home() / "Downloads")
|
||||||
self.up_speed_limit: int = 0
|
self.up_speed_limit: int = 0
|
||||||
@@ -27,11 +29,11 @@ class AppState(QObject):
|
|||||||
self.max_downloads: int = 10
|
self.max_downloads: int = 10
|
||||||
self.settings_path: str = ""
|
self.settings_path: str = ""
|
||||||
self.dl_session: Any = None
|
self.dl_session: Any = None
|
||||||
self.active_downloads: List[str] = {}
|
self.active_downloads: Dict = {}
|
||||||
self.window_transparency: bool = False
|
self.window_transparency: bool = False
|
||||||
self.interfaces: List = []
|
self.interfaces: List = []
|
||||||
self.active_interfaces: List = []
|
self.active_interfaces: List = []
|
||||||
self.bound_interface: str = None
|
self.bound_interface: Any = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def image_path(self) -> str:
|
def image_path(self) -> str:
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import requests
|
import requests
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from core.utils.data.state import state
|
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
|
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
|
from core.utils.network.download import run_download_direct
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
def split_data(data):
|
def split_data(data):
|
||||||
|
|
||||||
count = data.count
|
count = data.count
|
||||||
@@ -16,6 +18,11 @@ def check_completed(downloads, resume):
|
|||||||
run_download_direct(download.magnet_uri)
|
run_download_direct(download.magnet_uri)
|
||||||
consoleLog(f"Resuming {download.title}")
|
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 json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import time
|
||||||
|
|
||||||
_log_buffer = []
|
_log_buffer = []
|
||||||
|
|
||||||
@@ -21,7 +22,21 @@ def add_download_log(title, url, magnet_uri, completed) -> DownloadList:
|
|||||||
downloads = []
|
downloads = []
|
||||||
else:
|
else:
|
||||||
downloads = []
|
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 any(d.magnet_uri == magnet_uri or d.url == url for d in downloads):
|
||||||
if magnet_uri in state.active_downloads:
|
if magnet_uri in state.active_downloads:
|
||||||
@@ -37,7 +52,8 @@ def add_download_log(title, url, magnet_uri, completed) -> DownloadList:
|
|||||||
title=title,
|
title=title,
|
||||||
url=url,
|
url=url,
|
||||||
magnet_uri=magnet_uri,
|
magnet_uri=magnet_uri,
|
||||||
completed=completed,
|
path=path,
|
||||||
|
completed=completed
|
||||||
))
|
))
|
||||||
|
|
||||||
consoleLog(f"Added {title} to Log File")
|
consoleLog(f"Added {title} to Log File")
|
||||||
@@ -61,10 +77,12 @@ def remove_download_log(magnet_uri) -> DownloadList:
|
|||||||
else:
|
else:
|
||||||
downloads = []
|
downloads = []
|
||||||
|
|
||||||
|
|
||||||
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))
|
||||||
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 {magnet_link} from Log File")
|
consoleLog(f"Removed {title} from Log File")
|
||||||
|
|
||||||
download_list = DownloadList(data=downloads, count=len(downloads))
|
download_list = DownloadList(data=downloads, count=len(downloads))
|
||||||
with open(downloads_file, "w") as file:
|
with open(downloads_file, "w") as file:
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
from core.utils.data.state import state
|
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_item_url
|
||||||
from core.utils.data.tracker import get_magnet_link
|
from core.utils.data.tracker import get_magnet_link
|
||||||
from core.network.libtorrent_wrapper import add_download
|
from core.network.libtorrent_wrapper import add_download
|
||||||
from core.utils.general.wrappers import run_thread
|
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
|
import threading
|
||||||
|
|
||||||
@@ -21,8 +22,8 @@ def run_download(item, posts, post_titles):
|
|||||||
consoleLog(f"Selected URL: {post_url}")
|
consoleLog(f"Selected URL: {post_url}")
|
||||||
magnet_uri = get_magnet_link(post_url)
|
magnet_uri = get_magnet_link(post_url)
|
||||||
|
|
||||||
add_download_log(item, post_url, magnet_uri, False)
|
|
||||||
add_download(magnet_uri)
|
add_download(magnet_uri)
|
||||||
|
add_download_log(item, post_url, magnet_uri, False)
|
||||||
|
|
||||||
def run_download_direct(magnet_uri):
|
def run_download_direct(magnet_uri):
|
||||||
add_download(magnet_uri)
|
add_download(magnet_uri)
|
||||||
@@ -20,16 +20,16 @@ def format_data(data):
|
|||||||
|
|
||||||
return post_titles, post_links, post_author, post_seeders, post_leechers
|
return post_titles, post_links, post_author, post_seeders, post_leechers
|
||||||
|
|
||||||
def format_data_m0nkrus(data):
|
def format_data_minimal(data):
|
||||||
post_author = [post["author"] for post in data]
|
post_author = [post["author"] for post in data]
|
||||||
post_titles = [post["title"] for post in data]
|
post_titles = [post["title"] for post in data]
|
||||||
post_links = [post["url"] for post in data]
|
post_links = [post["url"] for post in data]
|
||||||
|
|
||||||
return post_titles, post_links, post_author
|
return post_author, post_titles, post_links
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# https://open.spotify.com/track/70vjXI7oBou5buTtgiJeRW?si=25640cc4243c4bf3
|
# https://open.spotify.com/track/0F0fMcIdL2FmcC9eYqwazX
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import requests
|
import requests
|
||||||
from core.utils.data.state import state
|
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():
|
def check_for_updates():
|
||||||
url = f"https://api.github.com/repos/KeksPirates/SoftwareManager/releases"
|
url = f"https://api.github.com/repos/KeksPirates/SoftwareManager/releases"
|
||||||
+10
-15
@@ -1,13 +1,12 @@
|
|||||||
from core.interface.gui import MainWindow
|
from core.interface.gui import MainWindow, windowCloseHelper
|
||||||
from core.utils.data.state import state
|
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.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.shutdown import closehelper, shutdown_event
|
||||||
from core.utils.general.wrappers import run_thread
|
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.interface import list_interfaces, init_interfaces
|
||||||
from core.network.libtorrent_int import update_bound_interface
|
|
||||||
from core.utils.config.config import read_config
|
from core.utils.config.config import read_config
|
||||||
from PySide6 import QtWidgets
|
from PySide6 import QtWidgets
|
||||||
from PySide6.QtCore import Qt
|
from PySide6.QtCore import Qt
|
||||||
@@ -15,15 +14,9 @@ import qdarktheme
|
|||||||
import threading
|
import threading
|
||||||
import platform
|
import platform
|
||||||
import signal
|
import signal
|
||||||
import argparse
|
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("--debug", action="store_true")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
def run_gui():
|
def run_gui():
|
||||||
app = QtWidgets.QApplication.instance() or QtWidgets.QApplication(sys.argv)
|
app = QtWidgets.QApplication.instance() or QtWidgets.QApplication(sys.argv)
|
||||||
qdarktheme.setup_theme("auto")
|
qdarktheme.setup_theme("auto")
|
||||||
@@ -33,21 +26,20 @@ def run_gui():
|
|||||||
widget.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
widget.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
||||||
qdarktheme.setup_theme("auto", custom_colors={"background": "#00000000"})
|
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)
|
set_main_window(widget)
|
||||||
widget.show()
|
widget.show()
|
||||||
sys.exit(app.exec())
|
sys.exit(app.exec())
|
||||||
|
|
||||||
def keyboardinterrupthandler(signum, frame):
|
def keyboardinterrupthandler(signum, frame):
|
||||||
closehelper()
|
closehelper()
|
||||||
|
windowCloseHelper()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def main():
|
||||||
start_time = time.perf_counter()
|
start_time = time.perf_counter()
|
||||||
read_config()
|
read_config()
|
||||||
logs = get_download_logs()
|
logs = get_download_logs()
|
||||||
_, downloads = split_data(logs)
|
_, downloads = split_data(logs)
|
||||||
if args.debug:
|
|
||||||
state.debug = args.debug # override of read_config
|
|
||||||
signal.signal(signal.SIGINT, keyboardinterrupthandler)
|
signal.signal(signal.SIGINT, keyboardinterrupthandler)
|
||||||
consoleLog("Starting SoftwareManager...")
|
consoleLog("Starting SoftwareManager...")
|
||||||
consoleLog("Fetching Network Interfaces...")
|
consoleLog("Fetching Network Interfaces...")
|
||||||
@@ -61,7 +53,10 @@ if __name__ == "__main__":
|
|||||||
consoleLog("Started Thread: update_log")
|
consoleLog("Started Thread: update_log")
|
||||||
run_thread(threading.Thread(target=check_completed, args=(downloads, state.autoresume)))
|
run_thread(threading.Thread(target=check_completed, args=(downloads, state.autoresume)))
|
||||||
consoleLog("Started Thread: check_completed")
|
consoleLog("Started Thread: check_completed")
|
||||||
|
check_downloads(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()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user