Compare commits

...

35 Commits

Author SHA1 Message Date
KeksNino ba4321b892 update github CI workflow 2026-07-11 00:03:54 +02:00
KeksNino a8f0186c2e re-enable appimage build step 2026-07-10 23:57:39 +02:00
KeksNino 1574ae594e fix github CI 2026-07-10 23:42:28 +02:00
KeksNino 2b45038a62 testing... 2026-07-10 23:40:41 +02:00
KeksNino 727d31fbcc fix stage artifact step in github CI 2026-07-10 23:32:28 +02:00
KeksNino 710ef26151 add appimage build step to github CI 2026-07-10 23:27:11 +02:00
KeksNino fdd8aa81ba add appimage build script (credits to OnTheSpot) 2026-07-10 23:21:09 +02:00
KeksNino a4c6c3381a install system dependencies on linux in CI workflow 2026-07-10 16:36:19 +02:00
Vxrtrauter 68a9e870cf feat: implement middleware for VST Search subdomain HTML rewriting 2026-07-06 23:25:52 +02:00
Vxrtrauter 02de32719d Merge branch 'main' of https://github.com/KeksPirates/SoftwareManager 2026-07-06 23:20:54 +02:00
Vxrtrauter c5a78a2da3 feat: add VST middleware for dynamic HTML content rewriting 2026-07-06 23:20:51 +02:00
shayaa 32f02d856d Merge pull request #104 from KeksPirates/feat-website-vstsearch
Add VST search functionality
2026-07-06 23:12:36 +02:00
Vxrtrauter 4edb1baa0b add vst search 2026-07-06 23:11:18 +02:00
shayaa 1ffa1cacb9 Merge pull request #99 from KeksPirates/auto/sync-readme-dependencies
Sync README dependencies
2026-05-14 23:36:19 +02:00
Vxrtrauter 207eb70f11 Sync README dependencies 2026-05-14 21:10:34 +00:00
shayaa 64144e5ed7 Merge pull request #98 from KeksPirates/dependabot/pip/requests-2.34.2
build(deps): bump requests from 2.34.1 to 2.34.2
2026-05-14 23:10:21 +02:00
dependabot[bot] d05fae7e87 build(deps): bump requests from 2.34.1 to 2.34.2
Bumps [requests](https://github.com/psf/requests) from 2.34.1 to 2.34.2.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.34.1...v2.34.2)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.34.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-14 19:37:56 +00:00
shayaa 9abdec6c33 Merge pull request #97 from KeksPirates/auto/sync-readme-dependencies
Sync README dependencies
2026-05-13 22:08:47 +02:00
Vxrtrauter fb5eb61d05 Sync README dependencies 2026-05-13 20:07:34 +00:00
shayaa eff2164ecc Merge pull request #89 from KeksPirates/dependabot/pip/pyinstaller-6.20.0
build(deps): bump pyinstaller from 6.19.0 to 6.20.0
2026-05-13 22:07:23 +02:00
shayaa d8aa206ea2 Merge pull request #94 from KeksPirates/dependabot/pip/pyside6-essentials-6.11.1
build(deps): bump pyside6-essentials from 6.11.0 to 6.11.1
2026-05-13 22:07:00 +02:00
dependabot[bot] 6de74303ff build(deps): bump pyside6-essentials from 6.11.0 to 6.11.1
Bumps [pyside6-essentials](https://pyside.org) from 6.11.0 to 6.11.1.

---
updated-dependencies:
- dependency-name: pyside6-essentials
  dependency-version: 6.11.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-13 20:06:46 +00:00
shayaa 5eb6314cb2 Merge pull request #96 from KeksPirates/auto/sync-readme-dependencies
Sync README dependencies
2026-05-13 22:05:49 +02:00
Vxrtrauter 179dd0af83 Sync README dependencies 2026-05-13 20:05:32 +00:00
shayaa 365700900f Merge pull request #95 from KeksPirates/dependabot/pip/requests-2.34.1
build(deps): bump requests from 2.34.0 to 2.34.1
2026-05-13 22:05:03 +02:00
dependabot[bot] babbccdc9a build(deps): bump requests from 2.34.0 to 2.34.1
Bumps [requests](https://github.com/psf/requests) from 2.34.0 to 2.34.1.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.34.0...v2.34.1)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.34.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-13 19:47:10 +00:00
shayaa 367ffab86a Merge pull request #93 from KeksPirates/auto/sync-readme-dependencies
Sync README dependencies
2026-05-12 10:57:57 +02:00
Vxrtrauter 71bad60617 Sync README dependencies 2026-05-12 08:46:59 +00:00
shayaa 27758f794c Merge pull request #92 from KeksPirates/dependabot/pip/requests-2.34.0
build(deps): bump requests from 2.33.1 to 2.34.0
2026-05-12 10:46:50 +02:00
dependabot[bot] d82afe8577 build(deps): bump requests from 2.33.1 to 2.34.0
Bumps [requests](https://github.com/psf/requests) from 2.33.1 to 2.34.0.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.33.1...v2.34.0)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.34.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-12 02:25:02 +00:00
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
Vxrtrauter 64ca3c76a2 Fix: update buzzheavier link detection logic and improve filename extraction from headers 2026-05-04 19:07:42 +02:00
dependabot[bot] d6302fc43f build(deps): bump pyinstaller from 6.19.0 to 6.20.0
Bumps [pyinstaller](https://github.com/pyinstaller/pyinstaller) from 6.19.0 to 6.20.0.
- [Release notes](https://github.com/pyinstaller/pyinstaller/releases)
- [Changelog](https://github.com/pyinstaller/pyinstaller/blob/develop/doc/CHANGES.rst)
- [Commits](https://github.com/pyinstaller/pyinstaller/compare/v6.19.0...v6.20.0)

---
updated-dependencies:
- dependency-name: pyinstaller
  dependency-version: 6.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-23 19:37:36 +00:00
18 changed files with 337 additions and 22 deletions
+15
View File
@@ -46,6 +46,21 @@ jobs:
version: "0.9.7"
enable-cache: true
- name: Install system dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y \
libxcb-xinerama0 \
libxkbcommon-x11-0 \
libxcb-cursor0 \
libxcb-keysyms1 \
libxcb-render-util0 \
libxcb-icccm4 \
libxcb-image0 \
libxcb-shape0 \
libxcb-util1
- name: Install Python dependencies
run: |
uv pip install -r requirements.txt
+15
View File
@@ -51,6 +51,21 @@ jobs:
version: "0.9.7"
enable-cache: true
- name: Install system dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y \
libxcb-xinerama0 \
libxkbcommon-x11-0 \
libxcb-cursor0 \
libxcb-keysyms1 \
libxcb-render-util0 \
libxcb-icccm4 \
libxcb-image0 \
libxcb-shape0 \
libxcb-util1
- name: Install Python dependencies
run: |
uv pip install -r requirements.txt
+28 -4
View File
@@ -18,11 +18,15 @@ jobs:
matrix:
include:
- os: ubuntu-22.04
artifact_name: SoftwareManager-test-${{ github.sha }}-linux
artifact_paths: |
dist-final/SoftwareManager-test-${{ github.sha }}-linux*
dist-final/SoftwareManager-x86_64*
- os: windows-latest
artifact_name: SoftwareManager-test-${{ github.sha }}-windows
artifact_paths: |
dist-final/SoftwareManager-test-${{ github.sha }}-windows*
- os: macos-latest
artifact_name: SoftwareManager-test-${{ github.sha }}-macos
artifact_paths: |
dist-final/SoftwareManager-test-${{ github.sha }}-macos*
runs-on: ${{ matrix.os }}
@@ -41,6 +45,21 @@ jobs:
version: "0.9.7"
enable-cache: true
- name: Install system dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y \
libxcb-xinerama0 \
libxkbcommon-x11-0 \
libxcb-cursor0 \
libxcb-keysyms1 \
libxcb-render-util0 \
libxcb-icccm4 \
libxcb-image0 \
libxcb-shape0 \
libxcb-util1
- name: Install Python dependencies
run: |
uv pip install -r requirements.txt
@@ -134,6 +153,11 @@ jobs:
--name ${{ env.APP_NAME }} \
src/main.py
- name: Build AppImage (Linux)
if: runner.os == 'Linux'
shell: bash
run: scripts/build_appimage.sh
- name: Build with PyInstaller (macOS)
if: runner.os == 'macOS'
shell: bash
@@ -196,5 +220,5 @@ jobs:
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.os }}-build
path: dist-final/${{ matrix.artifact_name }}*
path: ${{ matrix.artifact_paths }}
retention-days: 7
+3
View File
@@ -219,3 +219,6 @@ __marimo__/
# Local build files
build-local.ps1
build-local.bat
# UV
uv.lock
+3 -3
View File
@@ -41,11 +41,11 @@ SoftwareManager is a Python-based GUI tool that simplifies searching and downloa
<!-- python-dependencies:start -->
```text
requests (2.33.1)
PySide6-Essentials (6.11.0)
requests (2.34.2)
PySide6-Essentials (6.11.1)
beautifulsoup4 (4.14.3)
darkdetect (0.7.1)
pyinstaller (6.19.0)
pyinstaller (6.20.0)
PyQtDarkTheme-fork (2.3.6)
plyer (2.1.0)
psutil (7.2.2)
+7
View File
@@ -0,0 +1,7 @@
[Desktop Entry]
Name=SoftwareManager
Type=Application
Exec=python3 -m SoftwareManager
Icon=softwaremanager
Terminal=false
Categories=X-Software;X-Torrents;
+50 -4
View File
@@ -39,9 +39,10 @@
<h1>KeksPirates</h1>
</div>
<nav class="tab">
<a class="tablinks" onclick="openTab(event, 'start')" id="defaultOpen">Home</a>
<a class="tablinks" onclick="openTab(event, 'api')">API</a>
<a class="tablinks" onclick="openTab(event, 'docs')">Docs</a>
<a class="tablinks" onclick="openTab(event, 'start')" id="navHome">Home</a>
<a class="tablinks" onclick="openTab(event, 'api')" id="navApi">API</a>
<a class="tablinks" onclick="openTab(event, 'docs')" id="navDocs">Docs</a>
<a class="tablinks" id="navVst" href="https://vstsearch.softwaremanager.xyz">VST Search</a>
<!-- <a class="tablinks" onclick="openTab(event, 'notfound')">404</a> -->
</nav>
</header>
@@ -187,6 +188,17 @@
</div>
</div>
<div id="vstsearch" class="tabcontent">
<div class="cse-search">
<h3 class="vst-logo"><span style="color: #db65d5">VST Search</span></h3>
<div class="gcse-searchbox"></div>
<div class="gcse-searchresults"></div>
</div>
</div>
<div id="notfound" class="tabcontent">
<div class="notfound-frame">
<h1 class="notfound-title">404</h1>
@@ -203,6 +215,20 @@
</footer>
<script>
const MAIN_ORIGIN = 'https://softwaremanager.xyz';
const VST_ORIGIN = 'https://vstsearch.softwaremanager.xyz';
const onVstDomain = window.location.hostname.startsWith('vstsearch.');
let cseLoaded = false;
function loadCSE() {
if (cseLoaded) return;
cseLoaded = true;
var s = document.createElement('script');
s.async = true;
s.src = 'https://cse.google.com/cse.js?cx=e7334cef72a8b42e7';
document.head.appendChild(s);
}
function openTab(evt, tabName, pushToHistory = true) {
var i, tabcontent, tablinks;
@@ -218,6 +244,8 @@
var target = document.getElementById(tabName);
target.style.display = "block";
if (tabName === 'vstsearch') loadCSE();
// restart animation
target.classList.remove('tab-animate');
void target.offsetWidth;
@@ -247,6 +275,10 @@
// Determine the correct tab immediately (before full load) to prevent flash
(function() {
if (onVstDomain) {
document.getElementById('vstsearch').style.display = 'block';
return;
}
let currentPath = window.location.pathname;
let tabName = 'start';
if (currentPath === '/api') tabName = 'api';
@@ -257,9 +289,23 @@
})();
window.onload = function() {
if (onVstDomain) {
document.getElementById('navHome').href = MAIN_ORIGIN + '/';
document.getElementById('navApi').href = MAIN_ORIGIN + '/api';
document.getElementById('navDocs').href = MAIN_ORIGIN + '/docs';
['navHome', 'navApi', 'navDocs'].forEach(function(id) {
document.getElementById(id).removeAttribute('onclick');
});
openTab(null, 'vstsearch', false);
document.getElementById('navVst').className += " active";
return;
}
let currentPath = window.location.pathname;
if (currentPath === '/api') {
if (currentPath === '/vstsearch') {
window.location.replace(VST_ORIGIN);
} else if (currentPath === '/api') {
openTab(null, 'api', false);
} else if (currentPath === '/docs') {
openTab(null, 'docs', false);
+78
View File
@@ -378,6 +378,84 @@ pre.api-response {
font-size: 2rem;
}
.cse-search {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 70vh;
text-align: center;
}
.cse-search .vst-logo {
font-family: var(--main-font);
font-size: 3.5rem;
font-weight: 700;
letter-spacing: -0.02em;
margin-bottom: 1.5rem;
user-select: none;
}
.cse-search .gsc-control-cse,
.cse-search .gsc-results-wrapper-overlay,
.cse-search .gsc-results-wrapper-visible {
background: transparent !important;
border: none !important;
}
.cse-search .gsc-input-box,
.cse-search .gsc-input-box.gsc-input-box-hover,
.cse-search .gsc-input-box.gsc-input-box-focus {
background: #1a1b1d !important;
border-color: #1a1b1d !important;
}
.cse-search .gsc-input,
.cse-search input.gsc-input,
.cse-search .gsib_a input.gsc-input {
background: transparent !important;
color: #fff !important;
}
.cse-search .gsib_a {
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none'><path fill-rule='evenodd' clip-rule='evenodd' d='M14.76 13.27L20.49 19L19 20.49L13.27 14.76C12.2 15.53 10.91 16 9.5 16C5.91 16 3 13.09 3 9.5C3 5.91 5.91 3 9.5 3C13.09 3 16 5.91 16 9.5C16 10.91 15.53 12.2 14.76 13.27ZM9.5 5C7.01 5 5 7.01 5 9.5C5 11.99 7.01 14 9.5 14C11.99 14 14 11.99 14 9.5C14 7.01 11.99 5 9.5 5Z' fill='%23ffffff'/></svg>") !important;
}
.cse-search .gsc-search-button-v2 {
background: #db65d5 !important;
border-color: #db65d5 !important;
}
.cse-search .gsc-search-button-v2:hover {
background: #c94dc3 !important;
border-color: #c94dc3 !important;
}
.cse-search .gcsc-more-maybe-branding-root,
.gcsc-find-more-on-google {
display: none !important;
}
.gsc-completion-container,
.gssb_c, .gssb_e,
table.gssb_c, table.gssb_e {
background: #1a1b1d !important;
border-color: #333 !important;
color: #fff !important;
}
.gsc-completion-container td,
.gsc-completion-container .gsc-completion-suggestion,
.gssb_a, .gssb_a td, .gssb_i {
background: #1a1b1d !important;
color: #fff !important;
}
.gsc-completion-container .gsc-completion-selected,
.gsc-completion-container tr:hover td,
.gssb_a:hover td, .gssb_e-hover {
background: #2a2b2d !important;
}
@media(max-width: 560px) {
+55
View File
@@ -0,0 +1,55 @@
// Cloudflare Pages middleware.
//
// Both softwaremanager.xyz and vstsearch.softwaremanager.xyz serve the same
// index.html. Crawlers (Discord, Twitter, etc.) read the raw HTML and do NOT
// run JavaScript, so we rewrite the <title> and Open Graph / Twitter meta tags
// server-side when the request is for the VST Search subdomain. This gives the
// subdomain its own link embed without a separate deploy.
const VST = {
title: "VST Search - SoftwareManager",
description: "Search for plugins easily",
url: "https://vstsearch.softwaremanager.xyz/",
};
class AttributeSetter {
constructor(attribute, value) {
this.attribute = attribute;
this.value = value;
}
element(element) {
element.setAttribute(this.attribute, this.value);
}
}
class ContentSetter {
constructor(value) {
this.value = value;
}
element(element) {
element.setInnerContent(this.value);
}
}
export async function onRequest(context) {
const { request, next } = context;
const response = await next();
const onVstDomain = new URL(request.url).hostname.startsWith("vstsearch.");
const contentType = response.headers.get("content-type") || "";
// Leave everything untouched on the main domain and for non-HTML responses.
if (!onVstDomain || !contentType.includes("text/html")) {
return response;
}
return new HTMLRewriter()
.on("title", new ContentSetter(VST.title))
.on("meta[name='description']", new AttributeSetter("content", VST.description))
.on("meta[property='og:title']", new AttributeSetter("content", VST.title))
.on("meta[property='og:description']", new AttributeSetter("content", VST.description))
.on("meta[property='og:url']", new AttributeSetter("content", VST.url))
.on("meta[name='twitter:title']", new AttributeSetter("content", VST.title))
.on("meta[name='twitter:description']", new AttributeSetter("content", VST.description))
.transform(response);
}
+3
View File
@@ -0,0 +1,3 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
+3 -3
View File
@@ -1,8 +1,8 @@
requests==2.33.1
PySide6-Essentials==6.11.0
requests==2.34.2
PySide6-Essentials==6.11.1
beautifulsoup4==4.14.3
darkdetect==0.7.1
pyinstaller==6.19.0
pyinstaller==6.20.0
PyQtDarkTheme-fork==2.3.6
plyer==2.1.0
psutil==7.2.2
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
echo "Installing Dependencies"
mkdir build
cd build
curl -L -o appimagetool-x86_64.AppImage https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x appimagetool-x86_64.AppImage
curl -L -o python.AppImage https://github.com/niess/python-appimage/releases/download/python3.13/python3.13.9-cp313-cp313-manylinux2014_x86_64.AppImage
chmod +x python.AppImage
./python.AppImage --appimage-extract
mv squashfs-root SoftwareManager.AppDir
echo "Build SoftwareManager.whl"
cd ..
build/SoftwareManager.AppDir/AppRun -m build
echo "Prepare SoftwareManager AppImage"
cd build/SoftwareManager.AppDir
./AppRun -m pip install -r ../../requirements.txt
./AppRun -m pip install ../../dist/main-0.0.0-py3-none-any.whl
rm AppRun .DirIcon python.png python*.desktop usr/share/applications/python*.desktop
cp -t ./softwaremanager.png ../../src/interface/assets/logo.png
cp -t . ../../SoftwareManager.desktop
cp ../../SoftwareManager.desktop usr/share/applications/
echo '#! /bin/bash
HERE="$(dirname "$(readlink -f "${0}")")"
export PATH=$HERE/usr/bin:$PATH;
export APPIMAGE_COMMAND=$(command -v -- "$ARGV0")
"$HERE/opt/python3.13/bin/python3.13" "-m" "main" "$@"' > AppRun
chmod -R 0755 ../SoftwareManager.AppDir
chmod +x AppRun
cp /usr/lib/x86_64-linux-gnu/libxcb-cursor.so* ../SoftwareManager.AppDir/usr/lib/
cp /usr/lib/x86_64-linux-gnu/libxcb-xinerama.so* ../SoftwareManager.AppDir/usr/lib/
cp /usr/lib/x86_64-linux-gnu/libxcb.so* ../SoftwareManager.AppDir/usr/lib/
cp /usr/lib/x86_64-linux-gnu/libgssapi_krb5.so* ../SoftwareManager.AppDir/usr/lib
echo " => Build SoftwareManager AppImage"
cd ..
./appimagetool-x86_64.AppImage --appimage-extract
squashfs-root/AppRun SoftwareManager.AppDir
cd ..
echo " => Done "
+1 -1
View File
@@ -113,7 +113,7 @@ class SteamripScraper:
try:
consoleLog(f"Found download link: {best}")
if "buzzheavier" in best:
if "buzzheavier" or "bzzhr" in best:
link = scrape_buzzheavier(best)
return link
elif "gofile" in best:
-1
View File
@@ -12,7 +12,6 @@ import threading
import webbrowser
import subprocess
import platform
import time
import os
+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}")
return
filename = (
raw_filename = (
detect_filename_from_headers(url, DirectDownloadHandle.USER_AGENT)
or extract_filename_from_url(url)
or sanitize_filename(title) + ".zip"
)
filename = sanitize_filename(raw_filename)
handle = DirectDownloadHandle(url, filename, dl_path, headers, single_threaded)
with state.downloads_lock:
state.active_downloads[url] = handle
+17
View File
@@ -10,6 +10,7 @@ import requests
import hashlib
import json
import time
import sys
import os
@@ -231,7 +232,23 @@ class DirectDownloadHandle:
def _preallocate_file(self, total_size: int):
if os.path.exists(self._file_path) and os.path.getsize(self._file_path) == total_size:
return
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)
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:
parts = cd.split("filename=")
if len(parts) > 1:
fname = parts[-1].strip().strip('"').strip("'")
fname = parts[1].split(";")[0].strip().strip('"').strip("'")
if fname:
return fname
except Exception as e:
-1
View File
@@ -4,7 +4,6 @@ from PySide6.QtCore import QObject, QTimer
from utils.data.state import state
from plyer import notification
import libtorrent as lt
import time
import os