From 97c6f3ec1e5af0830e1f4fcb119e4284f5b5922a Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Fri, 11 Jul 2025 17:55:49 +0000 Subject: [PATCH] Improve camera reliability and diagnose capture issues more effectively Adds detailed logging and retry logic to address camera startup and dimension detection failures in CameraModal and useCamera. Replit-Commit-Author: Agent Replit-Commit-Session-Id: dec54137-d5a2-4406-8ad4-5f2f9fb110bf Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/eb403944-9342-469a-8dd7-95b8b1a1cf40.jpg --- client/src/components/CameraModal.tsx | 26 +++++++++-- client/src/components/NewSaleForm.tsx | 30 +++++++++++- client/src/hooks/useCamera.tsx | 66 +++++++++++++++++++++++---- 3 files changed, 108 insertions(+), 14 deletions(-) diff --git a/client/src/components/CameraModal.tsx b/client/src/components/CameraModal.tsx index 886f514..46ba93a 100644 --- a/client/src/components/CameraModal.tsx +++ b/client/src/components/CameraModal.tsx @@ -25,31 +25,47 @@ export default function CameraModal({ }: CameraModalProps) { useEffect(() => { if (isOpen && videoRef.current) { - // Ensure video starts playing when modal opens const video = videoRef.current; + console.log('CameraModal useEffect - video element ready'); const handleLoadedMetadata = () => { - console.log('Video loaded metadata:', video.videoWidth, 'x', video.videoHeight); + console.log('CameraModal: Video metadata loaded, dimensions:', video.videoWidth, 'x', video.videoHeight); }; const handleCanPlay = () => { - console.log('Video can play'); + console.log('CameraModal: Video can play, readyState:', video.readyState); }; const handleError = (error: Event) => { - console.error('Video error:', error); + console.error('CameraModal: Video error event:', error); + }; + + const handlePlay = () => { + console.log('CameraModal: Video started playing'); }; video.addEventListener('loadedmetadata', handleLoadedMetadata); video.addEventListener('canplay', handleCanPlay); video.addEventListener('error', handleError); + video.addEventListener('play', handlePlay); - video.play().catch(console.error); + // Forcer le play avec gestion d'erreur + const startPlayback = async () => { + try { + await video.play(); + console.log('CameraModal: Video play() successful'); + } catch (playError) { + console.error('CameraModal: Video play() failed:', playError); + } + }; + + startPlayback(); return () => { video.removeEventListener('loadedmetadata', handleLoadedMetadata); video.removeEventListener('canplay', handleCanPlay); video.removeEventListener('error', handleError); + video.removeEventListener('play', handlePlay); }; } }, [isOpen, videoRef]); diff --git a/client/src/components/NewSaleForm.tsx b/client/src/components/NewSaleForm.tsx index 22a7d4d..4444622 100644 --- a/client/src/components/NewSaleForm.tsx +++ b/client/src/components/NewSaleForm.tsx @@ -184,7 +184,8 @@ export default function NewSaleForm() { // Photo capture functions const handlePhotoCapture = async (photoType: PhotoType) => { - console.log('Photo capture requested for:', photoType); + console.log('=== PHOTO CAPTURE REQUESTED ==='); + console.log('Photo type:', photoType); // Vérifications préliminaires if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { @@ -208,8 +209,35 @@ export default function NewSaleForm() { try { console.log('Attempting to start camera...'); + + // Afficher toast de démarrage + toast({ + title: "Démarrage de la caméra", + description: "Préparation de la caméra en cours...", + }); + + // Démarrer la caméra await startCamera(photoType); console.log('Camera started successfully'); + + // Attendre 2 secondes supplémentaires pour s'assurer que la vidéo est prête + console.log('Waiting for video to be fully ready...'); + await new Promise(resolve => setTimeout(resolve, 2000)); + + // Vérifier l'état final de la vidéo + if (videoRef.current) { + const video = videoRef.current; + console.log('Final video state check:', { + videoWidth: video.videoWidth, + videoHeight: video.videoHeight, + readyState: video.readyState, + paused: video.paused, + currentTime: video.currentTime + }); + } + + console.log('Camera should be ready for capture'); + } catch (error) { console.error('Camera start failed:', error); toast({ diff --git a/client/src/hooks/useCamera.tsx b/client/src/hooks/useCamera.tsx index 6d9e7b7..d07b697 100644 --- a/client/src/hooks/useCamera.tsx +++ b/client/src/hooks/useCamera.tsx @@ -111,17 +111,33 @@ export function useCamera() { handleLoadedMetadata(); } - // Timeout de sécurité réduit + // Timeout de sécurité avec état détaillé setTimeout(() => { video.removeEventListener('loadedmetadata', handleLoadedMetadata); video.removeEventListener('error', handleError); video.removeEventListener('canplay', handleCanPlay); - console.log('Video load timeout, current state:', { + console.log('Video load timeout - final state:', { readyState: video.readyState, videoWidth: video.videoWidth, videoHeight: video.videoHeight, - paused: video.paused + paused: video.paused, + currentTime: video.currentTime, + duration: video.duration, + srcObject: !!video.srcObject }); + + // Vérifier si le stream est actif + if (video.srcObject) { + const stream = video.srcObject as MediaStream; + const tracks = stream.getTracks(); + console.log('Stream tracks status:', tracks.map(t => ({ + kind: t.kind, + enabled: t.enabled, + readyState: t.readyState, + label: t.label + }))); + } + resolve(true); }, 3000); }); @@ -158,23 +174,57 @@ export function useCamera() { console.log('Starting photo capture, current dimensions:', video.videoWidth, 'x', video.videoHeight); - // Attendre que la vidéo ait des dimensions valides + // Attendre que la vidéo ait des dimensions valides avec diagnostic détaillé let attempts = 0; - const maxAttempts = 50; // 5 secondes max + const maxAttempts = 100; // 10 secondes max const waitForDimensions = () => { return new Promise((resolveWait) => { const checkDimensions = () => { attempts++; - console.log(`Checking video dimensions, attempt ${attempts}: ${video.videoWidth}x${video.videoHeight}`); + + // Diagnostic détaillé à chaque tentative + const diagnosticInfo = { + attempt: attempts, + videoWidth: video.videoWidth, + videoHeight: video.videoHeight, + readyState: video.readyState, + paused: video.paused, + currentTime: video.currentTime, + duration: video.duration, + networkState: video.networkState, + srcObject: !!video.srcObject + }; + + console.log(`Capture attempt ${attempts}/100:`, diagnosticInfo); if (video.videoWidth > 0 && video.videoHeight > 0) { - console.log('Video dimensions are valid:', video.videoWidth, 'x', video.videoHeight); + console.log('✅ Video dimensions are valid:', video.videoWidth, 'x', video.videoHeight); resolveWait(); } else if (attempts >= maxAttempts) { + console.error('❌ Timeout: video dimensions not available after 10 seconds'); + console.error('Final video state:', diagnosticInfo); + + // Vérifier l'état du stream + if (video.srcObject) { + const stream = video.srcObject as MediaStream; + const tracks = stream.getTracks(); + console.error('Stream tracks final state:', tracks.map(t => ({ + kind: t.kind, + enabled: t.enabled, + readyState: t.readyState, + label: t.label + }))); + } + setIsCapturing(false); - reject(new Error('Timeout: video dimensions not available after 5 seconds')); + reject(new Error('Timeout: video dimensions not available after 10 seconds')); } else { + // Diagnostic périodique plus détaillé + if (attempts % 10 === 0) { + console.warn(`Still waiting for video dimensions after ${attempts} attempts...`); + console.warn('Current video state:', diagnosticInfo); + } setTimeout(checkDimensions, 100); } };