Indexar localmente un año de video en una MacBook de 2021 con Gemma 4 31B (50 GB de swap)
(blog.simbastack.com)- El cuello de botella del archivo de video no era la herramienta de edición, sino la imposibilidad de buscar; el enfoque fue convertir clips sin etiquetar en un índice consultable en inglés.
- Con un diseño local-first, se creó un archivo sidecar
.description.mdjunto a cada clip y se extrajeron en una sola llamada de visión rating, iluminación, ubicación, transcripción, palabras clave y una descripción en prosa. - El pipeline unió
ffprobe,exiftool, Nominatim,ffmpeg, WhisperX,insightfacey un modelo de visión para generar metadatos, GPS, frames, transcripción y embeddings faciales. - Una MacBook Pro de 16 pulgadas de 2021 con M1 Max y 64 GB ejecutó Gemma 4 31B Q4 en LM Studio, y durante el procesamiento masivo el swap llegó a un máximo de 50.89 GB.
- Un esquema estructurado y restricciones con enum redujeron las alucinaciones, y fue posible procesar el volumen grande con el 31B local para luego reevaluar en la nube solo el 10–20% más difícil.
El punto de partida del problema: buscar, no editar
- Durante casi medio año en Maasai Mara, se fueron acumulando videos grabados con iPhone, DJI Pocket, dron, Nikon Z8 y Ray-Ban Meta, pero la mayoría quedaron sin volver a abrirse.
- Los canales sociales de Mara Hilltop no se detuvieron por falta de contenido, sino por falta de tiempo de edición durante 3 meses.
- Con Claude Code y Opus 4.5/4.6, el trabajo de desarrollo ya permitía ejecutar agentes por largos periodos y tareas en paralelo, y al coincidir con el primer lanzamiento de alojamiento pago de KaribuKit, el tiempo para editar video se redujo todavía más.
- La primera solución imaginada fue un stack SaaS de $140 al mes combinando Eddie AI, Higgsfield MCP, Submagic y Buffer, pero no encajaba con el cuello de botella real.
- El video generativo con IA no iba con una marca de viajes real, y cuando los huéspedes esperan un lugar real, una escena de IA mal presentada puede dañar la confianza.
- La frecuencia realista de publicación era más cercana a 2–3 por semana que a 3–5, así que el plan inicial probablemente iba a fallar desde la segunda semana.
- DaVinci Resolve Studio y las funciones IntelliSearch, Smart Bins y Voice to Subtitle de Resolve 21 ya cubrían alrededor del 70% de lo que ofrecía Eddie.
- Lo que quedaba era una estructura donde Claude Code controlaba Resolve mediante el MCP open source de DaVinci Resolve y usaba ElevenLabs para voiceover solo en clips informativos, reduciendo el costo a $22 al mes.
El verdadero cuello de botella: antes que un editor con IA, hacía falta un índice
- Los editores de video con IA del mercado asumen que el video ya está etiquetado, pero el archivo real estaba disperso con nombres como
IMG_*.mov,DJI_*.mp4oMara june 2024 backup final FINAL. - Eddie permitía buscar por transcripción, pero no encontraba escenas como “elefantes sobre una colina en la hora dorada” dentro de un archivo sin etiquetas.
- Con solo nombre de archivo, carpeta padre, coordenadas GPS y texto transcrito, no se puede saber el contenido visual de algo como “un plano abierto al amanecer con jirafas en el encuadre”.
- La verdadera palanca no estaba encima del editor, sino antes de él: primero hacía falta un índice que volviera el archivo consultable en inglés.
Diseño del indexador local-first
- La estructura general se parecía a los builds AI-native para clientes que se hacen en SimbaStack, pero como aquí la misma persona era cliente e ingeniero, las decisiones se tomaban más rápido.
-
Cuatro restricciones
- Tenía que ser local-first
- El archivo de Mara Hilltop estaba en SSD físicos y los videos personales en la laptop; subir miles de clips de varios GB a la nube no tenía sentido ni por costo ni por privacidad.
- Se quería un archivo sidecar en vez de una base central
- Se colocó un
.description.mdjunto a cada clip para poder usar grep sobre texto plano. - Aunque el indexador se rompiera después, los archivos seguirían ahí, y al mover entre discos, los datos se irían con ellos.
- Se colocó un
- Había que extraer toda la información necesaria en una sola llamada de visión
- Como el paso de visión sobre los frames extraídos es caro, el esquema se definió amplio desde el inicio para obtener también lo que pudiera hacer falta más adelante en la primera llamada.
- Incluía rating, technical quality, lighting, time of day, color palette, audio quality, people count, keywords, faces, location, transcript y una prose description.
- Debían poder elegirse tres backends de visión
- El valor por defecto era el CLI de la suscripción Claude Max, sin costo marginal.
- Cuando hacía falta velocidad, se usaba la API de Anthropic.
- Para procesamiento masivo, se usó un backend local apuntando a LM Studio, y ese backend local fue la pieza clave.
- Tenía que ser local-first
Pipeline de procesamiento por clip
- Se leyó el metadata con
ffprobe. - Con
exiftoolse leyeron latitud, longitud y altitud GPS, funcionando igual con videos de iPhone, DJI Pocket y dron. - Se hizo reverse geocoding con Nominatim, que es gratis, tiene rate limits y no requiere API key.
- Con
ffmpegse extrajeron 5 frames de 1920 px a intervalos uniformes. - Con WhisperX se hizo la transcripción con alineación palabra por palabra y separación de hablantes con pyannote; soporta 97 idiomas, incluidos hindi, inglés y suajili.
- Con
insightfacese detectaron rostros y se guardaron embeddings ArcFace de 512 dimensiones en una base SQLite central de rostros para poder buscar personas más adelante en todo el archivo. - El modelo de visión leyó los frames, parte de la transcripción y el contexto de la carpeta, y devolvió YAML frontmatter y una descripción en prosa.
- El resultado final se guardó como sidecar
.description.mdjunto al clip. - En el clip real
IMG_1103.MOVde Mara Hilltop, el nombre de archivo no daba contexto, pero el sidecar generado por Gemma incluía el montaje de una tienda safari, un paneo de cámara desde interior hacia la sabana, tipo de toma y usos como reel de marketing o B-roll de vlog de viaje. - A nivel de carpeta, además de los sidecars junto a cada clip, se generaron
_INDEX.jsony_INDEX.mdpara grep rápido y para pasarlos a un LLM. - La implementación completa fue una skill de Claude Code de unas 1,400 líneas de Python; Claude Code escribió la mayor parte y la persona se encargó de la arquitectura, los prompts, el diseño del esquema y el bug triage.
Un modelo local 31B corriendo en una MacBook vieja
- La MacBook Pro de 16 pulgadas con M1 Max y 64 GB de RAM comprada en 2021 no se eligió originalmente para LLM, sino para correr a la vez Chrome, DaVinci Resolve, Slack, Discord y Drive.
- Cinco años después, esa misma laptop ejecutó Gemma 4 31B Q4 en LM Studio y procesó un año completo de archivo de video.
- En LM Studio, un modelo de 28.40 GB se cargó en memoria y la API REST corrió en
127.0.0.1:1234. - Durante el procesamiento masivo, 64 GB de RAM ya no alcanzaban y el uso de swap según Activity Monitor llegó a 50.89 GB.
- Ese estado no era sostenible para un día normal de trabajo, pero sí se consideró aceptable para forzar la máquina durante un fin de semana.
- La laptop se calentaba y los ventiladores sonaban fuerte, pero seguía generando sidecars mientras se hacían otras tareas.
- La M1 Max de 16 pulgadas mostró suficiente margen para correr un modelo de 31B parámetros a una velocidad usable incluso con hardware de 5 años, y si los LLM locales siguen volviéndose más eficientes, todavía podría servir otros 3–5 años.
Cuatro bugs y sus lecciones
-
Cambio en la API de diarización de WhisperX 3.8
- En WhisperX 3.8,
whisperx.DiarizationPipelinese movió al submódulowhisperx.diarize. - El argumento del constructor
use_auth_tokencambió atoken, siguiendo pyannote 3.x. - La solución fue la introspección de firma.
- El script primero probaba
token=y, si el constructor lanzabaTypeError, hacía fallback ause_auth_token=. - Al invocar librerías de IA que cambian rápido, una llamada defensiva al constructor es un seguro barato.
- En WhisperX 3.8,
-
Claude CLI devolvía errores de permisos como si fueran respuestas exitosas
- En la primera prueba del backend CLI, los 4 sidecars devolvieron el mismo texto: “I need permission to read the image frames...”.
- El exit code era 0 y la salida no estaba vacía, así que pasaba la validación de éxito del script.
- En modo no interactivo, si falta
--permission-mode bypassPermissions, Claude CLI devuelve el texto de denegación de permisos en el cuerpo de respuesta en lugar del prompt. - La solución fue agregar ese flag y añadir una validación defensiva para tratar respuestas cortas con “I need permission” como errores y no como descripciones.
- Al usar herramientas de IA dentro de scripts, hay fallas silenciosas escondidas en los flujos de permisos no interactivos.
-
Gemma devolvía
people_count: "many"- El prompt de visión decía
integer or the string "many" if >10, así que Gemma en realidad siguió bien la instrucción. - El bug no estaba en el modelo, sino en el diseño del esquema.
- Después se corrigió indicando que debía estimar un entero entre 0 y 99, y las respuestas previas con
"many"se convirtieron a la fuerza en el parser. - Los campos del esquema no deberían diseñarse como unions tipo
int o cierto string; es mejor fijarlos siempre como enteros o siempre como strings para simplificar a los consumidores downstream.
- El prompt de visión decía
-
Un clip movido de motocicleta fue descartado por error
- El prompt inicial de cull se parecía más al criterio de un portafolio fotográfico, así que evaluaba motion blur severo, soft focus y shake como
cull. - Un clip nocturno handheld de una motocicleta grabado durante un viaje por España también quedó marcado para descarte, aunque ese desenfoque era justamente parte de la atmósfera del recuerdo.
- El criterio de cull se cambió de “grabación imperfecta” a “algo que no es un registro real”.
- Los descartes se limitaron a clips como tapa de lente, video dentro del bolsillo, tests de 2 segundos o exposiciones completamente quemadas.
- Un archivo fotográfico debe hacerse cull de forma agresiva, pero los recuerdos en video requieren un cull más generoso; incluso con el mismo esquema, hace falta definir claramente el modo.
- El prompt inicial de cull se parecía más al criterio de un portafolio fotográfico, así que evaluaba motion blur severo, soft focus y shake como
Conclusiones sobre esquemas estructurados y modelos locales
-
Las restricciones por enumeración reducen las alucinaciones
- Gemma 4 E4B describió una foto nocturna de un coworking como “brightly lit, abundant natural light, floor-to-ceiling windows”, aunque afuera se veía una noche completamente oscura.
- Al darle al 31B un esquema estructurado y obligarlo a elegir entre
golden_hour | bright_daylight | overcast | dim_interior | nighttime | mixed | unclear, tanto con thinking-off como con thinking-on recuperónighttime. - En prosa abierta, el modelo puede inventar descripciones falsas, pero en un enum no puede inventar valores nuevos, solo equivocarse al elegir uno.
- El esquema resultó más seguro que la instrucción.
-
Un 31B local con prompts estructurados reduce la brecha frente a la nube
- Gemma 4 31B Q4 con thinking-off produjo, usando un esquema estructurado, salidas difíciles de distinguir de Sonnet 4.6 en muchos clips de prueba.
- La prima de los modelos en la nube sí valía la pena en el 10–20% más difícil.
- Para trabajo masivo, como indexar miles de clips durante la noche, era escalable correrlo localmente y luego reevaluar en la nube solo los clips que el modelo local marcara como
review.
-
Los editores de video con IA compiten en una capa demasiado alta
- La capa de valor no era el editor, sino el índice consultable.
- Si se puede consultar en lenguaje natural algo como “clips handheld de interior en Mara, hora dorada, con personas y de más de 8 segundos”, entonces el editor que va encima se simplifica.
- El mercado de editores de video con IA está compitiendo por una capa superficial montada sobre un índice que no existe, saltándose el prerequisito.
Siguientes pasos y límites
- El siguiente trabajo es construir un editor usando Claude Code como orquestador, DaVinci Resolve MCP para hacer los cortes y voiceover de ElevenLabs para clips informativos.
- Hay límites claros para el voice cloning
- Solo se usaría para contenido utilitario como indicaciones, descripción de habitaciones, versiones multilingües o información factual que la persona realmente diría.
- No se usaría para reseñas ni mensajes del fundador.
- En 2026, la legislación sobre obligación de divulgación ya es una realidad, y la confianza en una marca de hospitality se puede perder fácilmente.
- Con el índice, ya no hace falta scrubbear manualmente 47 GB de video de DJI Pocket para encontrar un plano abierto al amanecer.
- Ahora, en una laptop de 5 años, un año de video de Mara Hilltop ya se puede consultar en inglés, a cambio de tiempo de fin de semana y 50 GB de swap.
- Los años restantes que siguen guardados en SSD viejos son el próximo objetivo.
- Los canales sociales de Mara Hilltop todavía no han revivido
- El indexador solo resolvió el problema de encontrar clips adecuados.
- La otra mitad es un editor que los convierta en reels terminados; si funciona habrá una publicación de seguimiento, y si falla se explicará por qué.
- La respuesta correcta tal vez sea contratar a una persona
- Encontrar un editor con una sensibilidad cálida y observadora que encaje con Mara Hilltop puede ser más difícil que escribir otra skill.
- No se busca un reel estilo MTV con cortes excesivos.
- El código está publicado en github.com/Simbastack-hq/framedex y se aceptan PRs e issues.
1 comentarios
Comentarios en Hacker News
Parece que Claude eligió mal la URL para compartir cuando escribió el post. A menos que la carpeta home esté expuesta al exterior, no se puede acceder a
~/.claude/skills/video-index/, así que me pregunto si podrías compartir el archivo SkillActualización: armé este repositorio a las carreras - https://github.com/Simbastack-hq/framedex
La licencia es MIT y no he podido probarlo bien después de generalizarlo. Pronto lo revisaré como se debe y agregaré más actualizaciones
Los dos grandes TODO son: 1) usar este índice y la ayuda de Claude para editar video más rápido en DaVinci Resolve, y 2) ahora solo procesé video, pero quiero ampliarlo para que también pueda entender miles de imágenes fijas de la cámara
No me queda claro por qué hace falta tanto swapping. Considerando el ancho de banda de memoria necesario, eso podría acortar bastante rápido la vida útil del SSD
El modelo cuantizado a 4 bits de Gemma 4 31B debería ocupar alrededor de 19GiB, no 28.4GiB [1]. No suelo meter imágenes tan seguido, así que no sé cuánta memoria extra se necesita al ponerlas en contexto, pero no creo que pase de 10GiB
En el Monitor de Actividad se ven varias apps de Electron abiertas encima de Handy y de la máquina virtual para Claude Code, que parece ser donde se cargó el modelo, así que probablemente la causa real vaya por ahí. Cuando la laptop empieza a raspar duro el disco, esas apps se van a congelar, así que no parecen muy útiles
[1] https://huggingface.co/mlx-community/gemma-4-31b-it-4bit
Aun así se trababa un poco, pero me impresionó que podía seguir haciendo otras cosas incluso con un montón de pestañas abiertas en Brave
Me pregunto si sabías que esto ya existe, funciona bastante bien y no se traga 50GB de swap
https://github.com/iliashad/edit-mind
Está genial. Ojalá tuviera suficiente RAM para correr modelos locales. En las últimas semanas hice algo muy parecido, pero como una app local de Electron usando Whisper y ffmpeg, y le agregué búsqueda semántica y embeddings para conversar con los videos
El análisis visual, el etiquetado y el chat con video se comunican con Claude. Me pregunto si este proyecto envía solo una imagen. Yo encuentro varias imágenes distintas por video con un algoritmo personalizado de detección de escenas y se las mando a Claude en una sola solicitud junto con los subtítulos. Definitivamente es la parte más cara. Si usas Sonnet 4.6 para el análisis y Haiku para el etiquetado, sale como 1 dólar por una hora de video, y en local probablemente sería lento
Pero la forma de elegir los fotogramas es un punto débil. La detección de escenas sin duda ayudaría y es la prioridad número 1 del roadmap. Me interesa saber si podrías compartir cómo eliges los fotogramas en la detección de escenas
Decidí no meter búsqueda vectorial y mantenerlo simple con archivos Markdown comunes más portables. Si mueves el SSD, el conocimiento se va con los archivos; no hay un índice que sincronizar, y el texto plano sobrevive más que las herramientas. Aun así, la otra dirección que mencionas también vale la pena explorarla
También hay otras opciones decentes. Gemini 3.1 Flash Lite es muy bueno para este tipo de trabajo. Gemini 3.5 Flash no. Ese tiene un precio poco atractivo
https://openrouter.ai/google/gemma-4-31b-it
Tengo dos preguntas
description.mdhay cosas comofaces -> cluster_id. Me pregunto si eso viene del índice de rostros de DaVinci Resolve. En colecciones de fotos, información como rostros+nombres y ubicaciones es realmente importante, pero los LLM generales no suelen manejar eso bien.description.mdde texto plano que se coloca junto al video para cada clipMás adelante, cuando haces lluvia de ideas con Claude con algo como “quiero hacer un video de las habitaciones premium del lodge”, Claude puede revisar los archivos y entender qué videos podrían servir
También hay un archivo a nivel raíz de la carpeta que junta descripciones de texto para que sea más fácil buscar. Pegué una imagen de ejemplo en el blog - https://blog.simbastack.com/_media/gvcycx2n.png
Los rostros vienen de insightface. Los detecta con RetinaFace del paquete open source
buffalo_l, y corre localmente en CPU. Detecta y genera embeddings de rostros en fotogramas de muestra de cada clip, y luego escribe filas en~/.framedex/faces.dbSiendo sincero, sé que esta parte se está acumulando en una base de datos local, pero todavía no he podido probar bien qué tan bien funciona. Planeo revisarlo en serio pronto
Más ampliamente, por eso framedex intencionalmente no le deja al LLM el manejo de rostros o ubicaciones. Los rostros se manejan con embeddings de insightface / ArcFace, lo que permite comparaciones determinísticas entre clips. El modelo visual solo da un conteo aproximado de personas, no intenta identificar quién es quién
Las ubicaciones se manejan con EXIF GPS vía exiftool y geocodificación inversa con Nominatim/OpenStreetMap. No son suposiciones, sino metadatos sólidos
El LLM solo hace lo que sabe hacer bien: descripción de escenas, ambiente, tipo de toma, palabras clave y calificaciones de archivar/revisar/descartar. Aunque esa última parte de las calificaciones sí es debatible
Probé correr Gemma para hacer algo parecido en un ThinkPad de 2015. Por suerte pude ampliar la memoria, porque si no habría sido bastante doloroso
No les voy a mentir: al correr llama.cpp el ventilador se fue a velocidad máxima. Aun así funcionó y terminé el trabajo
A veces parece usarse como metáfora de “está usando el 100% de los recursos”, y probablemente aquí significa eso, pero en otros contextos claramente también lo dicen como una queja real
No creo que la mayoría de los hosts de Airbnb estén de acuerdo con la frase “el video generativo con IA no tiene lugar para una marca de viajes real”
Y la expresión “crucifixión en TripAdvisor” también me hace preguntarme de verdad cómo sobreviven los hosts de Airbnb que publican alojamientos falsos
Por otro lado, el video real toma tiempo y vuelve más lento todo el proceso
Creo que las aplicaciones de IA B2C tienen una limitación estructural porque es difícil construir contexto personalizado
Si un modelo local competente pudiera encargarse a gran escala de recopilar contexto, investigar, etiquetar, etc. desde cero, eso sería un gran avance aquí
Le pasas varias capturas de pantalla e intenta ponerles nombres inteligentes basados en lo que hay dentro. Igual con videos, PDFs, etc.
Pero sí, como dices, ni siquiera intenté cobrar porque parece el tipo de cosa que Apple simplemente va a meter como función
https://finalfinalreallyfinaluntitleddocumentv3.com/
Pero creo que es cuestión de tiempo para que los agentes se vuelvan lo bastante inteligentes como para que amigos no técnicos solo digan “ordena estos videos de esta carpeta para que se puedan entender” y lo haga tal cual