mirror of
https://github.com/KeksPirates/SoftwareManager.git
synced 2026-08-04 01:49:42 +02:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 12223d18de | |||
| 544733ce6b | |||
| 7aee2bcfe9 | |||
| d274e64841 | |||
| 23c74f6487 | |||
| 19ea09d035 | |||
| 92359d75e5 | |||
| 315a68c000 | |||
| 4b0ef44efd | |||
| 6dc28615a0 | |||
| 2ea9a329d1 | |||
| 9f8111ac73 | |||
| e5b809d6d4 | |||
| b573ca8fed |
+92
-32
@@ -15,7 +15,7 @@ permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
PYTHON_VERSION: "3.11"
|
||||
PYTHON_VERSION: "3.12"
|
||||
APP_NAME: "SoftwareManager"
|
||||
|
||||
jobs:
|
||||
@@ -57,7 +57,13 @@ jobs:
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
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:
|
||||
UV_SYSTEM_PYTHON: 1
|
||||
|
||||
@@ -82,36 +88,58 @@ jobs:
|
||||
}
|
||||
EOF
|
||||
|
||||
- name: Build executable with PyInstaller (Windows)
|
||||
- name: Install UPX (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
pyinstaller --onefile --windowed \
|
||||
--add-data "build_info.json;." \
|
||||
--add-data "build_info.json;core" \
|
||||
--add-data "build_info.json;core/interface" \
|
||||
--add-data "core/interface/assets;core/interface/assets" \
|
||||
--icon "core/interface/assets/logo.png" \
|
||||
--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 }} \
|
||||
main.py
|
||||
curl -sL https://github.com/upx/upx/releases/download/v5.1.0/upx-5.1.0-win64.zip -o upx.zip
|
||||
7z x upx.zip -oupx-dir
|
||||
UPX_DIR="$(pwd)/upx-dir/upx-5.1.0-win64"
|
||||
echo "$UPX_DIR" >> $GITHUB_PATH
|
||||
export PATH="$UPX_DIR:$PATH"
|
||||
echo "UPX_BINARY=$UPX_DIR/upx.exe" >> $GITHUB_ENV
|
||||
upx --version | head -1
|
||||
|
||||
- name: Build executable with PyInstaller (Unix)
|
||||
if: runner.os != 'Windows'
|
||||
- name: Convert icon for 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
|
||||
run: |
|
||||
pyinstaller --onefile --windowed \
|
||||
--add-data "build_info.json:." \
|
||||
--add-data "build_info.json:core" \
|
||||
--add-data "build_info.json:core/interface" \
|
||||
--add-data "core/interface/assets:core/interface/assets" \
|
||||
--icon "core/interface/assets/logo.png" \
|
||||
--add-data "build_info.json:src/core" \
|
||||
--add-data "build_info.json:src/core/interface" \
|
||||
--exclude-module PyQt6 \
|
||||
--exclude-module PyQt5 \
|
||||
--hidden-import=backports.tarfile \
|
||||
@@ -120,24 +148,43 @@ jobs:
|
||||
--hidden-import=jaraco.text \
|
||||
--hidden-import=jaraco.functools \
|
||||
--name ${{ env.APP_NAME }} \
|
||||
main.py
|
||||
src/main.py
|
||||
|
||||
- name: Make executable (Unix)
|
||||
if: runner.os != 'Windows'
|
||||
run: chmod +x dist/${{ env.APP_NAME }}
|
||||
- name: Build with PyInstaller (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
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
|
||||
shell: bash
|
||||
run: |
|
||||
cd dist
|
||||
mv ${{ env.APP_NAME }}${{ runner.os == 'Windows' && '.exe' || '' }} ${{ matrix.artifact_name }}
|
||||
ls -la
|
||||
mkdir -p dist-final
|
||||
if [ "$RUNNER_OS" == "Windows" ]; then
|
||||
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
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.os }}-build
|
||||
path: dist/${{ matrix.artifact_name }}
|
||||
path: dist-final/${{ matrix.artifact_name }}
|
||||
retention-days: 1
|
||||
|
||||
release:
|
||||
@@ -162,9 +209,16 @@ jobs:
|
||||
chmod +x release/SoftwareManager-dev-*-linux release/SoftwareManager-dev-*-macos
|
||||
ls -la release/
|
||||
|
||||
- name: Generate checksums
|
||||
run: |
|
||||
cd release
|
||||
sha256sum * > SHA256SUMS.txt
|
||||
cat SHA256SUMS.txt
|
||||
|
||||
- name: Generate release notes
|
||||
run: |
|
||||
SHORT_SHA="${{ needs.build.outputs.short_sha }}"
|
||||
CHECKSUMS=$(cat release/SHA256SUMS.txt)
|
||||
cat > release_notes.md << EOF
|
||||
## Development Build
|
||||
|
||||
@@ -177,6 +231,11 @@ jobs:
|
||||
- Linux: \`SoftwareManager-dev-${SHORT_SHA}-linux\`
|
||||
- Windows: \`SoftwareManager-dev-${SHORT_SHA}-windows.exe\`
|
||||
- macOS: \`SoftwareManager-dev-${SHORT_SHA}-macos\`
|
||||
|
||||
### SHA-256 Checksums
|
||||
\`\`\`
|
||||
${CHECKSUMS}
|
||||
\`\`\`
|
||||
EOF
|
||||
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 }}-windows.exe
|
||||
release/SoftwareManager-dev-${{ needs.build.outputs.short_sha }}-macos
|
||||
release/SHA256SUMS.txt
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -30,7 +30,7 @@ SoftwareManager is a Python-based GUI tool that simplifies searching and downloa
|
||||
## Additional Information
|
||||
- SoftwareManager is still in Development and most likely contains issues. Please report any bugs you find via the Issues tab. Contributions are also greatly appreciated.
|
||||
- MacOS Builds haven't been properly tested - We're thankful to receive feedback.
|
||||
- If you want to host your own server, clone the server branch of this repo, enter your cookie for rutracker.org and run server.py. Detailed Instructions on how to get your rutracker cookie are in the README of the server branch.
|
||||
- If you want to host your own server, clone our [server repository](https://github.com/KeksPirates/SoftwareManager-Server), enter your cookie for rutracker.org and run server.py. Detailed Instructions on how to get your rutracker cookie are in the README of the server repo.
|
||||
|
||||
## Python Dependencies
|
||||
(also included in requirements.txt)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
+1
-1
@@ -1,5 +1,5 @@
|
||||
requests==2.32.2
|
||||
PySide6==6.10.1
|
||||
PySide6-Essentials==6.10.1
|
||||
beautifulsoup4==4.13.5
|
||||
darkdetect==0.7.1
|
||||
pyinstaller==6.15.0
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
@@ -4,7 +4,8 @@ from core.utils.general.logs import consoleLog
|
||||
from core.interface.utils.tabhelper import general_tab
|
||||
from core.interface.utils.tabhelper import paths_tab
|
||||
from core.interface.utils.tabhelper import network_tab
|
||||
from PySide6 import QtWidgets
|
||||
from PySide6 import QtWidgets, Qt
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import (
|
||||
QLineEdit,
|
||||
QPushButton,
|
||||
@@ -31,6 +32,9 @@ def settings_dialog(self):
|
||||
|
||||
dialog.setLayout(QVBoxLayout())
|
||||
|
||||
if state.window_transparency and platform.system() != "Windows":
|
||||
dialog.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
||||
|
||||
def close_settings():
|
||||
dialog.reject()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from PySide6 import QtWidgets
|
||||
from PySide6 import QtWidgets, QtCore, QtGui
|
||||
from PySide6.QtCore import Qt, QTimer, QModelIndex, QAbstractTableModel, Signal, QEvent, QSize
|
||||
from PySide6.QtWidgets import (
|
||||
QLineEdit,
|
||||
@@ -29,6 +29,7 @@ import libtorrent as lt
|
||||
import time
|
||||
import sys
|
||||
import json
|
||||
import base64
|
||||
from core.utils.general.logs import consoleLog, remove_download_log, flush_log_buffer
|
||||
from core.utils.general.wrappers import run_thread
|
||||
from core.utils.data.state import state
|
||||
@@ -37,6 +38,9 @@ from core.utils.network.update_checker import check_for_updates
|
||||
from core.interface.utils.tabhelper import create_tab
|
||||
from core.interface.utils.searchhelper import return_pressed
|
||||
from core.interface.dialogs.settings import settings_dialog
|
||||
from core.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.network.libtorrent_misc import cleanup_session
|
||||
from core.utils.general.shutdown import closehelper
|
||||
|
||||
@@ -72,20 +76,10 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
||||
MainWindow._instance = self
|
||||
self.log_signal.connect(self._on_log_signal)
|
||||
|
||||
def get_asset_path(filename):
|
||||
if getattr(sys, 'frozen', False):
|
||||
base_path = sys._MEIPASS
|
||||
else:
|
||||
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")))
|
||||
|
||||
pixmap = QPixmap()
|
||||
image_data = base64.b64decode(logo_base64)
|
||||
pixmap.loadFromData(image_data)
|
||||
self.setWindowIcon(QIcon(pixmap))
|
||||
|
||||
if platform.system() == "Windows":
|
||||
try:
|
||||
@@ -102,23 +96,22 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
||||
state.version = build_info.get("version")
|
||||
|
||||
# Check for updates on Windows
|
||||
if state.ignore_updates is False:
|
||||
if platform.system() == "Windows":
|
||||
result = check_for_updates()
|
||||
if result != (None, None):
|
||||
assets, latest_version = result
|
||||
if state.ignore_updates is False and platform.system() == "Windows":
|
||||
result = check_for_updates()
|
||||
if result != (None, None):
|
||||
assets, latest_version = result
|
||||
|
||||
if assets:
|
||||
msg = QMessageBox()
|
||||
msg.setIcon(QMessageBox.Icon.Information)
|
||||
msg.setWindowTitle("Update Available")
|
||||
msg.setText("A new version is available.")
|
||||
msg.setInformativeText("Press Ok to download the update.")
|
||||
msg.setStandardButtons(QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Ignore)
|
||||
if assets:
|
||||
msg = QMessageBox()
|
||||
msg.setIcon(QMessageBox.Icon.Information)
|
||||
msg.setWindowTitle("Update Available")
|
||||
msg.setText("A new version is available.")
|
||||
msg.setInformativeText("Press Ok to download the update.")
|
||||
msg.setStandardButtons(QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Ignore)
|
||||
|
||||
response = msg.exec_()
|
||||
if response == QMessageBox.StandardButton.Ok:
|
||||
download_update(latest_version)
|
||||
response = msg.exec_()
|
||||
if response == QMessageBox.StandardButton.Ok:
|
||||
download_update(latest_version)
|
||||
|
||||
self.setWindowTitle("Software Manager")
|
||||
self.setGeometry(100, 100, 800, 600)
|
||||
@@ -417,10 +410,18 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
||||
self.settings_btn = QtWidgets.QToolButton()
|
||||
self.settings_btn.setIconSize(QSize(21, 21))
|
||||
self.settings_btn.setStyleSheet("background: transparent;")
|
||||
|
||||
if darkdetect.isDark():
|
||||
self.settings_btn.setIcon(QIcon(get_asset_path("settings_white.png")))
|
||||
settings_white_pixmap = QPixmap()
|
||||
settings_white = base64.b64decode(settings_white_base64)
|
||||
settings_white_pixmap.loadFromData(settings_white)
|
||||
self.settings_btn.setIcon(QIcon(settings_white_pixmap))
|
||||
else:
|
||||
self.settings_btn.setIcon(QIcon(get_asset_path("settings_black.png")))
|
||||
settings_black_pixmap = QPixmap()
|
||||
settings_black = base64.b64decode(settings_black_base64)
|
||||
settings_black_pixmap.loadFromData(settings_black)
|
||||
self.settings_btn.setIcon(QIcon(settings_black_pixmap))
|
||||
|
||||
self.settings_btn.setToolTip("Settings")
|
||||
self.settings_btn.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
self.settings_btn.clicked.connect(lambda: settings_dialog(self))
|
||||
@@ -475,10 +476,11 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
|
||||
MainWindow._instance.log_signal.emit(text)
|
||||
|
||||
def _on_log_signal(self, text):
|
||||
self.consoleLog.append(text)
|
||||
self.consoleLog.verticalScrollBar().setValue(
|
||||
self.consoleLog.verticalScrollBar().maximum()
|
||||
)
|
||||
if hasattr(self, 'consoleLog'):
|
||||
self.consoleLog.append(text)
|
||||
self.consoleLog.verticalScrollBar().setValue(
|
||||
self.consoleLog.verticalScrollBar().maximum()
|
||||
)
|
||||
|
||||
def update_image_overlay(self, new_image_path):
|
||||
self.image = QImage(new_image_path)
|
||||
@@ -36,7 +36,7 @@ def init_session():
|
||||
settings["listen_interfaces"] = f"{interface_ip}:6881"
|
||||
consoleLog(f"Binding to Interface IP: {interface_ip}")
|
||||
else:
|
||||
consoleLog(f"Error finding IP for Interface {state.bound_interface}")
|
||||
consoleLog(f"Skipping Binding, no Interface set. ({state.bound_interface})")
|
||||
|
||||
state.dl_session.apply_settings(settings)
|
||||
|
||||
@@ -51,7 +51,7 @@ def add_download(magnet_uri, dl_path=state.download_path):
|
||||
init_session()
|
||||
|
||||
if magnet_uri in state.active_downloads:
|
||||
consoleLog("Skipping, download already running...")
|
||||
consoleLog("Skipping Downloading, download already running...")
|
||||
return
|
||||
|
||||
magnetdl = lt.parse_magnet_uri(magnet_uri)
|
||||
@@ -114,7 +114,7 @@ def update_settings():
|
||||
settings["listen_interfaces"] = f"{interface_ip}:6881"
|
||||
consoleLog(f"Binding to Interface IP: {interface_ip}")
|
||||
else:
|
||||
consoleLog(f"Error finding IP for Interface {state.bound_interface}")
|
||||
consoleLog(f"Skipping Binding, no Interface set. ({state.bound_interface})")
|
||||
|
||||
state.dl_session.apply_settings(settings)
|
||||
|
||||
@@ -24,7 +24,12 @@ def add_download_log(title, url, magnet_uri, completed) -> DownloadList:
|
||||
|
||||
|
||||
if any(d.magnet_uri == magnet_uri or d.url == url for d in downloads):
|
||||
consoleLog("Skipped Logging, download already in file")
|
||||
if magnet_uri in state.active_downloads:
|
||||
consoleLog("Skipping Logging, download already running...")
|
||||
return
|
||||
consoleLog("File already in Log, updating Download State...")
|
||||
hash = extract_hash_from_magnet(magnet_uri)
|
||||
update_download_completed_by_hash(hash, False)
|
||||
return DownloadList(data=downloads, count=len(downloads)) # thanks again claude (im stupid)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user