const elements = { username: document.getElementById('username'), password: document.getElementById('password'), studio: document.getElementById('studio'), minPrice: document.getElementById('min_price'), startBtn: document.getElementById('startBtn'), stopBtn: document.getElementById('stopBtn'), saveBtn: document.getElementById('saveBtn'), downloadBtn: document.getElementById('downloadBtn'), clearLogsBtn: document.getElementById('clearLogsBtn'), errorBanner: document.getElementById('errorBanner'), successBanner: document.getElementById('successBanner'), connectionStatus: document.getElementById('connectionStatus'), currentState: document.getElementById('currentState'), totalItems: document.getElementById('totalItems'), currentPage: document.getElementById('currentPage'), lastRunTime: document.getElementById('lastRunTime'), progressBar: document.getElementById('progressBar'), progressLabel: document.getElementById('progressLabel'), csvLabel: document.getElementById('csvLabel'), logOutput: document.getElementById('logOutput'), }; let logCursor = 0; let pollTimer = null; let running = false; let screenshotTimer = null; function setBanner(element, message) { if (!message) { element.classList.add('hidden'); element.textContent = ''; return; } element.textContent = message; element.classList.remove('hidden'); } function showError(message) { setBanner(elements.errorBanner, message); setBanner(elements.successBanner, ''); } function showSuccess(message) { setBanner(elements.successBanner, message); setBanner(elements.errorBanner, ''); } function clearBanners() { setBanner(elements.errorBanner, ''); setBanner(elements.successBanner, ''); } function readFormData() { return { username: elements.username.value.trim(), password: elements.password.value, studio: elements.studio.value.trim(), min_price: elements.minPrice.value.trim(), }; } function setRunningState(isRunning) { running = isRunning; elements.startBtn.disabled = isRunning; elements.stopBtn.disabled = !isRunning; elements.connectionStatus.textContent = isRunning ? 'Running' : 'Ready'; if (isRunning) { elements.connectionStatus.parentElement.querySelector('.dot').style.background = '#f59e0b'; } else { elements.connectionStatus.parentElement.querySelector('.dot').style.background = '#22c55e'; } } function updateStatusPanel(data) { elements.currentState.textContent = data.current_state || 'Idle'; elements.totalItems.textContent = String(data.total_items_found ?? 0); elements.currentPage.textContent = String(data.current_page ?? 0); elements.lastRunTime.textContent = data.last_run_time || '-'; const progress = Number(data.progress || 0); elements.progressBar.style.width = `${Math.max(0, Math.min(100, progress))}%`; elements.progressLabel.textContent = `${Math.max(0, Math.min(100, progress))}%`; elements.csvLabel.textContent = data.last_csv_path ? `Latest CSV: ${data.last_csv_path}` : 'No CSV generated yet'; if (data.last_error) { showError(data.last_error); } } function appendLogs(logs) { if (!Array.isArray(logs) || logs.length <= logCursor) { return; } const newLogs = logs.slice(logCursor); if (!newLogs.length) { return; } const current = elements.logOutput.textContent === 'Waiting for the next run...' ? '' : elements.logOutput.textContent; const merged = [current.trimEnd(), ...newLogs].filter(Boolean).join('\n'); elements.logOutput.textContent = merged; elements.logOutput.scrollTop = elements.logOutput.scrollHeight; logCursor = logs.length; } async function fetchStatus() { try { const response = await fetch('/status', { cache: 'no-store' }); const data = await response.json(); setRunningState(Boolean(data.running)); updateStatusPanel(data); appendLogs(data.logs || []); if (!data.running && pollTimer) { window.clearInterval(pollTimer); pollTimer = null; } } catch (error) { console.error(error); } } async function startBot() { clearBanners(); const payload = readFormData(); if (!payload.username || !payload.password || !payload.studio) { showError('Please enter username, password, and a studio name or URL.'); return; } if (!payload.min_price || Number.isNaN(Number(payload.min_price))) { showError('Minimum price must be a number.'); return; } try { const response = await fetch('/start', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); const data = await response.json(); if (!response.ok || !data.ok) { showError(data.error || 'Failed to start automation.'); return; } showSuccess('Automation started.'); logCursor = 0; elements.logOutput.textContent = 'Waiting for the next run...'; setRunningState(true); if (!pollTimer) { pollTimer = window.setInterval(fetchStatus, 2000); } await fetchStatus(); } catch (error) { showError(error.message || 'Unable to start the bot.'); } } async function stopBot() { try { const response = await fetch('/stop', { method: 'POST' }); const data = await response.json(); showSuccess(data.message || 'Stop requested.'); await fetchStatus(); } catch (error) { showError(error.message || 'Unable to stop the bot.'); } } async function saveSettings() { clearBanners(); const payload = readFormData(); try { const response = await fetch('/save-settings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); const data = await response.json(); if (!response.ok || !data.ok) { showError(data.error || 'Unable to save settings.'); return; } showSuccess('Settings saved.'); } catch (error) { showError(error.message || 'Unable to save settings.'); } } function clearLogs() { logCursor = 0; elements.logOutput.textContent = 'Waiting for the next run...'; } function boot() { elements.startBtn.addEventListener('click', startBot); elements.stopBtn.addEventListener('click', stopBot); elements.saveBtn.addEventListener('click', saveSettings); elements.clearLogsBtn.addEventListener('click', clearLogs); elements.downloadBtn.addEventListener('click', () => { elements.downloadBtn.setAttribute('href', '/download'); }); setRunningState(false); fetchStatus(); pollTimer = window.setInterval(fetchStatus, 2000); // Start screenshot refresher const img = document.getElementById('screenshotImg'); if (img) { const refresh = () => { img.src = '/screenshot?ts=' + Date.now(); }; // refresh every 3 seconds screenshotTimer = window.setInterval(refresh, 3000); // try an initial load refresh(); } } document.addEventListener('DOMContentLoaded', boot);