Improve camera functionality and identify potential issues in production

Adds camera diagnostics and improves video constraints for production use in useSimpleCamera.tsx and cameraDebug.ts.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 4b008663-bd72-492b-b0b6-fa252367e730
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/4b008663-bd72-492b-b0b6-fa252367e730/tRJufBX
This commit is contained in:
michaelschal committed 2025-07-19 14:00:34 +00:00
1 parent a939b1068d
commit c68e72fa2a
2 files changed
+205 -50

No files matched your search

+99 -50
View File
@@ -1,5 +1,6 @@
import { useState, useRef, useCallback } from 'react';
import { PhotoType } from '@/types/sale';
import { getCameraCapabilities, logCameraCapabilities } from '@/utils/cameraDebug';
export function useSimpleCamera() {
const [isOpen, setIsOpen] = useState(false);
@@ -13,9 +14,14 @@ export function useSimpleCamera() {
const startCamera = useCallback(async (photoType: PhotoType): Promise<void> => {
console.log('=== SIMPLE CAMERA START ===');
// Diagnostic complet des capacités caméra
const capabilities = await getCameraCapabilities();
logCameraCapabilities(capabilities);
try {
// Nettoyer le stream précédent s'il existe
if (stream) {
console.log('Cleaning up previous stream...');
stream.getTracks().forEach(track => track.stop());
setStream(null);
}
@@ -25,6 +31,11 @@ export function useSimpleCamera() {
throw new Error('Votre navigateur ne supporte pas l\'accès à la caméra. Utilisez un navigateur moderne comme Chrome, Firefox ou Safari.');
}
// HTTPS check for production
if (location.protocol !== 'https:' && location.hostname !== 'localhost' && location.hostname !== '127.0.0.1') {
console.warn('Camera access may be restricted on non-HTTPS connections');
}
// État initial
setCurrentPhotoType(photoType);
setIsOpen(true);
@@ -32,45 +43,60 @@ export function useSimpleCamera() {
let mediaStream: MediaStream;
try {
// Essayer d'abord avec des contraintes simples
console.log('Trying basic video constraints...');
// Essayer d'abord avec des contraintes optimisées pour production
console.log('Trying production-optimized video constraints...');
mediaStream = await navigator.mediaDevices.getUserMedia({
video: {
width: { ideal: 1280, max: 1920 },
height: { ideal: 720, max: 1080 }
width: { ideal: 1280, min: 640, max: 1920 },
height: { ideal: 720, min: 480, max: 1080 },
facingMode: 'environment' // Préférer la caméra arrière
}
});
console.log('Basic video constraints successful');
console.log('Production video constraints successful');
} catch (basicError) {
console.warn('Basic constraints failed, trying minimal video:', basicError);
console.warn('Production constraints failed, trying user-facing camera:', basicError);
try {
// Fallback ultra-simple
// Fallback avec caméra avant
mediaStream = await navigator.mediaDevices.getUserMedia({
video: true
});
console.log('Minimal video constraints successful');
} catch (minimalError) {
console.error('All camera access failed:', minimalError);
// Fermer la modal en cas d'erreur
setIsOpen(false);
setCurrentPhotoType(null);
// Gestion des erreurs spécifiques
if (minimalError instanceof Error) {
if (minimalError.name === 'NotAllowedError') {
throw new Error('Permission d\'accès à la caméra refusée. Veuillez autoriser l\'accès dans votre navigateur et actualiser la page.');
} else if (minimalError.name === 'NotFoundError') {
throw new Error('Aucune caméra trouvée sur cet appareil. Veuillez connecter une caméra ou utiliser l\'option d\'importation de fichier.');
} else if (minimalError.name === 'NotReadableError') {
throw new Error('Caméra déjà utilisée par une autre application. Fermez les autres applications utilisant la caméra.');
} else if (minimalError.name === 'OverconstrainedError') {
throw new Error('Contraintes de caméra non supportées par votre appareil.');
video: {
width: { ideal: 1280, min: 640 },
height: { ideal: 720, min: 480 },
facingMode: 'user'
}
}
});
console.log('User-facing camera successful');
} catch (userError) {
console.warn('User-facing camera failed, trying basic constraints:', userError);
throw new Error('Erreur d\'accès à la caméra. Utilisez l\'option d\'importation de fichier comme alternative.');
try {
// Fallback ultra-simple
mediaStream = await navigator.mediaDevices.getUserMedia({
video: true
});
console.log('Basic video constraints successful');
} catch (minimalError) {
console.error('All camera access attempts failed:', minimalError);
// Fermer la modal en cas d'erreur
setIsOpen(false);
setCurrentPhotoType(null);
// Gestion des erreurs spécifiques
if (minimalError instanceof Error) {
if (minimalError.name === 'NotAllowedError') {
throw new Error('Permission d\'accès à la caméra refusée. Veuillez autoriser l\'accès dans votre navigateur et actualiser la page.');
} else if (minimalError.name === 'NotFoundError') {
throw new Error('Aucune caméra trouvée sur cet appareil. Veuillez connecter une caméra ou utiliser l\'option d\'importation de fichier.');
} else if (minimalError.name === 'NotReadableError') {
throw new Error('Caméra déjà utilisée par une autre application. Fermez les autres applications utilisant la caméra.');
} else if (minimalError.name === 'OverconstrainedError') {
throw new Error('Contraintes de caméra non supportées par votre appareil.');
}
}
throw new Error('Erreur d\'accès à la caméra. Utilisez l\'option d\'importation de fichier comme alternative.');
}
}
}
@@ -78,27 +104,50 @@ export function useSimpleCamera() {
setStream(mediaStream);
// Attendre que la modal soit rendue avant d'assigner le stream
setTimeout(() => {
if (videoRef.current && mediaStream && mediaStream.active) {
const video = videoRef.current;
console.log('Assigning stream to video element');
video.srcObject = mediaStream;
// Forcer le play avec gestion d'erreur
video.play().then(() => {
console.log('Video playing successfully');
}).catch(playError => {
console.error('Video play failed:', playError);
// Réessayer après un délai
setTimeout(() => {
video.play().catch(e => console.error('Retry video play failed:', e));
}, 200);
});
} else {
console.error('Video element not ready or stream inactive');
}
}, 200);
await new Promise((resolve) => {
setTimeout(() => {
if (videoRef.current && mediaStream && mediaStream.active) {
const video = videoRef.current;
console.log('Assigning stream to video element, tracks:', mediaStream.getVideoTracks().length);
video.srcObject = mediaStream;
// Configuration vidéo pour production
video.setAttribute('autoplay', 'true');
video.setAttribute('playsinline', 'true');
video.setAttribute('muted', 'true');
// Événements de diagnostic
video.onloadedmetadata = () => {
console.log('Video metadata loaded:', video.videoWidth, 'x', video.videoHeight);
resolve(void 0);
};
video.onerror = (e) => {
console.error('Video element error:', e);
resolve(void 0);
};
// Forcer le play avec gestion d'erreur robuste
video.play().then(() => {
console.log('Video playing successfully');
resolve(void 0);
}).catch(playError => {
console.error('Video play failed:', playError);
// Réessayer après un délai
setTimeout(() => {
video.play().catch(e => {
console.error('Retry video play failed:', e);
resolve(void 0);
});
}, 500);
});
} else {
console.error('Video element not ready or stream inactive');
resolve(void 0);
}
}, 300);
});
} catch (error) {
console.error('Camera start failed:', error);
+106
View File
@@ -0,0 +1,106 @@
// Utilitaires de diagnostic caméra pour production
export interface CameraCapabilities {
hasMediaDevices: boolean;
hasGetUserMedia: boolean;
isHTTPS: boolean;
userAgent: string;
availableDevices: MediaDeviceInfo[];
constraints: {
video: boolean;
audio: boolean;
};
}
export async function getCameraCapabilities(): Promise<CameraCapabilities> {
const capabilities: CameraCapabilities = {
hasMediaDevices: !!navigator.mediaDevices,
hasGetUserMedia: !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia),
isHTTPS: location.protocol === 'https:' || location.hostname === 'localhost',
userAgent: navigator.userAgent,
availableDevices: [],
constraints: {
video: false,
audio: false
}
};
// Énumérer les appareils disponibles si possible
if (capabilities.hasMediaDevices) {
try {
capabilities.availableDevices = await navigator.mediaDevices.enumerateDevices();
} catch (error) {
console.warn('Cannot enumerate devices:', error);
}
// Tester les contraintes supportées
if (capabilities.hasGetUserMedia) {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
capabilities.constraints.video = true;
stream.getTracks().forEach(track => track.stop());
} catch (error) {
console.warn('Video not supported:', error);
}
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
capabilities.constraints.audio = true;
stream.getTracks().forEach(track => track.stop());
} catch (error) {
console.warn('Audio not supported:', error);
}
}
}
return capabilities;
}
export function logCameraCapabilities(capabilities: CameraCapabilities): void {
console.group('🔍 Camera Capabilities Report');
console.log('Media Devices API:', capabilities.hasMediaDevices ? '✅' : '❌');
console.log('getUserMedia API:', capabilities.hasGetUserMedia ? '✅' : '❌');
console.log('HTTPS Connection:', capabilities.isHTTPS ? '✅' : '⚠️');
console.log('User Agent:', capabilities.userAgent);
console.log('Video Support:', capabilities.constraints.video ? '✅' : '❌');
console.log('Audio Support:', capabilities.constraints.audio ? '✅' : '❌');
if (capabilities.availableDevices.length > 0) {
console.log('Available Devices:');
capabilities.availableDevices.forEach((device, index) => {
console.log(` ${index + 1}. ${device.kind}: ${device.label || 'Unknown'}`);
});
} else {
console.log('Available Devices: None found or permission denied');
}
console.groupEnd();
}
export function generateCameraReport(capabilities: CameraCapabilities): string {
const deviceCount = capabilities.availableDevices.filter(d => d.kind === 'videoinput').length;
let report = `=== Rapport Caméra Production ===\n`;
report += `Navigateur: ${capabilities.userAgent.split(' ')[0]}\n`;
report += `HTTPS: ${capabilities.isHTTPS ? 'Oui' : 'Non'}\n`;
report += `API Média: ${capabilities.hasMediaDevices ? 'Disponible' : 'Indisponible'}\n`;
report += `getUserMedia: ${capabilities.hasGetUserMedia ? 'Disponible' : 'Indisponible'}\n`;
report += `Support Vidéo: ${capabilities.constraints.video ? 'Oui' : 'Non'}\n`;
report += `Caméras détectées: ${deviceCount}\n`;
if (!capabilities.isHTTPS && location.hostname !== 'localhost') {
report += `\n⚠️ ATTENTION: Connexion non sécurisée détectée.\n`;
report += `La caméra peut ne pas fonctionner sans HTTPS.\n`;
}
if (!capabilities.hasGetUserMedia) {
report += `\n❌ ERREUR: API getUserMedia non disponible.\n`;
report += `Utilisez un navigateur moderne (Chrome, Firefox, Safari).\n`;
}
if (deviceCount === 0) {
report += `\n⚠️ ATTENTION: Aucune caméra détectée.\n`;
report += `Vérifiez les permissions ou connectez une caméra.\n`;
}
return report;
}