diff --git a/src/components/scanner/AutoScanningCamera.tsx b/src/components/scanner/AutoScanningCamera.tsx index 0195d99..8fdc154 100644 --- a/src/components/scanner/AutoScanningCamera.tsx +++ b/src/components/scanner/AutoScanningCamera.tsx @@ -1,6 +1,6 @@ import React, { useRef, useState, useEffect, useCallback } from 'react'; import ScanningToast from './ScanningToast'; -import { aiCardOCR, type CardOCRResult } from '../../services/aiOcr'; +import { aiCardOCR, ollamaCardOCR, type CardOCRResult } from '../../services/aiOcr'; interface ScannedCard { id: string; @@ -44,6 +44,60 @@ const AutoScanningCamera: React.FC = ({ const [queueCount, setQueueCount] = useState(0); const [recentScans, setRecentScans] = useState>(new Set()); + const [isOcrConfigured, setIsOcrConfigured] = useState(false); + + // Initialize OCR services with saved settings + useEffect(() => { + const initializeOCR = async () => { + try { + // Check for saved API key in localStorage (from OCRSettings component) + const savedApiKey = localStorage.getItem('openai_api_key'); + + // Also check user preferences (from Settings page) + const token = localStorage.getItem('tcg_vault_token'); + if (token) { + try { + const response = await fetch('/api/user/preferences', { + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + } + }); + + if (response.ok) { + const data = await response.json(); + const userApiKey = data.preferences?.ocrSettings?.openai_api_key; + + if (userApiKey) { + aiCardOCR.setApiKey(userApiKey); + setIsOcrConfigured(true); + console.log('✅ OCR configured with user preferences API key'); + return; + } + } + } catch (error) { + console.log('Could not load user preferences, checking localStorage...'); + } + } + + // Fallback to localStorage API key + if (savedApiKey) { + aiCardOCR.setApiKey(savedApiKey); + setIsOcrConfigured(true); + console.log('✅ OCR configured with localStorage API key'); + } else { + console.log('❌ No OpenAI API key found. OCR scanning will not work.'); + setIsOcrConfigured(false); + } + + } catch (error) { + console.error('Error initializing OCR:', error); + setIsOcrConfigured(false); + } + }; + + initializeOCR(); + }, []); // Initialize camera useEffect(() => { @@ -60,7 +114,7 @@ const AutoScanningCamera: React.FC = ({ // Auto-scan detection useEffect(() => { - if (!isStreaming || !isActive) return; + if (!isStreaming || !isActive || !isOcrConfigured) return; const detectCardStabilization = () => { if (scanState.isProcessing || queueCount >= maxQueueSize) return; @@ -115,7 +169,7 @@ const AutoScanningCamera: React.FC = ({ clearTimeout(scanTimeoutRef.current); } }; - }, [isStreaming, isActive, scanState.isProcessing, queueCount, maxQueueSize]); + }, [isStreaming, isActive, scanState.isProcessing, queueCount, maxQueueSize, isOcrConfigured]); const startCamera = async () => { try { @@ -180,6 +234,24 @@ const AutoScanningCamera: React.FC = ({ const triggerScan = useCallback(async () => { if (scanState.isProcessing || queueCount >= maxQueueSize) return; + // Check if OCR is configured + if (!isOcrConfigured) { + setScanState(prev => ({ + ...prev, + isScanning: false, + isProcessing: false, + message: 'OCR not configured - please set up your API key in Settings', + })); + + setTimeout(() => { + setScanState(prev => ({ + ...prev, + message: '', + })); + }, 4000); + return; + } + setScanState(prev => ({ ...prev, isScanning: false, @@ -194,8 +266,23 @@ const AutoScanningCamera: React.FC = ({ throw new Error('Failed to capture image'); } - // Process with AI OCR - const cardData = await aiCardOCR.analyzeCard(imageDataUrl); + // Process with AI OCR - try OpenAI first, fallback to Ollama + let cardData: CardOCRResult; + + try { + console.log('🤖 Trying OpenAI Vision API...'); + cardData = await aiCardOCR.analyzeCard(imageDataUrl); + console.log('✅ OpenAI Vision result:', cardData); + } catch (openaiError) { + console.log('❌ OpenAI failed, trying Ollama:', openaiError); + try { + cardData = await ollamaCardOCR.analyzeCard(imageDataUrl); + console.log('✅ Ollama Vision result:', cardData); + } catch (ollamaError) { + console.error('❌ All AI OCR methods failed'); + throw new Error('AI OCR services unavailable. Please configure OpenAI API key or run Ollama locally.'); + } + } // Create unique ID for this scan const cardId = `scan_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; @@ -253,10 +340,24 @@ const AutoScanningCamera: React.FC = ({ } catch (error: any) { console.error('Scan processing error:', error); + + let errorMessage = 'Scan failed, try again'; + + // Provide more specific error messages + if (error.message.includes('OpenAI API key not configured')) { + errorMessage = 'OpenAI API key not configured - check Settings'; + } else if (error.message.includes('OpenAI API error: 401')) { + errorMessage = 'Invalid OpenAI API key - check Settings'; + } else if (error.message.includes('OpenAI API error: 429')) { + errorMessage = 'API rate limit exceeded - wait a moment'; + } else if (error.message.includes('AI OCR services unavailable')) { + errorMessage = 'OCR service unavailable - configure API key'; + } + setScanState(prev => ({ ...prev, isProcessing: false, - message: 'Scan failed, try again', + message: errorMessage, })); setTimeout(() => { @@ -264,9 +365,9 @@ const AutoScanningCamera: React.FC = ({ ...prev, message: '', })); - }, 3000); + }, 4000); } - }, [captureImage, onCardScanned, queueCount, maxQueueSize, recentScans, scanState.isProcessing]); + }, [captureImage, onCardScanned, queueCount, maxQueueSize, recentScans, scanState.isProcessing, isOcrConfigured]); const manualScan = () => { if (!scanState.isProcessing && queueCount < maxQueueSize) { @@ -281,9 +382,24 @@ const AutoScanningCamera: React.FC = ({ isVisible={scanState.isScanning || scanState.isProcessing || !!scanState.message} message={scanState.message} progress={scanState.isScanning ? ((3 - scanState.countdown) / 3) * 100 : undefined} - type={scanState.isProcessing ? 'processing' : scanState.message.includes('Added:') ? 'success' : 'scanning'} + type={scanState.isProcessing ? 'processing' : scanState.message.includes('Added:') ? 'success' : scanState.message.includes('not configured') || scanState.message.includes('unavailable') ? 'error' : 'scanning'} /> + {/* OCR Configuration Warning */} + {!isOcrConfigured && isStreaming && ( +
+
+ ⚠️ +
+

OCR Not Configured

+

+ Please configure your OpenAI API key in Settings to enable card scanning. +

+
+
+
+ )} + {/* Camera Preview */}