Tutorial 13: Creando el Método para Obtener la URL de la API


¡Hola y bienvenido de nuevo!

En esta lección vamos a crear un método que nos permita obtener las URLs correctas de la API de OpenAI según el tipo de operación que necesitemos realizar: transcripción o traducción. ¡Vamos a ello!


📋 Contenido del Tutorial

  1. Entendiendo los endpoints de OpenAI

  2. Agregando la propiedad dataType

  3. Creando el método getApiUrl

  4. Método para manejar errores

  5. Documentación de la API

  6. Código completo

  7. Explicación detallada

  8. Próximos pasos


🔗 1. Entendiendo los endpoints de OpenAI

¿Qué es un endpoint?

Un endpoint es una URL específica donde una API acepta solicitudes. Es como la dirección a la que enviamos nuestros datos para que sean procesados.

Los endpoints de Whisper

OpenAI proporciona dos endpoints principales para trabajar con audio:

EndpointURLFunción
Transcripcioneshttps://api.openai.com/v1/audio/transcriptionsConvierte audio a texto en el mismo idioma
Traduccioneshttps://api.openai.com/v1/audio/translationsConvierte audio a texto en inglés

¿Por qué necesitamos dos endpoints?

Documentación oficial

Puedes encontrar la documentación completa en:

text
https://platform.openai.com/docs/guides/speech-to-text

Pasos para acceder:

  1. Ve a platform.openai.com

  2. Haz clic en "Documentation"

  3. Selecciona "Speech to text" en el menú


📊 2. Agregando la propiedad dataType

¿Qué es dataType?

dataType es una propiedad que vamos a usar para indicar qué tipo de operación queremos realizar:

php
public $dataType;

Valores posibles

ValorSignificadoUso
"ASR"Automatic Speech RecognitionTranscribir audio a texto
"TRANSLATE"TraducciónTraducir el texto a otros idiomas

¿Cómo se usa?

php
// Para transcribir
$whisperObj->dataType = "ASR";

// Para traducir
$whisperObj->dataType = "TRANSLATE";

Agregando la propiedad a la clase

php
class Whisper {
    public $error;
    public $dataType;  // ← Nueva propiedad
    private $DB;
    
    // ... resto de la clase
}

🔧 3. Creando el método getApiUrl

Estructura del método

php
public function getApiUrl() {
    if ($this->dataType === "ASR") {
        return "https://api.openai.com/v1/audio/transcriptions";
    } else {
        return "https://api.openai.com/v1/chat/completions";
    }
}

¿Qué hace este método?

  1. Verifica el valor de dataType

  2. Si es "ASR": Devuelve la URL para transcripciones

  3. Si es otro valor: Devuelve la URL para traducciones

Endpoints correctos

TipoURL correcta
ASRhttps://api.openai.com/v1/audio/transcriptions
Traducciónhttps://api.openai.com/v1/chat/completions

Nota: Para traducciones de texto, usamos el endpoint de chat completions, que nos permite usar modelos como GPT para traducir.


❌ 4. Método para manejar errores

Agregando el método errors()

php
public function errors() {
    return $this->error;
}

¿Para qué sirve?

Este método nos permite obtener el mensaje de error almacenado en la propiedad $error de forma segura.

Uso del método

php
// En index.php
if ($file) {
    // Procesamiento exitoso
} else {
    // Mostrar el error
    $error = $whisperObj->errors();
    echo "Error: " . $error;
}

Beneficios

BeneficioExplicación
EncapsulamientoAcceso controlado a la propiedad
ConsistenciaSiempre devuelve el error actual
FlexibilidadPodemos modificar la lógica sin cambiar el código cliente

📚 5. Documentación de la API

Endpoint de Transcripciones

text
POST https://api.openai.com/v1/audio/transcriptions

Headers:

text
Authorization: Bearer TU_API_KEY
Content-Type: multipart/form-data

Parámetros:

ParámetroTipoRequeridoDescripción
fileFileArchivo de audio/video
modelStringwhisper-1
languageStringNoCódigo del idioma (ej: es)
response_formatStringNojson, text, srt, vtt

Endpoint de Traducción de Texto

text
POST https://api.openai.com/v1/chat/completions

Headers:

text
Authorization: Bearer TU_API_KEY
Content-Type: application/json

Parámetros:

ParámetroTipoRequeridoDescripción
modelStringgpt-3.5-turbo o gpt-4
messagesArrayMensajes para el chat
temperatureNumberNoCreatividad (0-1)

💻 6. Código completo

Archivo: backend/classes/Whisper.php

php
<?php
/**
 * Clase Whisper
 * 
 * Maneja la subida, transcripción y traducción de archivos
 * utilizando la API de OpenAI
 */
class Whisper {
    /**
     * @var string Mensajes de error
     */
    public $error;
    
    /**
     * @var string Tipo de operación (ASR o TRANSLATE)
     */
    public $dataType;
    
    /**
     * @var PDO Conexión a la base de datos
     */
    private $DB;
    
    /**
     * Constructor - Establece conexión a la base de datos
     */
    public function __construct() {
        $db = new DB();
        $this->DB = $db->connect();
    }
    
    /**
     * Obtiene el mensaje de error actual
     * 
     * @return string El mensaje de error
     */
    public function errors() {
        return $this->error;
    }
    
    /**
     * Obtiene la URL de la API según el tipo de operación
     * 
     * @return string URL del endpoint de la API
     */
    public function getApiUrl() {
        // Si es ASR (Automatic Speech Recognition)
        if ($this->dataType === "ASR") {
            return "https://api.openai.com/v1/audio/transcriptions";
        } else {
            // Para traducciones y otros usos
            return "https://api.openai.com/v1/chat/completions";
        }
    }
    
    /**
     * Sube un archivo al servidor
     * 
     * @param array $file Arreglo $_FILES
     * @return string|false Ruta del archivo o false en caso de error
     */
    public function upload($file) {
        // 1. Extraer información del archivo
        $fileTmp  = $file['tmp_name'];
        $filename = basename($file['name']);
        $fileSize = $file['size'];
        $errors   = $file['error'];
        $mime     = $file['type'];
        
        // 2. Obtener la extensión del archivo
        $ext = pathinfo($filename, PATHINFO_EXTENSION);
        $ext = strtolower($ext);
        
        // 3. Obtener el directorio raíz del proyecto
        $parentDirectory = dirname(dirname(dirname(__FILE__)));
        
        // 4. Definir los tipos de archivo permitidos
        $allowedMedia = [
            'video/mp4',
            'video/mpeg',
            'audio/mpeg',
            'audio/mpeg3',
            'audio/wav'
        ];
        
        // 5. Validar el tipo de archivo
        if (!in_array($mime, $allowedMedia)) {
            $this->error = "Formato de archivo inválido";
            return false;
        }
        
        // 6. Validar el tamaño del archivo (máximo 20 MB)
        if ($fileSize > 20000000) {
            $this->error = "El archivo es demasiado grande (máximo 20 MB)";
            return false;
        }
        
        // 7. Crear nombre único y subir el archivo
        $folder = 'files/';
        $uniqueName = md5(time() . mt_rand());
        $file = $folder . $uniqueName . '.' . $ext;
        
        // 8. Mover el archivo
        if (move_uploaded_file($fileTmp, $parentDirectory . '/' . $file)) {
            return $file;
        } else {
            $this->error = "Error al mover el archivo";
            return false;
        }
    }
    
    /**
     * Prepara la solicitud para la API de OpenAI
     * 
     * @param string $filePath Ruta del archivo
     * @return array Datos para la solicitud
     */
    public function prepareApiRequest($filePath) {
        // Verificar que la API Key esté configurada
        if (!defined('OPENAI_API_KEY') || OPENAI_API_KEY === '') {
            $this->error = "API Key no configurada";
            return false;
        }
        
        // Verificar que el archivo existe
        if (!file_exists($filePath)) {
            $this->error = "El archivo no existe";
            return false;
        }
        
        // Obtener la URL según el tipo de operación
        $url = $this->getApiUrl();
        
        // Preparar los datos según el tipo
        if ($this->dataType === "ASR") {
            // Datos para transcripción
            $postFields = [
                'file' => curl_file_create($filePath),
                'model' => 'whisper-1',
                'language' => 'es',
                'response_format' => 'json'
            ];
        } else {
            // Datos para traducción
            $postFields = json_encode([
                'model' => 'gpt-3.5-turbo',
                'messages' => [
                    [
                        'role' => 'system',
                        'content' => 'Traduce el siguiente texto al español'
                    ],
                    [
                        'role' => 'user',
                        'content' => file_get_contents($filePath)
                    ]
                ],
                'temperature' => 0.7
            ]);
        }
        
        return [
            'url' => $url,
            'postFields' => $postFields
        ];
    }
    
    /**
     * Envía la solicitud a la API de OpenAI
     * 
     * @param array $requestData Datos preparados
     * @return string|false Resultado de la API o false en caso de error
     */
    public function sendApiRequest($requestData) {
        $ch = curl_init();
        
        // Configurar cURL
        curl_setopt($ch, CURLOPT_URL, $requestData['url']);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 60);
        
        // Configurar headers
        $headers = [
            'Authorization: Bearer ' . OPENAI_API_KEY
        ];
        
        if ($this->dataType === "ASR") {
            $headers[] = 'Content-Type: multipart/form-data';
            curl_setopt($ch, CURLOPT_POSTFIELDS, $requestData['postFields']);
        } else {
            $headers[] = 'Content-Type: application/json';
            curl_setopt($ch, CURLOPT_POSTFIELDS, $requestData['postFields']);
        }
        
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        
        // Ejecutar la solicitud
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);
        
        // Verificar errores
        if ($error) {
            $this->error = "Error cURL: " . $error;
            return false;
        }
        
        if ($httpCode !== 200) {
            $this->error = "Error HTTP: " . $httpCode . " - " . $response;
            return false;
        }
        
        return $response;
    }
}
?>

Archivo: backend/init.php (actualizado)

php
<?php
/**
 * Archivo de inicialización de la aplicación
 */

// =============================================
// 1. CONFIGURACIÓN DE LA API KEY
// =============================================

// Cargar variables de entorno desde .env
function loadEnv($path = __DIR__ . '/../.env') {
    if (!file_exists($path)) {
        return false;
    }
    
    $lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    foreach ($lines as $line) {
        if (strpos($line, '#') === 0 || strpos($line, '=') === false) {
            continue;
        }
        
        list($key, $value) = explode('=', $line, 2);
        $key = trim($key);
        $value = trim($value, '"\'');
        putenv("$key=$value");
        $_ENV[$key] = $value;
    }
    return true;
}

// Cargar .env
loadEnv();

// Definir API Key
define('OPENAI_API_KEY', getenv('OPENAI_API_KEY') ?: '');

// =============================================
// 2. CARGAR CLASES
// =============================================

require 'classes/DB.php';
require 'classes/Whisper.php';

// =============================================
// 3. CREAR INSTANCIAS
// =============================================

$whisperObj = new Whisper();

// Configurar el tipo de operación (por defecto: ASR)
$whisperObj->dataType = "ASR";

// =============================================
// 4. VERIFICACIONES
// =============================================

// Verificar API Key
if (OPENAI_API_KEY === '') {
    die('⚠️ Error: La API Key de OpenAI no está configurada');
}

// Verificar conexión a la base de datos
if (!$whisperObj->DB) {
    die('⚠️ Error: No se pudo conectar a la base de datos');
}
?>

Archivo: index.php (sección PHP actualizada)

php
<?php
    // Cargar la configuración inicial
    include 'backend/init.php';
    
    // Variables para mensajes
    $error = null;
    $success = null;
    
    // Verificar si la solicitud es POST
    if ($_SERVER['REQUEST_METHOD'] === "POST") {
        
        // Verificar si el archivo está presente
        if (isset($_FILES['file'])) {
            
            // Verificar que no esté vacío
            if (!empty($_FILES['file']['name'])) {
                
                // Procesar el archivo
                $file = $whisperObj->upload($_FILES['file']);
                
                if ($file) {
                    // Determinar el tipo de archivo
                    $mime = $_FILES['file']['type'];
                    $type = strpos($mime, 'video/') === 0 ? 'video' : 'audio';
                    
                    // Guardar en la base de datos
                    $id = $whisperObj->saveFileInfo($file, $type);
                    
                    if ($id) {
                        $success = "¡Archivo subido correctamente! ID: " . $id;
                        
                        // Aquí llamaremos a la API para transcribir
                        // $result = $whisperObj->transcribe($file);
                    } else {
                        $error = $whisperObj->errors();
                    }
                    
                } else {
                    $error = $whisperObj->errors();
                }
                
            } else {
                $error = "Por favor selecciona un archivo para convertir a texto";
            }
            
        } else {
            $error = "Por favor selecciona un archivo para convertir a texto";
        }
    }
?>

📖 7. Explicación detallada

El método getApiUrl

php
public function getApiUrl() {
    if ($this->dataType === "ASR") {
        return "https://api.openai.com/v1/audio/transcriptions";
    } else {
        return "https://api.openai.com/v1/chat/completions";
    }
}

Flujo de decisión:

text
¿dataType es "ASR"?
    ↓
    ├── Sí → Usar endpoint de transcripciones
    │         https://api.openai.com/v1/audio/transcriptions
    │
    └── No → Usar endpoint de chat/completions
              https://api.openai.com/v1/chat/completions

¿Por qué dos endpoints diferentes?

EndpointCuándo usarEjemplo
TranscripcionesPara convertir audio a textoUn video en español → texto en español
Chat CompletionsPara traducir textoTexto en español → texto en inglés

El método errors()

php
public function errors() {
    return $this->error;
}

Ventajas de usar un método en lugar de acceder directamente:

php
// ❌ Acceso directo (menos seguro)
echo $whisperObj->error;

// ✅ Usando el método (más seguro)
echo $whisperObj->errors();

🧪 8. Probando la funcionalidad

Prueba 1: Verificar la URL para ASR

php
$whisperObj->dataType = "ASR";
$url = $whisperObj->getApiUrl();
echo $url; 
// Resultado esperado: https://api.openai.com/v1/audio/transcriptions

Prueba 2: Verificar la URL para traducción

php
$whisperObj->dataType = "TRANSLATE";
$url = $whisperObj->getApiUrl();
echo $url;
// Resultado esperado: https://api.openai.com/v1/chat/completions

Prueba 3: Verificar el método errors()

php
$whisperObj->error = "Error de prueba";
echo $whisperObj->errors();
// Resultado esperado: Error de prueba

📊 9. Resumen de la clase Whisper hasta ahora

Propiedades

PropiedadTipoDescripción
$errorstringAlmacena mensajes de error
$dataTypestringTipo de operación (ASR/TRANSLATE)
$DBPDOConexión a la base de datos

Métodos

MétodoParámetrosRetornoDescripción
__construct()-voidConstructor, conecta a BD
errors()-stringDevuelve el error actual
getApiUrl()-stringDevuelve la URL de la API
upload()$filestring/falseSube un archivo
prepareApiRequest()$filePatharray/falsePrepara la solicitud API
sendApiRequest()$requestDatastring/falseEnvía solicitud a API

Diagrama de flujo

text
┌─────────────────────────────────────────────┐
│            APLICACIÓN WHISPER                │
├─────────────────────────────────────────────┤
│                                             │
│  1. Usuario sube archivo                    │
│         ↓                                   │
│  2. upload() valida y guarda archivo        │
│         ↓                                   │
│  3. Se establece dataType                   │
│         ↓                                   │
│  4. getApiUrl() selecciona endpoint         │
│         ↓                                   │
│  5. prepareApiRequest() prepara datos       │
│         ↓                                   │
│  6. sendApiRequest() envía a OpenAI         │
│         ↓                                   │
│  7. Se recibe y procesa respuesta           │
│         ↓                                   │
│  8. Se guarda en la base de datos           │
│                                             │
└─────────────────────────────────────────────┘

🎯 10. Próximos pasos

Lo que hemos logrado

✅ Propiedad dataType añadida
✅ Método getApiUrl() implementado
✅ Método errors() para manejo de errores
✅ Preparación para usar la API de OpenAI

Lo que viene en la próxima lección

En la siguiente lección vamos a:

  1. Implementar la comunicación con la API de OpenAI

  2. Enviar archivos para transcripción

  3. Recibir y procesar la respuesta

  4. Guardar la transcripción en la base de datos

Avance del código de la próxima lección

php
// Nuevo método en Whisper.php
public function transcribe($filePath) {
    // 1. Preparar la solicitud
    $request = $this->prepareApiRequest($filePath);
    
    // 2. Enviar a la API
    $response = $this->sendApiRequest($request);
    
    // 3. Procesar respuesta
    if ($response) {
        $data = json_decode($response, true);
        return $data['text'] ?? false;
    }
    
    return false;
}

❓ Preguntas frecuentes

¿Qué significa ASR?

  • Automatic Speech Recognition (Reconocimiento Automático del Habla)

  • Es el proceso de convertir audio en texto

¿Por qué usar el endpoint de chat para traducciones?

  • Porque usamos modelos de lenguaje (GPT) para traducir texto

  • Ofrece mejores resultados que modelos específicos de traducción

¿Puedo usar el endpoint de traducciones de audio?

  • Sí, OpenAI también tiene /v1/audio/translations

  • Traduce audio directamente a texto en inglés

¿Qué pasa si dataType no está configurado?

  • Por defecto, usará el endpoint de chat/completions

  • Es mejor configurarlo explícitamente

¿Cómo sé qué endpoint usar?

  • Si quieres transcribir audio → ASR

  • Si quieres traducir texto → TRANSLATE u otro

¿Los endpoints son gratuitos?

  • No, ambos endpoints tienen costo

  • Los precios varían según el modelo usado


🎓 Ejercicio práctico

Ejercicio 1: Probar ambos endpoints

php
// Probar ASR
$whisperObj->dataType = "ASR";
echo $whisperObj->getApiUrl();

// Probar TRADUCCIÓN
$whisperObj->dataType = "TRANSLATE";
echo $whisperObj->getApiUrl();

Ejercicio 2: Agregar más tipos

Amplía el método getApiUrl() para soportar más operaciones:

php
public function getApiUrl() {
    switch ($this->dataType) {
        case "ASR":
            return "https://api.openai.com/v1/audio/transcriptions";
        case "TRANSLATE_AUDIO":
            return "https://api.openai.com/v1/audio/translations";
        case "TRANSLATE_TEXT":
            return "https://api.openai.com/v1/chat/completions";
        default:
            return "https://api.openai.com/v1/audio/transcriptions";
    }
}

Ejercicio 3: Agregar validación

Agrega validación para dataType:

php
public function setDataType($type) {
    $allowedTypes = ['ASR', 'TRANSLATE_AUDIO', 'TRANSLATE_TEXT'];
    if (in_array($type, $allowedTypes)) {
        $this->dataType = $type;
        return true;
    }
    $this->error = "Tipo de operación no válido";
    return false;
}

¡Excelente trabajo! Ahora tenemos la capacidad de seleccionar dinámicamente el endpoint correcto de la API según la operación que necesitemos realizar. En la próxima lección, implementaremos la comunicación real con la API.

Comentarios

Entradas más populares de este blog

Cómo usar Whisper para sacar el texto de un video

Tutorial 18: Creando la Página de Visualización de Archivos Recientes

Tutorial 11: ¿Qué es Whisper AI y Cómo Funciona?