// @ts-nocheck import { captureModeSuffix, effectiveScanMs, formatScanInterval } from "./config.js"; import { applyAnalysisOutputs, getAnalyzeButton, readAnalysisRequestInputs, readResultSignature } from "./analysisDom.js"; import { updateSmartDelayFromLatestResult, updateCountdown } from "./timing.js"; function findAnalyzeDependency() { const dependencies = window.gradio_config?.dependencies || []; return dependencies.find((dependency) => dependency?.api_name === 'analyze_screenshot') || dependencies.find((dependency) => String(dependency?.api_name || '').includes('analyze_screenshot')) || dependencies[0] || {}; } function makeSessionHash() { return `trackwhat_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`; } function getApiOrigin() { return window.location.origin; } async function readJsonResponse(response) { const payload = await response.json().catch(() => ({})); if (!response.ok) { const error = new Error(payload?.detail || payload?.error || `Analysis request failed (${response.status})`); error.status = response.status; error.payload = payload; throw error; } return payload; } function shouldFallbackToQueue(error) { const message = String(error?.message || error || '').toLowerCase(); return error?.status === 404 || message.includes('queue') || message.includes('failed to fetch') || message.includes('direct http post'); } async function callAnalyzeDirect(data) { const dependency = findAnalyzeDependency(); const sessionHash = makeSessionHash(); const fnIndex = Number.isFinite(Number(dependency.id)) ? Number(dependency.id) : 0; const triggerId = Array.isArray(dependency.targets?.[0]) ? dependency.targets[0][0] : undefined; const response = await fetch(`${getApiOrigin()}/run/${String(dependency.api_name || 'analyze_screenshot').replace(/^\//, '')}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ data, event_data: null, fn_index: fnIndex, session_hash: sessionHash, ...(triggerId !== undefined ? { trigger_id: triggerId } : {}), }), }); return readJsonResponse(response); } async function callAnalyzeQueue(data) { const dependency = findAnalyzeDependency(); const sessionHash = makeSessionHash(); const fnIndex = Number.isFinite(Number(dependency.id)) ? Number(dependency.id) : 0; const triggerId = Array.isArray(dependency.targets?.[0]) ? dependency.targets[0][0] : undefined; const joinBody = { data, event_data: null, fn_index: fnIndex, session_hash: sessionHash, }; if (triggerId !== undefined) joinBody.trigger_id = triggerId; await readJsonResponse(await fetch(`${getApiOrigin()}/queue/join`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(joinBody), })); return new Promise((resolve, reject) => { const events = new EventSource(`${getApiOrigin()}/queue/data?session_hash=${encodeURIComponent(sessionHash)}`); const timeout = window.setTimeout(() => { events.close(); reject(new Error('Queued analysis timed out.')); }, 180000); events.onmessage = (event) => { let message = {}; try { message = JSON.parse(event.data || '{}'); } catch (error) { return; } if (message.msg === 'process_completed') { window.clearTimeout(timeout); events.close(); if (message.success === false || message.output?.error) { reject(new Error(message.output?.error || 'Queued analysis failed.')); return; } resolve(message.output || {}); } }; events.onerror = () => { window.clearTimeout(timeout); events.close(); reject(new Error('Queued analysis connection failed.')); }; }); } async function callAnalyze(data) { try { return await callAnalyzeQueue(data); } catch (error) { if (!shouldFallbackToQueue(error)) throw error; window['__trackwhatDesktopCaptureTrace']?.('analysis.request.queue-fallback', { message: error?.message || String(error), status: error?.status || null, }); return callAnalyzeDirect(data); } } export function createAnalysisController({ state, scheduleNextScan, setCaptureStatus, onAnalysisPendingChange }) { function setAnalysisPending(pending) { state.analysisPending = pending; if (state.analysisGuardTimer) { window.clearTimeout(state.analysisGuardTimer); state.analysisGuardTimer = null; } if (pending) { state.analysisStartedAt = Date.now(); state.analysisStartSignature = readResultSignature(); state.analysisSawResultChange = false; } else { state.analysisStartedAt = 0; state.analysisStartSignature = ''; state.analysisSawResultChange = false; } const { button } = getAnalyzeButton(); if (button) button.dataset.analysisPending = pending ? 'true' : 'false'; const resultBox = document.getElementById('important-response'); if (resultBox) resultBox.dataset.analysisPending = pending ? 'true' : 'false'; const headerButton = document.getElementById('header-analyze-btn'); if (headerButton) headerButton.dataset.analysisPending = pending ? 'true' : 'false'; onAnalysisPendingChange?.(pending); } function retryAnalysisLater(message, delayMs = 1000) { window['__trackwhatDesktopCaptureTrace']?.('analysis.retry', { message, delayMs, active: state.active, analysisPending: state.analysisPending, nextScanAt: state.nextScanAt, }); setCaptureStatus(message); if (state.active) scheduleNextScan(delayMs); } function recoverStaleAnalysis(reason = 'manual') { if (!state.analysisPending) return false; window['__trackwhatDesktopCaptureTrace']?.('analysis.recover', { reason, startedAt: state.analysisStartedAt, pending: state.analysisPending }); setAnalysisPending(false); if (state.active) scheduleNextScan(); return true; } function markAnalysisComplete() { if (!state.analysisPending) return; const rearmNextScan = state.analysisRearmNextScan !== false; const manualOneShot = state.analysisOneShot === true; if (state.analysisGuardTimer) { window.clearTimeout(state.analysisGuardTimer); state.analysisGuardTimer = null; } setAnalysisPending(false); updateSmartDelayFromLatestResult(); if (state.active && rearmNextScan && !manualOneShot) { const delayText = state.smartDelayMs ? ` Smart delay added ${Math.round(state.smartDelayMs / 1000)}s.` : ''; setCaptureStatus(`Desktop capture is live${captureModeSuffix()}. Base interval ${formatScanInterval(effectiveScanMs())}.${delayText}`); scheduleNextScan(); } state.analysisRearmNextScan = true; state.analysisOneShot = false; } function waitForAnalysisCompletion() { if (state.analysisCompletionTicker) { window.clearInterval(state.analysisCompletionTicker); state.analysisCompletionTicker = null; } const startedAt = state.analysisStartedAt || Date.now(); const maxWaitMs = Math.max(10000, effectiveScanMs() * 2 + (state.smartDelayMs || 0) + 2500); state.analysisCompletionTicker = window.setInterval(() => { if (!state.analysisPending || startedAt !== state.analysisStartedAt) { if (state.analysisCompletionTicker) { window.clearInterval(state.analysisCompletionTicker); state.analysisCompletionTicker = null; } return; } const currentSignature = readResultSignature(); if (currentSignature && currentSignature !== state.analysisStartSignature) { if (state.analysisCompletionTicker) { window.clearInterval(state.analysisCompletionTicker); state.analysisCompletionTicker = null; } markAnalysisComplete(); return; } if (Date.now() - startedAt > maxWaitMs) { if (state.analysisCompletionTicker) { window.clearInterval(state.analysisCompletionTicker); state.analysisCompletionTicker = null; } markAnalysisComplete(); } }, 250); state.analysisGuardTimer = window.setTimeout(() => { if (!state.analysisPending || startedAt !== state.analysisStartedAt) return; markAnalysisComplete(); }, maxWaitMs); } function requestAnalysis(rearmNextScan = true) { const { frameData, lockedTrackName, note, useHintOnce, visionModelId } = readAnalysisRequestInputs(); if (!frameData) { retryAnalysisLater('Desktop frame is not ready yet. Next capture will retry.', 1000); return; } window['__trackwhatDesktopCaptureTrace']?.('analysis.request.start', { active: state.active, analysisPending: state.analysisPending, rearmNextScan, scanMs: state.scanMs, effectiveScanMs: effectiveScanMs(), requestMode: 'direct-api', }); state.analysisRearmNextScan = rearmNextScan; state.analysisOneShot = !rearmNextScan; setCaptureStatus('Analyzing current desktop capture...'); setAnalysisPending(true); waitForAnalysisCompletion(); (async () => { try { const payload = await callAnalyze([frameData, lockedTrackName, note, useHintOnce, visionModelId]); const outputs = Array.isArray(payload?.data) ? payload.data : []; window['__trackwhatDesktopCaptureTrace']?.('analysis.request.success', { outputs: outputs.length, track: outputs[0] || '', }); applyAnalysisOutputs(outputs); markAnalysisComplete(); } catch (error) { setAnalysisPending(false); retryAnalysisLater(`Could not start analysis: ${error?.message || error}`, 1500); window['__trackwhatDesktopCaptureTrace']?.('analysis.request.error', { message: error?.message || String(error), }); console.warn('Desktop capture analysis request failed:', error); } })(); } return { getAnalyzeButton, readResultSignature, setAnalysisPending, retryAnalysisLater, recoverStaleAnalysis, markAnalysisComplete, waitForAnalysisCompletion, requestAnalysis }; }