diff --git a/CHANGELOG.md b/CHANGELOG.md index b7116d39..35f8ae13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Audio export failing when Tauri save dialog returns object instead of string path +- PyInstaller sidecar packaging now bundles `qwen_tts` source files needed at runtime (fixes missing `_MEI/.../qwen_tts/core/models/modeling_qwen3_tts.py` during model initialization) ### Added - **Makefile** - Comprehensive development workflow automation with commands for setup, development, building, testing, and code quality checks diff --git a/backend/build_binary.py b/backend/build_binary.py index 73f21d23..a7157d2f 100644 --- a/backend/build_binary.py +++ b/backend/build_binary.py @@ -64,7 +64,9 @@ def build_server(): '--hidden-import', 'qwen_tts.cli', '--copy-metadata', 'qwen-tts', '--collect-submodules', 'qwen_tts', - '--collect-data', 'qwen_tts', + # Ensure qwen_tts Python source files are materialized in the bundle + # (needed by runtime code paths that access modeling_qwen3_tts.py by path). + '--collect-all', 'qwen_tts', # Fix for pkg_resources and jaraco namespace packages '--hidden-import', 'pkg_resources.extern', '--collect-submodules', 'jaraco', diff --git a/backend/voicebox-server.spec b/backend/voicebox-server.spec index feccfae0..80ac622d 100644 --- a/backend/voicebox-server.spec +++ b/backend/voicebox-server.spec @@ -1,19 +1,19 @@ # -*- mode: python ; coding: utf-8 -*- -from PyInstaller.utils.hooks import collect_data_files from PyInstaller.utils.hooks import collect_submodules from PyInstaller.utils.hooks import copy_metadata datas = [] hiddenimports = ['backend', 'backend.main', 'backend.config', 'backend.database', 'backend.models', 'backend.profiles', 'backend.history', 'backend.tts', 'backend.transcribe', 'backend.platform_detect', 'backend.backends', 'backend.backends.pytorch_backend', 'backend.utils.audio', 'backend.utils.cache', 'backend.utils.progress', 'backend.utils.hf_progress', 'backend.utils.validation', 'torch', 'transformers', 'fastapi', 'uvicorn', 'sqlalchemy', 'librosa', 'soundfile', 'qwen_tts', 'qwen_tts.inference', 'qwen_tts.inference.qwen3_tts_model', 'qwen_tts.inference.qwen3_tts_tokenizer', 'qwen_tts.core', 'qwen_tts.cli', 'pkg_resources.extern', 'backend.backends.mlx_backend', 'mlx', 'mlx.core', 'mlx.nn', 'mlx_audio', 'mlx_audio.tts', 'mlx_audio.stt'] -datas += collect_data_files('qwen_tts') # Use collect_all (not collect_data_files) so native .dylib and .metallib # files are bundled as binaries, not data. Without this, MLX raises OSError # when loading Metal shaders inside the PyInstaller bundle. from PyInstaller.utils.hooks import collect_all as _collect_all +_qwen_datas, _qwen_bins, _qwen_hidden = _collect_all('qwen_tts') _mlx_datas, _mlx_bins, _mlx_hidden = _collect_all('mlx') _mlxa_datas, _mlxa_bins, _mlxa_hidden = _collect_all('mlx_audio') -datas += _mlx_datas + _mlxa_datas +datas += _qwen_datas + _mlx_datas + _mlxa_datas datas += copy_metadata('qwen-tts') +hiddenimports += _qwen_hidden hiddenimports += collect_submodules('qwen_tts') hiddenimports += collect_submodules('jaraco') hiddenimports += collect_submodules('mlx') @@ -23,7 +23,7 @@ hiddenimports += collect_submodules('mlx_audio') a = Analysis( ['server.py'], pathex=[], - binaries=_mlx_bins + _mlxa_bins, + binaries=_qwen_bins + _mlx_bins + _mlxa_bins, datas=datas, hiddenimports=hiddenimports, hookspath=[],