Spaces:
Sleeping
Sleeping
| // const API_BASE_URL = 'http://localhost:8000' | |
| const API_BASE_URL = 'https://2nzi-pnlcalib.hf.space' | |
| class FootballVisionAPI { | |
| constructor() { | |
| this.baseURL = API_BASE_URL | |
| } | |
| async healthCheck() { | |
| try { | |
| const response = await fetch(`${this.baseURL}/health`) | |
| return await response.json() | |
| } catch (error) { | |
| throw new Error(`Health check failed: ${error.message}`) | |
| } | |
| } | |
| async calibrateCamera(imageFile, linesData) { | |
| const formData = new FormData() | |
| formData.append('image', imageFile) | |
| formData.append('lines_data', JSON.stringify(linesData)) | |
| try { | |
| const response = await fetch(`${this.baseURL}/calibrate`, { | |
| method: 'POST', | |
| body: formData | |
| }) | |
| // Toujours retourner un objet de résultat, même en cas d'erreur | |
| if (!response.ok) { | |
| let errorMessage = `HTTP error! status: ${response.status}` | |
| // Essayer de récupérer le message d'erreur du serveur | |
| try { | |
| const errorData = await response.json() | |
| if (errorData.detail) { | |
| // Gestion spéciale pour les erreurs Pydantic | |
| if (typeof errorData.detail === 'string') { | |
| errorMessage = errorData.detail | |
| } else if (Array.isArray(errorData.detail)) { | |
| // Erreurs de validation Pydantic | |
| errorMessage = errorData.detail.map(err => { | |
| if (err.msg && err.loc) { | |
| return `${err.loc.join('.')}: ${err.msg}` | |
| } | |
| return err.msg || 'Erreur de validation' | |
| }).join(', ') | |
| } | |
| } else if (errorData.message) { | |
| errorMessage = errorData.message | |
| } | |
| } catch (parseError) { | |
| // Si on ne peut pas parser la réponse, garder le message d'erreur HTTP | |
| } | |
| return { | |
| status: 'failed', | |
| message: errorMessage, | |
| error: errorMessage | |
| } | |
| } | |
| const result = await response.json() | |
| // S'assurer que le résultat a un status, sinon l'ajouter | |
| if (!result.status) { | |
| result.status = 'success' | |
| } | |
| return result | |
| } catch (error) { | |
| // En cas d'erreur de réseau ou autre, retourner un objet d'erreur | |
| return { | |
| status: 'failed', | |
| message: `Calibration failed: ${error.message}`, | |
| error: error.message | |
| } | |
| } | |
| } | |
| async inferenceImage(imageFile, options = {}) { | |
| if (!imageFile) { | |
| throw new Error('No image file provided') | |
| } | |
| const formData = new FormData() | |
| formData.append('image', imageFile) | |
| const { kpThreshold = 0.15, lineThreshold = 0.15 } = options | |
| // S'assurer que les valeurs sont des nombres | |
| const kpThresholdNum = Number(kpThreshold) | |
| const lineThresholdNum = Number(lineThreshold) | |
| formData.append('kp_threshold', kpThresholdNum.toString()) | |
| formData.append('line_threshold', lineThresholdNum.toString()) | |
| console.log('🔥 Sending inference request with:', { | |
| fileName: imageFile.name, | |
| fileType: imageFile.type, | |
| fileSize: imageFile.size, | |
| kpThreshold, | |
| lineThreshold | |
| }) | |
| try { | |
| const response = await fetch(`${this.baseURL}/inference/image`, { | |
| method: 'POST', | |
| body: formData | |
| }) | |
| if (!response.ok) { | |
| let errorMessage = `HTTP error! status: ${response.status}` | |
| // Essayer de récupérer le message d'erreur détaillé du serveur | |
| try { | |
| const errorData = await response.json() | |
| console.error('🔥 Detailed API error:', errorData) | |
| if (errorData.detail) { | |
| // Gestion spéciale pour les erreurs Pydantic | |
| if (typeof errorData.detail === 'string') { | |
| errorMessage = errorData.detail | |
| } else if (Array.isArray(errorData.detail)) { | |
| // Erreurs de validation Pydantic | |
| errorMessage = errorData.detail.map(err => { | |
| if (err.msg && err.loc) { | |
| return `${err.loc.join('.')}: ${err.msg}` | |
| } | |
| return err.msg || 'Erreur de validation' | |
| }).join(', ') | |
| } | |
| } else if (errorData.message) { | |
| errorMessage = errorData.message | |
| } | |
| } catch (parseError) { | |
| console.error('🔥 Could not parse error response:', parseError) | |
| } | |
| throw new Error(errorMessage) | |
| } | |
| return await response.json() | |
| } catch (error) { | |
| throw new Error(`Image inference failed: ${error.message}`) | |
| } | |
| } | |
| async inferenceVideo(videoFile, options = {}) { | |
| const formData = new FormData() | |
| formData.append('video', videoFile) | |
| const { kpThreshold = 0.15, lineThreshold = 0.15, frameStep = 10 } = options | |
| formData.append('kp_threshold', kpThreshold) | |
| formData.append('line_threshold', lineThreshold) | |
| formData.append('frame_step', frameStep) | |
| try { | |
| const response = await fetch(`${this.baseURL}/inference/video`, { | |
| method: 'POST', | |
| body: formData | |
| }) | |
| if (!response.ok) { | |
| throw new Error(`HTTP error! status: ${response.status}`) | |
| } | |
| return await response.json() | |
| } catch (error) { | |
| throw new Error(`Video inference failed: ${error.message}`) | |
| } | |
| } | |
| async manualCalibration(file, calibrationData) { | |
| const formData = new FormData() | |
| formData.append('file', file) | |
| formData.append('calibration_data', JSON.stringify(calibrationData)) | |
| try { | |
| const response = await fetch(`${this.baseURL}/calibrate/manual`, { | |
| method: 'POST', | |
| body: formData | |
| }) | |
| if (!response.ok) { | |
| throw new Error(`HTTP error! status: ${response.status}`) | |
| } | |
| return await response.json() | |
| } catch (error) { | |
| throw new Error(`Manual calibration failed: ${error.message}`) | |
| } | |
| } | |
| } | |
| export default new FootballVisionAPI() |