YouTube Chapter Generator
Generate YouTube-compatible chapter markers from video transcripts.Problem Statement
Who: YouTubers, video editors, content managersWhy: 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) |
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
Copy
export OPENAI_API_KEY=your_key_here
pip install praisonaiagents
Step-by-Step Build
1
Create Recipe Directory
Copy
mkdir -p ~/.praison/templates/youtube-chapter-generator
cd ~/.praison/templates/youtube-chapter-generator
2
Create TEMPLATE.yaml
Copy
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
3
Create recipe.py
Copy
# recipe.py
import os
import re
import json
from praisonaiagents import Agent, Task, PraisonAIAgents
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 = PraisonAIAgents(
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)
4
Create test_recipe.py
Copy
# 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
Copy
# 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
- Model 2: CLI Invocation
- Model 3: Plugin Mode
- Model 4: Local HTTP Sidecar
- Model 5: Remote Managed Runner
- Model 6: Event-Driven
Copy
from praisonai import recipe
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"])
Copy
# Pipe transcript from file
cat transcript.txt | praison recipes run youtube-chapter-generator \
--input '{"transcript_text": "'"$(cat)"'"}'
Copy
class YouTubeChapterPlugin:
def generate_chapters(self, transcript):
from praisonai import recipe
return recipe.run(
"youtube-chapter-generator",
input={"transcript_text": transcript}
)
Copy
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();
Copy
response = requests.post(
"https://api.video-tools.com/chapters",
headers={"Authorization": f"Bearer {api_key}"},
json={"transcript_text": transcript}
)
Copy
def on_video_transcribed(event):
queue.send({
"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
Too few chapters generated
Try using
style: "detailed" or provide a longer transcript with more topic changes.Timestamps don't match video
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

