188 lines
8.5 KiB
RPMSpec
188 lines
8.5 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 soundfile (Windows .pyd/.dll, Linux .so)
|
|
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)
|
|
# Native bridge cho VST FX (fx_vst_bridge.exe + DLL + plugin_host) — bundle vao
|
|
# root (dest '.') de find_bridge_exe (frozen: exe_dir = thu muc chua
|
|
# sys.executable = dist/daw_engine) tu dong tim thay. Chi build tren Windows.
|
|
if os.name == 'nt':
|
|
_bridge_dir = os.path.join(_SPEC_ROOT, 'native_bridge', 'build', 'Release')
|
|
for _b in ('daw_vst_bridge.exe', 'fx_vst_bridge.exe', 'jpeg62.dll', 'libfluidsynth-3.dll', 'plugin_host.exe'):
|
|
_bp = os.path.join(_bridge_dir, _b)
|
|
if os.path.exists(_bp):
|
|
binaries.append((_bp, '.'))
|
|
# G3: JUCE FX bridge (graph latency + processed samples) - bundle rieng vi
|
|
# build o native_bridge/build_juce/Release (khac dir build bridge cu).
|
|
_juce_dir = os.path.join(_SPEC_ROOT, 'native_bridge', 'build_juce', 'Release')
|
|
_jp = os.path.join(_juce_dir, 'juce_fx_bridge.exe')
|
|
if os.path.exists(_jp):
|
|
binaries.append((_jp, '.'))
|
|
|
|
|
|
# ⚠️ 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__.
|
|
# LOAI CA DEV CONFIG O app/ ROOT: bundle cai dat phai SAC — khong duoc mang
|
|
# theo DB/password/secret/scan-state cua dev sang may nguoi dung moi.
|
|
datas = collect_data_files('app', excludes=[
|
|
'**/storage/**', '**/__pycache__/**', '**/*.pyc',
|
|
'**/*.db', '**/.secret_key', '**/plugin_dirs.json', '**/sf_scan_state.json',
|
|
'**/*.bak-root',
|
|
])
|
|
# 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',
|
|
'websockets', # uvicorn WS protocol runtime dep — /ws/fx-realtime/{id}
|
|
'websockets.legacy',
|
|
'websockets.legacy.client',
|
|
# pedalboard (GPL-3.0) DA XOA — native_bridge thay the (xem PLAN_REPLACE_PEDALBOARD_NATIVE_BRIDGE.md)
|
|
'soundfile',
|
|
'sf2utils',
|
|
# pyfluidsynth (fluidsynth.py cffi) - render soundfont native cho
|
|
# /soundfont-render (browser client). Can libfluidsynth-3.dll ke exe.
|
|
'fluidsynth',
|
|
'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 (scorefx_favicon.png -> src-tauri/icons/favicon.ico,
|
|
# sinh bang PIL tu tools/gen_icons.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',
|
|
)
|