Clean up debug components and finalize OCR scanning

🧹 Cleanup:
- Remove DebugInfo and SimpleCameraTest components
- Remove debug state and logging from CameraScanner
- Clean up Scanner page imports

 OCR Scanning Ready:
- Camera UI now works perfectly
- OCR processing with Tesseract.js implemented
- Card matching service connected
- Collection integration ready
- Full scanning workflow functional

🎯 Features Available:
📷 Camera capture with live preview
�� OCR text extraction from card images
🃏 Smart card name/set detection
🔄 Card database matching with confidence scores
📚 Add scanned cards to collections
This commit is contained in:
Randall Stillwell 2025-07-22 10:03:07 -05:00
parent 9532d410a5
commit 6986c52ca5
4 changed files with 3 additions and 286 deletions

View file

@ -18,7 +18,7 @@ const CameraScanner: React.FC<CameraScannerProps> = ({ onCardScanned, onError })
const [isProcessing, setIsProcessing] = useState(false);
const [scanResult, setScanResult] = useState<ScanResult | null>(null);
const [capturedImage, setCapturedImage] = useState<string | null>(null);
const [debugInfo, setDebugInfo] = useState<string>('Camera not started');
const videoRef = useRef<HTMLVideoElement>(null);
const canvasRef = useRef<HTMLCanvasElement>(null);
@ -26,8 +26,6 @@ const CameraScanner: React.FC<CameraScannerProps> = ({ onCardScanned, onError })
// Start camera stream
const startCamera = async () => {
setDebugInfo('🔄 Requesting camera access...');
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: {
@ -37,35 +35,27 @@ const CameraScanner: React.FC<CameraScannerProps> = ({ onCardScanned, onError })
}
});
setDebugInfo('✅ Camera stream obtained');
if (videoRef.current) {
videoRef.current.srcObject = stream;
streamRef.current = stream;
// Wait for video to be ready
videoRef.current.onloadedmetadata = () => {
setDebugInfo('🎥 Video metadata loaded, starting playback...');
videoRef.current?.play().then(() => {
setIsStreaming(true);
setDebugInfo('✅ Camera streaming successfully');
}).catch((err) => {
setDebugInfo(`❌ Video play failed: ${err.message}`);
onError(`Video playback failed: ${err.message}`);
});
};
videoRef.current.onerror = (err) => {
setDebugInfo(`❌ Video error: ${err}`);
onError('Video element error occurred');
};
} else {
setDebugInfo('❌ Video ref is null');
onError('Video element not available');
}
} catch (err: any) {
console.error('Camera access error:', err);
setDebugInfo(`❌ Camera error: ${err.message}`);
onError(`Unable to access camera: ${err.message}`);
}
};
@ -79,7 +69,6 @@ const CameraScanner: React.FC<CameraScannerProps> = ({ onCardScanned, onError })
setIsStreaming(false);
setCapturedImage(null);
setScanResult(null);
setDebugInfo('Camera stopped');
};
// Capture image from video stream
@ -351,19 +340,7 @@ const CameraScanner: React.FC<CameraScannerProps> = ({ onCardScanned, onError })
</ul>
</div>
{/* Debug Info */}
<div className="bg-gray-100 rounded-lg p-3 mt-4 text-xs text-gray-800">
<div className="font-medium">Camera Status:</div>
<p>{debugInfo}</p>
<div className="mt-2 space-y-1">
<div>isStreaming: {isStreaming ? '✅ true' : '❌ false'}</div>
<div>videoRef.current: {videoRef.current ? '✅ exists' : '❌ null'}</div>
<div>streamRef.current: {streamRef.current ? '✅ exists' : '❌ null'}</div>
{videoRef.current && (
<div>Video ready state: {videoRef.current.readyState}</div>
)}
</div>
</div>
</div>
);
};

View file

@ -1,144 +0,0 @@
import React, { useState, useEffect } from 'react';
import { useAuth } from '../contexts/AuthContext';
const DebugInfo: React.FC = () => {
const { user } = useAuth();
const [debugInfo, setDebugInfo] = useState<any>(null);
const [cameraSupported, setCameraSupported] = useState<boolean | null>(null);
useEffect(() => {
// Check camera support
setCameraSupported(
navigator.mediaDevices &&
navigator.mediaDevices.getUserMedia &&
typeof navigator.mediaDevices.getUserMedia === 'function'
);
// Test API connectivity
testAPIs();
}, [user]);
const testAPIs = async () => {
const token = localStorage.getItem('token');
const results: any = {
token: token ? `${token.substring(0, 20)}...` : 'No token',
tokenLength: token?.length || 0,
user: user ? {
id: user.id,
username: user.username,
roles: user.roles
} : 'Not logged in'
};
try {
// Test collections API
const collectionsResponse = await fetch('/api/collections', {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
results.collectionsAPI = {
status: collectionsResponse.status,
ok: collectionsResponse.ok,
data: collectionsResponse.ok ? await collectionsResponse.json() : await collectionsResponse.text()
};
} catch (error: any) {
results.collectionsAPI = {
error: error.message
};
}
setDebugInfo(results);
};
const testCamera = async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: 'environment' }
});
setDebugInfo((prev: any) => ({
...prev,
cameraTest: '✅ Camera access successful',
cameraStream: {
active: stream.active,
tracks: stream.getVideoTracks().length
}
}));
// Stop the test stream
stream.getTracks().forEach(track => track.stop());
} catch (error: any) {
setDebugInfo((prev: any) => ({
...prev,
cameraTest: `❌ Camera error: ${error.message}`,
cameraError: error.name
}));
}
};
if (!debugInfo) return <div>Loading debug info...</div>;
return (
<div className="bg-gray-100 border border-gray-300 rounded-lg p-4 mb-6">
<h3 className="font-bold text-gray-900 mb-3">🔧 Debug Information</h3>
<div className="space-y-3 text-sm">
<div>
<strong>Camera Support:</strong> {cameraSupported ? '✅ Supported' : '❌ Not supported'}
</div>
<div>
<strong>User:</strong> {JSON.stringify(debugInfo.user, null, 2)}
</div>
<div>
<strong>Token:</strong> {debugInfo.token} (Length: {debugInfo.tokenLength})
</div>
<div>
<strong>Collections API:</strong>
<pre className="mt-1 bg-white p-2 rounded text-xs overflow-x-auto">
{JSON.stringify(debugInfo.collectionsAPI, null, 2)}
</pre>
</div>
{debugInfo.cameraTest && (
<div>
<strong>Camera Test:</strong> {debugInfo.cameraTest}
{debugInfo.cameraStream && (
<div className="ml-4 text-xs">
Active: {debugInfo.cameraStream.active ? 'Yes' : 'No'},
Tracks: {debugInfo.cameraStream.tracks}
</div>
)}
{debugInfo.cameraError && (
<div className="ml-4 text-xs text-red-600">
Error Type: {debugInfo.cameraError}
</div>
)}
</div>
)}
</div>
<div className="mt-4 space-x-2">
<button
onClick={testCamera}
className="bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded text-sm"
>
Test Camera
</button>
<button
onClick={testAPIs}
className="bg-green-600 hover:bg-green-700 text-white px-3 py-1 rounded text-sm"
>
Refresh API Test
</button>
</div>
</div>
);
};
export default DebugInfo;

View file

@ -1,110 +0,0 @@
import React, { useState, useRef } from 'react';
const SimpleCameraTest: React.FC = () => {
const [stream, setStream] = useState<MediaStream | null>(null);
const [error, setError] = useState<string>('');
const [status, setStatus] = useState<string>('Not started');
const videoRef = useRef<HTMLVideoElement>(null);
const startCamera = async () => {
setStatus('🔄 Starting camera...');
setError('');
try {
const mediaStream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: 'environment' }
});
setStatus('✅ Camera stream obtained');
setStream(mediaStream);
if (videoRef.current) {
videoRef.current.srcObject = mediaStream;
setStatus('✅ Stream assigned to video element');
// Force play
videoRef.current.play().then(() => {
setStatus('✅ Video playing successfully');
}).catch((err) => {
setError(`Play error: ${err.message}`);
setStatus('❌ Video play failed');
});
} else {
setError('Video ref is null');
setStatus('❌ No video element');
}
} catch (err: any) {
setError(`Camera error: ${err.message}`);
setStatus('❌ Camera failed');
}
};
const stopCamera = () => {
if (stream) {
stream.getTracks().forEach(track => track.stop());
setStream(null);
}
setStatus('Camera stopped');
setError('');
};
return (
<div className="p-4 border border-gray-300 rounded-lg">
<h3 className="font-bold mb-4">🔬 Simple Camera Test</h3>
<div className="space-y-4">
<div className="flex gap-2">
<button
onClick={startCamera}
className="bg-green-600 text-white px-4 py-2 rounded"
disabled={!!stream}
>
Start Camera
</button>
<button
onClick={stopCamera}
className="bg-red-600 text-white px-4 py-2 rounded"
disabled={!stream}
>
Stop Camera
</button>
</div>
<div className="text-sm">
<div><strong>Status:</strong> {status}</div>
{error && <div className="text-red-600"><strong>Error:</strong> {error}</div>}
<div><strong>Stream:</strong> {stream ? '✅ Active' : '❌ None'}</div>
<div><strong>Video Ref:</strong> {videoRef.current ? '✅ Exists' : '❌ Null'}</div>
</div>
{/* Always show video element */}
<div className="bg-black rounded-lg overflow-hidden">
<video
ref={videoRef}
autoPlay
playsInline
muted
className="w-full h-auto max-h-64 object-cover"
style={{ minHeight: '200px' }}
/>
</div>
{/* Debug info */}
<div className="text-xs bg-gray-100 p-2 rounded">
<div>Video element exists: {videoRef.current ? 'Yes' : 'No'}</div>
{videoRef.current && (
<>
<div>Video width: {videoRef.current.videoWidth}</div>
<div>Video height: {videoRef.current.videoHeight}</div>
<div>Ready state: {videoRef.current.readyState}</div>
<div>Paused: {videoRef.current.paused ? 'Yes' : 'No'}</div>
<div>Muted: {videoRef.current.muted ? 'Yes' : 'No'}</div>
</>
)}
</div>
</div>
</div>
);
};
export default SimpleCameraTest;

View file

@ -2,8 +2,6 @@ import React, { useState, useEffect } from 'react';
import CameraScanner from '../components/CameraScanner';
import GlowingCard from '../components/GlowingCard';
import CardImageDisplay from '../components/CardImageDisplay';
import DebugInfo from '../components/DebugInfo';
import SimpleCameraTest from '../components/SimpleCameraTest';
import { cardMatcher } from '../services/cardMatcher';
import { useAuth } from '../contexts/AuthContext';
@ -277,11 +275,7 @@ const Scanner: React.FC = () => {
</p>
</div>
{/* Debug Information */}
<DebugInfo />
{/* Simple Camera Test */}
<SimpleCameraTest />
{/* Error Display */}
{error && (