mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-17 05:40:42 -07:00
Add the ability to download a CUDA-enabled backend binary (~2.4 GB) and swap it in via a backend-only restart, solving the #1 user pain point (19 open 'GPU not detected' issues caused by GitHub's 2 GB asset limit). Backend: - cuda_download.py: download from R2 (primary) or GitHub split-parts (fallback), SHA-256 verification, atomic writes, progress via SSE - 4 new endpoints: GET/POST/DELETE /backend/cuda-*, GET cuda-progress - server.py: --version flag, auto-detect variant from binary name - build_binary.py: --cuda flag for CUDA PyInstaller builds - split_binary.py: split large binaries into <2GB GitHub Release assets - CI workflow for building CUDA binary Tauri: - restart_server command (stop -> wait -> start) - start_server prefers CUDA binary from {data_dir}/backends/ if present - Version mismatch check: runs --version before launching CUDA binary Frontend: - GpuAcceleration component: download, progress, restart, switch, delete - API client + types for CUDA status and management - Platform lifecycle: restartServer() on Tauri/Web - Aggressive 1s health polling during restart for fast reconnection
81 lines
2.5 KiB
TypeScript
81 lines
2.5 KiB
TypeScript
import { invoke } from '@tauri-apps/api/core';
|
|
import { emit, listen } from '@tauri-apps/api/event';
|
|
import type { PlatformLifecycle } from '@/platform/types';
|
|
|
|
class TauriLifecycle implements PlatformLifecycle {
|
|
onServerReady?: () => void;
|
|
|
|
async startServer(remote = false): Promise<string> {
|
|
try {
|
|
const result = await invoke<string>('start_server', { remote });
|
|
console.log('Server started:', result);
|
|
this.onServerReady?.();
|
|
return result;
|
|
} catch (error) {
|
|
console.error('Failed to start server:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async stopServer(): Promise<void> {
|
|
try {
|
|
await invoke('stop_server');
|
|
console.log('Server stopped');
|
|
} catch (error) {
|
|
console.error('Failed to stop server:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async restartServer(): Promise<string> {
|
|
try {
|
|
const result = await invoke<string>('restart_server');
|
|
console.log('Server restarted:', result);
|
|
this.onServerReady?.();
|
|
return result;
|
|
} catch (error) {
|
|
console.error('Failed to restart server:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async setKeepServerRunning(keepRunning: boolean): Promise<void> {
|
|
try {
|
|
await invoke('set_keep_server_running', { keepRunning });
|
|
} catch (error) {
|
|
console.error('Failed to set keep server running setting:', error);
|
|
}
|
|
}
|
|
|
|
async setupWindowCloseHandler(): Promise<void> {
|
|
try {
|
|
// Listen for window close request from Rust
|
|
await listen<null>('window-close-requested', async () => {
|
|
// Import store here to avoid circular dependency
|
|
const { useServerStore } = await import('@/stores/serverStore');
|
|
const keepRunning = useServerStore.getState().keepServerRunningOnClose;
|
|
|
|
// Check if server was started by this app instance
|
|
// @ts-expect-error - accessing module-level variable from another module
|
|
const serverStartedByApp = window.__voiceboxServerStartedByApp ?? false;
|
|
|
|
if (!keepRunning && serverStartedByApp) {
|
|
// Stop server before closing (only if we started it)
|
|
try {
|
|
await this.stopServer();
|
|
} catch (error) {
|
|
console.error('Failed to stop server on close:', error);
|
|
}
|
|
}
|
|
|
|
// Emit event back to Rust to allow close
|
|
await emit('window-close-allowed');
|
|
});
|
|
} catch (error) {
|
|
console.error('Failed to setup window close handler:', error);
|
|
}
|
|
}
|
|
}
|
|
|
|
export const tauriLifecycle = new TauriLifecycle();
|