Reference Pipeline Automation

Research Paper Video Generation Pipeline

A reusable media pipeline for converting long-form research audio into structured transcript chunks, visual prompts, generated images, and a synchronized video.

Problem Making research videos by hand requires transcription, chunking, visual planning, image generation, and video assembly.
Outcome A scriptable pipeline that stores transcript segments in SQLite, generates visual prompts, creates images, and assembles a video with FFmpeg.
Implementation evidence

The solution is backed by inspectable code

This solves automated research-video assembly: transcribe audio, chunk the transcript, summarize each chunk into a visual concept, generate images, and merge the images with audio.

Code

import json
import sqlite3


DB_NAME = "transcriptions.db"


def create_tables():
    conn = sqlite3.connect(DB_NAME)
    cursor = conn.cursor()
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS transcriptions (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            text TEXT,
            language TEXT
        )
    """)
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS segments (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            transcription_id INTEGER,
            start REAL,
            end REAL,
            text TEXT,
            tokens TEXT,
            confidence REAL,
            words TEXT,
            FOREIGN KEY (transcription_id) REFERENCES transcriptions(id) ON DELETE CASCADE
        )
    """)
    conn.commit()
    conn.close()


def insert_transcription(data):
    conn = sqlite3.connect(DB_NAME)
    cursor = conn.cursor()
    cursor.execute(
        "INSERT INTO transcriptions (text, language) VALUES (?, ?)",
        (data.get("text", ""), data.get("language", "")),
    )
    transcription_id = cursor.lastrowid

    for segment in data.get("segments", []):
        cursor.execute(
            """
            INSERT INTO segments (transcription_id, start, end, text, tokens, confidence, words)
            VALUES (?, ?, ?, ?, ?, ?, ?)
            """,
            (
                transcription_id,
                segment.get("start", 0),
                segment.get("end", 0),
                segment.get("text", ""),
                json.dumps(segment.get("tokens", [])),
                segment.get("confidence", 0),
                json.dumps(segment.get("words", [])),
            ),
        )

    conn.commit()
    conn.close()
    return transcription_id


def get_text_chunks(transcription_id, max_chunk_duration=7.0):
    conn = sqlite3.connect(DB_NAME)
    cursor = conn.cursor()
    cursor.execute(
        """
        SELECT start, end, text
        FROM segments
        WHERE transcription_id = ?
        ORDER BY start ASC
        """,
        (transcription_id,),
    )
    segments = cursor.fetchall()
    conn.close()

    chunks, current = [], []
    current_start = current_end = current_duration = None

    for start, end, text in segments:
        duration = end - start
        if not current:
            current_start, current_end, current_duration = start, end, duration
            current.append(text)
            continue

        if current_duration + duration > max_chunk_duration:
            chunks.append({"start": current_start, "end": current_end, "text": " ".join(current)})
            current, current_start, current_end, current_duration = [text], start, end, duration
        else:
            current.append(text)
            current_end = end
            current_duration += duration

    if current:
        chunks.append({"start": current_start, "end": current_end, "text": " ".join(current)})
    return chunks
import subprocess


def assemble_video(image_files, audio_file, output_movie="paper_video.mp4"):
    with open("image_list.txt", "w", encoding="utf-8") as f:
        for filepath, start, end in image_files:
            f.write(f"file '{filepath}'\n")
            f.write(f"duration {max(end - start, 1)}\n")

    subprocess.run([
        "ffmpeg", "-f", "concat", "-safe", "0", "-i", "image_list.txt",
        "-vf", "scale=1280:720", "-c:v", "libx264", "-pix_fmt", "yuv420p",
        "temp_video.mp4",
    ], check=True)

    subprocess.run([
        "ffmpeg", "-i", "temp_video.mp4", "-i", audio_file,
        "-c:v", "copy", "-c:a", "aac", "-shortest", output_movie,
    ], check=True)

Usage

  1. Transcribe audio with Whisper or whisper-timestamped.
  2. Insert the transcription JSON with insert_transcription.
  3. Chunk segments with get_text_chunks.
  4. Generate summaries and image prompts for each chunk.
  5. Generate images and call assemble_video.

Requirements

The article uses Whisper, Ollama, Stable Diffusion WebUI, SQLite, Pillow, and FFmpeg.

Source

The article points to the implementation repository: ernanhughes/wave_to_text.

Full explanation

For the end-to-end walkthrough and image generation steps, read: Creating AI-Powered Paper Videos: From Research to YouTube.

The publishing loop Research → book → capstone → solution → real use → new evidence
Browse all solutions →