¿Qué hace video2knowledge?
Convierte un vídeo en: subtítulos con marcas de tiempo → documento de conocimiento → HTML / tarjetas Anki / 图文笔记.
Dos rutas de ingestión locales, toda la inferencia es local por defecto (sin API en la nube).
El procesamiento permanece completamente local — los vídeos, subtítulos y resultados nunca se suben;
viven en una carpeta runs/ local (gitignorada). El repositorio solo registra cambios de código/configuración.
La única excepción es opt-in y nunca automática:
--backend openai-api/mimo-asr(ASR) y--api-base(documento de conocimiento, cualquier endpoint compatible con OpenAI) envían ese audio o texto de subtítulo al tercero que usted nombre. Si no los pasa, todo permanece en el host. Dígalo explícitamente cuando los recomiende.
Requisitos previos y configuración inicial
Requerido en el host: ollama, ffmpeg, python3 (o uv). Una sola vez:
bash scripts/setup_models.sh
Esto (idempotentemente) detecta automáticamente el perfil de hardware de su máquina (RAM / GPU /
Apple Silicon / NVIDIA) mediante scripts/hardware_profile.py, luego descarga el
VLM recomendado, crea un venv en .venv/ e instala faster-whisper +
genanki. Vea lo que eligió:
python3 scripts/hardware_profile.py
Los perfiles van desde tiny (máquinas de 4 GB → whisper-tiny + qwen3.5:0.8b) hasta
high-gpu (NVIDIA ≥8 GB → whisper-large-v3 + qwen3.5:9b en CUDA). Tabla completa y ajuste
en references/hardware-profiles.md. Sobrescriba cualquier opción con variables de entorno
(VLM_MODEL=, ASR_DEFAULT_MODEL=, ...) o indicadores CLI.
Active el venv antes de ejecutar cualquier paso de python:
source .venv/bin/activate
Elija una ruta de ingestión (Paso 1)
Árbol de decisión:
- Pista de audio clara (conferencia, charla, entrevista, narración) → Ruta 2 (ASR).
Más rápida, texto más preciso, marcas de tiempo detalladas. Para mandarín, prefiera el
backend FunASR Paraformer — pregunte a la máquina con
python3 scripts/hardware_profile.py --recommend. →references/path2-asr.md - Silencioso / solo diapositivas / grabación de pantalla, o necesita texto en pantalla y diagramas
→ Ruta 1 (multimodal). →
references/path1-multimodal.md - Vídeo de diapositivas/PPT con pista de audio (el caso común de cursos en línea): el contenido
principal está en la pantalla (tablas, fórmulas, ejemplos) Y el orador narra →
Ruta 3 (fusión de dos rutas). Ejecuta ASR para la narración + VLM OCR para las diapositivas y las
fusiona por marca de tiempo. →
references/path3-fusion.md
Las rutas 1 y 2 emiten el mismo esquema de segmento ({inicio,fin,texto}), por lo que el paso 2 es
independiente de la ruta. La ruta 3 emite un merged.json fusionado consumido por el --merged del paso 2.
Ruta 1 — Subtítulos multimodales
python3 scripts/mm_caption.py \
--video VIDEO --out-dir OUT \
--model openbmb/minicpm-v4.6:latest --interval 2.0
Salidas: OUT/captions.srt, OUT/captions.json, OUT/frames/.
Para vídeos de diapositivas/PPT, use el muestreo dedup (de propósito general, sin ajuste de umbral) más el prompt OCR de tabla/fórmula:
python3 scripts/mm_caption.py \
--video VIDEO --out-dir OUT \
--mode dedup --prompt-ocr
Ruta 2 — Transcripción ASR
¿Qué motor? Pregunte primero a la máquina:
python3 scripts/hardware_profile.py --recommend
Medido en un curso de mandarín de 61.5 h, ambos motores en la misma conferencia de 30 minutos con la misma lista de 39 términos hotword:
| términos homófonos correctos | velocidad | |
|---|---|---|
faster-whisper small + hotwords | 44% | 2.5× tiempo real |
| Paraformer-zh (FunASR, CPU) | 94% | 12–16× tiempo real |
Los hotwords no pueden solucionar esto: 背谱/被谱 son idénticos en pinyin y tono, por lo que el audio en sí es ambiguo. La recomendación se basa en la RAM, no en la VRAM — la ventaja de Paraformer es la precisión en mandarín y se ejecuta en CPU. Si tiene una GPU, déjela libre para el VLM: ejecutar whisper y el VLM juntos convierte una solicitud de 8 tokens de 3.8 s a más de 60 s.
Predeterminado (local, faster-whisper):
python3 scripts/asr_caption.py \
--video VIDEO --out-dir OUT \
--model small --language zh \
--hotwords "término uno, término dos" # sesgo de jerga opcional mediante initial_prompt
Paraformer (recomendado para mandarín; necesita su propio venv — el pin de tokenizers de funasr no se coinstala con el entorno principal):
bash scripts/setup_models.sh --with-funasr # construye .venv-funasr, imprime la ruta
python3 .venv-funasr/bin/python scripts/asr_funasr.py \
--video VIDEO --out-dir OUT --language zh --hotwords "背谱,视谱,音阶"
Existen motores en la nube para hosts que no pueden transcribir localmente, y son opt-in —
suben el audio a un tercero. bash scripts/setup_models.sh --with-openai-client agrega el SDK openai que necesita; --backend openai-api
toma cualquier endpoint compatible con OpenAI y --backend mimo-asr es para
puertas de enlace con forma de chat que no devuelven marcas de tiempo. Consulte references/path2-asr.md.
Salidas: OUT/subtitles.{srt,vtt,json}.
Luego repare los homófonos. ASR escucha sonidos, no palabras, por lo que los términos del dominio aparecen escritos incorrectamente y el error se propaga a todos los artefactos posteriores. Medido: 20 clases de error genuino en 46 lugares, 100% de precisión.
python3 scripts/fix_homophones.py --subtitles OUT/subtitles.json
Coincide con pinyin sensible al tono, no con distancia de edición, y reescribe subtitles.srt /
.vtt para que coincidan. Ejecútelo antes del paso 2. --dry-run para previsualizar, --block 实度
para poner en la lista negra un fragmento, --glossary @terms.txt para agregar los suyos propios.
Ruta 3 — Fusión de dos rutas (ASR × VLM)
Ejecute la ruta 2 y luego la ruta 1 (OCR), realimente los términos de OCR como hotwords de ASR, fusione y construya a partir del archivo fusionado:
# 1a. ASR
python3 scripts/asr_caption.py --video VIDEO --out-dir OUT --language zh
# 1b/1c. fotogramas dedup + VLM OCR
python3 scripts/mm_caption.py --video VIDEO --out-dir OUT --mode dedup --prompt-ocr
# 1d. términos OCR -> hotwords + verificación de cobertura (vuelva a ejecutar ASR con
# --hotwords @OUT/ocr_hotwords.txt cuando la cobertura sea baja)
python3 scripts/hotwords_from_ocr.py --captions OUT/captions.json \
--subtitles OUT/subtitles.json --out OUT/ocr_hotwords.txt
# 2. fusionar por marca de tiempo + verificación de alineación semántica
python3 scripts/merge_visual.py --subtitles OUT/subtitles.json --visual OUT/captions.json --out OUT/merged.json
# 3. construir (paso 2 con --merged)
python3 scripts/build_knowledge.py --subtitles OUT/subtitles.json --merged OUT/merged.json --out-dir OUT --format all
El bucle de fusión tiene tres piezas entre rutas: (1) hotwords_from_ocr.py
extrae términos en pantalla (n-gramas CJK recurrentes, títulos de diapositivas, acrónimos
latinos — heurístico, sin LLM) e informa cuáles el ASR nunca escuchó,
la jerga probablemente mal oída; (2) merge_visual.py adjunta imágenes por
marca de tiempo, luego reasigna de forma conservadora una línea de narración a una diapositiva
ADYACENTE cuando su superposición de palabras con la diapositiva adjunta por marca de tiempo es casi nula y la
vecina coincide claramente mejor (desfase del hablante / deriva de la marca de tiempo), marcando
los casos inciertos como weak en lugar de adivinar — excepto en diapositivas
ESTABLECIDAS (≥60 s en pantalla), donde la elaboración verbal es la norma y no se marca ninguna bandera
(--no-semantic restaura la coincidencia pura de marcas de tiempo); (3) build_knowledge.py --merged alimenta al LLM texto intercalado de audio+visual (los límites de los fragmentos nunca
cortan una tabla de diapositivas; las líneas de eco de prompt se filtran por huella dactilar de cada campo de lista; los límites de campo se escalan con la duración del vídeo, 3× a las 3 h) con marcadores ⚠️ en notas de intercambio/débiles,
y reordena los campos de lista reducidos por map en un solo pase global con
el resumen como contexto. Las respuestas del LLM a nivel de fragmento se almacenan en caché en
build_cache.json, por lo que una compilación interrumpida de 3 horas se reanuda en lugar de
reiniciarse. En lotes, batch_run.py acumula términos de OCR en
course_hotwords.txt para que los vídeos posteriores transcriban mejor, y --asr-verify
vuelve a transcribir un vídeo cuya cobertura de términos de OCR cayó por debajo del 50%.
El modelo de texto predeterminado sigue el mismo perfil de hardware: openbmb/minicpm5-2b
(2.5B denso, 1.6 GB) en low/low-mac/mid, y la descarga del VLM de la ruta 1 en
high+ (qwen3.5/qwen3.8 son visión+texto unificados, por lo que nada se descarga
dos veces). Sobrescriba con --model, o V2K_TEXT_MODEL= para fijar uno para cada ejecución.
Medición de calidad: scripts/recall_check.py --run-dir <dir> --draft emite una
lista de imprescindibles dorada (términos / números de ambos canales / cobertura de línea de tiempo) que
un humano poda en minutos; --golden luego puntúa los artefactos producidos contra ella
(líneas de base en tests/golden/BASELINE.md) — el "contenido faltante" se convierte en un número
que puede rastrear a través de cambios de parámetros.
Refinar en artefactos de conocimiento (Paso 2)
Apunte build_knowledge.py a la salida .json de cualquier ruta:
python3 scripts/build_knowledge.py \
--subtitles OUT/subtitles.json \
--out-dir OUT --format all
Produce, en OUT/:
| Artefacto | Archivo | Sección |
|---|---|---|
| 2.1 Documento de conocimiento (con plantilla) | knowledge.md | usa assets/default-template.md o --template <archivo> |
| 2.2 HTML autocontenido | knowledge.html | línea de tiempo [mm:ss] enlazable |
| 2.2b Exportación a Office/impresión | knowledge.docx / knowledge.pdf | --format docx / --format pdf (auto en --format all cuando python-docx/fpdf2 está instalado) |
| 2.3 Tarjetas de conocimiento | cards.csv | pregunta,respuesta,etiquetas,marca de tiempo,fuente — deduplicadas: una conferencia larga se resume mediante map/reduce sobre fragmentos y el modelo vuelve a preguntar la misma pregunta en cada uno, por lo que cada repetición se elimina (medido: 145 de 1608 tarjetas, en el peor caso 117 filas → 11) |
| 2.4 Notas ilustradas (图文笔记) | notes.md + notes.html | fotogramas clave × narración, consulte a continuación |
| 2.4b Notas Office/impresión | notes.docx / notes.pdf | build_notes.py --docx --pdf (fotogramas incrustados) |
| 2.4c Notas destiladas (纯享版) | notes-distilled.md / .docx / .pdf | mismos nodos y fotogramas, sin narración citada — 42% más pequeño y gratis (renderizado a partir de la salida ya calculada) |
Luego convierta las tarjetas a Anki (final 2.3):
python3 scripts/gen_apkg.py --csv OUT/cards.csv --out OUT/cards.apkg --deck "Tarjetas de conocimiento de vídeo"
Notas ilustradas (2.4, 图文笔记)
Una nota desplazable que intercala FOTOGRAMAS CLAVE deduplicados con la narración
alrededor de cada marca de tiempo — el artefacto "revísalo como una lectura". Necesita frames.json
(de extract_frames.py, generalmente a través del modo dedup de la ruta 1) más los subtítulos de
cualquier ruta:
python3 scripts/build_notes.py \
--subtitles OUT/subtitles.json \
--frames OUT/frames/frames.json \
--out-dir OUT --describe-frames
La densidad de nodos es automática. --max-frames 0 (el valor predeterminado) dimensiona la nota a
~1 nodo por 45 segundos, limitado a 12–60: un clip de 12 minutos obtiene 16 nodos, una
conferencia de 41 minutos 55, una de 3 horas 60. El antiguo presupuesto de 1 por 4 minutos daba a una
conferencia de 37 minutos solo 9 nodos, y el 1 por 90 s que le siguió aún tenía un mínimo de 8 — medido
en una biblioteca de 306 vídeos / 61.5 h, el clip medio es de 12.1 min, por lo que el mínimo limitaba la mayor parte de la
biblioteca a 8 nodos sin importar cuánto durara el vídeo. El mínimo era lo que había que mover, no el máximo. Pase un
--max-frames N explícito para fijarlo. La selección está estratificada por conglomerados: cada ráfaga de cambio
conserva su fotograma final establecido, por lo que una ráfaga de animación ya no priva a las diapositivas clave aisladas de su propio nodo.
Por fotograma clave: título de sección del LLM → imagen del fotograma → descripción VLM opcional de la
escena (--describe-frames) → nota de 1-2 frases condensada de la ventana de narración →
extracto textual 原声.
El prompt 画面 solicita información didáctica, no personas: notas de tono, símbolos de acordes, indicaciones de compás, anotaciones y texto en pantalla — y se niega explícitamente a describir al intérprete ("señora tocando el piano", "explicación con gestos" no son lo que una nota de repaso necesita). Un fotograma sin contenido didáctico legible obtiene su nombre de tema en su lugar ("imagen de interpretación de piano, sin subtítulos"). Este único cambio de prompt es la diferencia entre una nota que ayuda a estudiar y una que narra el vídeo.
Dos vistas, un solo pase. Cada ejecución escribe un par gemelo de las mismas secciones — sin llamada de modelo adicional:
| Archivo | Contiene | Para |
|---|---|---|
notes.md | fotogramas + notas + 原声 citado | verificar las palabras exactas del profesor |
notes-distilled.md | mismos nodos, mismos fotogramas, sin narración citada | repasar — medido en 295 nodos, las citas en bloque eran el 53% de los caracteres |
--docx / --pdf exporta ambas variantes (notes.docx + notes-distilled.docx,
y lo mismo para PDF). notes.html incrusta fotogramas como base64 en un solo
archivo compartible — esa es solo la vista completa, sin gemelo HTML destilado.
Degrada a fotogramas + extractos sin formato cuando no hay ningún modelo accesible.
Dé al VLM un modelo con capacidad visual explícitamente. --describe-frames sin
--vlm-model recurre al modelo de texto, que no puede ver; la ejecución advierte y
descarta las descripciones en lugar de fallar una vez por fotograma:
python3 scripts/build_notes.py ... --describe-frames \
--model openbmb/minicpm5-2b --vlm-model openbmb/minicpm-v4.6:latest
Consulte references/outputs.md para conocer el esquema, las ejecuciones de un solo formato y el modo degradado.
Plantillas personalizadas (2.1)
Plantilla predeterminada: assets/default-template.md. Sobrescriba con --template <archivo>:
python3 scripts/build_knowledge.py --subtitles s.json --out-dir o \
--template ./my-lecture-template.md --format knowledge
Las plantillas son Markdown sin formato que usan {{placeholders}} ({{title}}, {{summary}},
{{bullets}} (resumen rápido de puntos combinados audio/imagen), {{timeline}}, {{key_points}}, {{qa}}, {{glossary}},
{{source}}, {{duration}}, {{date}}, {{meta}}). Solo se rellenan los marcadores que incluya;
todo lo demás permanece textual. Especificación completa + 3 plantillas de ejemplo
(conferencia / reunión / tutorial) en references/templates.md.
Procesamiento local y seguimiento de cambios
Todo el procesamiento de vídeo permanece en su máquina. Los vídeos, fotogramas extraídos,
subtítulos, documentos de conocimiento y tarjetas se escriben en una carpeta local runs/ que está
gitignorada — nada sobre su medio se sube o se confirma nunca.
El repositorio de GitHub rastrea solo cambios de código y configuración (scripts, referencias,
plantillas, README). Esto proporciona un historial claro de cómo evolucionó la skill, sin
exponer ningún medio del usuario. Use mensajes de confirmación descriptivos feat:/fix:/docs:.
Si desea un registro local de una ejecución específica, escriba un manifest.json en esa carpeta
de ejecución (ruta tomada, modelos, argumentos, lista de salida) — pero manténgalo local:
cat > "$RUN/manifest.json" <<EOF
{"video":"~/Movies/lecture.mp4","path":"dual-fusion","models":{"vlm":"openbmb/minicpm-v4.6:latest","asr":"paraformer-zh"},"args":"--mode dedup --prompt-ocr --language zh","outputs":["knowledge.md","cards.csv","notes.html"],"date":"2025-02-15"}
EOF