From 14372b5b294ca0aada181d7752bd570b13ff6699 Mon Sep 17 00:00:00 2001 From: Hanzo_dev <2002samudiojohan@gmail.com> Date: Wed, 1 Apr 2026 09:00:20 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20usar=20m=C3=A9tricas=20reales=20para=20?= =?UTF-8?q?ordenar=20referencias=20del=20generador?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Vistas y Likes son ahora obligatorios al analizar un video - El generador ordena referencias por likes/vistas reales en lugar del score_virabilidad estimado por GPT-4o - Agrega CLAUDE.md con guía de arquitectura y comandos Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 80 +++++++++++++++++++++++ backend/api/analizar.js | 6 +- backend/api/generar.js | 3 +- backend/server.js | 9 ++- frontend/src/views/AnalysisCreateView.vue | 14 ++-- 5 files changed, 101 insertions(+), 11 deletions(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..7643401 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,80 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +AI-powered video script analysis and generation system. Analyzes TikTok/Reels/Shorts via a 5-step pipeline (extract audio → Whisper transcription → GPT-4o 49-field analysis → vector embeddings → Supabase storage) and generates new scripts using top-performing references. + +## Development Commands + +**Start both services (two terminals required):** + +```bash +# Terminal 1 — Backend (port 3001) +cd backend && npm install && npm run dev + +# Terminal 2 — Frontend (port 5173) +cd frontend && npm install && npm run dev +``` + +The frontend Vite dev server proxies `/api/*` to `http://localhost:3001`, so no CORS issues in development. + +There are no test or lint scripts configured. + +## Architecture + +This is a **monorepo with three layers**: + +### `/frontend` — Vue 3 + Vite + Tailwind +- Single-page app with 7 views (Dashboard, Analysis list/detail/create, Scripts, Generate, Login) +- Routes defined in [frontend/src/router/index.js](frontend/src/router/index.js) +- API calls centralized in [frontend/src/lib/api.js](frontend/src/lib/api.js) +- Auth is a mock Pinia store ([frontend/src/stores/auth.js](frontend/src/stores/auth.js)) with hardcoded credentials — not production-ready +- Design system: dark Obsidian theme. Colors defined in [frontend/tailwind.config.js](frontend/tailwind.config.js) as semantic tokens (`canvas`, `surface`, `ink`, `accent`, etc.). Fonts: Bricolage Grotesque (headlines) + Outfit (body) + +### `/backend` — Express.js (local) + `/api` (Vercel serverless) +Two parallel sets of endpoint files exist: +- `/backend/api/` — used by Express server locally +- `/api/` (root) — Vercel serverless functions for production + +When modifying API logic, **both files must be kept in sync** (or changes made to the root `/api/` file if targeting production). + +Core pipeline modules in `/backend/lib/`: +| Module | Role | +|--------|------| +| `extractor.js` | RapidAPI Social Download → audio URL (TikTok/Reels/Shorts) | +| `transcriptor.js` | Whisper-1 → text transcript | +| `analizador.js` | GPT-4o → 49-field JSON analysis (storytelling, Cialdini, neuromarketing) | +| `validador.js` | Zod schema validation of GPT-4o output | +| `embeddings.js` | OpenAI embeddings → pgvector | +| `generador.js` | GPT-4o script generation from top-scoring references | +| `supabase.js` | Supabase client (SERVICE_ROLE_KEY — bypasses RLS) | + +### `/database` — Supabase PostgreSQL + pgvector +Migrations must be applied in order in the Supabase SQL console: +`01_schema → 02_funciones → 03_rls → 04_datos_prueba → 05_analisis_extendido → 06_guiones_generados → 07_diagnostico_contexto` + +Two primary tables: +- **`guiones`** — analyzed scripts, ~49 fields including enums, Cialdini booleans, psychographic scores (1-100), and a `embedding_vector` pgvector column +- **`guiones_generados`** — AI-generated scripts linked to `guiones` references via `referencias_ids UUID[]` + +## Environment Variables + +Create `/backend/.env`: +``` +OPENAI_API_KEY=... +RAPIDAPI_KEY=... # Social Download All In One API +SUPABASE_URL=... +SUPABASE_SERVICE_ROLE_KEY=... +PORT=3001 +``` + +For Vercel production, these same variables must be set in the Vercel project dashboard (the `/api/*.js` functions read from `process.env`). + +## Key Constraints + +- **Dual file sync**: The `/api/*.js` (Vercel) and `/backend/api/*.js` (Express) files implement the same logic — they diverged in past fixes. Always check both when debugging endpoint behavior. +- **No auth on backend endpoints**: API routes have no authentication middleware. Security relies on Supabase RLS + CORS. The service role key bypasses RLS, so backend lib files must never be exposed client-side. +- **Vercel function timeout**: Set to 60s in `vercel.json`. The full analysis pipeline (extract + transcribe + GPT-4o) can take 30-50s on long videos. +- **Node 24.x** required for backend (`--watch` flag in dev script). diff --git a/backend/api/analizar.js b/backend/api/analizar.js index ea9697f..7265dd4 100644 --- a/backend/api/analizar.js +++ b/backend/api/analizar.js @@ -36,8 +36,10 @@ export default async function handler(req, res) { contexto_video = '', } = req.body - if (!url) return res.status(400).json({ error: 'El campo "url" es requerido' }) - if (!niche) return res.status(400).json({ error: 'El campo "niche" es requerido' }) + if (!url) return res.status(400).json({ error: 'El campo "url" es requerido' }) + if (!niche) return res.status(400).json({ error: 'El campo "niche" es requerido' }) + if (!vistas || Number(vistas) <= 0) return res.status(400).json({ error: 'El campo "vistas" es requerido y debe ser mayor a 0' }) + if (!likes || Number(likes) <= 0) return res.status(400).json({ error: 'El campo "likes" es requerido y debe ser mayor a 0' }) const URL_SOPORTADAS = /^https?:\/\/(www\.)?(tiktok\.com|vm\.tiktok\.com|instagram\.com|youtube\.com|youtu\.be)/ if (!URL_SOPORTADAS.test(url)) { diff --git a/backend/api/generar.js b/backend/api/generar.js index 2c2f482..9fb457d 100644 --- a/backend/api/generar.js +++ b/backend/api/generar.js @@ -48,7 +48,8 @@ export default async function handler(req, res) { `) .eq('procesado_ok', true) .eq('niche', niche) - .order('score_virabilidad', { ascending: false }) + .order('likes', { ascending: false }) + .order('vistas', { ascending: false }) .limit(num_referencias) if (plataforma) query = query.eq('plataforma', plataforma) diff --git a/backend/server.js b/backend/server.js index a5d395d..c730a84 100644 --- a/backend/server.js +++ b/backend/server.js @@ -114,8 +114,10 @@ app.post('/api/analizar', async (req, res) => { contexto_video = '', } = req.body - if (!url) return res.status(400).json({ error: 'El campo "url" es requerido' }) - if (!niche) return res.status(400).json({ error: 'El campo "niche" es requerido' }) + if (!url) return res.status(400).json({ error: 'El campo "url" es requerido' }) + if (!niche) return res.status(400).json({ error: 'El campo "niche" es requerido' }) + if (!vistas || Number(vistas) <= 0) return res.status(400).json({ error: 'El campo "vistas" es requerido y debe ser mayor a 0' }) + if (!likes || Number(likes) <= 0) return res.status(400).json({ error: 'El campo "likes" es requerido y debe ser mayor a 0' }) const URL_SOPORTADAS = /^https?:\/\/(www\.)?(tiktok\.com|vm\.tiktok\.com|instagram\.com|youtube\.com|youtu\.be)/ if (!URL_SOPORTADAS.test(url)) { @@ -260,7 +262,8 @@ app.post('/api/generar', async (req, res) => { `) .eq('procesado_ok', true) .eq('niche', niche) - .order('score_virabilidad', { ascending: false }) + .order('likes', { ascending: false }) + .order('vistas', { ascending: false }) .limit(num_referencias) if (plataforma) query = query.eq('plataforma', plataforma) diff --git a/frontend/src/views/AnalysisCreateView.vue b/frontend/src/views/AnalysisCreateView.vue index 04a3331..92cac8e 100644 --- a/frontend/src/views/AnalysisCreateView.vue +++ b/frontend/src/views/AnalysisCreateView.vue @@ -109,16 +109,16 @@
- - + +
- - + +
- +
@@ -288,6 +288,10 @@ async function iniciarAnalisis() { error.value = "URL y Nicho son obligatorios para iniciar el pipeline." return } + if (!form.value.vistas || form.value.vistas <= 0 || !form.value.likes || form.value.likes <= 0) { + error.value = "Vistas y Likes son obligatorios. Cópialos directamente del video antes de analizar." + return + } const URL_SOPORTADAS = /^https?:\/\/(www\.)?(tiktok\.com|vm\.tiktok\.com|instagram\.com|youtube\.com|youtu\.be)/ if (!URL_SOPORTADAS.test(form.value.url)) {