Tutorial 19: Obteniendo y Mostrando Datos del Archivo por ID
Tutorial 19: Obteniendo y Mostrando Datos del Archivo por ID
¡Hola y bienvenido de nuevo!
En esta lección vamos a crear un método para obtener los datos de un archivo específico usando su ID, y vamos a mostrar toda la información en la página view.php. Esto incluye el reproductor de audio/video, la transcripción y la traducción si existe. ¡Vamos a ello!
📋 Contenido del Tutorial
Creando el método getFileById
Procesando la solicitud GET en view.php
Mostrando el reproductor correcto
Mostrando la transcripción
Mostrando la traducción
Código completo
Explicación detallada
Próximos pasos
🔍 1. Creando el método getFileById
Estructura del método
public function getFileById($fileID) { // 1. Preparar la consulta $stmt = $this->DB->prepare( "SELECT * FROM `files` WHERE `ID` = :fileID" ); // 2. Vincular el parámetro $stmt->bindParam(":fileID", $fileID, PDO::PARAM_INT); // 3. Ejecutar $stmt->execute(); // 4. Retornar el resultado como objeto return $stmt->fetch(PDO::FETCH_OBJ); }
¿Qué hace este método?
| Paso | Función | Explicación |
|---|---|---|
| 1 | prepare() | Prepara la consulta SQL con placeholder |
| 2 | bindParam() | Vincula el ID como entero |
| 3 | execute() | Ejecuta la consulta |
| 4 | fetch() | Obtiene una sola fila como objeto |
¿Por qué PDO::PARAM_INT?
$stmt->bindParam(":fileID", $fileID, PDO::PARAM_INT);
Especifica que el parámetro es un número entero
Mejora la seguridad y el rendimiento
Previene inyección SQL
Consulta SQL
SELECT * FROM `files` WHERE `ID` = :fileID
| Parte | Significado |
|---|---|
SELECT * | Selecciona todas las columnas |
FROM files | De la tabla files |
WHERE ID = :fileID | Filtra por el ID proporcionado |
:fileID | Placeholder (será reemplazado) |
📨 2. Procesando la solicitud GET en view.php
Estructura del código
<?php include 'backend/init.php'; $file = ''; // Variable para almacenar los datos if ($_SERVER['REQUEST_METHOD'] === "GET") { if (isset($_GET['file'])) { $file = $whisperObj->getFileById($_GET['file']); if (!$file) { header('Location: index.php'); exit; } } } ?>
¿Qué hace este código?
| Línea | Función | Explicación |
|---|---|---|
$file = '' | Inicializa variable | Evita errores si no hay archivo |
$_SERVER['REQUEST_METHOD'] | Verifica método | Solo procesa solicitudes GET |
isset($_GET['file']) | Verifica parámetro | Comprueba que existe el ID |
getFileById() | Obtiene datos | Recupera el archivo de la BD |
header('Location: ...') | Redirecciona | Si no existe, va a index.php |
Flujo de redirección
1. Usuario escribe: view.php?file=999 ↓ 2. getFileById(999) busca en la BD ↓ 3. No encuentra el archivo ↓ 4. Redirecciona a index.php ↓ 5. Usuario ve la página principal
🎬 3. Mostrando el reproductor correcto
Código HTML para el reproductor
<!-- Audio Player --> <?php if($file->type === "audio"): ?> <audio controls style="width:100%;border-radius: 20px;"> <source src="<?php echo $file->fileUrl; ?>" type="audio/mpeg"> Your browser does not support the audio element. </audio> <?php else: ?> <!-- Video Player --> <video style="height:240px; width:100%;" controls> <source src="<?php echo $file->fileUrl; ?>"> Your browser does not support the video tag. </video> <?php endif; ?>
¿Cómo funciona?
| Condición | Resultado | Elemento mostrado |
|---|---|---|
$file->type === "audio" | Verdadero | Reproductor de audio |
$file->type === "video" | Verdadero | Reproductor de video |
| Otro caso | Verdadero | Reproductor de video (por defecto) |
Atributos importantes
| Atributo | Propósito | Ejemplo |
|---|---|---|
controls | Muestra controles de reproducción | controls |
style | Estilos CSS | width:100% |
src | Ruta del archivo | files/abc123.mp3 |
type | Tipo MIME | audio/mpeg |
📝 4. Mostrando la transcripción
Código para mostrar el texto
<li class="flex flex-col my-5 w-full"> <h3 class="text-2xl">Text</h3> <div> <?php echo $file->content; ?> </div> </li>
Mejora de seguridad
<div> <?php echo nl2br(htmlspecialchars($file->content)); ?> </div>
¿Por qué usar estas funciones?
| Función | Propósito | Ejemplo |
|---|---|---|
htmlspecialchars() | Escapa caracteres HTML | <script> → <script> |
nl2br() | Convierte saltos de línea | \n → <br> |
🌍 5. Mostrando la traducción
Código para mostrar traducción
<!-- TRANSLATED CONTENT --> <?php if($file->translated !== NULL): ?> <li id="translateContent" class="flex flex-col my-5 w-full"> <h3 class="text-2xl">[LANG]</h3> <div> <?php echo $file->translated; ?> </div> </li> <?php endif; ?>
Mejora: Mostrar el idioma
<h3 class="text-2xl"> <?php echo $file->lang ?: 'Traducción'; ?> </h3>
¿Cómo verificar si hay traducción?
| Condición | Significado | Resultado |
|---|---|---|
$file->translated !== NULL | Hay traducción | Muestra la traducción |
$file->translated === NULL | No hay traducción | No muestra nada |
💻 6. Código completo
Archivo: backend/classes/Whisper.php (método agregado)
<?php class Whisper{ public $error; public $dataType; public $file; public $lang; public $content; private $DB; public function __construct(){ $db = new DB; $this->DB = $db->connect(); } public function errors(){ return $this->error; } public function getApiUrl(){ if($this->dataType === "ASR"){ return "https://api.openai.com/v1/audio/transcriptions"; }else{ return "https://api.openai.com/v1/chat/completions"; } } public function getHeader(){ if($this->dataType === "ASR"){ return [ 'Authorization: Bearer ' . API_TOKEN, 'Content-Type: multipart/form-data' ]; }else{ return [ 'Authorization: Bearer ' . API_TOKEN, 'Content-Type: application/json' ]; } } public function getData(){ if($this->dataType === "ASR"){ return [ 'file' => $this->file, 'model' => 'whisper-1' ]; }else{ return json_encode([ 'model' => 'gpt-3.5-turbo', 'messages' => [ [ 'role' => 'system', 'content' => 'You will be provided with a text, and your task is to translate it into ' . $this->lang ], [ 'role' => 'user', 'content' => $this->content ] ] ]); } } public function getFile(){ if($this->dataType === 'ASR'){ $this->file = curl_file_create($this->file); } } public function covert(){ $apiUrl = $this->getApiUrl(); $ch = curl_init($apiUrl); curl_setopt($ch, CURLOPT_POST, true); $this->getFile(); curl_setopt($ch, CURLOPT_POSTFIELDS, $this->getData()); curl_setopt($ch, CURLOPT_HTTPHEADER, $this->getHeader()); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); if($response){ return json_decode($response, true); }else{ $this->error = "API REQUEST FAILED"; } } public function upload($file){ $fileTmp = $file['tmp_name']; $filename = basename($file['name']); $fileSize = $file['size']; $errors = $file['error']; $mime = $file['type']; //get file extension $ext = pathinfo($filename, PATHINFO_EXTENSION); $ext = strtolower($ext); $parentDirectoy = dirname(dirname(dirname(__FILE__))); $allowedMedia = ['video/mp4','video/mpeg', 'audio/mpeg','audio/mpeg3','audio/wav']; if(in_array($mime, $allowedMedia)){ if($fileSize <= 20000000){ $folder = 'files/'; $file = $folder. md5(time() . mt_rand()) . '.'.$ext; move_uploaded_file($fileTmp, $parentDirectoy . '/'.$file); return $file; }else{ $this->error = "File is large!"; } }else{ $this->error = "invalid file format!"; } } public function save($file, $content, $type){ $stmt = $this->DB->prepare("INSERT INTO `files` (`fileUrl`, `content`, `type`) VALUES (:fileUrl, :content, :type)"); $stmt->bindParam(":fileUrl", $file, PDO::PARAM_STR); $stmt->bindParam(":content", $content, PDO::PARAM_STR); $stmt->bindParam(":type", $type, PDO::PARAM_STR); $stmt->execute(); return $this->DB->lastInsertId(); } /** * Obtiene y muestra la lista de archivos recientes */ public function getRecentList(){ $stmt = $this->DB->prepare("SELECT * FROM `files` ORDER BY `ID` DESC"); $stmt->execute(); $files = $stmt->fetchAll(PDO::FETCH_OBJ); foreach($files as $file){ echo '<a href="view.php?file='.$file->ID.'"> <li class="rounded flex my-5 cursor-pointer hover:bg-gray-100 items-center"> <div class="w-14"> '.(($file->type === 'audio') ? '<img src="frontend/images/audio-img.png"/>' : '<img src="frontend/images/video-img.png"/>' ) .' </div> <div class=" overflow-hidden w-60 font-normal h-auto font-bold p-2 "> <div> <p class="truncate">'.htmlspecialchars($file->content).'</p> </div> </div> </li> </a>'; } } /** * Obtiene los datos de un archivo por su ID * * @param int $fileID ID del archivo * @return object|false Datos del archivo o false si no existe */ public function getFileById($fileID){ try { // 1. Preparar la consulta $stmt = $this->DB->prepare( "SELECT * FROM `files` WHERE `ID` = :fileID" ); // 2. Vincular el parámetro como entero $stmt->bindParam(":fileID", $fileID, PDO::PARAM_INT); // 3. Ejecutar la consulta $stmt->execute(); // 4. Retornar el resultado como objeto return $stmt->fetch(PDO::FETCH_OBJ); } catch (PDOException $e) { $this->error = "Error al obtener el archivo: " . $e->getMessage(); return false; } } } ?>
Archivo: view.php (completo)
<?php include 'backend/init.php'; // Inicializar variable $file = null; // Procesar la solicitud GET if ($_SERVER['REQUEST_METHOD'] === "GET") { if (isset($_GET['file'])) { // Obtener el archivo por ID $file = $whisperObj->getFileById($_GET['file']); // Si no existe, redirigir a index.php if (!$file) { header('Location: index.php'); exit; } } } ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title> My Whisper Ai - Convert Video/Audio language into written text </title> <!-- FONT-AWESOME LINK --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" integrity="sha512-z3gLpd7yknf1YoNbCzqRKc4qyor8gaKU1qmn+CShxbuBusANI9QpRohGBreCFkKxLhei6S9CQXFEbbKuqLg0DA==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <!-- CSS LINK --> <link rel="stylesheet" type="text/css" href="frontend/style/style.css"/> <!-- TAILWIND CSS LINK --> <script src="https://cdn.tailwindcss.com"></script> <!-- GOOGLE FONTS LINK --> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Varela+Round&display=swap" rel="stylesheet"> <!-- JQUERY LINK --> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> </head> <body class=""> <div class="Wrapper"> <div class="inner-wrapper h-screen flex justify-center p-20"> <div class="h-full border-2 rounded-xl border-gray-700" style="width: 1280px; max-height: 1080px; box-shadow: -20px 20px 0px 0px #74a840,-21px 21px 0px 3px #343333"> <div class="flex h-full flex-col"> <!--header-section--> <div class="flex w-full justify-between p-6"> <div class="flex justify-center flex-col"> <h2 class="text-2xl font-semibold">My Whisper</h2> <span><a class="font-semibold text-lime-600" href="https://www.meralesson.com">Meralesson</a></span> </div> <div class="flex items-center justify-center"> <a href="index.php" class="border rounded px-4 py-2 cursor-pointer hover:bg-gray-100">Back</a> </div> </div> <!--Body-section--> <div class="px-10 overflow-hidden"> <div class="flex"> <!--Left-video-section--> <div class="w-1/3"> <div> <!-- Audio Player --> <?php if ($file && $file->type === "audio"): ?> <audio controls style="width:100%;border-radius: 20px;"> <source src="<?php echo $file->fileUrl; ?>" type="audio/mpeg"> Your browser does not support the audio element. </audio> <?php elseif ($file && $file->type === "video"): ?> <!-- Video Player --> <video style="height:240px; width:100%;" controls> <source src="<?php echo $file->fileUrl; ?>"> Your browser does not support the video tag. </video> <?php else: ?> <p class="text-gray-500 text-center py-4">No hay archivo seleccionado</p> <?php endif; ?> </div> <!--Tabs--> <div class="flex flex-col"> <div> <ul class="flex border-b justify-between"> <li class="flex-1 flex font-sm items-center justify-center text-2xl text-gray-300 cursor-pointer hover:bg-gray-100"> <span><i class="fa-solid fa-hands-asl-interpreting"></i></span> </li> <li class="flex-1 flex items-center justify-center font-sm text-2xl text-gray-300 cursor-pointer hover:bg-gray-100"> <span><i class="fa-solid fa-database"></i></span> </li> </ul> </div> <div style="height: 400px; overflow:scroll;"> <div class="flex" > <ul class="w-full h-full"> <!-- RECENT GENERATED FILES --> <?php $whisperObj->getRecentList(); ?> </ul> </div> </div> </div> </div> <!--Right-Transcript-section--> <div class="flex-1"> <div id="scroll" class="py-10 px-5 overflow-y-scroll w-full" style="height:500px;"> <ul class="w-full"> <li class="flex flex-col my-5 w-full"> <h3 class="text-2xl">Text</h3> <div> <?php if ($file) { echo nl2br(htmlspecialchars($file->content)); } else { echo "No hay contenido para mostrar"; } ?> </div> </li> <li class="flex flex-col my-5 w-full"> <!--loader--> <div id="loader" class="hidden flex flex-col text-center flex-1 rounded-xl w-full"> <div class="flex items-center flex-col"> <img class="w-20 animate-pulse mt-20" src="frontend/images/loader.gif"/> <span class="animate-pulse"> Translating.... </span> </div> </div> </li> <!-- TRANSLATED CONTENT --> <?php if ($file && $file->translated !== NULL): ?> <li id="translateContent" class="flex flex-col my-5 w-full"> <h3 class="text-2xl"> <?php echo $file->lang ?: 'Traducción'; ?> </h3> <div> <?php echo nl2br(htmlspecialchars($file->translated)); ?> </div> </li> <?php endif; ?> </ul> </div> <form id="form" method="POST"> <div class="flex flex-col justify-center items-center"> <div class=""> <label for="lang">Select Language</label> <select id="lang" name="lang" class="px-4 py-3 mx-2 my-3"> <option value="English">English</option> <option value="French">French</option> <option value="Spanish">Spanish</option> <option value="German">German</option> </select> <button id="translateBtn" name="translate" class="border border-gray-700 rounded px-6 py-2 cursor-pointer hover:bg-gray-100 font-normal">Translate</button> </div> <!-- ERROR HERE --> <!-- <div class="flex bg-red-100 border border-red-400 text-red-700 px-4 py-2 rounded relative"> <strong class="font-bold">Error:</strong> <span class="block sm:inline">[ERROR]</span> </div> --> </div> </form> </div> </div> </div> </div> </div> </div> </div> <!-- JS CODE HERE --> <script> $(document).ready(function() { // Traducción $('#translateBtn').click(function(e) { e.preventDefault(); $('#loader').removeClass('hidden'); // Obtener idioma seleccionado var lang = $('#lang').val(); var content = $('div#scroll h3:first').next('div').text(); var fileId = <?php echo $file ? $file->ID : 0; ?>; if (content && content !== 'No hay contenido para mostrar') { $.ajax({ url: 'translate.php', method: 'POST', data: { content: content, lang: lang, file_id: fileId }, success: function(response) { $('#loader').addClass('hidden'); alert('Traducción completada'); // Recargar la página para mostrar la traducción location.reload(); }, error: function() { $('#loader').addClass('hidden'); alert('Error al traducir'); } }); } else { $('#loader').addClass('hidden'); alert('No hay contenido para traducir'); } }); }); </script> </body> </html>
📖 7. Explicación detallada
El método getFileById paso a paso
public function getFileById($fileID) { try { // 1. Preparar la consulta SQL $stmt = $this->DB->prepare( "SELECT * FROM `files` WHERE `ID` = :fileID" ); // 2. Vincular el parámetro $stmt->bindParam(":fileID", $fileID, PDO::PARAM_INT); // 3. Ejecutar la consulta $stmt->execute(); // 4. Obtener el resultado return $stmt->fetch(PDO::FETCH_OBJ); } catch (PDOException $e) { $this->error = "Error al obtener el archivo: " . $e->getMessage(); return false; } }
¿Qué es PDO::FETCH_OBJ?
PDO::FETCH_OBJ devuelve una fila como un objeto:
// Con PDO::FETCH_OBJ $file = $stmt->fetch(PDO::FETCH_OBJ); echo $file->ID; // Acceso como objeto echo $file->content; echo $file->fileUrl; // Alternativa: PDO::FETCH_ASSOC $file = $stmt->fetch(PDO::FETCH_ASSOC); echo $file['ID']; // Acceso como array
Manejo de errores en view.php
if (!$file) { header('Location: index.php'); exit; }
¿Qué hace?
Si
$fileesfalseonullRedirige al usuario a
index.phpexitdetiene la ejecución del resto del código
🧪 8. Probando la aplicación
Paso 1: Subir un archivo
Ve a
http://localhost/whisper/Selecciona un archivo de audio o video
Espera el procesamiento
Paso 2: Verificar la vista
Después de procesar, serás redirigido a
view.php?file=1Verás:
✅ Reproductor de audio/video
✅ Transcripción en el panel derecho
✅ Lista de archivos recientes
Paso 3: Navegar entre archivos
Haz clic en cualquier archivo de la lista
La página se actualiza con el nuevo archivo
El reproductor y la transcripción cambian
Paso 4: Verificar archivos sin traducción
Si el archivo no tiene traducción
No se muestra la sección de traducción
El botón de traducción sigue disponible
📊 9. Resumen del flujo
Diagrama de secuencia
Usuario → view.php?file=1
↓
index.php (GET)
↓
getFileById(1)
↓
SELECT * FROM files WHERE ID = 1
↓
Retorna objeto con datos
↓
Mostrar reproductor
↓
Mostrar transcripción
↓
Mostrar traducción (si existe)Datos mostrados en la página
| Sección | Datos | Fuente |
|---|---|---|
| Reproductor | $file->fileUrl | Base de datos |
| Transcripción | $file->content | API de OpenAI |
| Traducción | $file->translated | Base de datos |
| Idioma | $file->lang | Base de datos |
❓ Preguntas frecuentes
¿Qué pasa si el ID no existe en la base de datos?
getFileById()retornafalseSe redirige a
index.php
¿Qué pasa si no hay archivo seleccionado?
$fileesnullNo se muestra reproductor
No se muestra contenido
¿Cómo se determina qué reproductor mostrar?
Si
$file->type === "audio"→ Reproductor de audioSi
$file->type === "video"→ Reproductor de video
¿Qué hace htmlspecialchars()?
Convierte caracteres especiales a entidades HTML
Previene ataques XSS
¿Qué hace nl2br()?
Convierte saltos de línea en
<br>HTMLMejora la legibilidad del texto
¿Por qué usar exit después de header()?
Detiene la ejecución del script
Evita que se ejecute código no deseado
🎯 Próximos pasos
Lo que hemos logrado
✅ Método getFileById() implementado
✅ Procesamiento de solicitudes GET
✅ Mostrar reproductor según el tipo
✅ Mostrar transcripción
✅ Mostrar traducción (si existe)
✅ Redirección segura
Lo que viene en la próxima lección
En la siguiente lección vamos a:
Implementar la traducción del texto
Usar la API de ChatGPT para traducir
Guardar la traducción en la base de datos
Actualizar la vista con la traducción
Avance del código de la próxima lección
// translate.php <?php include 'backend/init.php'; $content = $_POST['content'] ?? ''; $lang = $_POST['lang'] ?? ''; $fileId = $_POST['file_id'] ?? 0; if ($content && $lang && $fileId) { $whisperObj->dataType = 'TRANSLATE'; $whisperObj->content = $content; $whisperObj->lang = $lang; $result = $whisperObj->convert(); if ($result) { $translated = $result['choices'][0]['message']['content']; // Guardar en la base de datos $whisperObj->saveTranslation($fileId, $translated, $lang); echo json_encode(['success' => true]); } } ?>
¡Excelente trabajo! Ahora tenemos una página completa que muestra el archivo seleccionado con su reproductor, transcripción y traducción. En la próxima lección, implementaremos la funcionalidad de traducción.
Comentarios
Publicar un comentario