18. Creating a page to display recent generated files

 

18. Creación de una página para mostrar archivos generados recientemente

¡Hola y bienvenido de nuevo!

En esta lección vamos a crear una página de visualización completa donde los usuarios podrán ver sus archivos procesados, reproducirlos, leer el texto transcrito y acceder a todos sus archivos anteriores. ¡Es como crear el "historial" de nuestra aplicación!


🎯 ¿Qué vamos a crear?

Vamos a crear view.php que incluye:

  1. Reproductor multimedia (audio/video)

  2. Texto transcrito por la IA

  3. Lista de archivos recientes (historial)

  4. Interfaz para traducciones (próxima lección)


🔍 Estructura de la página view.php

text

┌─────────────────────────────────────────────────────────────┐

│                       VIEW.PHP                              │

├─────────────────────────────────────────────────────────────┤

│ ┌──────────────┐ ┌────────────────────────────────────────┐ │

│ │  REPRODUCTOR │ │         TEXTO TRANSCRITO               │ │

│ │  Audio/Video │ │  • "Hola, esto es una prueba..."       │ │

│ │              │ │                                        │ │

│ ├──────────────┤ ├────────────────────────────────────────┤ │

│ │  LISTA DE    │ │                                        │ │

│ │  ARCHIVOS    │ │  TRADUCCIÓN:                           │ │

│ │  RECIENTES   │ │  ┌─────────────────┐ ┌─────────────┐  │ │

│ │  • Entrevista│ │  │ Select Language │ │ Translate   │  │ │

│ │  • Podcast   │ │  └─────────────────┘ └─────────────┘  │ │

│ │  • Clase     │ │                                        │ │

│ └──────────────┘ └────────────────────────────────────────┘ │

└─────────────────────────────────────────────────────────────┘


🛠️ Creando la página view.php

1. Estructura básica (copiada del HTML template):

php

<?php 

    include 'backend/init.php';  // Incluye conexión DB y clase Whisper

?>

<!DOCTYPE html>

<html>

<!-- ... head section igual que index.php ... -->

<body>

<div class="Wrapper">

    <!-- Contenedor principal -->

    <div class="inner-wrapper h-screen flex justify-center p-20">

        <div class="h-full border-2 rounded-xl border-gray-700">

            <!-- Encabezado con botón "Back" -->

            <div class="flex w-full justify-between p-6">

                <div>

                    <h2 class="text-2xl font-semibold">My Whisper</h2>

                    <span><a class="font-semibold text-lime-600" href="#">Meralesson</a></span>

                </div>

                <div>

                    <a href="index.php" class="border rounded px-4 py-2 cursor-pointer hover:bg-gray-100">

                        Back

                    </a>

                </div>

            </div>

            

            <!-- Cuerpo de la página -->

            <div class="px-10 overflow-hidden">

                <div class="flex">

                    <!-- COLUMNA IZQUIERDA: Reproductor + Lista reciente -->

                    <div class="w-1/3">

                        <!-- Aquí va el reproductor -->

                        <!-- Aquí va la lista de archivos recientes -->

                    </div>

                    

                    <!-- COLUMNA DERECHA: Texto + Traducción -->

                    <div class="flex-1">

                        <!-- Aquí va el texto transcrito -->

                        <!-- Aquí va el formulario de traducción -->

                    </div>

                </div>

            </div>

        </div>

    </div>

</div>

</body>

</html>


📝 Método getRecentList() en la clase Whisper

php

/**

 * Obtiene y muestra la lista de archivos procesados recientemente

 * 

 * @return void Muestra HTML directamente

 */

public function getRecentList()

{

    // 1. Preparamos consulta SQL para obtener archivos

    $stmt = $this->DB->prepare("SELECT * FROM `files` ORDER BY `ID` DESC");

    

    // 2. Ejecutamos la consulta

    $stmt->execute();

    

    // 3. Obtenemos resultados como objetos

    $files = $stmt->fetchAll(PDO::FETCH_OBJ);

    

    // 4. Recorremos y mostramos cada archivo

    foreach($files as $file){

        // Creamos el enlace con el ID del archivo

        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">';

        

        // 5. Mostramos icono según tipo (audio o video)

        // Operador ternario: condición ? si_true : si_false

        echo ($file->type === 'audio') ? 

            '<img src="frontend/images/audio-img.png"/>' : 

            '<img src="frontend/images/video-img.png"/>';

        

        echo '</div>

                    <div class="overflow-hidden w-60 font-normal h-auto font-bold p-2">

                        <div>

                            <p class="truncate">'.$file->content.'</p>

                        </div>

                    </div>

                </li>

              </a>';

    }

}


🔍 Explicando conceptos importantes

1. PDO::FETCH_OBJ vs PDO::FETCH_ASSOC

php

// FETCH_OBJ → Acceso con -> (objeto)

$files = $stmt->fetchAll(PDO::FETCH_OBJ);

echo $file->ID;      // Acceso como objeto

echo $file->content;


// FETCH_ASSOC → Acceso con [] (array)

$files = $stmt->fetchAll(PDO::FETCH_ASSOC);

echo $file['ID'];    // Acceso como array

echo $file['content'];

Analogía:

  • FETCH_OBJ = Como recibir una caja con etiquetas (propiedades)

  • FETCH_ASSOC = Como recibir una caja con instrucciones (claves)

2. Operador ternario (? :)

php

// Forma larga (if-else):

if($file->type === 'audio'){

    echo '<img src="audio-img.png"/>';

} else {

    echo '<img src="video-img.png"/>';

}


// Forma corta (ternario):

echo ($file->type === 'audio') ? 

    '<img src="audio-img.png"/>' : 

    '<img src="video-img.png"/>';

Estructura: condición ? valor_si_true : valor_si_false

3. ORDER BY ID DESC

sql

-- DESC (Descendente): Más reciente primero

SELECT * FROM files ORDER BY ID DESC

-- Resultado: ID 10, 9, 8, 7... (nuevo → viejo)


-- ASC (Ascendente): Más antiguo primero  

SELECT * FROM files ORDER BY ID ASC

-- Resultado: ID 1, 2, 3, 4... (viejo → nuevo)


🎨 Diagrama del flujo de datos

text

┌─────────────────────────────────────────────────────────────┐

│               FLUJO DE DATOS: BD → PHP → HTML               │

├─────────────────────────────────────────────────────────────┤

│  BASE DE DATOS:                                              │

│  ┌─────────┬──────────────┬──────────────────────┬────────┐ │

│  │ ID: 25  │ fileUrl      │ content              │ type   │ │

│  ├─────────┼──────────────┼──────────────────────┼────────┤ │

│  │ 26      │ files/b.mp3  │ "Segunda prueba"     │ audio  │ │

│  │ 25      │ files/a.mp3  │ "Primera prueba"     │ audio  │ │

│  └─────────┴──────────────┴──────────────────────┴────────┘ │

│                    ↓ ORDER BY ID DESC                        │

│                    ↓ (26 aparece primero)                    │

│                    ↓                                         │

│  PHP: getRecentList()                                        │

│  • SELECT * FROM files ORDER BY ID DESC                     │ │

│  • fetchAll(PDO::FETCH_OBJ)                                 │ │

│  • foreach($files as $file)                                 │ │

│                    ↓                                         │

│  HTML GENERADO:                                             │ │

│  <a href="view.php?file=26">                                │ │

│    <li>                                                     │ │

│      <img src="audio-img.png"/> ← Ternario evalúa 'audio'   │ │

│      <p>Segunda prueba</p> ← Truncado si es muy largo       │ │

│    </li>                                                    │ │

│  </a>                                                       │ │

│  <a href="view.php?file=25">                                │ │

│    ... (similar para ID 25)                                 │ │

│  </a>                                                       │ │

└─────────────────────────────────────────────────────────────┘


🔧 Cómo integrar en view.php

php

<!-- En view.php, en la sección de lista reciente: -->

<div style="height: 400px; overflow:scroll;">

    <div class="flex">

        <ul class="w-full h-full">

            <!-- RECENT GENERATED FILES -->

            <?php $whisperObj->getRecentList(); ?>

        </ul>

    </div>

</div>

Explicación del código:

  • <?php ... ?> → Tags PHP embebidos en HTML

  • $whisperObj → Instancia de la clase Whisper (creada en init.php)

  • ->getRecentList() → Llama al método que muestra la lista

  • El método echo directamente el HTML


💡 Mejoras y consideraciones

1. Truncar texto largo (truncate)

css

/* En Tailwind, truncate corta texto con puntos suspensivos */

.truncate {

    overflow: hidden;

    text-overflow: ellipsis;

    white-space: nowrap;

}

Resultado: "Este es un texto muy largo que se cortará..." en lugar de desbordar.

2. Paginación (para muchos archivos)

php

// Mejora futura: LIMIT para paginación

$stmt = $this->DB->prepare("

    SELECT * FROM `files` 

    ORDER BY `ID` DESC 

    LIMIT 0, 10  -- 10 primeros resultados

");

3. Imágenes responsivas

html

<!-- Mejor práctica: tamaño fijo y alt descriptivo -->

<img src="frontend/images/audio-img.png" 

     class="w-10 h-10" 

     alt="Icono de audio">


🚀 Proceso completo de funcionamiento

Paso a paso:

  1. Usuario sube archivo en index.php

  2. Se procesa con OpenAI y guarda en BD (lección anterior)

  3. Redirecciona a view.php?file=ID

  4. view.php carga y muestra:

    • Archivo actual (próxima lección)

    • Lista de TODOS los archivos (getRecentList())

  5. Usuario hace click en cualquier archivo de la lista

  6. Recarga view.php con nuevo ID en URL

  7. Ciclo continúa...


🔍 Explicando problemas comunes

Error: "Column 'content' not found"

sql

-- PROBLEMA: La tabla tenía 'name' en lugar de 'content'

ALTER TABLE files CHANGE name content TEXT;


-- SOLUCIÓN: Cambiar nombre de columna o ajustar consulta

Solución paso a paso:

  1. Abrir phpMyAdmin o MySQL Workbench

  2. Ir a la tabla files

  3. Verificar nombres de columnas

  4. Cambiar si es necesario:

    • fileUrl → VARCHAR(255)

    • content → TEXT

    • type → ENUM('audio', 'video') o VARCHAR(20)


🎨 Diseño visual de la lista

text

┌─────────────────────────────────────────────────────────────┐

│                    LISTA DE ARCHIVOS                         │

├─────────────────────────────────────────────────────────────┤

│  ┌─────┐  ┌─────────────────────────────────────────────┐   │

│  │ 🔊  │  │ Entrevista con el cliente sobre el nuevo... │   │

│  └─────┘  └─────────────────────────────────────────────┘   │

│                 (click → view.php?file=26)                   │

│                                                              │

│  ┌─────┐  ┌─────────────────────────────────────────────┐   │

│  │ 🎥  │  │ Tutorial completo de PHP para principia...  │   │

│  └─────┘  └─────────────────────────────────────────────┘   │

│                 (click → view.php?file=25)                   │

│                                                              │

│  ┌─────┐  ┌─────────────────────────────────────────────┐   │

│  │ 🔊  │  │ Podcast sobre inteligencia artificial y...  │   │

│  └─────┘  └─────────────────────────────────────────────┘   │

│                 (click → view.php?file=24)                   │

└─────────────────────────────────────────────────────────────┘

Características:

  • Icono diferente para audio (🔊) y video (🎥)

  • Texto truncado con puntos suspensivos

  • Efecto hover (cambio de fondo al pasar mouse)

  • Enlace clickeable a view.php con ID


📊 Tabla de métodos y responsabilidades

Método

Clase

¿Qué hace?

Ejemplo de uso

getRecentList()

Whisper

Obtiene y muestra archivos recientes

<?php $whisperObj->getRecentList(); ?>

save()

Whisper

Guarda archivo en BD

$id = $whisperObj->save($file, $text, $type);

convert()

Whisper

Envía a OpenAI y obtiene texto

$text = $whisperObj->convert();

upload()

Whisper

Sube archivo al servidor

$file = $whisperObj->upload($_FILES['file']);


🎯 Puntos Clave para Recordar

  1. ORDER BY ID DESC → Orden descendente (más reciente primero)

  2. PDO::FETCH_OBJ → Convierte resultados a objetos accesibles con ->

  3. Operador ternario → condición ? true : false (if-else en una línea)

  4. echo en métodos → Puede generar HTML directamente (no siempre buena práctica)

  5. <p class="truncate"> → Corta texto largo con ...


📋 Cuestionario de Repaso

Pregunta 1:

¿Qué hace ORDER BY ID DESC en la consulta SQL?
a) Ordena alfabéticamente por contenido
b) Ordena por ID de mayor a menor (más reciente primero)
c) Ordena por fecha de modificación
d) Ordena aleatoriamente

Pregunta 2:

¿Cuál es la diferencia entre PDO::FETCH_OBJ y PDO::FETCH_ASSOC?
a) Uno es más rápido que el otro
b) Uno retorna objetos ($file->ID) y otro arrays ($file['ID'])
c) Uno funciona con MySQL y otro con PostgreSQL
d) No hay diferencia

Pregunta 3:

¿Qué hace el operador ternario ($file->type === 'audio') ? 'A' : 'B'?
a) Asigna 'A' si es audio, 'B' si no
b) Retorna 'A' si es audio, 'B' si es video
c) Compara tres valores diferentes
d) Es un operador matemático

Pregunta 4:

¿Por qué usamos class="truncate" en el párrafo del contenido?
a) Para hacer el texto en negrita
b) Para cortar texto largo con puntos suspensivos (...)
c) Para cambiar el color del texto
d) Para hacer el texto más pequeño

Pregunta 5:

¿Qué problema soluciona include 'backend/init.php' al inicio de view.php?
a) Incluye la conexión a BD y la instancia de Whisper
b) Carga los estilos CSS de la página
c) Inicia la sesión del usuario
d) Configura las variables de entorno


📝 Respuestas del Cuestionario

  1. b) DESC significa "descendente", por lo que ordena del ID más alto (más reciente) al más bajo (más antiguo). ASC sería ascendente (antiguo → nuevo).

  2. b) FETCH_OBJ retorna objetos donde accedes con -> (ej: $file->ID). FETCH_ASSOC retorna arrays asociativos donde accedes con [] (ej: $file['ID']).

  3. b) El operador ternario evalúa la condición antes del ?. Si es verdadera, retorna el valor después del ?. Si es falsa, retorna el valor después del :.

  4. b) La clase truncate de Tailwind CSS aplica text-overflow: ellipsis que corta texto largo y añade ... al final para evitar desbordamientos visuales.

  5. a) init.php incluye DB.php y Whisper.php, crea la conexión a la base de datos y la instancia $whisperObj que necesitamos en toda la página.


🚀 Lo que viene después

¡Perfecto! Ahora tenemos:
✅ Página de visualización (view.php)
✅ Lista de archivos recientes
✅ Navegación entre archivos

En la próxima lección vamos a:

  1. Cargar y mostrar el archivo específico según el ID en la URL

  2. Implementar el reproductor de audio/video

  3. Mostrar el texto transcrito correspondiente

  4. Preparar la funcionalidad de traducción

Comentarios

Entradas más populares de este blog

1-7. Transforma tu audio a texto

10. Haz que tu asistente hable

8. NUEVO - Solución si tu micrófono no está captando tu audio