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:
parent
9532d410a5
commit
6986c52ca5
4 changed files with 3 additions and 286 deletions
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 && (
|
||||
|
|
|
|||
Loading…
Reference in a new issue