Communitygithub.com

白底简笔吐槽短视频系列

Produce una serie de cortometrajes de comedia china estilo boceto en fondo blanco y líneas negras: elige un tema, escribe el guion, dibuja las escenas, narra cada línea, renderiza, crea una portada 3:4 y redacta los textos para WeChat Channels (视频号).

¿Qué es 白底简笔吐槽短视频系列?

Este skill automatiza la producción de series de cortometrajes de comedia tipo 'sketch de queja' con un estilo visual único: fondo blanco, líneas negras, cabezas grandes y cuerpo pequeño. Está diseñado para creadores de contenido que quieren generar videos virales para plataformas como WeChat Channels (视频号), a partir de un tema o una solicitud del usuario. El flujo completo abarca: redacción del guion (en formato JSON con segmentos y palabras destacadas), construcción de storyboards con funciones Python específicas (personajes, burbujas de diálogo, etc.), síntesis de voz por línea (TTS con MiniMax), renderizado de video (con y sin subtítulos), diseño de portada (usando las mismas primitivas gráficas que el video) y generación de metadatos para publicación (títulos, descripción, hashtags y comentario fijado). Soporta episodios individuales o lotes de varios episodios. El skill verifica la calidad del producto final: audita superposiciones de layout, sincronización de subtítulos, codificación de video y coherencia del guion con el audio. Las herramientas clave son Python (Pillow, NumPy), ffmpeg/ffprobe y el skill auxiliar mcode-tools (para TTS/ASR). Está optimizado para macOS y tiene notas específicas para Windows.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/inhai-wiki/doodle-comedy-series/tree/HEAD

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Serie de Cortometrajes de Queja en Boceto Blanco y Negro

Un video = un guion + un storyboard + una pista de audio + una portada + un conjunto de materiales de publicación. Todo se dibuja con el mismo conjunto de primitivas gráficas y fuentes, así que cada episodio de la serie tiene un estilo consistente y la portada coincide perfectamente con el video.

Contrato de Producción

Cada episodio debe entregar estos 5 grupos de archivos, sin excepción:

ProductoArchivoEspecificaciones
Video con subtítulosout/<título>_boceto_queja_con_subtítulos.mp41080×1440, 30 fps, H.264+AAC, ~68 s
Video sin subtítulosout/<título>_boceto_queja_sin_subtítulos.mp4Idem, sin capa de subtítulos
Archivo de subtítulosout/<título>.srtLínea única ≤16 caracteres, tiempos ascendentes sin solapamiento
Portada 3:4out/<título>_portada.png1080×1440, mismo juego de líneas y fuentes que el video
Materiales para WeChat Channelsproject_*/publish.md + publish.json3 títulos, 1 descripción, 5–8 hashtags, 1 comentario fijado

Además, se entrega out/<título>_vista_previa.png (tira de 4 fotogramas clave) para que el usuario pueda ver un episodio completo de un vistazo.

Entorno

Python 3.10+, Pillow, NumPy, ffmpeg/ffprobe. Primero crear un entorno virtual, no tocar el Python del sistema:

python3 -m venv .venv && .venv/bin/pip install -r "$skill_dir/scripts/requirements.txt"

En macOS, ffmpeg a menudo no está en el PATH predeterminado; primero ejecuta export PATH="/opt/homebrew/bin:$PATH". La locución utiliza mcode-tools (MiniMax TTS/ASR); se necesita que el skill mcode-tools-master esté en el PATH.

Si python3 da error You have not agreed to the Xcode license agreements (código de salida 69), significa que el Python del sistema no está configurado. Usa otro intérprete para crear el entorno, por ejemplo /opt/homebrew/bin/python3.13 -m venv .venv. No modifiques la licencia de Xcode.

Flujo de Trabajo

1. Definir el Tema y la Orientación de la Serie

Episodio único: basta con dar una dirección de humor. Múltiples episodios: primero asigna a cada episodio un enfoque de humor mutuamente excluyente; no uses el mismo tipo de broma con diferente apariencia.

  • Corriente Autocrítica Absurda (cada vez se vuelve más disparatado)
  • Giro en el Diálogo (dos personas conversan, la última frase lo cambia todo)
  • Observación Fría (queja desde la distancia, humor contenido)
  • Premisa Absurda (tomar en serio una premisa ridícula)

En una misma serie, al menos dos tipos diferentes de humor deben estar cerca. El lenguaje visual puede compartirse, pero el ritmo narrativo no debe ser idéntico.

2. Escribir el Guion

Lee [references/writing-style.md]. Requisitos estrictos:

  • Una frase por plano, aproximadamente 40–45 frases, 480–520 caracteres, duración ~68 s (caracteres ÷ 7.4 ≈ segundos).
  • Cada remate debe materializarse con una acción visible o un cambio de atrezo; no puede depender solo de los subtítulos.
  • El final debe invertir la relación de los personajes, dejando una frase que se pueda capturar sola.
  • Escribir en JSON: {"title": ..., "target_seconds": 68, "segments": [{"scene": ..., "text": ..., "red": [...]}]} scene es el ID de la imagen, red son las palabras a resaltar en rojo (deben aparecer exactamente igual en text).

3. Dividir en Storyboard

"$python_bin" "$skill_dir/scripts/build_segments.py" --story story.json --out project_ep2

Los subtítulos se dividen según signos de puntuación en líneas ≤16 caracteres; el renderizador no soporta más de 17 caracteres por línea. El punto de división se elige cerca de los 10 caracteres, evitando partir palabras ("gran anticipo" no debe cortarse en "gran anti" ni "anticipo"); las frases cortas entre comillas no se dividen; no se generarán líneas que comiencen con 」. El script verifica que las palabras rojas estén en los subtítulos, que no se pierdan caracteres al dividir, que no aparezcan subtítulos vacíos (solo puntuación), e imprime la duración estimada: caracteres ÷ 7.4 ≈ segundos. Si se desvía del objetivo en ±10%, se mostrará una advertencia: ajusta añadiendo o quitando frases según la advertencia, no modificando la velocidad de locución.

4. Dibujar el Storyboard

En project_ep2/scenes.py, escribe la función scene_<id>(c, t, d) y al final del archivo redefine el diccionario SCENES (solo con los IDs de este episodio). Las primitivas de dibujo están en scripts/render/doodles.py.

  • person(c,x,y,s,expr,boss,pose,t,arms,legs): centro de la cabeza en (x,y), cuerpo hasta y+307*s. expr puede ser dead/sad/happy/angry/shocked/sleep. pose puede ser stand/shrug/point/phone/pockets/walk.
  • woman(c,x,y,s,expr,t): personaje femenino (moño + vestido).
  • speech(c,x,y,w,h,tail,size): burbuja de diálogo. c.text((x,y),'texto',size) usa anclaje centrado.
  • c.curve solo acepta 4 puntos de control; c.text no soporta contorno ni múltiples líneas.

Después de editar, pasa dos filtros:

"$python_bin" "$skill_dir/scripts/check_overlap.py" project_ep2/scenes.py   # Auditoría de diseño estática
"$python_bin" "$skill_dir/scripts/check_project.py" project_ep2             # Inspección de tabla de contactos

check_overlap.py detecta texto sobre personajes, texto superpuesto y elementos fuera de la zona segura. Ya reconoce dos situaciones que "parecen superposición pero no lo son": sustituciones de palabras mutuamente excluyentes en if/else y bloques de color blanco que tapan elementos antiguos. Por lo tanto, lo que reporta suelen ser problemas reales: si el código de salida no es 0, corrígelo antes de renderizar.

check_project.py primero verifica los subtítulos y luego genera la tabla de contactos: los subtítulos deben ser la misma frase que el diálogo (se permite puntuación refinada manualmente, pero no caracteres extra, faltantes o palabras cambiadas); no se permiten subtítulos vacíos sin texto; si no pasa, se sale directamente, sin perder tiempo renderizando la tabla.

Si realmente se quiere mantener una superposición (por ejemplo, una insignia dibujada sobre un personaje), regístrala explícitamente en scenes.py:

OVERLAP_ALLOW = {"three_years", "me_too"}

La tabla de contactos debe revisarse plano por plano: ¿la imagen coincide con la frase? ¿El atrezo está claro?

5. Locución

"$python_bin" "$skill_dir/scripts/build_voice.py" --project project_ep2 --speed 1.5 --gap 0.10

La síntesis frase por frase es clave en este flujo: cada frase se sintetiza individualmente con TTS, se recorta el silencio inicial y final, y se concatenan con un intervalo fijo. Así, los tiempos de inicio y fin de cada frase son conocidos; asr.json se construye directamente con estos tiempos, logrando una alineación de subtítulos sin error y sin necesidad de ASR a nivel de palabra.

  • Voz predeterminada: lengdan_xiongzhang (compañero frío). Si el usuario especifica otra, se usa esa.
  • TTS tiene limitación de frecuencia (HTTP 429). El script reintenta con un intervalo fijo de 10 segundos (el intervalo fijo es mucho más efectivo que la backoff exponencial).
  • Un episodio tiene aproximadamente 40–45 frases; el proceso completo dura unos 15 minutos y se ejecuta en segundo plano.
  • Después de la síntesis, verifica con scripts/tts.py asr --file project_ep2/voice.wav.
  • Si hay errores de reconocimiento de sonidos cercanos (por ejemplo, "banshi/banshishi"), solo corrige los caracteres en el resultado ASR; no modifiques el guion ni regrabes.

6. Línea de Tiempo, Renderizado y Verificación

"$python_bin" "$skill_dir/scripts/deliver.py" project_ep2 <título> --target 68
"$python_bin" "$skill_dir/scripts/verify.py" project_ep2

deliver.py calcula el atempo inverso según la duración objetivo (speed = pista original / (objetivo - silencio final)), genera la línea de tiempo, subtítulos, mezcla de audio, renderiza las versiones con y sin subtítulos, y archiva el video y SRT en out/.

verify.py comprueba: parámetros de codificación, decodificación completa del video, temporización y longitud de los subtítulos, y escucha de ASR. Solo se considera terminado si la verificación es completamente verde; si falla la decodificación o hay problemas de temporización en los subtítulos, hay que corregir y renderizar de nuevo.

7. Portada

La portada no usa imágenes generadas por IA. Se redibuja con el mismo conjunto de primitivas gráficas y fuentes del video, asegurando cero desviación de estilo.

En project_ep2/cover.py, escribe la función illustration(c) (las primitivas de dibujo ya están inyectadas en el espacio de nombres). En project_ep2/cover.json, escribe las líneas de texto:

{"lines": [
  {"text": "En la cita a ciegas", "size": 146, "y": 170, "red": []},
  {"text": "Mi mamá me apuntó a siete", "size": 96, "y": 312, "red": ["siete"]},
  {"text": "Conocer a los padres y a la abuela", "size": 64, "y": 1196, "red": []},
  {"text": "Con 87 años y no presiona para tener hijos", "size": 64, "y": 1280, "red": ["presiona"]}
]}
"$python_bin" "$skill_dir/scripts/make_cover.py" project_ep2

Las reglas de diseño están en [references/cover-design.md]. La portada debe revisarse visualmente antes de entregar.

8. Materiales de Publicación para WeChat Channels

Primero recopila los materiales, luego escribe los textos:

"$python_bin" "$skill_dir/scripts/make_metadata.py" project_ep2

El script imprime el guion completo, las palabras en rojo, las palabras frecuentes y las últimas tres frases. A partir de ahí, escribe:

  • 3 títulos, con gancho emoji. Cada título ≤22 caracteres es ideal (se puede exceder, límite máximo 100).
  • 1 descripción definitiva, de 2–3 líneas. La primera línea es el gancho; los hashtags van en línea con #hashtag.
  • 5–8 hashtags, todos deben aparecer en la descripción (el script lo verificará).
  • 1 comentario fijado, para ampliar información o autocrítica.

Después de escribir:

"$python_bin" "$skill_dir/scripts/make_metadata.py" project_ep2 \
  --titles "Título uno" "Título dos" "Título tres" \
  --desc "Texto de la descripción #hashtag1 #hashtag2" \
  --tags "hashtag1" "hashtag2" \
  --comment "Comentario fijado"

Los detalles de las reglas están en [references/publish-channels.md]. Los archivos resultantes son publish.md / publish.json.

Restricciones Estrictas

  • Lienzo 1080×1440 (3:4), 30 fps, fondo blanco puro, contorno 4–6 px negro, cabeza grande y cuerpo delgado.
  • El sujeto principal se concentra en y=400–1050, dejando espacio en blanco arriba; la banda de subtítulos comienza en y=1084, tamaño de fuente mínimo 54.
  • Subtítulos en fuente Xingkai (cursiva), texto blanco con sombra suave oscura, palabras clave del remate en rojo.
  • Cada línea de subtítulo ≤16 caracteres; el renderizador no puede manejar más.
  • No quemes el texto en el cuadro; los subtítulos se renderizan como una capa separada.
  • La portada debe usar la misma fuente y líneas que el video; no se permite cambiar de estilo.
  • Conserva los videos ya terminados. Si modificas el guion, usa un nuevo directorio de proyecto; no sobrescribas episodios ya entregados.

Manejo de Fallos

  • TTS sigue dando 429: El script reintenta con intervalo fijo 40 veces. Si aún falla, reduce la velocidad a 1.4 o ejecuta en lotes (--limit N para continuar, el caché omitirá las frases ya sintetizadas).
  • prepare.py reporta inconsistencia en ASR: Significa que asr.json no coincide con el guion, generalmente porque se modificó el guion sin regrabar la locución. Vuelve a ejecutar build_voice.py; no uses --allow-asr-mismatch para saltarlo.
  • render.py reporta subtítulo demasiado largo: Algún caption supera los 16 caracteres. Vuelve a build_segments.py y ajusta las reglas de división.
  • check_overlap.py devuelve código de salida distinto de 0: Corrige las coordenadas una por una según el ID de escena reportado, hasta que el código de salida sea 0.
  • No digas que la verificación de escucha ha pasado sin haber escuchado todo el audio; no digas que has visto el video completo sin haberlo visto por completo. En el registro de entrega, solo escribe las verificaciones que realmente realizaste.

Referencias

Notas para la plataforma Windows (win32)

Equivalencias en Windows:

  • Entorno virtual: py -3 -m venv .venv. Luego usa .venv\Scripts\python.exe en lugar de $python_bin.
  • Dependencias: .venv\Scripts\pip.exe install -r "<skill_dir>\scripts\requirements.txt".
  • ffmpeg/ffprobe: instálalos directamente en el PATH (winget install Gyan.FFmpeg). No necesitas añadir /opt/homebrew/bin. Sin embargo, las rutas de ffmpeg en scripts/deliver.py, scripts/make_preview.py y scripts/verify.py son rutas absolutas de macOS. Cámbialas a ffmpeg / ffprobe para que funcionen en todas las plataformas.
  • scripts/build_voice.py depende de que mcode-tools esté en el PATH y de curl. Windows 10 1803+ incluye curl; si falta, puedes usar Invoke-WebRequest -OutFile <ruta> <url> como alternativa.
  • Separador de rutas: todos los scripts usan pathlib. Al pasar argumentos, envuelve las rutas entre comillas.

Skills relacionados