mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-29 07:05:14 -07:00
Compare commits
4
Commits
fix/misc-bugs
...
v0.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac68052945 | ||
|
|
e601fd2ca4 | ||
|
|
7b25e0ba0b | ||
|
|
a6817cd082 |
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 0.2.3
|
current_version = 0.3.0
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
tag_name = v{new_version}
|
tag_name = v{new_version}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ app/openapi.json
|
|||||||
tauri/src-tauri/binaries/*
|
tauri/src-tauri/binaries/*
|
||||||
tauri/src-tauri/gen/Assets.car
|
tauri/src-tauri/gen/Assets.car
|
||||||
tauri/src-tauri/gen/voicebox.icns
|
tauri/src-tauri/gen/voicebox.icns
|
||||||
|
tauri/src-tauri/gen/partial.plist
|
||||||
|
|
||||||
# Temporary
|
# Temporary
|
||||||
tmp/
|
tmp/
|
||||||
|
|||||||
+29
-2
@@ -7,9 +7,25 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
This release rewrites the backend into a modular architecture, migrates the documentation site to Fumadocs, and ships a batch of bug fixes and UI polish across the stack.
|
## [0.3.0] - 2026-03-17
|
||||||
|
|
||||||
The backend's 3,000-line monolith `main.py` has been decomposed into domain routers, a services layer, and a proper database package. A style guide and ruff configuration now enforce consistency. On the frontend, model loading status is now visible in the UI, effects presets get a dropdown, and several race conditions and accessibility gaps are closed.
|
This release rewrites the backend into a modular architecture, overhauls the settings UI into routed sub-pages, fixes audio player freezing, migrates documentation to Fumadocs, and ships a batch of bug fixes targeting the most-reported issues from the tracker.
|
||||||
|
|
||||||
|
The backend's 3,000-line monolith `main.py` has been decomposed into domain routers, a services layer, and a proper database package. A style guide and ruff configuration now enforce consistency. On the frontend, settings have been split into dedicated routed pages with server logs, a changelog viewer, and an about page. The audio player no longer freezes mid-playback, and model loading status is now visible in the UI. Seven user-reported bugs have been fixed, including server crashes during sample uploads, generation list staleness, cryptic error messages, and CUDA support for RTX 50-series GPUs.
|
||||||
|
|
||||||
|
### Settings Overhaul ([#294](https://github.com/jamiepine/voicebox/pull/294))
|
||||||
|
- Split settings into routed sub-tabs: General, Generation, GPU, Logs, Changelog, About
|
||||||
|
- Added live server log viewer with auto-scroll
|
||||||
|
- Added in-app changelog page that parses `CHANGELOG.md` at build time
|
||||||
|
- Added About page with version info, license, and generation folder quick-open
|
||||||
|
- Extracted reusable `SettingRow` component for consistent setting layouts
|
||||||
|
|
||||||
|
### Audio Player Fix ([#293](https://github.com/jamiepine/voicebox/pull/293))
|
||||||
|
- Fixed audio player freezing during playback
|
||||||
|
- Improved playback UX with better state management and listener cleanup
|
||||||
|
- Fixed restart race condition during regeneration
|
||||||
|
- Added stable keys for audio element re-rendering
|
||||||
|
- Improved accessibility across player controls
|
||||||
|
|
||||||
### Backend Refactor ([#285](https://github.com/jamiepine/voicebox/pull/285))
|
### Backend Refactor ([#285](https://github.com/jamiepine/voicebox/pull/285))
|
||||||
- Extracted all routes from `main.py` into 13 domain routers under `backend/routes/` — `main.py` dropped from ~3,100 lines to ~10
|
- Extracted all routes from `main.py` into 13 domain routers under `backend/routes/` — `main.py` dropped from ~3,100 lines to ~10
|
||||||
@@ -40,6 +56,17 @@ The backend's 3,000-line monolith `main.py` has been decomposed into domain rout
|
|||||||
- Softened select focus indicator opacity
|
- Softened select focus indicator opacity
|
||||||
- Addressed 4 critical and 12 major issues from CodeRabbit review
|
- Addressed 4 critical and 12 major issues from CodeRabbit review
|
||||||
|
|
||||||
|
### Bug Fixes ([#295](https://github.com/jamiepine/voicebox/pull/295))
|
||||||
|
- Fixed sample uploads crashing the server — audio decoding now runs in a thread pool instead of blocking the async event loop ([#278](https://github.com/jamiepine/voicebox/issues/278))
|
||||||
|
- Fixed generation list not updating when a generation completes — switched to `refetchQueries` for reliable cache busting, added SSE error fallback, and page reset on completion ([#231](https://github.com/jamiepine/voicebox/issues/231))
|
||||||
|
- Fixed error toasts showing `[object Object]` instead of the actual error message ([#290](https://github.com/jamiepine/voicebox/issues/290))
|
||||||
|
- Added Whisper model selection (`base`, `small`, `medium`, `large`, `turbo`) and expanded language support to the `/transcribe` endpoint ([#233](https://github.com/jamiepine/voicebox/issues/233))
|
||||||
|
- Upgraded CUDA backend build from cu121 to cu126 for RTX 50-series (Blackwell) GPU support ([#289](https://github.com/jamiepine/voicebox/issues/289))
|
||||||
|
- Handled client disconnects in SSE and streaming endpoints to suppress `[Errno 32] Broken Pipe` errors ([#248](https://github.com/jamiepine/voicebox/issues/248))
|
||||||
|
- Fixed Docker build failure from pip hash mismatch on Qwen3-TTS dependencies ([#286](https://github.com/jamiepine/voicebox/issues/286))
|
||||||
|
- Added 50 MB upload size limit with chunked reads to prevent unbounded memory allocation on sample uploads
|
||||||
|
- Eliminated redundant double audio decode in sample processing pipeline
|
||||||
|
|
||||||
### Platform Fixes
|
### Platform Fixes
|
||||||
- Replaced `netstat` with `TcpStream` + PowerShell for Windows port detection ([#277](https://github.com/jamiepine/voicebox/pull/277))
|
- Replaced `netstat` with `TcpStream` + PowerShell for Windows port detection ([#277](https://github.com/jamiepine/voicebox/pull/277))
|
||||||
- Fixed Docker frontend build and cleaned up Docker docs
|
- Fixed Docker frontend build and cleaned up Docker docs
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@voicebox/app",
|
"name": "@voicebox/app",
|
||||||
"version": "0.2.3",
|
"version": "0.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
# Backend package
|
# Backend package
|
||||||
|
|
||||||
__version__ = "0.2.3"
|
__version__ = "0.3.0"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@voicebox/landing",
|
"name": "@voicebox/landing",
|
||||||
"version": "0.2.3",
|
"version": "0.3.0",
|
||||||
"description": "Landing page for voicebox.sh",
|
"description": "Landing page for voicebox.sh",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun --bun next dev --turbo",
|
"dev": "bun --bun next dev --turbo",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "voicebox",
|
"name": "voicebox",
|
||||||
"version": "0.2.3",
|
"version": "0.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"app",
|
"app",
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@voicebox/tauri",
|
"name": "@voicebox/tauri",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.2.3",
|
"version": "0.3.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "voicebox"
|
name = "voicebox"
|
||||||
version = "0.2.3"
|
version = "0.3.0"
|
||||||
description = "A production-quality desktop app for Qwen3-TTS voice cloning and generation"
|
description = "A production-quality desktop app for Qwen3-TTS voice cloning and generation"
|
||||||
authors = ["you"]
|
authors = ["you"]
|
||||||
license = ""
|
license = ""
|
||||||
|
|||||||
@@ -23,15 +23,15 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let project_root = env!("CARGO_MANIFEST_DIR");
|
||||||
|
let gen_dir = format!("{}/gen", project_root);
|
||||||
|
std::fs::create_dir_all(&gen_dir).expect("Failed to create gen directory");
|
||||||
|
|
||||||
// Compile macOS Liquid Glass icon
|
// Compile macOS Liquid Glass icon
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
{
|
{
|
||||||
let project_root = env!("CARGO_MANIFEST_DIR");
|
|
||||||
// voicebox.icon is in tauri/assets/voicebox.icon (one level up from src-tauri)
|
// voicebox.icon is in tauri/assets/voicebox.icon (one level up from src-tauri)
|
||||||
let icon_source = format!("{}/../assets/voicebox.icon", project_root);
|
let icon_source = format!("{}/../assets/voicebox.icon", project_root);
|
||||||
let gen_dir = format!("{}/gen", project_root);
|
|
||||||
|
|
||||||
std::fs::create_dir_all(&gen_dir).expect("Failed to create gen directory");
|
|
||||||
|
|
||||||
if std::path::Path::new(&icon_source).exists() {
|
if std::path::Path::new(&icon_source).exists() {
|
||||||
println!("cargo:rerun-if-changed={}", icon_source);
|
println!("cargo:rerun-if-changed={}", icon_source);
|
||||||
@@ -76,6 +76,71 @@ fn main() {
|
|||||||
panic!("Icon compilation failed");
|
panic!("Icon compilation failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate voicebox.icns from the source PNG via sips + iconutil
|
||||||
|
let icns_path = format!("{}/voicebox.icns", gen_dir);
|
||||||
|
if !std::path::Path::new(&icns_path).exists() {
|
||||||
|
let source_png = format!("{}/Assets/Voicebox.png", icon_source);
|
||||||
|
if std::path::Path::new(&source_png).exists() {
|
||||||
|
let iconset_dir = format!("{}/voicebox.iconset", gen_dir);
|
||||||
|
std::fs::create_dir_all(&iconset_dir).ok();
|
||||||
|
|
||||||
|
let sizes: &[(u32, &str)] = &[
|
||||||
|
(16, "icon_16x16.png"),
|
||||||
|
(32, "[email protected]"),
|
||||||
|
(32, "icon_32x32.png"),
|
||||||
|
(64, "[email protected]"),
|
||||||
|
(128, "icon_128x128.png"),
|
||||||
|
(256, "[email protected]"),
|
||||||
|
(256, "icon_256x256.png"),
|
||||||
|
(512, "[email protected]"),
|
||||||
|
(512, "icon_512x512.png"),
|
||||||
|
(1024, "[email protected]"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (size, name) in sizes {
|
||||||
|
let dest = format!("{}/{}", iconset_dir, name);
|
||||||
|
let status = Command::new("sips")
|
||||||
|
.args([
|
||||||
|
"-z",
|
||||||
|
&size.to_string(),
|
||||||
|
&size.to_string(),
|
||||||
|
&source_png,
|
||||||
|
"--out",
|
||||||
|
&dest,
|
||||||
|
])
|
||||||
|
.output();
|
||||||
|
if let Ok(out) = status {
|
||||||
|
if !out.status.success() {
|
||||||
|
eprintln!(
|
||||||
|
"sips failed for {}: {}",
|
||||||
|
name,
|
||||||
|
String::from_utf8_lossy(&out.stderr)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let iconutil_output = Command::new("iconutil")
|
||||||
|
.args(["-c", "icns", "-o", &icns_path, &iconset_dir])
|
||||||
|
.output();
|
||||||
|
|
||||||
|
match iconutil_output {
|
||||||
|
Ok(out) if out.status.success() => {
|
||||||
|
println!("Generated voicebox.icns");
|
||||||
|
}
|
||||||
|
Ok(out) => {
|
||||||
|
eprintln!("iconutil failed: {}", String::from_utf8_lossy(&out.stderr));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to run iconutil: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up iconset directory
|
||||||
|
std::fs::remove_dir_all(&iconset_dir).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
"cargo:warning=Icon source not found at {}, skipping icon compilation",
|
"cargo:warning=Icon source not found at {}, skipping icon compilation",
|
||||||
@@ -84,5 +149,18 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure all resource files exist so Tauri's bundler doesn't fail.
|
||||||
|
// On non-macOS these are always stubs. On macOS, actool may not produce
|
||||||
|
// Assets.car if the Xcode version doesn't support the .icon format.
|
||||||
|
{
|
||||||
|
let required = ["Assets.car", "voicebox.icns", "partial.plist"];
|
||||||
|
for name in required {
|
||||||
|
let path = format!("{}/{}", gen_dir, name);
|
||||||
|
if !std::path::Path::new(&path).exists() {
|
||||||
|
std::fs::write(&path, b"").ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tauri_build::build()
|
tauri_build::build()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Voicebox",
|
"productName": "Voicebox",
|
||||||
"version": "0.2.3",
|
"version": "0.3.0",
|
||||||
"identifier": "sh.voicebox.app",
|
"identifier": "sh.voicebox.app",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "bun run dev",
|
"beforeDevCommand": "bun run dev",
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@voicebox/web",
|
"name": "@voicebox/web",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.2.3",
|
"version": "0.3.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
Reference in New Issue
Block a user