feat: contexto de video, análisis extendido y métricas sociales

- Campo "Contexto del Video" en formulario de análisis (Paso 03)
  → se pasa a GPT-4o para enriquecer el análisis
- 4 nuevos campos de diagnóstico: fortalezas, debilidades,
  sugerencias_mejora, hashtags_sugeridos (click para copiar)
- Vista de detalle: card de métricas sociales (vistas/likes/compartidos
  con engagement rate calculado)
- Muestra contexto original ingresado por el usuario
- Migración SQL 07: 5 nuevas columnas en tabla guiones
- validador.js: 4 nuevos campos en schema Zod
- server.js + api/analizar.js: acepta y guarda contexto_video

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-29 21:44:24 -05:00
parent 2fc4168301
commit be69c0aa48
7 changed files with 173 additions and 11 deletions

View File

@ -33,6 +33,7 @@ export default async function handler(req, res) {
likes = null,
compartidos = null,
fecha_publicacion = null,
contexto_video = '',
} = req.body
if (!url) return res.status(400).json({ error: 'El campo "url" es requerido' })
@ -56,7 +57,7 @@ export default async function handler(req, res) {
// ── PASO 3: Analizar con GPT-4o ───────────────────────
paso = 'analisis'
const analisisRaw = await analizarTranscript(transcript, niche, plataforma, duracion)
const analisisRaw = await analizarTranscript(transcript, niche, plataforma, duracion, contexto_video)
// ── PASO 4: Validar con Zod ───────────────────────────
paso = 'validacion'
@ -136,6 +137,13 @@ export default async function handler(req, res) {
persona_narradora: analisis.persona_narradora,
promesa_explicita: analisis.promesa_explicita,
nivel_especificidad: analisis.nivel_especificidad,
contexto_video: contexto_video || null,
// Diagnóstico
fortalezas: analisis.fortalezas,
debilidades: analisis.debilidades,
sugerencias_mejora: analisis.sugerencias_mejora,
hashtags_sugeridos: analisis.hashtags_sugeridos,
// Métricas (score_engagement lo calcula el trigger de Supabase)
score_virabilidad: analisis.score_virabilidad,

View File

@ -1,7 +1,7 @@
// ============================================================
// ANALIZADOR — GPT-4o
// Prompt maestro multidisciplinario: Storytelling + Cialdini
// + Neuropublicidad + Copywriting → JSON de 45 campos
// + Neuropublicidad + Copywriting → JSON de 49 campos
// ============================================================
import OpenAI from 'openai'
@ -22,12 +22,17 @@ SOLO devuelve el JSON, sin texto adicional, sin markdown, sin explicaciones.`
* @param {string} niche Nicho del video (ej: "fitness", "finanzas")
* @param {string} plataforma tiktok | reels | shorts
* @param {number} duracion Duración en segundos
* @param {string} contextoVideo Contexto adicional opcional sobre el video
* @returns {object} JSON con todos los campos de análisis
*/
export async function analizarTranscript(transcript, niche, plataforma, duracion) {
export async function analizarTranscript(transcript, niche, plataforma, duracion, contextoVideo = '') {
const bloqueContexto = contextoVideo
? `CONTEXTO ADICIONAL DEL VIDEO (úsalo para enriquecer el análisis):\n"""\n${contextoVideo}\n"""\n\n`
: ''
const promptUsuario = `Analiza este video de ${plataforma} de ${duracion} segundos del nicho "${niche}".
TRANSCRIPCIÓN:
${bloqueContexto}TRANSCRIPCIÓN:
"""
${transcript}
"""
@ -88,6 +93,11 @@ Devuelve EXACTAMENTE este JSON con los valores que correspondan:
"ingredientes_clave": ["<elemento 1 que NO puede faltar si se replica este guion>", "<elemento 2>", "<elemento 3>"],
"replicabilidad": "<alta|media|baja>",
"fortalezas": ["<fortaleza 1 del video: qué hace especialmente bien>", "<fortaleza 2>", "<fortaleza 3>"],
"debilidades": ["<debilidad o área de mejora 1>", "<debilidad 2>"],
"sugerencias_mejora": ["<sugerencia accionable concreta 1 para mejorar el video>", "<sugerencia 2>", "<sugerencia 3>"],
"hashtags_sugeridos": ["<hashtag1>", "<hashtag2>", "<hashtag3>", "<hashtag4>", "<hashtag5>", "<hashtag6>", "<hashtag7>"],
"score_virabilidad": <número entero del 1 al 100>,
"resumen_patron": "<párrafo de 3-4 oraciones describiendo el patrón ganador de este video: qué hace, por qué funciona psicológicamente y cómo se puede replicar>"
}`

View File

@ -81,6 +81,12 @@ export const AnalisisSchema = z.object({
ingredientes_clave: z.array(z.string()).min(1).max(7),
replicabilidad: ReplicabilidadEnum,
// Diagnóstico y mejora
fortalezas: z.array(z.string()).min(1).max(5),
debilidades: z.array(z.string()).min(1).max(5),
sugerencias_mejora: z.array(z.string()).min(1).max(5),
hashtags_sugeridos: z.array(z.string()).min(1).max(10),
// Métricas
score_virabilidad: z.number().int().min(1).max(100),
resumen_patron: z.string().min(10).max(1500),

View File

@ -111,6 +111,7 @@ app.post('/api/analizar', async (req, res) => {
competidor_referente = false,
vistas = null, likes = null, compartidos = null,
fecha_publicacion = null,
contexto_video = '',
} = req.body
if (!url) return res.status(400).json({ error: 'El campo "url" es requerido' })
@ -135,7 +136,7 @@ app.post('/api/analizar', async (req, res) => {
paso = 'analisis'
console.log(`[3/5] Analizando con GPT-4o...`)
const analisisRaw = await analizarTranscript(transcript, niche, plataforma, duracion)
const analisisRaw = await analizarTranscript(transcript, niche, plataforma, duracion, contexto_video)
paso = 'validacion'
console.log(`[4/5] Validando schema...`)
@ -152,6 +153,7 @@ app.post('/api/analizar', async (req, res) => {
proyecto_nombre, competidor_referente,
url_origen: url, plataforma, duracion_segundos: duracion,
vistas, likes, compartidos, fecha_publicacion,
contexto_video: contexto_video || null,
...analisis,
transcript,
embedding_vector: `[${vector.join(',')}]`,