// Global variables to store session data let sessionData = { deploymentUrl: '', cookies: null, isConnected: false }; // DOM elements const connectionStatus = document.getElementById('connectionStatus'); const deviceStatus = document.getElementById('deviceStatus'); const deviceList = document.getElementById('deviceList'); const statusIndicator = document.getElementById('statusIndicator'); const deviceSearch = document.getElementById('deviceSearch'); // Connection buttons const disconnectBtn = document.getElementById('disconnectBtn'); // Cookie proxy elements const cookieDeviceUUID = document.getElementById('cookieDeviceUUID'); const cookieKey = document.getElementById('cookieKey'); const startCookieProxyBtn = document.getElementById('startCookieProxyBtn'); const stopCookieProxyBtn = document.getElementById('stopCookieProxyBtn'); // Track selected device let selectedDevice = null; let activeCookieProxyConnections = new Map(); // Track cookie-based proxy connections let allDevices = []; // Store all devices for search functionality // Event listeners disconnectBtn.addEventListener('click', handleDisconnect); // Cookie proxy event listeners startCookieProxyBtn.addEventListener('click', handleStartCookieProxy); stopCookieProxyBtn.addEventListener('click', handleStopCookieProxy); // Cookie key input listener to update button states cookieKey.addEventListener('input', updateCookieProxyButtonStates); // Device search event listener deviceSearch.addEventListener('input', handleDeviceSearch); // Handle disconnect function handleDisconnect() { sessionData.isConnected = false; sessionData.cookies = null; sessionData.deploymentUrl = ''; selectedDevice = null; activeCookieProxyConnections.clear(); allDevices = []; // Clear stored devices updateConnectionStatus(false); updateButtonStates(); clearDeviceList(); cookieDeviceUUID.value = ''; cookieKey.value = ''; deviceSearch.value = ''; // Clear search input // Clear device status message deviceStatus.style.display = 'none'; deviceStatus.textContent = ''; showStatus(connectionStatus, 'Disconnected from API', 'info'); } // Handle start cookie proxy async function handleStartCookieProxy() { if (!selectedDevice) { showStatus(connectionStatus, 'Please select a device first', 'error'); return; } const cookieKeyValue = cookieKey.value.trim(); if (!cookieKeyValue) { showStatus(connectionStatus, 'Please enter a cookie key', 'error'); return; } if (!sessionData.deploymentUrl) { showStatus(connectionStatus, 'Please connect to API first to get deployment URL', 'error'); return; } // Check if this device already has an active cookie connection const deviceId = selectedDevice.guid || selectedDevice.id; if (activeCookieProxyConnections.has(deviceId)) { showStatus(connectionStatus, `Cookie proxy already running for device ${selectedDevice.name}`, 'warning'); return; } // Disable button during launch startCookieProxyBtn.disabled = true; showStatus(connectionStatus, `Starting cookie proxy for device ${selectedDevice.name}...`, 'info'); try { const result = await window.electronAPI.launchCookieCameraProxy({ deploymentUrl: sessionData.deploymentUrl, cookieKey: cookieKeyValue, deviceUuid: deviceId }); if (result.success) { // Track this connection activeCookieProxyConnections.set(deviceId, { processId: result.processId, deviceName: selectedDevice.name, deviceId: deviceId, startTime: Date.now(), type: 'cookie' }); updateCookieProxyButtonStates(); showStatus(connectionStatus, `${result.message} (Cookie proxy active for ${selectedDevice.name})`, 'success'); } else { showStatus(connectionStatus, `Failed to start cookie proxy: ${result.message}`, 'error'); } } catch (error) { console.error('Cookie proxy launch error:', error); showStatus(connectionStatus, `Error launching cookie proxy: ${error.message}`, 'error'); } finally { startCookieProxyBtn.disabled = false; } } // Handle stop cookie proxy async function handleStopCookieProxy() { if (activeCookieProxyConnections.size === 0) { showStatus(connectionStatus, 'No active cookie proxy connections found', 'warning'); return; } showStatus(connectionStatus, 'Stopping cookie proxy connections...', 'info'); try { const result = await window.electronAPI.stopCameraProxy(null); // Stop all processes if (result.success) { activeCookieProxyConnections.clear(); showStatus(connectionStatus, 'Cookie proxy connections stopped successfully', 'success'); // Update visual indicators for all devices const deviceItems = deviceList.querySelectorAll('.device-item'); deviceItems.forEach(item => { item.classList.remove('cookie-proxy-active'); }); } else { showStatus(connectionStatus, `Failed to stop cookie proxy: ${result.message}`, 'warning'); } } catch (error) { console.error('Stop cookie proxy error:', error); showStatus(connectionStatus, 'Error stopping cookie proxy', 'error'); } updateCookieProxyButtonStates(); } // Update cookie proxy button states function updateCookieProxyButtonStates() { const hasSelectedDevice = selectedDevice !== null; const hasCookieKey = cookieKey.value.trim().length > 0; const hasActiveConnection = activeCookieProxyConnections.size > 0; if (sessionData.isConnected && hasSelectedDevice && hasCookieKey) { const deviceId = selectedDevice.guid || selectedDevice.id; const isThisDeviceActive = activeCookieProxyConnections.has(deviceId); startCookieProxyBtn.disabled = isThisDeviceActive; stopCookieProxyBtn.disabled = !hasActiveConnection; } else { startCookieProxyBtn.disabled = true; stopCookieProxyBtn.disabled = !hasActiveConnection; } } // Update connection status indicator function updateConnectionStatus(connected) { const statusDot = statusIndicator.querySelector('.status-dot'); const statusText = statusIndicator.querySelector('.status-text'); if (connected) { statusDot.className = 'status-dot online'; statusText.textContent = 'Connected'; } else { statusDot.className = 'status-dot offline'; statusText.textContent = 'Disconnected'; } } // Update button states based on connection status function updateButtonStates() { if (sessionData.isConnected) { disconnectBtn.disabled = false; } else { disconnectBtn.disabled = true; startCookieProxyBtn.disabled = true; stopCookieProxyBtn.disabled = true; } // Always update cookie proxy button states updateCookieProxyButtonStates(); } // Handle get devices (now called automatically) async function handleGetDevices() { if (!sessionData.isConnected) { showStatus(deviceStatus, 'Please connect to the API first', 'error'); return; } showStatus(deviceStatus, 'Fetching devices...', 'info'); clearDeviceList(); try { const result = await window.electronAPI.getDevices({ deploymentUrl: sessionData.deploymentUrl, cookies: sessionData.cookies }); if (result.success) { // Filter devices to only show non-cloud cameras (localStorage = false) const filteredDevices = result.devices.filter(device => { // Check if device has capabilities and localStorage property if (device.capabilities && device.capabilities.localStorage !== undefined) { // Only show devices where localStorage is false (non-cloud cameras) return device.capabilities.localStorage === false; } // If no capabilities or localStorage property, include the device (fallback) return true; }); const totalDevices = result.devices.length; const filteredCount = filteredDevices.length; const cloudDevicesHidden = totalDevices - filteredCount; let statusMessage = `Found ${filteredCount} local camera${filteredCount !== 1 ? 's' : ''}`; if (cloudDevicesHidden > 0) { statusMessage += ` (${cloudDevicesHidden} cloud camera${cloudDevicesHidden !== 1 ? 's' : ''} hidden)`; } showStatus(deviceStatus, statusMessage, 'success'); // Store all devices for search functionality allDevices = filteredDevices; displayDevices(filteredDevices); } else { showStatus(deviceStatus, `Failed to get devices: ${result.message}`, 'error'); } } catch (error) { console.error('Get devices error:', error); showStatus(deviceStatus, `Error getting devices: ${error.message}`, 'error'); } } // Helper function to determine device status from API data function getDeviceStatus(device) { // Check for live.display_status first (Alta API standard) if (device.live && device.live.display_status) { const status = device.live.display_status.toLowerCase(); // Handle color-based status responses from Alta API if (status === 'green') { return { isOnline: true, statusText: 'Online' }; } else if (status === 'red') { return { isOnline: false, statusText: 'Offline' }; } else if (status === 'yellow' || status === 'orange') { return { isOnline: false, statusText: 'Warning' }; } // Handle text-based status responses return { isOnline: status === 'online' || status === 'live' || status === 'connected', statusText: status === 'online' || status === 'live' || status === 'connected' ? 'Online' : 'Offline' }; } // Fallback to other possible status fields if (device.online !== undefined) { return { isOnline: device.online, statusText: device.online ? 'Online' : 'Offline' }; } if (device.status) { const status = device.status.toLowerCase(); // Handle color-based status in other fields if (status === 'green') { return { isOnline: true, statusText: 'Online' }; } else if (status === 'red') { return { isOnline: false, statusText: 'Offline' }; } return { isOnline: status === 'online' || status === 'live' || status === 'connected', statusText: status === 'online' || status === 'live' || status === 'connected' ? 'Online' : 'Offline' }; } // Default to offline if no status information available return { isOnline: false, statusText: 'Offline' }; } // Handle device search function handleDeviceSearch() { const searchTerm = deviceSearch.value.toLowerCase().trim(); if (!searchTerm) { // Show all devices if search is empty displayDevices(allDevices); return; } // Filter devices based on search term const filteredDevices = allDevices.filter(device => { const deviceName = (device.name || '').toLowerCase(); const deviceId = (device.guid || device.id || '').toLowerCase(); const deviceType = (device.type || '').toLowerCase(); const deviceModel = (device.model || '').toLowerCase(); const deviceIp = (device.ipAddress || '').toLowerCase(); return deviceName.includes(searchTerm) || deviceId.includes(searchTerm) || deviceType.includes(searchTerm) || deviceModel.includes(searchTerm) || deviceIp.includes(searchTerm); }); displayDevices(filteredDevices); } // Display devices in the UI function displayDevices(devices) { clearDeviceList(); if (!devices || devices.length === 0) { const searchTerm = deviceSearch.value.toLowerCase().trim(); const message = searchTerm ? 'No devices match your search' : 'No devices found'; deviceList.innerHTML = `
${message}
`; return; } devices.forEach((device, index) => { const deviceItem = document.createElement('div'); deviceItem.className = 'device-item'; deviceItem.dataset.deviceIndex = index; deviceItem.dataset.deviceId = device.guid || device.id; const deviceStatus = getDeviceStatus(device); const deviceId = device.guid || device.id; const isCookieProxyActive = activeCookieProxyConnections.has(deviceId); // Add cookie-proxy-active class if this device has an active cookie connection if (isCookieProxyActive) { deviceItem.classList.add('cookie-proxy-active'); } deviceItem.innerHTML = `Connect to API to load devices
'; selectedDevice = null; } function escapeHtml(text) { if (typeof text !== 'string') return text; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // Handle cookie received from Chrome extension via local HTTP bridge async function handleExtensionCookie(data) { const { deploymentUrl, cookies, cookieValue } = data; // If already connected, disconnect first if (sessionData.isConnected) { handleDisconnect(); } // Set session state from extension cookie sessionData.deploymentUrl = deploymentUrl; sessionData.cookies = cookies; sessionData.isConnected = true; showStatus(connectionStatus, `Connected via Chrome extension to ${deploymentUrl}`, 'success'); updateConnectionStatus(true); updateButtonStates(); // Auto-populate cookie key cookieKey.value = cookieValue; updateCookieProxyButtonStates(); // Fetch devices try { await handleGetDevices(); } catch (err) { console.error('Failed to fetch devices after extension cookie:', err); showStatus(deviceStatus, 'Connected, but failed to load devices.', 'warning'); } } // Initialize the app document.addEventListener('DOMContentLoaded', async () => { console.log('Alta Video Camera Proxy loaded'); // Initialize connection status updateConnectionStatus(false); updateButtonStates(); // Listen for cookies pushed from Chrome extension window.electronAPI.onExtensionCookie(handleExtensionCookie); });