Compare commits

..

14 Commits

Author SHA1 Message Date
Vxrtrauter 8592b3058e Merge branch 'main' of https://github.com/KeksPirates/SoftwareManager 2026-05-04 21:51:20 +02:00
Vxrtrauter 7a82e948b9 refactor: remove unused imports 2026-05-04 21:51:18 +02:00
shayaa 782b0b6bc9 Merge pull request #91 from KeksPirates/feat/bzzhr-impl
Add bzzhr implementation and improve file preallocation on Windows
2026-05-04 21:49:51 +02:00
KeksNino 71cc0d9f1f fix: workflows (forgot to git add again) 2026-05-04 20:58:38 +02:00
KeksNino 675dead1d9 fix: readd dependency installation step for linux in workflow
gets rid of pyinstaller warnings and is needed for future appimages
2026-05-04 20:47:05 +02:00
Vxrtrauter 64ca3c76a2 Fix: update buzzheavier link detection logic and improve filename extraction from headers 2026-05-04 19:07:42 +02:00
shayaa d378ee8258 Enhance macOS build process in create-release.yml
Added installation step for create-dmg on macOS and updated artifact handling for macOS builds.
2026-04-24 11:28:56 +02:00
shayaa e9b01426f8 Merge pull request #90 from KeksPirates/feat/crossplatform-updatecheck
Feat/crossplatform updatecheck
2026-04-24 10:37:40 +02:00
Vxrtrauter 631f6592ac Refactor: remove unused imports from update dialog 2026-04-24 10:36:14 +02:00
Vxrtrauter 468faff16b Refactor: streamline update notification logic and enhance settings dialog for cross-platform compatibility 2026-04-24 10:20:26 +02:00
shayaa 6b45c40aad Update GitHub Actions workflow for macOS builds 2026-04-24 09:17:42 +02:00
shayaa e768c7518b Remove Activity section from README
Removed the Activity section and its associated analytics image from the README.
2026-04-18 19:01:34 +02:00
shayaa 49c390131a Merge pull request #87 from KeksPirates/dependabot/github_actions/softprops/action-gh-release-3
build(deps): bump softprops/action-gh-release from 2 to 3
2026-04-18 03:28:01 +02:00
dependabot[bot] 04aaff3b29 build(deps): bump softprops/action-gh-release from 2 to 3
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v2...v3)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-13 20:41:00 +00:00
12 changed files with 124 additions and 39 deletions
+22 -7
View File
@@ -141,7 +141,7 @@ jobs:
if: runner.os == 'macOS' if: runner.os == 'macOS'
shell: bash shell: bash
run: | run: |
pyinstaller --onefile \ pyinstaller --windowed \
--add-data "build_info.json:." \ --add-data "build_info.json:." \
--exclude-module PyQt6 \ --exclude-module PyQt6 \
--exclude-module PyQt5 \ --exclude-module PyQt5 \
@@ -153,6 +153,10 @@ jobs:
--name ${{ env.APP_NAME }} \ --name ${{ env.APP_NAME }} \
src/main.py src/main.py
- name: Install create-dmg (macOS)
if: runner.os == 'macOS'
run: brew install create-dmg
- name: Rename artifact - name: Rename artifact
shell: bash shell: bash
run: | run: |
@@ -160,9 +164,20 @@ jobs:
if [ "$RUNNER_OS" == "Windows" ]; then if [ "$RUNNER_OS" == "Windows" ]; then
mv nuitka-build/main.dist dist-final/${{ env.APP_NAME }} mv nuitka-build/main.dist dist-final/${{ env.APP_NAME }}
ls -la dist-final/${{ env.APP_NAME }}/ ls -la dist-final/${{ env.APP_NAME }}/
else elif [ "$RUNNER_OS" == "Linux" ]; then
chmod +x dist/${{ env.APP_NAME }} chmod +x dist/${{ env.APP_NAME }}
mv dist/${{ env.APP_NAME }} dist-final/${{ matrix.artifact_name }} mv dist/${{ env.APP_NAME }} dist-final/${{ matrix.artifact_name }}
elif [ "$RUNNER_OS" == "macOS" ]; then
create-dmg \
--volname "${{ env.APP_NAME }}" \
--window-pos 200 120 \
--window-size 600 300 \
--icon-size 100 \
--icon "${{ env.APP_NAME }}.app" 150 150 \
--hide-extension "${{ env.APP_NAME }}.app" \
--app-drop-link 450 150 \
"dist-final/${{ matrix.artifact_name }}.dmg" \
"dist/"
fi fi
ls -la dist-final/ ls -la dist-final/
@@ -203,7 +218,7 @@ jobs:
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
name: ${{ matrix.os }}-build name: ${{ matrix.os }}-build
path: dist-final/${{ matrix.artifact_name }} path: dist-final/${{ matrix.artifact_name }}*
retention-days: 1 retention-days: 1
release: release:
@@ -225,8 +240,8 @@ jobs:
cp artifacts/ubuntu-22.04-build/SoftwareManager-stable-*-linux release/SoftwareManager-stable-${SHORT_SHA}-linux cp artifacts/ubuntu-22.04-build/SoftwareManager-stable-*-linux release/SoftwareManager-stable-${SHORT_SHA}-linux
cp artifacts/windows-latest-build/SoftwareManager-stable-*-windows.zip release/SoftwareManager-stable-${SHORT_SHA}-windows.zip cp artifacts/windows-latest-build/SoftwareManager-stable-*-windows.zip release/SoftwareManager-stable-${SHORT_SHA}-windows.zip
cp artifacts/windows-latest-build/SoftwareManager-stable-*-windows-setup.exe release/SoftwareManager-stable-${SHORT_SHA}-windows-setup.exe cp artifacts/windows-latest-build/SoftwareManager-stable-*-windows-setup.exe release/SoftwareManager-stable-${SHORT_SHA}-windows-setup.exe
cp artifacts/macos-latest-build/SoftwareManager-stable-*-macos release/SoftwareManager-stable-${SHORT_SHA}-macos cp artifacts/macos-latest-build/SoftwareManager-stable-*-macos.dmg release/SoftwareManager-stable-${SHORT_SHA}-macos.dmg
chmod +x release/SoftwareManager-stable-*-linux release/SoftwareManager-stable-*-macos chmod +x release/SoftwareManager-stable-*-linux
ls -la release/ ls -la release/
- name: Generate checksums - name: Generate checksums
@@ -236,7 +251,7 @@ jobs:
cat SHA256SUMS.txt cat SHA256SUMS.txt
- name: Create Release (Public) - name: Create Release (Public)
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v3
with: with:
token: ${{ secrets.RELEASE_TOKEN }} token: ${{ secrets.RELEASE_TOKEN }}
tag_name: ${{ needs.build.outputs.version }} tag_name: ${{ needs.build.outputs.version }}
@@ -246,5 +261,5 @@ jobs:
release/SoftwareManager-stable-${{ needs.build.outputs.short_sha }}-linux release/SoftwareManager-stable-${{ needs.build.outputs.short_sha }}-linux
release/SoftwareManager-stable-${{ needs.build.outputs.short_sha }}-windows.zip release/SoftwareManager-stable-${{ needs.build.outputs.short_sha }}-windows.zip
release/SoftwareManager-stable-${{ needs.build.outputs.short_sha }}-windows-setup.exe release/SoftwareManager-stable-${{ needs.build.outputs.short_sha }}-windows-setup.exe
release/SoftwareManager-stable-${{ needs.build.outputs.short_sha }}-macos release/SoftwareManager-stable-${{ needs.build.outputs.short_sha }}-macos.dmg
release/SHA256SUMS.txt release/SHA256SUMS.txt
+10 -1
View File
@@ -58,6 +58,15 @@ jobs:
env: env:
UV_SYSTEM_PYTHON: 1 UV_SYSTEM_PYTHON: 1
- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y \
libxcb-cursor0 libxcb-render-util0 libxcb-xkb1 \
libxcb-icccm4 libxcb-keysyms1 libxcb-image0 \
libxcb-shape0 libxcb-util1 libxkbcommon-x11-0
- name: Install Nuitka (Windows only) - name: Install Nuitka (Windows only)
if: runner.os == 'Windows' if: runner.os == 'Windows'
run: uv pip install nuitka run: uv pip install nuitka
@@ -266,7 +275,7 @@ jobs:
cat release_notes.md cat release_notes.md
- name: Create Release (Public) - name: Create Release (Public)
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v3
with: with:
tag_name: ${{ needs.build.outputs.version }} tag_name: ${{ needs.build.outputs.version }}
name: ${{ needs.build.outputs.version }} name: ${{ needs.build.outputs.version }}
+19 -4
View File
@@ -14,7 +14,7 @@ jobs:
build: build:
name: Build ${{ matrix.os }} name: Build ${{ matrix.os }}
strategy: strategy:
fail-fast: false # keep other platforms building even if one fails fail-fast: false
matrix: matrix:
include: include:
- os: ubuntu-22.04 - os: ubuntu-22.04
@@ -138,7 +138,7 @@ jobs:
if: runner.os == 'macOS' if: runner.os == 'macOS'
shell: bash shell: bash
run: | run: |
pyinstaller --onefile \ pyinstaller --windowed \
--add-data "build_info.json:." \ --add-data "build_info.json:." \
--exclude-module PyQt6 \ --exclude-module PyQt6 \
--exclude-module PyQt5 \ --exclude-module PyQt5 \
@@ -150,15 +150,30 @@ jobs:
--name ${{ env.APP_NAME }} \ --name ${{ env.APP_NAME }} \
src/main.py src/main.py
- name: Install create-dmg (macOS)
if: runner.os == 'macOS'
run: brew install create-dmg
- name: Stage artifact - name: Stage artifact
shell: bash shell: bash
run: | run: |
mkdir -p dist-final mkdir -p dist-final
if [ "$RUNNER_OS" == "Windows" ]; then if [ "$RUNNER_OS" == "Windows" ]; then
mv nuitka-build/main.dist dist-final/${{ env.APP_NAME }} mv nuitka-build/main.dist dist-final/${{ env.APP_NAME }}
else elif [ "$RUNNER_OS" == "Linux" ]; then
chmod +x dist/${{ env.APP_NAME }} chmod +x dist/${{ env.APP_NAME }}
mv dist/${{ env.APP_NAME }} dist-final/${{ matrix.artifact_name }} mv dist/${{ env.APP_NAME }} dist-final/${{ matrix.artifact_name }}
elif [ "$RUNNER_OS" == "macOS" ]; then
create-dmg \
--volname "${{ env.APP_NAME }} Test" \
--window-pos 200 120 \
--window-size 600 300 \
--icon-size 100 \
--icon "${{ env.APP_NAME }}.app" 150 150 \
--hide-extension "${{ env.APP_NAME }}.app" \
--app-drop-link 450 150 \
"dist-final/${{ matrix.artifact_name }}.dmg" \
"dist/"
fi fi
ls -la dist-final/ ls -la dist-final/
@@ -181,5 +196,5 @@ jobs:
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
name: ${{ matrix.os }}-build name: ${{ matrix.os }}-build
path: dist-final/${{ matrix.artifact_name }} path: dist-final/${{ matrix.artifact_name }}*
retention-days: 7 retention-days: 7
-4
View File
@@ -55,10 +55,6 @@ SoftwareManager is a Python-based GUI tool that simplifies searching and downloa
``` ```
<!-- python-dependencies:end --> <!-- python-dependencies:end -->
## Activity
![Alt](https://repobeats.axiom.co/api/embed/3a61ea02ffdeb9ed5dd948d974a21e0d6cfae36c.svg "Repobeats analytics image")
## Star History ## Star History
<a href="https://www.star-history.com/#KeksPirates/SoftwareManager&type=date&legend=top-left"> <a href="https://www.star-history.com/#KeksPirates/SoftwareManager&type=date&legend=top-left">
+1 -1
View File
@@ -113,7 +113,7 @@ class SteamripScraper:
try: try:
consoleLog(f"Found download link: {best}") consoleLog(f"Found download link: {best}")
if "buzzheavier" in best: if "buzzheavier" or "bzzhr" in best:
link = scrape_buzzheavier(best) link = scrape_buzzheavier(best)
return link return link
elif "gofile" in best: elif "gofile" in best:
-1
View File
@@ -12,7 +12,6 @@ import threading
import webbrowser import webbrowser
import subprocess import subprocess
import platform import platform
import time
import os import os
-1
View File
@@ -79,7 +79,6 @@ def settings_dialog(self):
# Ignore Updates checkbox # Ignore Updates checkbox
update_checkbox_container, update_checkbox = create_widget(QCheckBox, "Ignore Updates: ") update_checkbox_container, update_checkbox = create_widget(QCheckBox, "Ignore Updates: ")
if platform.system() == "Windows":
update_checkbox.setChecked(state.ignore_updates) update_checkbox.setChecked(state.ignore_updates)
update_checkbox.toggled.connect(lambda checked: setattr(state, 'ignore_updates', checked)) update_checkbox.toggled.connect(lambda checked: setattr(state, 'ignore_updates', checked))
+37 -3
View File
@@ -1,13 +1,17 @@
from PySide6.QtWidgets import QDialog, QMessageBox, QCheckBox
from utils.network.update_checker import get_updates from utils.network.update_checker import get_updates
from PySide6.QtWidgets import QDialog, QMessageBox
from utils.network.updater import download_update from utils.network.updater import download_update
from utils.config.config import create_config
from utils.logging.logs import consoleLog from utils.logging.logs import consoleLog
from utils.data.state import state from utils.data.state import state
from pathlib import Path from pathlib import Path
import webbrowser
import platform import platform
import json import json
import os import os
RELEASES_URL = "https://github.com/KeksPirates/SoftwareManager/releases/latest"
def _get_build_info_path() -> (Path | None): def _get_build_info_path() -> (Path | None):
current_dir = Path(__file__).resolve().parent current_dir = Path(__file__).resolve().parent
@@ -31,12 +35,40 @@ def get_version() -> None:
else: else:
consoleLog(f"Could not find build info file (Path: {build_info_path})") consoleLog(f"Could not find build info file (Path: {build_info_path})")
def _show_notify_dialog(latest: str) -> None:
msg = QMessageBox()
msg.setIcon(QMessageBox.Icon.Information)
msg.setWindowTitle("Update Available")
msg.setText(f"A new version is available: <b>{latest}</b>")
msg.setInformativeText("Visit the releases page to download it.")
dont_show = QCheckBox("Don't show again")
msg.setCheckBox(dont_show)
open_btn = msg.addButton("Open Releases Page", QMessageBox.ButtonRole.AcceptRole)
msg.addButton("Dismiss", QMessageBox.ButtonRole.RejectRole)
msg.exec()
if dont_show.isChecked():
state.ignore_updates = True
create_config()
if msg.clickedButton() == open_btn:
webbrowser.open(RELEASES_URL)
class UpdateDialog(QDialog): class UpdateDialog(QDialog):
def __init__(self, parent=None): def __init__(self, parent=None):
if state.ignore_updates is False and platform.system() == "Windows": if state.ignore_updates:
return
assets, latest = get_updates() assets, latest = get_updates()
if assets != None: if assets is None:
return
if platform.system() == "Windows":
msg = QMessageBox() msg = QMessageBox()
msg.setIcon(QMessageBox.Icon.Information) msg.setIcon(QMessageBox.Icon.Information)
msg.setWindowTitle("Update Available") msg.setWindowTitle("Update Available")
@@ -46,4 +78,6 @@ 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)
else:
_show_notify_dialog(latest)
+3 -1
View File
@@ -20,12 +20,14 @@ def add_direct_download(url: str, title: str, dl_path: Optional[str] = None, hea
consoleLog(f"Download already active: {title}") consoleLog(f"Download already active: {title}")
return return
filename = ( raw_filename = (
detect_filename_from_headers(url, DirectDownloadHandle.USER_AGENT) detect_filename_from_headers(url, DirectDownloadHandle.USER_AGENT)
or extract_filename_from_url(url) or extract_filename_from_url(url)
or sanitize_filename(title) + ".zip" or sanitize_filename(title) + ".zip"
) )
filename = sanitize_filename(raw_filename)
handle = DirectDownloadHandle(url, filename, dl_path, headers, single_threaded) handle = DirectDownloadHandle(url, filename, dl_path, headers, single_threaded)
with state.downloads_lock: with state.downloads_lock:
state.active_downloads[url] = handle state.active_downloads[url] = handle
+17
View File
@@ -10,6 +10,7 @@ import requests
import hashlib import hashlib
import json import json
import time import time
import sys
import os import os
@@ -231,7 +232,23 @@ class DirectDownloadHandle:
def _preallocate_file(self, total_size: int): def _preallocate_file(self, total_size: int):
if os.path.exists(self._file_path) and os.path.getsize(self._file_path) == total_size: if os.path.exists(self._file_path) and os.path.getsize(self._file_path) == total_size:
return return
with open(self._file_path, "wb") as f: with open(self._file_path, "wb") as f:
if sys.platform == "win32":
import msvcrt
import ctypes
from ctypes.wintypes import DWORD
handle = msvcrt.get_osfhandle(f.fileno())
kernel32 = ctypes.windll.kernel32
bytes_returned = DWORD()
kernel32.DeviceIoControl(
handle, 590020, None, 0, None, 0, ctypes.byref(bytes_returned), None
)
kernel32.SetFilePointerEx(handle, ctypes.c_int64(total_size), None, 0)
kernel32.SetEndOfFile(handle)
else:
f.truncate(total_size) f.truncate(total_size)
def _compute_chunks(self, total_size: int) -> list[ChunkSpec]: def _compute_chunks(self, total_size: int) -> list[ChunkSpec]:
+1 -1
View File
@@ -34,7 +34,7 @@ def detect_filename_from_headers(url: str, user_agent: str) -> Optional[str]:
if "filename=" in cd: if "filename=" in cd:
parts = cd.split("filename=") parts = cd.split("filename=")
if len(parts) > 1: if len(parts) > 1:
fname = parts[-1].strip().strip('"').strip("'") fname = parts[1].split(";")[0].strip().strip('"').strip("'")
if fname: if fname:
return fname return fname
except Exception as e: except Exception as e:
-1
View File
@@ -4,7 +4,6 @@ from PySide6.QtCore import QObject, QTimer
from 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 os import os