Spaces:
Running
Running
// 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() |