> ## Documentation Index
> Fetch the complete documentation index at: https://docs.praison.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 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.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    Input[📄 Transcript] --> Analyze[Analyze Topics]
    Analyze --> Segment[Identify Segments]
    Segment --> Format[Format Chapters]
    Format --> Output[📋 Chapters JSON]

    classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff

    class Input,Output input
    class Analyze,Segment,Format process
```

### 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

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
export OPENAI_API_KEY=your_key_here
pip install praisonaiagents
```

## Step-by-Step Build

<Steps>
  <Step title="Create Recipe Directory">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    mkdir -p ~/.praisonai/templates/youtube-chapter-generator
    cd ~/.praisonai/templates/youtube-chapter-generator
    ```
  </Step>

  <Step title="Create TEMPLATE.yaml">
    ```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    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
    ```
  </Step>

  <Step title="Create recipe.py">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    # 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)
    ```
  </Step>

  <Step title="Create test_recipe.py">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    # 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
    ```
  </Step>
</Steps>

## Run Locally

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# 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

<Tabs>
  <Tab title="Model 1: Embedded SDK">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    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"])
    ```
  </Tab>

  <Tab title="Model 2: CLI Invocation">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    # Pipe transcript from file
    cat transcript.txt | praison recipes run youtube-chapter-generator \
      --input '{"transcript_text": "'"$(cat)"'"}'
    ```
  </Tab>

  <Tab title="Model 3: Plugin Mode">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    class YouTubeChapterPlugin:
        def generate_chapters(self, transcript):
            from praisonai import recipe
            return recipe.run(
                "youtube-chapter-generator",
                input={"transcript_text": transcript}
            )
    ```
  </Tab>

  <Tab title="Model 4: Local HTTP Sidecar">
    ```javascript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    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();
    ```
  </Tab>

  <Tab title="Model 5: Remote Managed Runner">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    response = requests.post(
        "https://api.video-tools.com/chapters",
        headers={"Authorization": f"Bearer {api_key}"},
        json={"transcript_text": transcript}
    )
    ```
  </Tab>

  <Tab title="Model 6: Event-Driven">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    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"
        })
    ```
  </Tab>
</Tabs>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Too few chapters generated">
    Try using `style: "detailed"` or provide a longer transcript with more topic changes.
  </Accordion>

  <Accordion title="Timestamps don't match video">
    Ensure your transcript includes accurate timestamps. The recipe uses the timestamps from your input.
  </Accordion>
</AccordionGroup>

## Next Steps

* **[Video Caption Generator](/docs/examples/recipe-examples/video-caption-generator)** - Generate transcripts first
* **[Video Highlights Reel Planner](/docs/examples/recipe-examples/simple-video-highlights-reel-planner)** - Plan highlight clips
