- Python 39.1%
- Go 36.8%
- Shell 20.8%
- Makefile 3.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
- Fix qb-webui loading screen bug: CSS "%" was misparsed by fmt.Sprintf causing "%!s(MISSING)" to render. Switched to html/template. - qb-webui: exponential backoff retries, new -autostart flag for standalone use without the GUI. - qb-main: new status/start/stop/version subcommands (daemon.go) to manage qbittorrent-nox headlessly, e.g. over SSH. - core/main.py split into config.py, netutil.py, qbit_process.py, theme.py, dialogs.py, main.py. - GUI: new Settings dialog (zoom/window size/kiosk/start-hidden), new Logs dialog, desktop notifications on start/stop/restart. - Fix: stdout was being closed right after password capture, risking SIGPIPE in the still-running daemon. Now drained continuously, which also feeds the new Logs dialog. |
||
| assets | ||
| builder | ||
| core | ||
| go-src | ||
| .gitignore | ||
| install.sh | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
qbit-forge
A lightweight, privacy-first management suite for qbittorrent-nox on Debian Linux — including ARM64 and proot-Termux environments.
qbit-forge wraps qbittorrent-nox with a PyQt6 desktop GUI, a native WebUI launcher,
and a package manager. Everything runs locally, offline-first, with no cloud dependency.
Table of Contents
- Architecture
- Project Structure
- Requirements
- Quick Start
- Build
- Install
- Components
- How It Works
- Known Limitations / TODO
- Author
Architecture
┌─────────────────────────────────────────┐
│ qbit-forge (GUI) │ PyQt6 — system tray app
│ Start · Stop · Restart · WebUI │
└──────────────┬──────────────────────────┘
│ subprocess + poll
▼
┌──────────────────────┐ ┌──────────────────────┐
│ qbittorrent-nox │──▶│ qb-webui │
│ (managed process) │ │ (GTK native webview)│
└──────────────────────┘ └──────────────────────┘
┌──────────────────────┐
│ qb-main │ standalone CLI — install / upgrade / clean
└──────────────────────┘
Project Structure
qbit-forge/
├── assets/
│ └── qbit-forge.png # Application icon
├── bin/ # Built binaries (git-ignored)
│ ├── qbit-forge # Desktop GUI binary
│ ├── qb-main # Package manager CLI
│ └── qb-webui # Native WebUI launcher
├── builder/
│ ├── build-bin.sh # Go build script (qb-main + qb-webui)
│ └── build-main.sh # PyInstaller build script
├── core/
│ ├── main.py # GUI entry point (MainWindow, wiring)
│ ├── config.py # Paths, constants, persisted settings
│ ├── netutil.py # Port allocation, WebUI-readiness checks
│ ├── qbit_process.py # Start/stop qbittorrent-nox, credentials, log buffer
│ ├── theme.py # Catppuccin Mocha stylesheet + tray icon
│ ├── dialogs.py # Credentials / Settings / Logs windows
│ ├── qbit-forge.spec # PyInstaller spec
│ └── requirements.txt # Python dependencies
├── go-src/
│ ├── qb-main/ # qb-main CLI source (Go)
│ │ ├── main.go # CLI dispatch
│ │ ├── manager.go # Install / Update / Upgrade / Clean
│ │ ├── daemon.go # Status / Start / Stop / Version
│ │ ├── runner.go # Privilege-aware command runner
│ │ ├── deb_install.go # .deb fallback installer
│ │ └── print.go # Colored output helpers
│ └── qb-webui/ # Native WebUI launcher source (Go)
│ ├── main.go # webview wrapper, retry loop, autostart
│ ├── ui.go # Loading/error screen (html/template)
│ └── tls.go # TLS config (-insecure flag)
├── install.sh # Symlink + desktop entry installer
├── Makefile # Build / install / clean targets
├── LICENSE # MIT
└── README.md
Requirements
Runtime
| Dependency | Version | Purpose |
|---|---|---|
qbittorrent-nox |
any | The managed application |
| Python | 3.10+ | GUI runtime |
| PyQt6 | 6.4+ | GUI framework |
Build
| Tool | Version | Purpose |
|---|---|---|
| Go | 1.21+ | Build qb-main, qb-webui |
| GCC / libwebkit2gtk | — | Build qb-webui (CGO=1) |
| PyInstaller | 6.0+ | Build standalone qbit-forge GUI binary |
System (Debian/Ubuntu)
# qb-webui build dependency
sudo apt-get install libwebkit2gtk-4.0-dev gcc
# GUI runtime
pip install PyQt6
Quick Start
# 1. Clone
git clone https://github.com/dev-boffin-io/qbit-forge
cd qbit-forge
# 2. Install qbittorrent-nox (if not already installed)
./bin/qb-main install
# 3. Build everything
make build
# 4. Install (symlinks + desktop entry)
make install
# 5. Launch
qbit-forge
Build
All at once
make build
Go binaries only
make build-bin
# Cross-compile for ARM64
./builder/build-bin.sh --arch arm64
# Specific OS/arch
./builder/build-bin.sh --os linux --arch amd64
qb-main is built with CGO=0 (fully static, cross-compile supported).
qb-webui is built with CGO=1 (requires host C compiler and libwebkit2gtk).
GUI binary only
make build-gui
The build script creates a fresh .venv, installs dependencies, runs PyInstaller,
and cleans up. Only bin/qbit-forge remains.
Run from source
pip install -r core/requirements.txt
make run
Install
make install
This runs install.sh which:
- Symlinks
qbit-forge,qb-main,qb-webuiinto~/.local/bin/ - Copies
assets/qbit-forge.pngto~/.local/share/icons/ - Writes
~/.local/share/applications/qbit-forge.desktop - Refreshes the desktop database
To uninstall:
make uninstall
Components
qbit-forge — Desktop GUI
A PyQt6 system tray application that manages a single qbittorrent-nox instance.
Features:
- Lives in the system tray — minimises to tray on close
- Auto-starts
qbittorrent-noxon launch - Captures the WebUI password automatically from process output
- Caches credentials so subsequent starts skip the capture step
- Auto-detects a free port starting from
8081 - Opens
qb-webui(native window) or browser fallback on ready - Start / Stop / Restart controls with confirmation dialogs
- Show Password dialog — displays username, password, and WebUI URL
- Settings dialog — WebUI zoom, window size, kiosk mode, start-hidden toggle
- Logs dialog — live-tails
qbittorrent-noxoutput from inside the GUI - Desktop notifications on start/stop/restart completion (via the tray icon)
- Continuously drains the daemon's stdout instead of closing it after the password is captured, avoiding a SIGPIPE-on-write risk for long sessions
State files (stored in $HOME):
| File | Purpose |
|---|---|
~/.qbit-forge-initialized |
First-run flag |
~/.qbit-forge-credentials |
Cached username:password |
~/.qbit-forge-port |
Active WebUI port |
~/.qbit-forge-settings.json |
Zoom / window size / kiosk / start-hidden |
qb-webui — Native WebUI Launcher
Opens the qBittorrent WebUI in a native GTK webview window. Built with webview/webview_go.
Usage:
qb-webui [flags]
Flags:
-url string WebUI URL (e.g. http://localhost:8081)
-host string Host (default: localhost)
-port int WebUI port (default: 8081)
-scheme string http or https (default: http)
-retries int Connection retry limit (default: 15)
-width int Window width in pixels (default: 1600)
-height int Window height in pixels (default: 960)
-zoom float Page zoom level (default: 1.0)
-kiosk Fullscreen kiosk mode
-insecure Skip TLS verification
-autostart If unreachable, try launching qbittorrent-nox
directly (PATH lookup). Lets qb-webui work
stand-alone without the qbit-forge GUI running it.
Examples:
qb-webui -url http://localhost:8081
qb-webui -url http://localhost:8081 -zoom 1.8
qb-webui -url https://myserver -insecure
# Stand-alone, without the GUI:
qb-webui -port 8081 -autostart
A Catppuccin Mocha loading screen is shown while connecting, with retries using exponential backoff (500ms → 3s cap) instead of a flat 1s poll. On failure, an error card appears with a Retry button.
Fixed: the loading screen used to be built with
fmt.Sprintfdirectly on the HTML/CSS string. A literal%in the CSS (border-radius: 50%;) was misparsed as a format verb, corrupting the page with%!s(MISSING). It's now rendered withhtml/template, which has no such issue.
Reminder:
qb-webuionly displays the WebUI — it does not startqbittorrent-noxfor you unless-autostartis passed. Normally theqbit-forgeGUI startsqbittorrent-noxfirst and launchesqb-webuionce it's ready.
qb-main — Package Manager
Handles installation, upgrade, and removal of qbittorrent-nox.
Usage:
qb-main install Install qbittorrent-nox (apt first, .deb fallback)
qb-main update Refresh apt cache
qb-main upgrade Upgrade to latest available version
qb-main reinstall Remove and cleanly reinstall qbittorrent-nox
qb-main clean Uninstall qbittorrent-nox + remove instance data
qb-main status Show whether qbittorrent-nox is running
qb-main start [--port N] Start qbittorrent-nox (headless, no GUI needed)
qb-main stop Stop the running instance
qb-main version Print qb-main and qbittorrent-nox versions
install strategy: tries apt-get install first; if apt is unavailable
or fails, falls back to downloading a .deb directly from ftp.debian.org.
upgrade strategy: runs apt-get install --only-upgrade; falls back to
.deb download if apt is unavailable.
clean: purges the package via apt/dpkg and removes ~/.config/qb-manager/.
status / start / stop: manage the same qbittorrent-nox instance
the qbit-forge GUI manages — both read/write ~/.qbit-forge-port, so you
can start it from the GUI and check on it from a terminal (or vice versa),
including headless over SSH where there's no display for the GUI at all.
How It Works
First run
On the very first launch, qbit-forge spawns qbittorrent-nox in the foreground,
reads its stdout to capture the auto-generated temporary password, saves it to
~/.qbit-forge-credentials, and marks initialization complete with
~/.qbit-forge-initialized.
Subsequent runs
Credentials are read from ~/.qbit-forge-credentials. qbittorrent-nox is
started in daemon mode (-d) with the saved port. The GUI polls the WebUI
endpoint until it responds, then opens qb-webui.
Password capture
qbittorrent-nox prints the temporary WebUI password to stdout on first start.
qbit-forge reads this line with a regex, saves admin:<password> to the
credentials file, and uses it for all subsequent API calls and the Show Password
dialog.
Port detection
On each start, qbit-forge scans ports from 8081 upward and picks the first
free one. The chosen port is saved to ~/.qbit-forge-port.
Known Limitations / TODO
- Password capture relies on stdout — versions that write only to
/dev/ttyfall back toadminadmin qb-mainonly supports Debian/dpkg-based systemsqb-webuikiosk mode relies on window manager co-operation- No torrent management UI (add, remove, pause individual torrents)
- GUI binary not yet signed or packaged as
.deb/ AppImage — fixed, now usesqb-webui's loading screen could be corrupted by%in its CSShtml/templateClosing stdout right after password capture risked SIGPIPE in a live daemon— fixed, stdout is drained continuously instead
Author
Sumit — github.com/dev-boffin-io