29ebbfc1c0
- Thay librosa bang app/core/audio_features.py (numpy/scipy/soundfile):
load, beat_track, frames_to_time, spectral_centroid, rms, zero_crossing_rate,
time_stretch, pitch_shift, chroma_stft. A/B ngang librosa (BPM <1% sai lech,
pitch_shift chuan toi Hz). Loai bo llvmlite 171MB + scikit-learn + numba.
- Task layer 2 che do (app/tasks/worker.py): server giu celery; desktop slim
chay in-process thread + registry, giu nguyen API contract (.delay/.id/status
/tasks/{id}) nen frontend khong doi.
- engine.spec: excludes librosa/numba/llvmlite/sklearn/celery/redis/kombu/
billiard/amqp/msgpack/yaml/PIL/cairosvg/zstandard/...; scan scipy gioi han
scipy.signal; giu click (uvicorn.main import click).
- render_engine: scipy.signal thanh lazy import (giam cold start).
- tauri.conf.json: targets [nsis, msi] - NSIS tro lai (bundle nho) de
hooks.nsh cai VC++ Redistributable - sua bug daw_engine.exe khong chay
tren Windows (truoc day MSI-only khong chay hooks).
- build_linux.sh / build_macos.sh: build 1 lenh moi OS.
- Doc: DESKTOP_INSTALL_PLAN.md muc 5.1.
- Verify: 86 tests pass, engine dong goi upload/analyze/waveform/export OK.
161 lines
7.0 KiB
RPMSpec
161 lines
7.0 KiB
RPMSpec
# engine.spec — PyInstaller config cho daw_engine (sidecar Python)
|
|
# Chay: pyinstaller engine.spec --clean --noconfirm (Windows/Linux/macOS)
|
|
# -*- mode: python ; coding: utf-8 -*-
|
|
|
|
import os
|
|
from PyInstaller.utils.hooks import collect_dynamic_libs, collect_data_files
|
|
|
|
# Native DLL cho pedalboard va soundfile (Windows .pyd/.dll, Linux .so)
|
|
binaries = collect_dynamic_libs('pedalboard')
|
|
binaries += collect_dynamic_libs('soundfile')
|
|
|
|
# Root tuyet doi cua thu muc chua spec — dung cho cac datas KHONG nam trong
|
|
# package 'app' (vd thu muc md/). Cac assets cua app (static/templates/models)
|
|
# duoc bundle qua collect_data_files('app').
|
|
_SPEC_ROOT = os.path.abspath(SPECPATH)
|
|
|
|
# ⚠️ GOC ROOT CUA MOI LOI 'app\\static does not exist' (gap 3 lan):
|
|
# lenh `pyinstaller engine.spec` (entry-point script) KHONG them CWD vao
|
|
# sys.path (chi `python -m PyInstaller` moi them). collect_data_files('app')
|
|
# import package qua sys.path -> khong thay 'app' -> tra ve [] AM THAM ->
|
|
# bundle thieu static/templates -> exe crash luc chay. Fix: chen _SPEC_ROOT
|
|
# vao sys.path TRUOC khi collect de import 'app' luon hoạt dong.
|
|
import sys as _sys
|
|
if _SPEC_ROOT not in _sys.path:
|
|
_sys.path.insert(0, _SPEC_ROOT)
|
|
|
|
# Assets cua app: bundle QUA IMPORT SYSTEM (collect_data_files) — an toan nhat.
|
|
# Loai tru storage (57MB soundfonts/uploads — vo ich, config.py da chuyen
|
|
# storage sang %APPDATA%\\SonicForgeDAW khi frozen) va __pycache__.
|
|
datas = collect_data_files('app', excludes=['**/storage/**', '**/__pycache__/**', '**/*.pyc'])
|
|
# Fallback cuoi cung: neu collect_data_files van tra ve rong (phong moi truong
|
|
# hop ky la), dung datas TINH absolute — tinh huong xau nhat van co du assets.
|
|
if not datas:
|
|
print("WARN: collect_data_files('app') tra ve rong - dung datas tinh absolute")
|
|
datas = [
|
|
(os.path.join(_SPEC_ROOT, 'app', 'templates'), 'app/templates'), # index.html, favicon.svg
|
|
(os.path.join(_SPEC_ROOT, 'app', 'static'), 'app/static'), # js/css/processors
|
|
(os.path.join(_SPEC_ROOT, 'app', 'models'), 'app/models'), # project_schema.json (projects.py)
|
|
]
|
|
datas += [
|
|
(os.path.join(_SPEC_ROOT, 'md'), 'md'), # /ai-prompt-generator (doc, ngoai package app)
|
|
]
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════
|
|
# TOI UU BUNDLE (SonicForgeStudio 1.1): 409MB -> ~120MB
|
|
# ──────────────────────────────────────────────────────────────────────────
|
|
# 1. librosa/numba/llvmlite (~171MB) + scikit-learn (~17MB) da DUOC LOAI BO
|
|
# khoi code (app/core/audio_features.py thay the, numpy/scipy/soundfile).
|
|
# 2. celery/kombu/billiard/redis (~40MB) KHONG bundle — desktop chay task
|
|
# eager dong bo, khong can broker (app/api/v1/tasks.py da lazy + fallback).
|
|
# 3. scipy: KHONG con quet toan bo site-packages/scipy (truoc day bundle ca
|
|
# scipy.stats/sparse/optimize/linalg ~48MB). Chi quet scipy.signal — goi
|
|
# lazy-import noi bo cua no van duoc bat day du (scipy.signal.windows,
|
|
# _savitzky_golay, _spectral_py... duoc import bang ten ben trong ham).
|
|
# scipy.signal la goi DUY NHAT con duoc app dung (sub_tab_dsp,
|
|
# render_engine, audio_features).
|
|
# ══════════════════════════════════════════════════════════════════════════
|
|
import importlib.util as _ilu
|
|
import glob as _glob
|
|
|
|
def _scan_pkg_modules(pkg_name: str):
|
|
"""Scan filesystem cua 1 package con (khong import, khong walk) ->
|
|
bat moi module .py/.pyd/.so -> hiddenimports day du, tranh lazy-import miss."""
|
|
_spec = _ilu.find_spec(pkg_name)
|
|
if _spec is None or _spec.origin is None:
|
|
print(f"WARN: khong tim thay package '{pkg_name}' - bo qua scan")
|
|
return []
|
|
_pkg_dir = os.path.dirname(os.path.abspath(_spec.origin))
|
|
_out = []
|
|
for _ext in ('*.py', '*.pyd', '*.so'):
|
|
for _f in _glob.glob(os.path.join(_pkg_dir, '**', _ext), recursive=True):
|
|
_rel = os.path.relpath(_f, _pkg_dir)
|
|
_base = os.path.basename(_rel).split('.')[0] # bo .cpython-312-x86_64... .so
|
|
_sub = os.path.dirname(_rel).replace(os.sep, '.')
|
|
_mod = (pkg_name + '.' + _sub + '.' + _base) if _sub else (pkg_name + '.' + _base)
|
|
if _mod not in _out:
|
|
_out.append(_mod)
|
|
return _out
|
|
|
|
_scipy_signal_hidden = _scan_pkg_modules('scipy.signal')
|
|
|
|
# Uvicorn lazy-load loop/protocol theo ten (string) -> hiddenimport tinh.
|
|
_hidden = [
|
|
'uvicorn.logging',
|
|
'uvicorn.loops',
|
|
'uvicorn.loops.auto',
|
|
'uvicorn.protocols',
|
|
'uvicorn.protocols.http',
|
|
'uvicorn.protocols.http.auto',
|
|
'uvicorn.protocols.websockets',
|
|
'uvicorn.protocols.websockets.auto',
|
|
'pedalboard',
|
|
'soundfile',
|
|
'sf2utils',
|
|
'mido.backends.rtmidi',
|
|
] + _scipy_signal_hidden
|
|
|
|
# Khoa khong bundle: loai toan bo cay nang khong con duoc dung.
|
|
_excludes = [
|
|
'tkinter',
|
|
# libs da thay the (audio_features.py)
|
|
'librosa', 'numba', 'llvmlite', 'sklearn', 'scikit-learn',
|
|
'joblib', 'threadpoolctl', 'audioread', 'lazy_loader', 'soxr',
|
|
# celery/redis chi dung cho server (Docker), khong cho desktop
|
|
'celery', 'kombu', 'billiard', 'vine', 'amqp', 'redis',
|
|
'click_didyoumean', 'click_plugins', 'click_repl',
|
|
# LUU Y: KHONG exclude 'click' — uvicorn.main import click (CLI parser)!
|
|
'dateutil', 'pytz', 'tzdata', 'msgpack', 'yaml',
|
|
# khong dung trong desktop
|
|
'matplotlib', 'pandas', 'IPython', 'jupyter', 'pytest', 'setuptools',
|
|
# keo vao nham boi hooks_contrib (app khong import bao gio)
|
|
'PIL', 'Pillow', 'cairosvg', 'zstandard', 'imageio',
|
|
]
|
|
|
|
a = Analysis(
|
|
['desktop_engine.py'],
|
|
pathex=[_SPEC_ROOT],
|
|
binaries=binaries,
|
|
datas=datas,
|
|
hiddenimports=_hidden,
|
|
hookspath=[],
|
|
hooksconfig={},
|
|
runtime_hooks=[],
|
|
excludes=_excludes,
|
|
win_no_prefer_redirects=False,
|
|
win_private_assemblies=False,
|
|
cipher=None,
|
|
noarchive=False,
|
|
)
|
|
|
|
pyz = PYZ(a.pure, a.zipped_data, cipher=None)
|
|
|
|
exe = EXE(
|
|
pyz,
|
|
a.scripts,
|
|
[],
|
|
exclude_binaries=True,
|
|
name='daw_engine',
|
|
debug=False,
|
|
bootloader_ignore_signals=False,
|
|
strip=False,
|
|
upx=True,
|
|
upx_exclude=[],
|
|
runtime_tmpdir=None,
|
|
console=False, # True khi debug (xem log truc tiep), False cho production
|
|
# Icon exe = favicon cua app (app/templates/favicon.svg -> render PNG -> ICO,
|
|
# sinh boi tools/gen_favicon_ico.py). Cung nguon voi icon hien thi trong app.
|
|
icon='src-tauri/icons/favicon.ico',
|
|
)
|
|
|
|
# ONEDIR (khong phai onefile): exe 700MB+ onefile phai giai nen toan bo vao
|
|
# %TEMP% moi lan chay -> load RAT CHAM tren Windows. Onedir chay truc tiep tu
|
|
# thu muc (bundle qua Tauri resources), khoi dong gan nhu tuc thi.
|
|
coll = COLLECT(
|
|
exe,
|
|
a.binaries,
|
|
a.zipfiles,
|
|
a.datas,
|
|
name='daw_engine',
|
|
)
|