Spaces:
Sleeping
Sleeping
| """ | |
| HTTP clients for external analysis services with enhanced validation. | |
| """ | |
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| import os | |
| from pathlib import Path | |
| from typing import Optional | |
| import httpx | |
| from .validation import validate_audio_path, validate_timeout | |
| class ClientResponse: | |
| available: bool | |
| response: Optional[dict] | |
| error: Optional[str] | |
| class MusicAIDetectorClient: | |
| def __init__(self, base_url: Optional[str] = None, timeout_sec: float = 30.0) -> None: | |
| self.base_url = base_url or os.getenv("MUSIC_AI_API_URL") | |
| if not validate_timeout(timeout_sec): | |
| timeout_sec = 30.0 | |
| self.timeout_sec = timeout_sec | |
| async def predict(self, audio_path: Path) -> ClientResponse: | |
| if not self.base_url: | |
| return ClientResponse(available=False, response=None, error="music_ai_not_configured") | |
| is_valid, error_msg = validate_audio_path(audio_path) | |
| if not is_valid: | |
| return ClientResponse(available=True, response=None, error=f"music_ai_{error_msg}") | |
| try: | |
| async with httpx.AsyncClient(timeout=self.timeout_sec) as client: | |
| with audio_path.open("rb") as handle: | |
| files = {"file": (audio_path.name, handle, _guess_content_type(audio_path))} | |
| response = await client.post(f"{self.base_url.rstrip('/')}/predict", files=files) | |
| if response.status_code != 200: | |
| return ClientResponse( | |
| available=True, | |
| response=None, | |
| error=f"music_ai_http_{response.status_code}", | |
| ) | |
| return ClientResponse(available=True, response=response.json(), error=None) | |
| except httpx.TimeoutException: | |
| return ClientResponse(available=True, response=None, error="music_ai_timeout") | |
| except httpx.NetworkError as exc: | |
| return ClientResponse(available=True, response=None, error=f"music_ai_network_error: {type(exc).__name__}") | |
| except OSError as exc: | |
| return ClientResponse(available=True, response=None, error=f"music_ai_file_error: {type(exc).__name__}") | |
| except Exception as exc: | |
| return ClientResponse(available=True, response=None, error=f"music_ai_error: {type(exc).__name__}") | |
| class SesAnaliziClient: | |
| def __init__(self, base_url: Optional[str] = None, timeout_sec: float = 30.0) -> None: | |
| self.base_url = base_url or os.getenv("SES_ANALIZI_API_URL") | |
| if not validate_timeout(timeout_sec): | |
| timeout_sec = 30.0 | |
| self.timeout_sec = timeout_sec | |
| async def analyze(self, audio_path: Path) -> ClientResponse: | |
| if not self.base_url: | |
| return ClientResponse(available=False, response=None, error="ses_analizi_not_configured") | |
| is_valid, error_msg = validate_audio_path(audio_path) | |
| if not is_valid: | |
| return ClientResponse(available=True, response=None, error=f"ses_analizi_{error_msg}") | |
| try: | |
| async with httpx.AsyncClient(timeout=self.timeout_sec) as client: | |
| with audio_path.open("rb") as handle: | |
| files = {"file": (audio_path.name, handle, _guess_content_type(audio_path))} | |
| response = await client.post(f"{self.base_url.rstrip('/')}/analyze", files=files) | |
| if response.status_code != 200: | |
| return ClientResponse( | |
| available=True, | |
| response=None, | |
| error=f"ses_analizi_http_{response.status_code}", | |
| ) | |
| return ClientResponse(available=True, response=response.json(), error=None) | |
| except httpx.TimeoutException: | |
| return ClientResponse(available=True, response=None, error="ses_analizi_timeout") | |
| except httpx.NetworkError as exc: | |
| return ClientResponse(available=True, response=None, error=f"ses_analizi_network_error: {type(exc).__name__}") | |
| except OSError as exc: | |
| return ClientResponse(available=True, response=None, error=f"ses_analizi_file_error: {type(exc).__name__}") | |
| except Exception as exc: | |
| return ClientResponse(available=True, response=None, error=f"ses_analizi_error: {type(exc).__name__}") | |
| def service_status() -> dict: | |
| return { | |
| "music_ai": { | |
| "configured": bool(os.getenv("MUSIC_AI_API_URL")), | |
| }, | |
| "ses_analizi": { | |
| "configured": bool(os.getenv("SES_ANALIZI_API_URL")), | |
| }, | |
| } | |
| def _guess_content_type(path: Path) -> str: | |
| ext = path.suffix.lower() | |
| if ext == ".wav": | |
| return "audio/wav" | |
| if ext in {".mp3", ".m4a"}: | |
| return "audio/mpeg" | |
| if ext == ".flac": | |
| return "audio/flac" | |
| if ext == ".ogg": | |
| return "audio/ogg" | |
| if ext == ".webm": | |
| return "audio/webm" | |
| if ext == ".opus": | |
| return "audio/opus" | |
| return "application/octet-stream" | |