mirror of
https://github.com/R0m1k3/Regisflow.git
synced 2026-10-11 17:29:48 +02:00
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:
1 parent
a939b1068d
commit
c68e72fa2a
2 files changed
+205
-50
No files matched your search
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useRef, useCallback } from 'react';
|
import { useState, useRef, useCallback } from 'react';
|
||||||
import { PhotoType } from '@/types/sale';
|
import { PhotoType } from '@/types/sale';
|
||||||
|
import { getCameraCapabilities, logCameraCapabilities } from '@/utils/cameraDebug';
|
||||||
|
|
||||||
export function useSimpleCamera() {
|
export function useSimpleCamera() {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
@@ -13,9 +14,14 @@ export function useSimpleCamera() {
|
|||||||
const startCamera = useCallback(async (photoType: PhotoType): Promise<void> => {
|
const startCamera = useCallback(async (photoType: PhotoType): Promise<void> => {
|
||||||
console.log('=== SIMPLE CAMERA START ===');
|
console.log('=== SIMPLE CAMERA START ===');
|
||||||
|
|
||||||
|
// Diagnostic complet des capacités caméra
|
||||||
|
const capabilities = await getCameraCapabilities();
|
||||||
|
logCameraCapabilities(capabilities);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Nettoyer le stream précédent s'il existe
|
// Nettoyer le stream précédent s'il existe
|
||||||
if (stream) {
|
if (stream) {
|
||||||
|
console.log('Cleaning up previous stream...');
|
||||||
stream.getTracks().forEach(track => track.stop());
|
stream.getTracks().forEach(track => track.stop());
|
||||||
setStream(null);
|
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.');
|
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
|
// État initial
|
||||||
setCurrentPhotoType(photoType);
|
setCurrentPhotoType(photoType);
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
@@ -32,45 +43,60 @@ export function useSimpleCamera() {
|
|||||||
let mediaStream: MediaStream;
|
let mediaStream: MediaStream;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Essayer d'abord avec des contraintes simples
|
// Essayer d'abord avec des contraintes optimisées pour production
|
||||||
console.log('Trying basic video constraints...');
|
console.log('Trying production-optimized video constraints...');
|
||||||
mediaStream = await navigator.mediaDevices.getUserMedia({
|
mediaStream = await navigator.mediaDevices.getUserMedia({
|
||||||
video: {
|
video: {
|
||||||
width: { ideal: 1280, max: 1920 },
|
width: { ideal: 1280, min: 640, max: 1920 },
|
||||||
height: { ideal: 720, max: 1080 }
|
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) {
|
} catch (basicError) {
|
||||||
console.warn('Basic constraints failed, trying minimal video:', basicError);
|
console.warn('Production constraints failed, trying user-facing camera:', basicError);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fallback ultra-simple
|
// Fallback avec caméra avant
|
||||||
mediaStream = await navigator.mediaDevices.getUserMedia({
|
mediaStream = await navigator.mediaDevices.getUserMedia({
|
||||||
video: true
|
video: {
|
||||||
});
|
width: { ideal: 1280, min: 640 },
|
||||||
console.log('Minimal video constraints successful');
|
height: { ideal: 720, min: 480 },
|
||||||
} catch (minimalError) {
|
facingMode: 'user'
|
||||||
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.');
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
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);
|
setStream(mediaStream);
|
||||||
|
|
||||||
// Attendre que la modal soit rendue avant d'assigner le stream
|
// Attendre que la modal soit rendue avant d'assigner le stream
|
||||||
setTimeout(() => {
|
await new Promise((resolve) => {
|
||||||
if (videoRef.current && mediaStream && mediaStream.active) {
|
setTimeout(() => {
|
||||||
const video = videoRef.current;
|
if (videoRef.current && mediaStream && mediaStream.active) {
|
||||||
console.log('Assigning stream to video element');
|
const video = videoRef.current;
|
||||||
|
console.log('Assigning stream to video element, tracks:', mediaStream.getVideoTracks().length);
|
||||||
video.srcObject = mediaStream;
|
|
||||||
|
video.srcObject = mediaStream;
|
||||||
// Forcer le play avec gestion d'erreur
|
|
||||||
video.play().then(() => {
|
// Configuration vidéo pour production
|
||||||
console.log('Video playing successfully');
|
video.setAttribute('autoplay', 'true');
|
||||||
}).catch(playError => {
|
video.setAttribute('playsinline', 'true');
|
||||||
console.error('Video play failed:', playError);
|
video.setAttribute('muted', 'true');
|
||||||
// Réessayer après un délai
|
|
||||||
setTimeout(() => {
|
// Événements de diagnostic
|
||||||
video.play().catch(e => console.error('Retry video play failed:', e));
|
video.onloadedmetadata = () => {
|
||||||
}, 200);
|
console.log('Video metadata loaded:', video.videoWidth, 'x', video.videoHeight);
|
||||||
});
|
resolve(void 0);
|
||||||
} else {
|
};
|
||||||
console.error('Video element not ready or stream inactive');
|
|
||||||
}
|
video.onerror = (e) => {
|
||||||
}, 200);
|
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) {
|
} catch (error) {
|
||||||
console.error('Camera start failed:', error);
|
console.error('Camera start failed:', error);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user