FIX (lan 2): bundle app assets qua collect_data_files (import-system) + fallback static dir trong main.py + verify TOC trong build_windows.ps1
This commit is contained in:
+17
@@ -62,6 +62,23 @@ app.mount("/static/audio", StaticFiles(directory=settings.STORAGE_DIR), name="au
|
||||
# khi PyInstaller onefile, __file__ trỏ vào thư mục giải nén tạm _MEI...
|
||||
# nhưng static/templates nằm trong sys._MEIPASS/app (config.py đã xử lý).
|
||||
STATIC_DIR = os.path.join(settings.APP_DIR, "static")
|
||||
# ⚠️ Fallback an toàn: nếu vì lý do nào đó static không nằm đúng chỗ (vd
|
||||
# bundle thiếu file, chạy từ nơi khác), thử các vị trí khác; nếu vẫn không
|
||||
# có → TỰ TẠO thư mục rỗng để app KHÔNG crash khi khởi động (lỗi "Directory
|
||||
# does not exist" từ StaticFiles làm engine chết ngay lúc import — đã gặp).
|
||||
if not os.path.isdir(STATIC_DIR):
|
||||
for cand in [
|
||||
os.path.join(getattr(sys, "_MEIPASS", ""), "app", "static"),
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), "static"),
|
||||
]:
|
||||
if cand and os.path.isdir(cand):
|
||||
STATIC_DIR = cand
|
||||
break
|
||||
else:
|
||||
try:
|
||||
os.makedirs(STATIC_DIR, exist_ok=True)
|
||||
except OSError:
|
||||
pass
|
||||
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
|
||||
|
||||
# Include routers
|
||||
|
||||
@@ -14,6 +14,30 @@ node build.mjs # @babel/standalone; thay cho 'npm run build' (Babel 8 ESM-only
|
||||
Write-Host "== [3/6] Build daw_engine.exe (PyInstaller) =="
|
||||
pyinstaller engine.spec --clean --noconfirm
|
||||
|
||||
Write-Host "== [3.5/6] Verify bundle contents (app/static, app/templates phải có) =="
|
||||
# Bắt lỗi bundle NGAY tại build (đã gặp 2 lần: chạy pyinstaller từ nơi khác
|
||||
# -> static/templates thiếu -> exe crash 'Directory ...\app\static does not exist').
|
||||
$missing = @()
|
||||
if (-not (Test-Path "build\engine\Analysis-00.toc")) {
|
||||
# PyInstaller 6.x: đọc TOC trực tiếp từ Analysis
|
||||
$tocFiles = Get-ChildItem "build\engine" -Filter "*.toc" -ErrorAction SilentlyContinue
|
||||
if ($tocFiles) {
|
||||
$tocContent = Get-Content $tocFiles[0].FullName -Raw
|
||||
if ($tocContent -notmatch "app/static") { $missing += "app/static" }
|
||||
if ($tocContent -notmatch "app/templates") { $missing += "app/templates" }
|
||||
}
|
||||
} else {
|
||||
$tocContent = Get-Content "build\engine\Analysis-00.toc" -Raw
|
||||
if ($tocContent -notmatch "app/static") { $missing += "app/static" }
|
||||
if ($tocContent -notmatch "app/templates") { $missing += "app/templates" }
|
||||
}
|
||||
if ($missing.Count -gt 0) {
|
||||
Write-Host "ERROR: Bundle thiếu: $($missing -join ', '). Dừng build — kiểm tra engine.spec datas!" -ForegroundColor Red
|
||||
exit 1
|
||||
} else {
|
||||
Write-Host "OK: app/static + app/templates có trong bundle." -ForegroundColor Green
|
||||
}
|
||||
|
||||
Write-Host "== [4/6] Sidecar binary -> src-tauri/binaries (tauri triple naming) =="
|
||||
New-Item -ItemType Directory -Force src-tauri\binaries | Out-Null
|
||||
Copy-Item dist\daw_engine.exe src-tauri\binaries\daw_engine-x86_64-pc-windows-msvc.exe -Force
|
||||
|
||||
+12
-12
@@ -9,20 +9,20 @@ from PyInstaller.utils.hooks import collect_dynamic_libs, collect_submodules, co
|
||||
binaries = collect_dynamic_libs('pedalboard')
|
||||
binaries += collect_dynamic_libs('soundfile')
|
||||
|
||||
# Root tuyet doi cua thu muc chua spec — datas PHẢI absolute: PyInstaller
|
||||
# resolve duong dan relative theo CWD luc chay lenh (KHONG theo spec file),
|
||||
# chay tu noi khac -> khong tim thay -> bo qua am tham -> app/static thieu
|
||||
# trong bundle -> RuntimeError 'app\static does not exist' luc chay (da gap).
|
||||
# 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') — PyInstaller tu tim qua import
|
||||
# system, KHONG phu thuoc CWD/SPECPATH khi chay lenh (da gap 2 lan: chay
|
||||
# pyinstaller tu noi khac -> relative path khong resolve -> app/static thieu
|
||||
# trong bundle -> RuntimeError 'app\\static does not exist' luc chay).
|
||||
_SPEC_ROOT = os.path.abspath(SPECPATH)
|
||||
|
||||
# Assets doc (read-only) + thu muc storage (khoi tao rong; khi frozen,
|
||||
# config.py chuyen storage sang %APPDATA%\SonicForgeDAW\storage)
|
||||
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)
|
||||
(os.path.join(_SPEC_ROOT, 'app', 'storage'), 'app/storage'),
|
||||
(os.path.join(_SPEC_ROOT, 'md'), 'md'), # /ai-prompt-generator
|
||||
# Assets cua app: bundle QUA IMPORT SYSTEM (collect_data_files) — an toan nhat.
|
||||
# Loai tru storage (57MB soundfonts/uploads — vo ich trong onefile, config.py
|
||||
# da chuyen storage sang %APPDATA%\\SonicForgeDAW khi frozen) va __pycache__.
|
||||
datas = collect_data_files('app', excludes=['**/storage/**', '**/__pycache__/**', '**/*.pyc'])
|
||||
datas += [
|
||||
(os.path.join(_SPEC_ROOT, 'md'), 'md'), # /ai-prompt-generator (doc, ngoai package app)
|
||||
]
|
||||
|
||||
# librosa 0.11 dùng lazy_loader.attach_stub -> lúc RUNTIME cần file .pyi
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
### [2026-08-08] FIX (LẦN 2): Windows exe vẫn crash 'app\static does not exist' + stuck 'Đang khởi động Engine' — bundle qua collect_data_files + fallback + verify build
|
||||
- **Tóm tắt thay đổi:** User build bản Windows vẫn gặp RuntimeError `Directory '..._MEIxxxx\app\static' does not exist` (lần 2 — fix trước dùng datas ABSOLUTE theo SPECPATH nhưng trên máy user vẫn thiếu static trong bundle) + UI stuck "Đang khởi động SonicForge Engine". 3 lớp phòng thủ:
|
||||
1. **engine.spec — bundle qua IMPORT SYSTEM**: bỏ datas đường dẫn tĩnh, thay bằng `collect_data_files('app', excludes=['**/storage/**','**/__pycache__/**','**/*.pyc'])` — PyInstaller tự tìm assets (static/templates/models) qua package import, KHÔNG phụ thuộc CWD/SPECPATH lúc chạy lệnh (nguyên nhân gốc: chạy pyinstaller từ thư mục khác → relative path không resolve → bỏ qua âm thầm). Loại luôn `app/storage` (57MB soundfonts — vô ích trong onefile, config.py đã redirect %APPDATA%). Giữ `md/` (ngoài package) + librosa .pyi.
|
||||
2. **app/main.py — fallback an toàn**: nếu STATIC_DIR không tồn tại → thử `sys._MEIPASS/app/static` và `dirname(__file__)/static`; vẫn thiếu → **tự os.makedirs** → StaticFiles không còn crash lúc import (engine không chết, app hiện lỗi rõ thay vì 6 hộp thoại + stuck loader).
|
||||
3. **build_windows.ps1 — verify post-build [3.5/6]**: sau pyinstaller, đọc Analysis-00.toc kiểm tra `app/static` + `app/templates` có trong bundle — thiếu → in ERROR đỏ + `exit 1` (bắt lỗi NGAY lúc build, không đợi chạy app mới vỡ).
|
||||
- **Các file ảnh hưởng:** `engine.spec` (collect_data_files('app') + bỏ storage), `app/main.py` (fallback static dir), `build_windows.ps1` (verify TOC post-build)
|
||||
- **Ghi chú/Test (nếu có):** VERIFY THẬT trên Linux (PyInstaller 6.21, cùng spec): Analysis-00.toc chứa app/static (124), app/templates (12), app/models (5); exe 155MB (giảm 57MB); chạy binary frozen → /health OK, index 200, static js 200, favicon 200, không crash. pytest 86 passed. User cần chạy lại `build_windows.ps1` (bước 3.5 sẽ tự kiểm tra bundle).
|
||||
|
||||
---
|
||||
### [2026-08-08] IMPROVE: About modal — logo dùng favicon của app (app/templates/favicon.svg, serve /favicon.svg)
|
||||
- **Tóm tắt thay đổi:** User yêu cầu logo trong About modal phải là logo của web/app — favicon lưu trong hệ thống. Trước đây AboutModal dùng div gradient chữ "SF". Fix: thay bằng `<img src="/favicon.svg">` (route đã có sẵn trong app/main.py — serve từ TEMPLATES_DIR; file app/templates/favicon.svg, SVG 1254x1254). Hiển thị 48x48 object-contain, nền tối + border cho nổi trên modal.
|
||||
- **Các file ảnh hưởng:** `app/static/js/app.jsx` (AboutModal img), `app/static/js/app.precompiled.js` (rebuild), `app/templates/index.html` (bump v=202608081600)
|
||||
|
||||
Reference in New Issue
Block a user