#!/usr/bin/env bash set -euo pipefail # Cross-build Windows distribution on Linux # Usage: ./build-dist-windows.sh [VERSION] # --- Version detection --- VERSION="${1:-}" if [ -z "$VERSION" ]; then VERSION=$(git describe --tags --exact-match 2>/dev/null || true) fi if [ -z "$VERSION" ]; then VERSION="${GITEA_REF_NAME:-${GITHUB_REF_NAME:-}}" fi if [ -z "$VERSION" ]; then VERSION=$(grep -oP '__version__\s*=\s*"\K[^"]+' \ media_server/__init__.py 2>/dev/null || echo "0.0.0") fi VERSION_CLEAN="${VERSION#v}" echo "Building Media Server v${VERSION_CLEAN} for Windows" # --- Configuration --- PYTHON_VERSION="3.11.9" PYTHON_SHORT="311" DIST_DIR="dist/media-server" WHEEL_DIR="build/win-wheels" SITE_PACKAGES="${DIST_DIR}/python/Lib/site-packages" BUILD_OUTPUT="build/MediaServer-v${VERSION_CLEAN}-win-x64" rm -rf dist build mkdir -p "${DIST_DIR}" "${WHEEL_DIR}" "${SITE_PACKAGES}" # --- Download embedded Python --- echo "Downloading embedded Python ${PYTHON_VERSION}..." curl -sL "https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-embed-amd64.zip" \ -o build/python-embed.zip unzip -qo build/python-embed.zip -d "${DIST_DIR}/python" # Patch ._pth to enable site-packages and app source PTH_FILE=$(ls "${DIST_DIR}"/python/python*._pth | head -1) sed -i 's/^#\s*import site/import site/' "$PTH_FILE" echo 'Lib\site-packages' >> "$PTH_FILE" echo '..\..\app' >> "$PTH_FILE" # --- Download Windows wheels --- echo "Downloading Windows wheels..." # Core dependencies CORE_DEPS=( "fastapi>=0.109.0" "uvicorn[standard]>=0.27.0" "pydantic>=2.0" "pydantic-settings>=2.0" "pyyaml>=6.0" "mutagen>=1.47.0" "pillow>=10.0.0" ) # Windows-specific dependencies WIN_DEPS=( "winsdk>=1.0.0b10" "pywin32>=306" "comtypes>=1.2.0" "pycaw>=20230407" "screen-brightness-control>=0.20.0" "monitorcontrol>=3.0.0" ) # Visualizer dependencies VIS_DEPS=( "soundcard>=0.4.0" "numpy>=1.24.0" ) ALL_DEPS=("${CORE_DEPS[@]}" "${WIN_DEPS[@]}" "${VIS_DEPS[@]}") for dep in "${ALL_DEPS[@]}"; do pip download --quiet --dest "$WHEEL_DIR" \ --platform win_amd64 --python-version "${PYTHON_SHORT}" \ --implementation cp --only-binary :all: \ "$dep" 2>/dev/null || \ pip download --quiet --dest "$WHEEL_DIR" "$dep" done # Install wheels into site-packages echo "Installing wheels..." for whl in "$WHEEL_DIR"/*.whl; do unzip -qo "$whl" -d "$SITE_PACKAGES" done # --- Size optimization --- echo "Optimizing size..." find "$SITE_PACKAGES" -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true find "$SITE_PACKAGES" -type d -name tests -exec rm -rf {} + 2>/dev/null || true find "$SITE_PACKAGES" -type d -name "*.dist-info" -exec rm -rf {} + 2>/dev/null || true find "$SITE_PACKAGES" -name "*.pyi" -delete 2>/dev/null || true rm -rf "$SITE_PACKAGES"/{pip,setuptools,pkg_resources}* 2>/dev/null || true # Trim numpy if present rm -rf "$SITE_PACKAGES"/numpy/{tests,f2py,typing} 2>/dev/null || true # --- Verify frontend bundle --- if [ ! -f "media_server/static/dist/app.bundle.js" ]; then echo "ERROR: Frontend bundle not found. Run 'npm ci && npm run build' first." exit 1 fi # --- Copy application --- echo "Copying application files..." mkdir -p "${DIST_DIR}/app" cp -r media_server "${DIST_DIR}/app/" # Remove source JS (bundle is in dist/) rm -rf "${DIST_DIR}/app/media_server/static/js" # Remove source maps from release rm -f "${DIST_DIR}/app/media_server/static/dist/"*.map # Copy config example cp config.example.yaml "${DIST_DIR}/" # Copy scripts needed for auto-start mkdir -p "${DIST_DIR}/scripts" cp scripts/start-hidden.vbs "${DIST_DIR}/scripts/" # --- Write version --- echo "$VERSION_CLEAN" > "${DIST_DIR}/VERSION" # --- Create launcher --- cat > "${DIST_DIR}/media-server.bat" << 'LAUNCHER' @echo off setlocal set "ROOT=%~dp0" "%ROOT%python\python.exe" -m media_server.main %* LAUNCHER # --- Package --- echo "Creating archives..." mkdir -p build # Portable ZIP cp -r "${DIST_DIR}" "${BUILD_OUTPUT}" cd build zip -qr "MediaServer-v${VERSION_CLEAN}-win-x64.zip" "MediaServer-v${VERSION_CLEAN}-win-x64" cd .. echo "Build complete: build/MediaServer-v${VERSION_CLEAN}-win-x64.zip" echo "Dist directory ready for NSIS: ${DIST_DIR}"