From 099cc252fe22d8cf7bd83bc127b2c31184fccf5e Mon Sep 17 00:00:00 2001 From: Zac Date: Tue, 10 Feb 2026 19:23:48 -0500 Subject: [PATCH] Fix proxy path resolution for portable builds, simplify landing page The proxy launch used __dirname to find aware-cam-proxy.exe, which points to a temp extraction directory in portable builds. Added getAppDirectory() helper that resolves the actual .exe location via PORTABLE_EXECUTABLE_FILE. Rewrote landing page as a simple kit download page. Added build-kit script to bundle app + proxy + extension into zip. Co-Authored-By: Claude Opus 4.6 --- build-kit.ps1 | 72 +++++++++++++ docs/index.html | 274 +++++++++++++++++++----------------------------- main.js | 14 ++- package.json | 1 + 4 files changed, 193 insertions(+), 168 deletions(-) create mode 100644 build-kit.ps1 diff --git a/build-kit.ps1 b/build-kit.ps1 new file mode 100644 index 0000000..1d8f250 --- /dev/null +++ b/build-kit.ps1 @@ -0,0 +1,72 @@ +# build-kit.ps1 — Build the Alta Proxy Tool Kit zip for distribution +# Usage: powershell -ExecutionPolicy Bypass -File build-kit.ps1 +# +# Produces: dist/AltaProxyToolKit.zip containing: +# - AltaCameraProxy--portable.exe +# - aware-cam-proxy.exe +# - chrome-extension/ (folder) + +$ErrorActionPreference = "Stop" + +# Read version from package.json +$pkg = Get-Content "package.json" -Raw | ConvertFrom-Json +$version = $pkg.version +Write-Host "Building Alta Proxy Tool Kit v$version" -ForegroundColor Cyan + +# Step 1: Build the Electron portable exe +Write-Host "`n[1/4] Building Electron app..." -ForegroundColor Yellow +npm run build +if ($LASTEXITCODE -ne 0) { + Write-Host "Build failed!" -ForegroundColor Red + exit 1 +} + +$portableExe = "dist\AltaCameraProxy-$version-portable.exe" +if (-not (Test-Path $portableExe)) { + Write-Host "Expected output not found: $portableExe" -ForegroundColor Red + exit 1 +} +Write-Host " Built: $portableExe" -ForegroundColor Green + +# Step 2: Verify aware-cam-proxy.exe exists +Write-Host "`n[2/4] Checking for aware-cam-proxy.exe..." -ForegroundColor Yellow +if (-not (Test-Path "aware-cam-proxy.exe")) { + Write-Host "aware-cam-proxy.exe not found in project root!" -ForegroundColor Red + Write-Host "Place aware-cam-proxy.exe in the project root and try again." -ForegroundColor Red + exit 1 +} +Write-Host " Found: aware-cam-proxy.exe" -ForegroundColor Green + +# Step 3: Verify chrome-extension folder exists +Write-Host "`n[3/4] Checking for chrome-extension/..." -ForegroundColor Yellow +if (-not (Test-Path "chrome-extension\manifest.json")) { + Write-Host "chrome-extension/ folder not found or missing manifest.json!" -ForegroundColor Red + exit 1 +} +Write-Host " Found: chrome-extension/" -ForegroundColor Green + +# Step 4: Create the kit zip +Write-Host "`n[4/4] Creating AltaProxyToolKit.zip..." -ForegroundColor Yellow + +$kitDir = "dist\AltaProxyToolKit" +$zipPath = "dist\AltaProxyToolKit.zip" + +# Clean previous kit +if (Test-Path $kitDir) { Remove-Item $kitDir -Recurse -Force } +if (Test-Path $zipPath) { Remove-Item $zipPath -Force } + +# Assemble kit contents +New-Item -ItemType Directory -Path $kitDir | Out-Null +Copy-Item $portableExe "$kitDir\AltaCameraProxy.exe" +Copy-Item "aware-cam-proxy.exe" "$kitDir\aware-cam-proxy.exe" +Copy-Item "chrome-extension" "$kitDir\chrome-extension" -Recurse + +# Create zip +Compress-Archive -Path "$kitDir\*" -DestinationPath $zipPath -Force + +# Clean up temp directory +Remove-Item $kitDir -Recurse -Force + +$zipSize = [math]::Round((Get-Item $zipPath).Length / 1MB, 1) +Write-Host "`nKit ready: $zipPath ($zipSize MB)" -ForegroundColor Green +Write-Host "Upload this file as a release asset named 'AltaProxyToolKit.zip'" -ForegroundColor Cyan diff --git a/docs/index.html b/docs/index.html index 5fe8f2d..33c4a6c 100644 --- a/docs/index.html +++ b/docs/index.html @@ -13,7 +13,6 @@ --text-secondary: #999999; --accent-primary: #0E7AFE; --accent-primary-hover: #0A5FD9; - --success: #4CAF50; --card-bg: #2D2D30; } @@ -29,6 +28,9 @@ color: var(--text-primary); font-size: 16px; line-height: 1.6; + min-height: 100vh; + display: flex; + flex-direction: column; } a { @@ -48,7 +50,7 @@ } .container { - max-width: 960px; + max-width: 720px; margin: 0 auto; padding: 0 24px; } @@ -86,9 +88,13 @@ } /* Hero */ + main { + flex: 1; + } + .hero { text-align: center; - padding: 80px 0 64px; + padding: 100px 0 48px; } .hero-icon { @@ -105,10 +111,10 @@ } .hero .tagline { - font-size: 20px; + font-size: 18px; color: var(--text-secondary); - margin-bottom: 36px; - max-width: 600px; + margin-bottom: 40px; + max-width: 500px; margin-left: auto; margin-right: auto; } @@ -119,7 +125,7 @@ color: #fff; font-size: 18px; font-weight: 600; - padding: 14px 36px; + padding: 16px 40px; border-radius: 6px; transition: background 0.2s; } @@ -135,125 +141,111 @@ color: var(--text-secondary); } - /* Sections */ - section { - padding: 56px 0; - } - - section + section { + /* Kit contents */ + .kit-info { + padding: 48px 0; border-top: 1px solid var(--border); } - section h2 { - font-size: 24px; + .kit-info h2 { + font-size: 20px; font-weight: 600; - color: var(--accent-primary); - margin-bottom: 24px; + color: var(--text-primary); + margin-bottom: 16px; + text-align: center; } - /* Features */ - .features-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); - gap: 20px; + .kit-contents { + display: flex; + gap: 16px; + justify-content: center; } - .feature-card { + .kit-item { background: var(--card-bg); border: 1px solid var(--border); border-radius: 8px; - padding: 24px; + padding: 20px 28px; + text-align: center; } - .feature-card h3 { - font-size: 16px; - font-weight: 600; - margin-bottom: 8px; - color: var(--text-primary); - } - - .feature-card p { + .kit-item strong { + display: block; font-size: 14px; + margin-bottom: 4px; + } + + .kit-item span { + font-size: 13px; color: var(--text-secondary); - line-height: 1.5; } - /* Setup Steps */ - .steps { - counter-reset: step; - list-style: none; - } - - .steps li { - counter-increment: step; - position: relative; - padding: 16px 0 16px 52px; - } - - .steps li + li { + /* Setup */ + .setup { + padding: 48px 0; border-top: 1px solid var(--border); } - .steps li::before { + .setup h2 { + font-size: 20px; + font-weight: 600; + color: var(--text-primary); + margin-bottom: 20px; + text-align: center; + } + + .setup-steps { + list-style: none; + max-width: 480px; + margin: 0 auto; + counter-reset: step; + } + + .setup-steps li { + counter-increment: step; + padding: 12px 0 12px 44px; + position: relative; + font-size: 15px; + color: var(--text-secondary); + } + + .setup-steps li + li { + border-top: 1px solid var(--border); + } + + .setup-steps li::before { content: counter(step); position: absolute; left: 0; - top: 16px; - width: 32px; - height: 32px; + top: 12px; + width: 28px; + height: 28px; background: var(--accent-primary); color: #fff; font-weight: 700; - font-size: 14px; + font-size: 13px; border-radius: 50%; display: flex; align-items: center; justify-content: center; } - .steps li strong { - display: block; - margin-bottom: 4px; - color: var(--text-primary); - } - - .steps li span { - font-size: 14px; - color: var(--text-secondary); - } - - .steps code { + .setup-steps code { background: var(--bg-primary); border: 1px solid var(--border); - padding: 2px 6px; - border-radius: 4px; + padding: 1px 5px; + border-radius: 3px; font-size: 13px; color: var(--accent-primary); } - /* Requirements */ - .requirements-list { - list-style: none; - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 12px; - } - - .requirements-list li { - background: var(--card-bg); - border: 1px solid var(--border); - border-radius: 6px; - padding: 14px 18px; - font-size: 14px; + .setup-note { + text-align: center; + margin-top: 20px; + font-size: 13px; color: var(--text-secondary); } - .requirements-list li strong { - color: var(--text-primary); - display: block; - margin-bottom: 2px; - } - /* Footer */ .footer { border-top: 1px solid var(--border); @@ -274,7 +266,7 @@ /* Responsive */ @media (max-width: 600px) { .hero { - padding: 48px 0 40px; + padding: 60px 0 36px; } .hero h1 { @@ -290,16 +282,14 @@ padding: 12px 28px; } - section { - padding: 36px 0; + .kit-contents { + flex-direction: column; + align-items: center; } - .features-grid { - grid-template-columns: 1fr; - } - - .requirements-list { - grid-template-columns: 1fr; + .kit-item { + width: 100%; + max-width: 280px; } } @@ -322,87 +312,39 @@

Alta Proxy Tool

-

Connect to Avigilon Alta cameras through a local RTSP proxy — no cloud relay required.

- Download for Windows -

Portable .exe — no installation needed

+

Local RTSP proxy for Avigilon Alta cameras. No cloud relay required.

+ Download Kit for Windows +

Portable — extract the zip and run. No installation needed.

-
-

Features

-
-
-

Cookie-Based Auth

-

Authenticate seamlessly via the companion Chrome extension. No credentials stored in the app.

+
+

What's in the kit

+
+
+ AltaCameraProxy.exe + Electron desktop app
-
-

Device Discovery

-

Automatically discovers all on-premise cameras in your Alta deployment with online/offline status.

+
+ aware-cam-proxy.exe + Camera proxy engine
-
-

Local RTSP Proxy

-

Launches aware-cam-proxy to establish direct camera connections for RTSP streaming.

-
-
-

Batch Operations

-

Connect to multiple cameras at once with batch proxy launch and sequential port assignment.

-
-
-

Self-Updating

-

Built-in update checker downloads new releases directly from GitHub.

-
-
-

Portable

-

Single .exe, no installer. Run it from anywhere on any Windows machine.

+
+ chrome-extension/ + Cookie bridge for Chrome
-
-

Quick Start

-
    -
  1. - Download the app - Grab the latest .exe from the Releases page and place it in a folder alongside aware-cam-proxy.exe. -
  2. -
  3. - Install the Chrome extension - Open chrome://extensions, enable Developer mode, click "Load unpacked", and select the chrome-extension folder from this repo. -
  4. -
  5. - Log in to Alta - Sign in to your Avigilon Alta deployment in Chrome as you normally would. -
  6. -
  7. - Send cookies to the app - Click the extension icon and press "Send Cookie to APT". The app will connect automatically. -
  8. -
  9. - Start proxying - Select cameras from the device list and click Start Proxy. Use the generated RTSP URL in your video client. -
  10. +
    +

    Setup

    +
      +
    1. Extract the zip to any folder
    2. +
    3. Load chrome-extension/ in Chrome via chrome://extensions (Developer mode)
    4. +
    5. Log into your Alta deployment in Chrome
    6. +
    7. Click the extension icon and send cookies to the app
    8. +
    9. Run AltaCameraProxy.exe and start proxying
    -
    - -
    -

    Requirements

    -
      -
    • - Windows 10+ - The proxy executable is Windows-only. -
    • -
    • - Google Chrome - Required for the cookie bridge extension. -
    • -
    • - aware-cam-proxy.exe - Must be in the same folder as the app. -
    • -
    • - Alta Deployment - An active Avigilon Alta Video account. -
    • -
    +

    Requires Windows 10+, Google Chrome, and an Avigilon Alta account.

diff --git a/main.js b/main.js index 6c1179e..883936c 100644 --- a/main.js +++ b/main.js @@ -13,6 +13,16 @@ let cookieServer = null; const COOKIE_SERVER_PORT = 18247; const COOKIE_SERVER_TOKEN = 'apt-local-bridge-token'; +// Get the directory where the user-facing executable resides. +// In portable builds, __dirname points to a temp extraction directory, +// so we use the actual .exe location instead. +function getAppDirectory() { + if (app.isPackaged) { + return path.dirname(process.env.PORTABLE_EXECUTABLE_FILE || app.getPath('exe')); + } + return __dirname; +} + // Sanitize strings before embedding in batch files to prevent command injection function sanitizeBatchInput(input) { if (typeof input !== 'string') return ''; @@ -238,7 +248,7 @@ ipcMain.handle('api-get-auth-info', async (event, { deploymentUrl, cookies }) => ipcMain.handle('camera-proxy-cookie-launch', async (event, { deploymentUrl, cookieKey, deviceUuid }) => { try { // Path to the cookie-based camera proxy executable - const proxyExePath = path.join(__dirname, 'aware-cam-proxy.exe'); + const proxyExePath = path.join(getAppDirectory(), 'aware-cam-proxy.exe'); // Check if the executable exists if (!fs.existsSync(proxyExePath)) { @@ -502,7 +512,7 @@ ipcMain.handle('download-and-install-update', async (event, { downloadUrl }) => try { // Determine the path to the currently running executable const currentExePath = process.env.PORTABLE_EXECUTABLE_FILE || app.getPath('exe'); - const currentDir = path.dirname(currentExePath); + const currentDir = getAppDirectory(); const currentExeName = path.basename(currentExePath); // Check write permission on the app directory diff --git a/package.json b/package.json index 4995a90..7cd11d3 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "dev": "electron . --dev", "build": "electron-builder --win --publish=never", "build-test": "electron-builder --win --dir", + "build-kit": "powershell -ExecutionPolicy Bypass -File build-kit.ps1", "prebuild": "echo Checking build requirements..." }, "build": {