feat: initial project scaffold — server, client, Android APK

- FastAPI server with audio mixer (Windows + Voicemeeter), media tracking,
  notifications (progress + alerts), telemetry, and system tray .exe build
- Test suite covering notifications, audio mixer, media, telemetry, and API
- E-Ink web client (vanilla JS, DOM API, block meters, swipe gestures)
- Android WebView APK for BOOX Go 7 Color Gen II (Android 13, Kotlin)
- Design decision records in .pi/docs/design/
- PyInstaller build script for server .exe
This commit is contained in:
Imrayya
2026-07-01 11:19:32 +00:00
commit 0b4f739d12
36 changed files with 3916 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
"""Build script: compile PaperDash server to a standalone .exe with system tray.
Usage:
python build_exe.py # Build in release mode
python build_exe.py --debug # Build in debug mode (with console)
Output: server/dist/PaperDash.exe
"""
from __future__ import annotations
import argparse
import os
import subprocess
import sys
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
DIST_DIR = os.path.join(SCRIPT_DIR, "dist")
def build(debug: bool = False) -> None:
"""Compile the server to an .exe using PyInstaller."""
os.makedirs(DIST_DIR, exist_ok=True)
# PyInstaller spec content — wraps the server with system tray
spec_content = f'''
# -*- mode: python ; coding: utf-8 -*-
"""PyInstaller spec for PaperDash with system tray."""
import os
from pathlib import Path
block_cipher = None
a = Analysis(
["tray_wrapper.py"],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[
"fastapi",
"uvicorn",
"pydantic",
"pystray",
"PIL",
"audio_mixer",
"media",
"notifications",
"telemetry",
"main",
],
hookspath=[],
hooksconfig={{}},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name="PaperDash",
debug={"True": True, "False": False}[str(debug)],
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False if not debug else True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=None,
)
'''
spec_path = os.path.join(SCRIPT_DIR, "PaperDash.spec")
with open(spec_path, "w") as f:
f.write(spec_content)
try:
cmd = [
sys.executable,
"-m",
"PyInstaller",
spec_path,
"--clean",
]
if debug:
cmd.append("--debug=all")
print(f"Building PaperDash.exe (debug={debug})...")
print(f" Spec: {spec_path}")
print(f" Output: {DIST_DIR}/PaperDash.exe")
print()
subprocess.run(cmd, check=True)
exe_path = os.path.join(DIST_DIR, "PaperDash.exe")
if os.path.exists(exe_path):
size_mb = os.path.getsize(exe_path) / (1024 * 1024)
print(f"\nBuild complete: {exe_path} ({size_mb:.1f} MB)")
else:
print("\nBuild completed but .exe not found in dist/")
except subprocess.CalledProcessError as e:
print(f"\nBuild failed: {e}", file=sys.stderr)
sys.exit(1)
finally:
# Clean up spec file
if os.path.exists(spec_path):
os.remove(spec_path)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Build PaperDash .exe")
parser.add_argument("--debug", action="store_true", help="Build with debug console")
args = parser.parse_args()
build(debug=args.debug)