From a56969f19d6a005a52fdb9d08f8070538f566e4c Mon Sep 17 00:00:00 2001
From: shayaa <101264710+Vxrtrauter@users.noreply.github.com>
Date: Tue, 17 Mar 2026 23:43:47 +0100
Subject: [PATCH 1/4] Add GitHub Actions workflow for building testing
executables
---
.github/workflows/test.yml | 185 +++++++++++++++++++++++++++++++++++++
1 file changed, 185 insertions(+)
create mode 100644 .github/workflows/test.yml
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..73b42b8
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,185 @@
+name: Build Executables (Test)
+
+on:
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+env:
+ PYTHON_VERSION: "3.12"
+ APP_NAME: "SoftwareManager"
+
+jobs:
+ build:
+ name: Build ${{ matrix.os }}
+ strategy:
+ fail-fast: false # keep other platforms building even if one fails
+ matrix:
+ include:
+ - os: ubuntu-22.04
+ artifact_name: SoftwareManager-test-${{ github.sha }}-linux
+ - os: windows-latest
+ artifact_name: SoftwareManager-test-${{ github.sha }}-windows
+ - os: macos-latest
+ artifact_name: SoftwareManager-test-${{ github.sha }}-macos
+
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v6
+ with:
+ python-version: ${{ env.PYTHON_VERSION }}
+
+ - name: Install uv
+ uses: astral-sh/setup-uv@v7
+ with:
+ version: "0.9.7"
+ enable-cache: true
+
+ - name: Install Python dependencies
+ run: |
+ uv pip install -r requirements.txt
+ 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
+
+ - name: Generate version
+ id: version
+ shell: bash
+ run: |
+ SHORT_SHA="${{ github.sha }}"
+ SHORT_SHA="${SHORT_SHA:0:7}"
+ BRANCH="${{ github.head_ref || github.ref_name }}"
+ BRANCH_SLUG="${BRANCH//\//-}" # replace slashes with dashes
+ VERSION="${BRANCH_SLUG}-${SHORT_SHA}-test"
+ echo "VERSION=$VERSION" >> $GITHUB_ENV
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
+ echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
+ echo "Generated version: $VERSION"
+
+ - name: Inject build info
+ shell: bash
+ run: |
+ cat > build_info.json << EOF
+ {
+ "version": "${{ env.VERSION }}"
+ }
+ EOF
+
+ - name: Install UPX (Windows)
+ if: runner.os == 'Windows'
+ shell: bash
+ run: |
+ 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: 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 \
+ --standalone \
+ --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 \
+ --output-filename=${{ env.APP_NAME }}.exe \
+ --output-dir=nuitka-build \
+ src/main.py
+
+ - name: Build with PyInstaller (Linux)
+ if: runner.os == 'Linux'
+ shell: bash
+ run: |
+ pyinstaller --onefile --windowed \
+ --add-data "build_info.json:." \
+ --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: Build with PyInstaller (macOS)
+ if: runner.os == 'macOS'
+ shell: bash
+ run: |
+ pyinstaller --onefile \
+ --add-data "build_info.json:." \
+ --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: Stage artifact
+ shell: bash
+ run: |
+ mkdir -p dist-final
+ if [ "$RUNNER_OS" == "Windows" ]; then
+ mv nuitka-build/main.dist dist-final/${{ env.APP_NAME }}
+ else
+ chmod +x dist/${{ env.APP_NAME }}
+ mv dist/${{ env.APP_NAME }} dist-final/${{ matrix.artifact_name }}
+ fi
+ ls -la dist-final/
+
+ - name: Create ZIP (Windows)
+ if: runner.os == 'Windows'
+ shell: pwsh
+ run: |
+ Compress-Archive -Path "dist-final/${{ env.APP_NAME }}/*" -DestinationPath "dist-final/${{ matrix.artifact_name }}.zip"
+
+ - name: Upload artifact (Windows)
+ if: runner.os == 'Windows'
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ matrix.os }}-build
+ path: dist-final/${{ matrix.artifact_name }}.zip
+ retention-days: 7
+
+ - name: Upload artifact (Linux/macOS)
+ if: runner.os != 'Windows'
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ matrix.os }}-build
+ path: dist-final/${{ matrix.artifact_name }}
+ retention-days: 7
From cdbf9165d2083c269b5885a283f8d92175fc8775 Mon Sep 17 00:00:00 2001
From: shayaa <101264710+Vxrtrauter@users.noreply.github.com>
Date: Sat, 21 Mar 2026 00:55:34 +0100
Subject: [PATCH 2/4] Update image path for logo in test workflow
---
.github/workflows/test.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 73b42b8..9997bbe 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -95,7 +95,7 @@ jobs:
run: |
python -c "
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)])
print('Icon converted successfully')
"
From cac2ad3ab105d72851586d204c5d2412da882d8a Mon Sep 17 00:00:00 2001
From: shayaa <101264710+Vxrtrauter@users.noreply.github.com>
Date: Sat, 21 Mar 2026 00:58:17 +0100
Subject: [PATCH 3/4] Revise disclaimer in README.md
Updated disclaimer for legal and ethical use of SoftwareManager.
---
README.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/README.md b/README.md
index ccc2eae..751f26e 100644
--- a/README.md
+++ b/README.md
@@ -62,7 +62,6 @@ SoftwareManager is a Python-based GUI tool that simplifies searching and downloa
----
**Disclaimer:** SoftwareManager is intended for legal and ethical use only. Ensure compliance with applicable laws and regulations when using this tool.
From 3b51552313a671f18ff00d6fb35ef23e48e5ce21 Mon Sep 17 00:00:00 2001
From: KeksNino
Date: Sat, 21 Mar 2026 02:03:58 +0100
Subject: [PATCH 4/4] check for free disk space at startup
resolve merge
---
src/core/network/libtorrent_int.py | 19 +++++++++++++++++++
src/main.py | 4 +++-
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/src/core/network/libtorrent_int.py b/src/core/network/libtorrent_int.py
index b199bdd..931d223 100644
--- a/src/core/network/libtorrent_int.py
+++ b/src/core/network/libtorrent_int.py
@@ -21,6 +21,25 @@ def get_free_space_mb(dirname):
st = os.statvfs(dirname)
return st.f_bavail * st.f_frsize
+def check_space():
+ while not state.shutdown_event.is_set():
+ for magnet_uri, magnetdl in list(state.active_downloads.items()):
+ try:
+ status = magnetdl.status()
+ except RuntimeError:
+ continue
+
+ if status.state == lt.torrent_status.downloading:
+ free_space = get_free_space_mb(state.download_path)
+ total_size = status.total_wanted
+
+ if free_space < total_size:
+ consoleLog(f"Not enough free space to continue downloading: {status.name}")
+ magnetdl.pause()
+ break
+
+ time.sleep(5)
+
def init_session():
if state.dl_session is not None:
return
diff --git a/src/main.py b/src/main.py
index 66d6998..2b47fe4 100644
--- a/src/main.py
+++ b/src/main.py
@@ -3,6 +3,7 @@ from core.utils.logging.loghandler import split_data, check_completed, check_dow
from core.network.interface import list_interfaces, init_interfaces
from core.utils.logging.logs import get_download_logs
from core.utils.logging.logs import set_main_window
+from core.network.libtorrent_int import check_space
from core.utils.general.shutdown import closehelper
from core.utils.general.wrappers import run_thread
from core.utils.general.shutdown import force_exit
@@ -59,6 +60,7 @@ def main():
# Start background non-daemon threads
run_thread(threading.Thread(target=check_completed, args=(downloads, state.autoresume)))
run_thread(threading.Thread(target=check_downloads, args=(downloads,)))
+ run_thread(threading.Thread(target=check_space, args=()))
# Finish counting startup time
elapsed = time.perf_counter() - start_time
consoleLog(f"Initialization completed in {elapsed:.2f}s. Launching GUI")
@@ -66,4 +68,4 @@ def main():
run_gui()
if __name__ == "__main__":
- main()
\ No newline at end of file
+ main()