YouTube Chapter Generator
Generate YouTube chapters from transcripts with timestamps and descriptions
YouTube Chapter Generator
Generate YouTube-compatible chapter markers from video transcripts.
Problem Statement
Who: YouTubers, video editors, content managers
Why: Chapters improve viewer navigation and SEO. Manual chapter creation is tedious for long videos.
What You’ll Build
A recipe that analyzes transcripts, identifies topic changes, and generates timestamped chapters.
Input/Output Contract
| Input | Type | Required | Description |
|---|---|---|---|
transcript_text | string | Yes* | Transcript text with timestamps |
video_path | string | Yes* | Path to video (alternative to transcript) |
style | string | No | concise or detailed (default: concise) |
*One of transcript_text or video_path is required.
| Output | Type | Description |
|---|---|---|
chapters_json | array | Chapter markers with timestamps |
description_text | string | YouTube-ready description with chapters |
ok | boolean | Success indicator |
Prerequisites
export OPENAI_API_KEY=your_key_here
pip install praisonaiagentsStep-by-Step Build
- Create Recipe Directory
bash mkdir -p ~/.praisonai/templates/youtube-chapter-generator cd ~/.praisonai/templates/youtube-chapter-generator - Create TEMPLATE.yaml
yaml name: youtube-chapter-generator version: "1.0.0" description: "Generate YouTube chapters from transcripts" author: "PraisonAI" license: "MIT" tags: - youtube - video - chapters - content requires: env: - OPENAI_API_KEY packages: - praisonaiagents inputs: transcript_text: type: string description: "Transcript text with timestamps" required: false video_path: type: string description: "Path to video file (alternative input)" required: false style: type: string description: "Chapter style" required: false default: "concise" enum: - concise - detailed outputs: chapters_json: type: array description: "Chapter markers with timestamps and titles" description_text: type: string description: "YouTube-ready description with chapters" ok: type: boolean description: "Success indicator" cli: command: "praison recipes run youtube-chapter-generator" examples: - 'praison recipes run youtube-chapter-generator --input ''{"transcript_text": "00:00 Introduction..."}''' safety: dry_run_default: false requires_consent: false overwrites_files: false network_access: true pii_handling: false - Create recipe.py
python # recipe.py import os import re import json from praisonaiagents import Agent, Task, AgentTeam def run(input_data: dict, config: dict = None) -> dict: """Generate YouTube chapters from transcript.""" transcript_text = input_data.get("transcript_text") video_path = input_data.get("video_path") style = input_data.get("style", "concise") if not transcript_text and not video_path: return { "ok": False, "error": {"code": "MISSING_INPUT", "message": "Either transcript_text or video_path is required"} } # If video_path provided, extract transcript first if video_path and not transcript_text: if not os.path.exists(video_path): return {"ok": False, "error": {"code": "FILE_NOT_FOUND", "message": f"Video not found: {video_path}"}} # Would integrate with transcription here return {"ok": False, "error": {"code": "NOT_IMPLEMENTED", "message": "Video transcription not implemented. Provide transcript_text."}} try: style_instructions = { "concise": "Create short, punchy chapter titles (3-5 words). Focus on key topics only.", "detailed": "Create descriptive chapter titles with context. Include subtopics." } # Create chapter analyzer agent analyzer = Agent( name="Content Analyzer", role="Video Content Specialist", goal="Identify logical chapter breaks in video content", instructions=f""" You are a YouTube content specialist. - Identify major topic transitions - Find natural break points - {style_instructions[style]} - Ensure first chapter starts at 00:00 - Aim for 5-15 chapters for typical videos """, ) # Create formatter agent formatter = Agent( name="Chapter Formatter", role="YouTube SEO Expert", goal="Format chapters for YouTube compatibility", instructions=""" You are a YouTube SEO expert. - Format timestamps as HH:MM:SS or MM:SS - Keep titles under 100 characters - Make titles searchable and descriptive - Ensure proper YouTube chapter format """, ) # Define tasks analyze_task = Task( name="analyze_content", description=f""" Analyze this transcript and identify chapter breaks: {transcript_text[:5000]} # Truncate for context Identify 5-15 logical chapter points with timestamps. """, expected_output="List of chapter points with timestamps and topics", agent=analyzer, ) format_task = Task( name="format_chapters", description=""" Format the chapters for YouTube: - Start with 00:00 - Use consistent timestamp format - Create engaging titles Output as JSON array: [{"timestamp": "00:00", "title": "Introduction"}, ...] """, expected_output="JSON array of formatted chapters", agent=formatter, context=[analyze_task], ) # Execute agents = AgentTeam( agents=[analyzer, formatter], tasks=[analyze_task, format_task], ) result = agents.start() # Parse chapters chapters_text = result.get("format_chapters", "[]") chapters = parse_chapters(chapters_text) # Generate YouTube description description = generate_description(chapters) return { "ok": True, "chapters_json": chapters, "description_text": description, "artifacts": [], "warnings": [], } except Exception as e: return {"ok": False, "error": {"code": "PROCESSING_ERROR", "message": str(e)}} def parse_chapters(text: str) -> list: """Parse chapters from agent output.""" try: # Try JSON parse first match = re.search(r'\[.*\]', text, re.DOTALL) if match: return json.loads(match.group()) except json.JSONDecodeError: pass # Fallback: parse timestamp lines chapters = [] for line in text.split('\n'): match = re.match(r'(\d{1,2}:\d{2}(?::\d{2})?)\s*[-–:]\s*(.+)', line.strip()) if match: chapters.append({ "timestamp": match.group(1), "title": match.group(2).strip() }) return chapters def generate_description(chapters: list) -> str: """Generate YouTube-compatible description.""" lines = ["📚 Chapters:", ""] for ch in chapters: lines.append(f"{ch['timestamp']} {ch['title']}") return '\n'.join(lines) - Create test_recipe.py
python # test_recipe.py import pytest from recipe import run, parse_chapters, generate_description def test_missing_input(): result = run({}) assert result["ok"] is False assert result["error"]["code"] == "MISSING_INPUT" def test_parse_chapters_json(): text = '[{"timestamp": "00:00", "title": "Intro"}]' chapters = parse_chapters(text) assert len(chapters) == 1 assert chapters[0]["timestamp"] == "00:00" def test_parse_chapters_text(): text = "00:00 - Introduction\n01:30 - Main Topic" chapters = parse_chapters(text) assert len(chapters) == 2 def test_generate_description(): chapters = [ {"timestamp": "00:00", "title": "Introduction"}, {"timestamp": "05:00", "title": "Main Content"} ] desc = generate_description(chapters) assert "00:00 Introduction" in desc assert "05:00 Main Content" in desc @pytest.mark.integration def test_end_to_end(): transcript = """ 00:00 Hello everyone, welcome to today's video about AI. 02:30 Let's start with the basics of machine learning. 10:00 Now let's look at some practical applications. 20:00 To summarize what we've learned today. """ result = run({"transcript_text": transcript, "style": "concise"}) assert result["ok"] is True assert len(result["chapters_json"]) > 0
Run Locally
# From transcript text
praison recipes run youtube-chapter-generator \
--input '{"transcript_text": "00:00 Welcome... 05:00 Topic 1..."}'
# Detailed style
praison recipes run youtube-chapter-generator \
--input '{"transcript_text": "...", "style": "detailed"}'Deploy & Integrate: 6 Integration Models
Model 1: Embedded SDK
from praisonai import recipe
# Load or define transcript
transcript = "00:00 Introduction to the topic..."
result = recipe.run(
"youtube-chapter-generator",
input={
"transcript_text": transcript,
"style": "concise"
}
)
if result.ok:
# Copy to clipboard or update video
print(result.output["description_text"])Model 2: CLI Invocation
# Pipe transcript from file
cat transcript.txt | praison recipes run youtube-chapter-generator \
--input '{"transcript_text": "'"$(cat)"'"}'Model 3: Plugin Mode
class YouTubeChapterPlugin:
def generate_chapters(self, transcript):
from praisonai import recipe
return recipe.run(
"youtube-chapter-generator",
input={"transcript_text": transcript}
)Model 4: Local HTTP Sidecar
const response = await fetch('http://localhost:8765/recipes/youtube-chapter-generator/run', {
method: 'POST',
body: JSON.stringify({ transcript_text: transcript })
});
const { chapters_json, description_text } = await response.json();Model 5: Remote Managed Runner
response = requests.post(
"https://api.video-tools.com/chapters",
headers={"Authorization": f"Bearer {api_key}"},
json={"transcript_text": transcript}
)Model 6: Event-Driven
def on_video_transcribed(event):
import queue as q
job_queue = q.Queue() # Replace with SQS/RabbitMQ in production
job_queue.put({
"recipe": "youtube-chapter-generator",
"input": {"transcript_text": event['transcript']},
"callback_url": f"https://api.example.com/videos/{event['video_id']}/chapters"
})Troubleshooting
Too few chapters generated
Try using style: "detailed" or provide a longer transcript with more topic changes.
Timestamps don't match video
Ensure your transcript includes accurate timestamps. The recipe uses the timestamps from your input.
Next Steps
- Video Caption Generator – Generate transcripts first
- Video Highlights Reel Planner – Plan highlight clips