diff --git a/admin/DashboardController.php b/admin/DashboardController.php index ed34bbf..f4b00b6 100644 --- a/admin/DashboardController.php +++ b/admin/DashboardController.php @@ -3170,6 +3170,9 @@ HTML; $aiModel35 = $aiModelVal === 'gpt-3.5-turbo'? ' selected' : ''; $geminiApiKeyVal = self::h($config['gemini_api_key'] ?? ''); $geminiModelVal = $config['gemini_model'] ?? 'gemini-2.5-flash'; + $whisperUrlVal = self::h($config['whisper_url'] ?? ''); + $whisperUserVal = self::h($config['whisper_user'] ?? ''); + $whisperPassVal = self::h($config['whisper_pass'] ?? ''); $geminiModelF25 = $geminiModelVal === 'gemini-2.5-flash' ? ' selected' : ''; $geminiModelF20 = $geminiModelVal === 'gemini-2.0-flash' ? ' selected' : ''; $geminiModelF15 = $geminiModelVal === 'gemini-1.5-flash' ? ' selected' : ''; @@ -3717,6 +3720,26 @@ HTML; + +
+ +
Si se configura, el audio se transcribe aquĆ­ en vez del proveedor seleccionado arriba.
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
@@ -3862,6 +3885,10 @@ function saveAiConfig() { if (oKey) fd.set('openai_api_key', oKey); if (gKey) fd.set('gemini_api_key', gKey); if (cKey) fd.set('claude_api_key', cKey); + fd.set('whisper_url', qs('input[name="whisper_url"]')?.value || ''); + fd.set('whisper_user', qs('input[name="whisper_user"]')?.value || ''); + const wPass = qs('input[name="whisper_pass"]')?.value || ''; + if (wPass) fd.set('whisper_pass', wPass); if (qs('input[name="ai_for_media"]')?.checked) fd.set('ai_for_media', '1'); if (qs('input[name="nlu"]')?.checked) fd.set('nlu', '1'); diff --git a/public/index.php b/public/index.php index bbba291..3e05534 100644 --- a/public/index.php +++ b/public/index.php @@ -634,6 +634,13 @@ $routes = [ $claudeModel = trim($_POST['claude_model'] ?? ''); if ($claudeModel !== '') $config['claude_model'] = $claudeModel; + $whisperUrl = trim($_POST['whisper_url'] ?? ''); + $config['whisper_url'] = $whisperUrl; + $whisperUser = trim($_POST['whisper_user'] ?? ''); + $config['whisper_user'] = $whisperUser; + $whisperPass = trim($_POST['whisper_pass'] ?? ''); + if ($whisperPass !== '') $config['whisper_pass'] = $whisperPass; + CompanyRepository::save(['id' => $companyId, 'config_json' => json_encode($config, JSON_UNESCAPED_UNICODE)]); echo json_encode(['ok' => true]); exit; diff --git a/services/MediaTranscriber.php b/services/MediaTranscriber.php index 9ab2506..3e862ef 100644 --- a/services/MediaTranscriber.php +++ b/services/MediaTranscriber.php @@ -69,13 +69,48 @@ class MediaTranscriber private static function whisper(string $bytes, string $mime, array $cfg): ?string { - $apiKey = $cfg['openai_api_key'] ?? env('OPENAI_API_KEY', ''); - if ($apiKey === '') return null; - $ext = str_contains($mime, 'ogg') ? 'ogg' : (str_contains($mime, 'mp4') ? 'mp4' : 'ogg'); $tmpFile = tempnam(sys_get_temp_dir(), 'wa_audio_') . '.' . $ext; file_put_contents($tmpFile, $bytes); + $whisperUrl = trim($cfg['whisper_url'] ?? ''); + $whisperUser = trim($cfg['whisper_user'] ?? ''); + $whisperPass = trim($cfg['whisper_pass'] ?? ''); + $useOwn = $whisperUrl !== ''; + + if ($useOwn) { + $t0 = (int)(microtime(true) * 1000); + $ch = curl_init($whisperUrl); + $opts = [ + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => ['audio_file' => new CURLFile($tmpFile, $mime, 'audio.' . $ext)], + CURLOPT_RETURNTRANSFER => true, + CURLOPT_TIMEOUT => 120, + ]; + if ($whisperUser !== '') { + $opts[CURLOPT_USERPWD] = "{$whisperUser}:{$whisperPass}"; + } + curl_setopt_array($ch, $opts); + $resp = curl_exec($ch); + $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + $ms = (int)(microtime(true) * 1000) - $t0; + @unlink($tmpFile); + + if ($code !== 200 || !$resp) return null; + + // Respuesta puede ser JSON {"text":"..."} o texto plano + $data = json_decode($resp, true); + $text = is_array($data) ? trim($data['text'] ?? '') : trim($resp); + if ($text === '') return null; + AiLogger::log($cfg['__company_id'] ?? null, 'whisper-own', 'whisper', 'transcribe', '[audio]', $text, $ms); + return $text; + } + + // Fallback: OpenAI cloud Whisper + $apiKey = $cfg['openai_api_key'] ?? env('OPENAI_API_KEY', ''); + if ($apiKey === '') { @unlink($tmpFile); return null; } + $t0 = (int)(microtime(true) * 1000); $ch = curl_init('https://api.openai.com/v1/audio/transcriptions'); curl_setopt_array($ch, [