Podcast Creator
An AI-powered podcast generation library that creates conversational audio content from text-based sources. This pip-installable package processes documents, generates structured outlines, creates natural dialogue transcripts, and converts them into high-quality audio podcasts using LangGraph workflow orchestration.
🎧 Live Demo
Listen to a real podcast generated with this tool - a 4-person debate on the Situational Awareness Paper. Includes my own cloned voice 😂
Generated using the diverse_panel episode profile with 4 AI experts discussing the nuances of artificial general intelligence and situational awareness.
And here is a one-speaker version of it, like your real dedicated teacher.
🚀 Quick Start
Installation
# Library only (programmatic use)
uv add podcast-creator
# or pip install podcast-creator
# Full installation with web UI
uv add podcast-creator --extra ui
# or pip install podcast-creator[ui]
# Or install from source
git clone <repository-url>
cd podcast-creator
uv sync
# Don't have uv? Install it with:
# curl -LsSf https://astral.sh/uv/install.sh | sh
# or
# pip install uv
Installation Options:
- Library only:
pip install podcast-creator- For programmatic use without web interface - With UI:
pip install podcast-creator[ui]- Includes Streamlit web interface for visual management
Configure API Keys
# Copy the example environment file
cp .env.example .env
# Edit .env and add your API keys:
# - OpenAI API key for LLM models
# - ElevenLabs API key for high-quality TTS
# - Other provider keys as needed
Initialize Your Project
# Create templates and configuration files
podcast-creator init
# This creates:
# - prompts/podcast/outline.jinja
# - prompts/podcast/transcript.jinja
# - speakers_config.json
# - episodes_config.json
# - example_usage.py
Generate Your First Podcast
🎨 New: Web Interface
# Launch the Streamlit web interface
podcast-creator ui
# Custom port/host
podcast-creator ui --port 8080 --host 0.0.0.0
# The UI provides:
# - Visual profile management
# - Multi-content podcast generation
# - Episode library with playback
# - Import/export functionality
🚀 Episode Profiles (Streamlined)
import asyncio
from podcast_creator import create_podcast
async def main():
# One-liner podcast creation with episode profiles!
result = await create_podcast(
content="Your content here...",
episode_profile="tech_discussion", # 🎯 Pre-configured settings
episode_name="my_podcast",
output_dir="output/my_podcast"
)
print(f"✅ Podcast created: {result['final_output_file_path']}")
asyncio.run(main())
📝 Classic: Full Configuration
import asyncio
from podcast_creator import create_podcast
async def main():
result = await create_podcast(
content="Your content here...",
briefing="Create an engaging discussion about...",
episode_name="my_podcast",
output_dir="output/my_podcast",
speaker_config="ai_researchers"
)
print(f"✅ Podcast created: {result['final_output_file_path']}")
asyncio.run(main())
🎯 Episode Profiles - Streamlined Podcast Creation
Episode Profiles are pre-configured sets of podcast generation parameters that enable one-liner podcast creation for common use cases while maintaining full customization flexibility.
🚀 Why Episode Profiles?
- 67% fewer parameters to specify for common use cases
- Consistent configurations across podcast series
- Faster iteration and prototyping
- Team collaboration with shared settings
- Full backward compatibility with existing code
📋 Bundled Profiles
| Profile | Description | Speakers | Segments | Use Case |
|---|---|---|---|---|
tech_discussion |
Technology topics with expert analysis | 2 AI researchers | 4 | Technical content, AI/ML topics |
solo_expert |
Educational explanations | 1 expert teacher | 3 | Learning content, tutorials |
business_analysis |
Market and business insights | 3 business analysts | 4 | Business strategy, market analysis |
diverse_panel |
Multi-perspective discussions | 4 diverse voices | 5 | Complex topics, debate-style content |
🎪 Usage Patterns
# 1. Simple profile usage
result = await create_podcast(
content="Your content...",
episode_profile="tech_discussion",
episode_name="my_podcast",
output_dir="output/my_podcast"
)
# 2. Profile with briefing suffix
result = await create_podcast(
content="Your content...",
episode_profile="business_analysis",
briefing_suffix="Focus on ROI and cost optimization",
episode_name="my_podcast",
output_dir="output/my_podcast"
)
# 3. Profile with parameter overrides
result = await create_podcast(
content="Your content...",
episode_profile="solo_expert",
outline_model="gpt-4o", # Override default
num_segments=5, # Override default
episode_name="my_podcast",
output_dir="output/my_podcast"
)
🔧 Custom Episode Profiles
from podcast_creator import configure
# Define your own episode profiles
configure("episode_config", {
"profiles": {
"my_startup_pitch": {
"speaker_config": "business_analysts",
"outline_model": "gpt-4o",
"default_briefing": "Create an engaging startup pitch...",
"num_segments": 6,
"audio_gap_ms": 500 # optional pause between turns (default 400, 0 disables)
}
}
})
# Use your custom profile
result = await create_podcast(
content="Your content...",
episode_profile="my_startup_pitch",
episode_name="pitch_deck",
output_dir="output/pitch_deck"
)
✨ Features
🔧 Flexible Configuration
from podcast_creator import configure
# Configure with custom templates
configure("templates", {
"outline": "Your custom outline template...",
"transcript": "Your custom transcript template..."
})
# Configure with custom paths
configure({
"prompts_dir": "./my_templates",
"speakers_config": "./my_speakers.json",
"output_dir": "./podcasts"
})
# Configure speakers inline
configure("speakers_config", {
"profiles": {
"my_hosts": {
"tts_provider": "elevenlabs",
"tts_model": "eleven_flash_v2_5",
"speakers": [...]
}
}
})
🎙️ Core Features
- 🎨 Web Interface: Complete Streamlit UI for visual podcast creation
- 🎯 Episode Profiles: Pre-configured settings for one-liner podcast creation
- 🔄 LangGraph Workflow: Advanced state management and parallel processing
- 🔁 Automatic Retry: Exponential backoff for transient API failures (LLM & TTS)
- 👥 Multi-Speaker Support: Dynamic 1-4 speaker configurations with rich personalities
- ⚡ Parallel Audio Generation: API-safe batching with concurrent processing
- 🔧 Fully Configurable: Multiple AI providers (OpenAI, Anthropic, Google, etc.)
- 📊 Multi-Content Support: Combine text, files, and URLs in structured arrays
- 🤖 AI-Powered Generation: Creates structured outlines and natural dialogues
- 🎵 Multi-Provider TTS: ElevenLabs, OpenAI, Google TTS support
- 📝 Flexible Templates: Jinja2-based prompt customization
- 🌍 Multilingual Support: Generate content in multiple languages
- 📚 Episode Library: Built-in audio playback and transcript viewing
🏗️ Architecture
Configuration Priority
The library uses a smart priority system for loading resources:
-
User Configuration (highest priority)
configure("templates", {"outline": "...", "transcript": "..."})
-
Custom Paths
configure("prompts_dir", "/path/to/templates")
-
Working Directory
./prompts/podcast/*.jinja./speakers_config.json./episodes_config.json
-
Bundled Defaults (lowest priority)
- Package includes production-ready templates
- Multiple speaker profiles included
📚 Usage Examples
🎯 Episode Profiles (Recommended)
import asyncio
from podcast_creator import create_podcast
# Simple episode profile usage
async def main():
result = await create_podcast(
content="AI has transformed many industries...",
episode_profile="tech_discussion", # 🚀 One-liner magic!
episode_name="ai_impact",
output_dir="output/ai_impact"
)
asyncio.run(main())
📝 Classic Configuration
import asyncio
from podcast_creator import create_podcast
async def main():
result = await create_podcast(
content="AI has transformed many industries...",
briefing="Create an informative discussion about AI impact",
episode_name="ai_impact",
output_dir="output/ai_impact",
speaker_config="ai_researchers"
)
asyncio.run(main())
Advanced Configuration
from podcast_creator import configure, create_podcast
# Custom speaker configuration (with optional per-speaker TTS overrides)
configure("speakers_config", {
"profiles": {
"tech_experts": {
"tts_provider": "elevenlabs",
"tts_model": "eleven_flash_v2_5",
"speakers": [
{
"name": "Dr. Alex Chen",
"voice_id": "your_voice_id",
"backstory": "Senior AI researcher with focus on ethics",
"personality": "Thoughtful, asks probing questions"
},
{
"name": "Jamie Rodriguez",
"voice_id": "alloy",
"backstory": "Tech journalist and startup advisor",
"personality": "Enthusiastic, great at explanations",
"tts_provider": "openai",
"tts_model": "tts-1"
}
]
}
}
})
# Custom templates
configure("templates", {
"outline": """
Create a {{ num_segments }}-part podcast outline about: {{ briefing }}
Content: {{ context }}
Speakers: {% for speaker in speakers %}{{ speaker.name }}: {{ speaker.personality }}{% endfor %}
""",
"transcript": """
Generate natural dialogue for: {{ segment.name }}
Keep it conversational and engaging.
"""
})
# Generate podcast with custom configuration
result = await create_podcast(
content="Your content...",
briefing="Your briefing...",
episode_name="custom_podcast",
speaker_config="tech_experts"
)
🎪 Episode Profile Variations
# Solo expert explanation
result = await create_podcast(
content="Technical content...",
episode_profile="solo_expert",
episode_name="deep_dive",
output_dir="output/deep_dive"
)
# Business analysis
result = await create_podcast(
content="Market trends...",
episode_profile="business_analysis",
episode_name="market_analysis",
output_dir="output/market_analysis"
)
# Panel discussion with diverse perspectives
result = await create_podcast(
content="Complex topic...",
episode_profile="diverse_panel",
episode_name="panel_discussion",
output_dir="output/panel_discussion"
)
🔧 Episode Profile Customization
# Use profile with briefing suffix
result = await create_podcast(
content="Cloud computing trends...",
episode_profile="business_analysis",
briefing_suffix="Focus on cost optimization and ROI metrics",
episode_name="cloud_economics",
output_dir="output/cloud_economics"
)
# Override specific parameters
result = await create_podcast(
content="Quantum computing...",
episode_profile="tech_discussion",
outline_model="gpt-4o", # Override default
num_segments=6, # Override default
episode_name="quantum_deep",
output_dir="output/quantum_deep"
)
🔧 Configuration API
Main Functions
from podcast_creator import configure, get_config, create_podcast
# Set configuration
configure(key, value)
configure({"key1": "value1", "key2": "value2"})
# Get configuration
value = get_config("key", default_value)
# Generate podcast
result = await create_podcast(...)
Configuration Options
| Key | Type | Description |
|---|---|---|
prompts_dir |
str |
Directory containing template files |
templates |
dict |
Inline template content |
speakers_config |
str/dict |
Path to speaker JSON or inline config |
episode_config |
str/dict |
Path to episode JSON or inline config |
output_dir |
str |
Default output directory |
🎭 Speaker Configuration
Speaker Profile Structure
{
"profiles": {
"profile_name": {
"tts_provider": "elevenlabs",
"tts_model": "eleven_flash_v2_5",
"speakers": [
{
"name": "Speaker Name",
"voice_id": "voice_id_from_provider",
"backstory": "Rich background that informs expertise",
"personality": "Speaking style and traits"
}
]
}
}
}
Per-Speaker TTS Overrides
Individual speakers can override the profile-level TTS provider, model, and config. This lets you mix different TTS services within the same podcast — for example, one speaker on ElevenLabs and another on OpenAI TTS.
{
"profiles": {
"mixed_providers": {
"tts_provider": "openai",
"tts_model": "tts-1",
"speakers": [
{
"name": "Dr. Sarah Chen",
"voice_id": "custom_eleven_voice_id",
"backstory": "AI researcher...",
"personality": "Analytical and methodical",
"tts_provider": "elevenlabs",
"tts_model": "eleven_flash_v2_5",
"tts_config": { "voice_settings": { "stability": 0.8 } }
},
{
"name": "Marcus Rivera",
"voice_id": "alloy",
"backstory": "Tech journalist...",
"personality": "Engaging and curious"
}
]
}
}
}
In this example, Dr. Sarah Chen uses ElevenLabs while Marcus Rivera uses the profile-level OpenAI TTS. All three fields (tts_provider, tts_model, tts_config) are optional per speaker — any field not set falls back to the profile-level value. If a speaker defines tts_config, it replaces the profile-level config entirely (no merging).
Creating Custom Speakers
- Get Voice IDs from your TTS provider
- Design Personalities that complement each other
- Write Rich Backstories to guide content expertise
- Test Combinations with different content types
🌐 Supported Providers
Language Models (via Esperanto)
- OpenAI: GPT-4o and newer GPT / o-series models
- Anthropic: Claude Sonnet 5.5, Claude Opus 5.5
- Google / Vertex AI: Gemini 2.5 Pro, Gemini 2.5 Flash
- Groq: GPT-OSS, Llama models
- Ollama: Local model support
- Perplexity: Research-enhanced models
- Azure OpenAI: Enterprise OpenAI
- Mistral: Mistral models
- DeepSeek: DeepSeek models
- xAI: Grok models
- OpenRouter: Multi-provider access
- Cohere, DashScope (Qwen), MiniMax, Z.ai, SiliconFlow, Novita, PayPerQ and any OpenAI-compatible endpoint (vLLM, LM Studio, ...)
Text-to-Speech Services
- ElevenLabs: Professional voice synthesis
- OpenAI TTS: High-quality voices
- Google (Gemini TTS) and Vertex AI
- Azure OpenAI
- Deepgram (Aura), Mistral (Voxtral), MiniMax, xAI
- OpenRouter, PayPerQ and OpenAI-compatible TTS servers (e.g. Speaches/Kokoro)
See the Esperanto provider docs for the full, current list.
📁 Output Structure
output/episode_name/
├── outline.json # Structured outline
├── transcript.json # Complete dialogue
├── clips/ # Individual audio clips
│ ├── 0000.mp3 # First segment
│ ├── 0001.mp3 # Second segment
│ └── ... # Additional segments
└── audio/ # Final output
└── episode_name.mp3 # Complete podcast
🛠️ CLI Commands
# Launch web interface (requires UI installation)
podcast-creator ui
# Launch on custom port/host
podcast-creator ui --port 8080 --host 0.0.0.0
# Skip dependency check
podcast-creator ui --skip-init-check
# Initialize project with templates
podcast-creator init
# Initialize in specific directory
podcast-creator init --output-dir /path/to/project
# Overwrite existing files
podcast-creator init --force
# Show version
podcast-creator version
Note: The ui command requires the UI installation: pip install podcast-creator[ui]
🎨 Web Interface Features
The podcast-creator ui command launches a comprehensive Streamlit interface that provides:
- 🏠 Dashboard: Statistics and quick actions
- 🎙️ Speaker Management: Visual profile creation with voice selection dropdowns
- 📺 Episode Management: Configure generation parameters and AI models
- 🎬 Podcast Generation: Multi-content support (text, files, URLs) with real-time progress
- 📚 Episode Library: Audio playback, transcript viewing, and downloads
- 📤 Import/Export: Share profiles via JSON files
The interface automatically detects missing dependencies and offers to run initialization if needed.
🚀 Performance
- ⚡ Parallel Processing: 5 concurrent audio clips per batch (configurable)
- 🔄 API-Safe Batching: Respects provider rate limits
- 📊 Scalable: Handles 30+ dialogue segments efficiently
- ⏱️ Fast Generation: ~2-3 minutes for typical podcasts
- 🎯 Optimized Workflow: Smart resource management
⚠️ Rate Limiting Configuration
If you encounter errors like ElevenLabs API error: Too many concurrent requests, you can adjust the parallel processing batch size:
# In your .env file
TTS_BATCH_SIZE=2 # Reduce from default 5 to 2 for ElevenLabs free plan
This is particularly useful for:
- ElevenLabs Free Plan: Limited to 2 concurrent requests
- Other TTS providers with stricter rate limits
- Debugging: Set to 1 for sequential processing
⏸️ Pauses Between Turns
When combining clips, a short pause is inserted between dialogue turns for natural conversational pacing (TTS clips usually end without trailing silence). The default is 400 ms; set audio_gap_ms on the episode profile or pass it to create_podcast() (the argument wins), and use 0 to disable it:
result = await create_podcast(
content="Your content...",
episode_profile="tech_discussion",
episode_name="my_podcast",
output_dir="output/my_podcast",
audio_gap_ms=600,
)
🔁 Retry Configuration
LLM and TTS API calls automatically retry on transient failures (network errors, timeouts, rate limits) with exponential backoff. Non-retryable errors are raised immediately without retry — this includes programming errors (e.g. ValueError) and HTTP 4xx client errors (e.g. 404 model not found, 401 auth failure), except 429 rate-limit which is retried.
# In your .env file
PODCAST_RETRY_MAX_ATTEMPTS=3 # Max retry attempts (default: 3)
PODCAST_RETRY_WAIT_MULTIPLIER=5 # Backoff multiplier in seconds (default: 5)
PODCAST_RETRY_WAIT_MAX=30 # Max wait between retries in seconds (default: 30)
You can also configure retries programmatically for LLM calls (outline and transcript generation):
result = await create_podcast(
content="Your content...",
episode_profile="tech_discussion",
episode_name="my_podcast",
output_dir="output/my_podcast",
retry_max_attempts=5, # Override default
retry_wait_multiplier=3, # Override default
)
To disable retries entirely, set PODCAST_RETRY_MAX_ATTEMPTS=1.
📏 LLM Output Limits
Outline and transcript generation request up to 8192 output tokens by default. This is podcast-creator's request limit, not a model cap: current models, including the default Claude Sonnet 5.5, support much larger outputs. If your model has a lower output limit (some legacy models cap at 4096), or you need more room, override max_tokens via outline_config / transcript_config, either in create_podcast() or in the episode profile:
result = await create_podcast(
content="Your content...",
episode_profile="tech_discussion",
episode_name="my_podcast",
output_dir="output/my_podcast",
outline_config={"max_tokens": 4096},
transcript_config={"max_tokens": 4096},
)
🧩 Structured Output
Outline and transcript generation ask the model for JSON that matches the package's own schemas (json_schema structured output via Esperanto), so providers that support it return exactly the expected shape. A few providers or older models cannot honor a JSON schema (for example, Anthropic requires Claude 4.5 or newer); Esperanto then fails fast with a clear error. For those, fall back to generic JSON mode:
result = await create_podcast(
content="Your content...",
episode_profile="tech_discussion",
episode_name="my_podcast",
output_dir="output/my_podcast",
outline_config={"structured": {"type": "json"}},
transcript_config={"structured": {"type": "json"}},
)
The same keys work inside an episode profile's outline_config / transcript_config.
🌐 Proxy Configuration
If you're behind a corporate firewall or need to route requests through a proxy, use standard environment variables:
# In your .env file or shell environment
HTTP_PROXY=http://proxy.example.com:8080
HTTPS_PROXY=http://proxy.example.com:8080
NO_PROXY=localhost,127.0.0.1
Authenticated Proxies:
# Proxies with authentication are supported
HTTP_PROXY=http://user:password@proxy.example.com:8080
HTTPS_PROXY=http://user:password@proxy.example.com:8080
The underlying libraries (esperanto, content-core) automatically detect and use these standard proxy environment variables for all network requests.
🧪 Development
Installing for Development
git clone <repository-url>
cd podcast-creator
# Install with uv (recommended)
uv sync
# This installs the package in editable mode
# along with all dependencies
Project Structure
podcast-creator/
├── src/
│ └── podcast_creator/
│ ├── __init__.py # Public API
│ ├── config.py # Configuration system
│ ├── cli.py # CLI commands (with UI command)
│ ├── core.py # Core utilities
│ ├── graph.py # LangGraph workflow
│ ├── nodes.py # Workflow nodes
│ ├── retry.py # Retry utilities with exponential backoff
│ ├── speakers.py # Speaker management
│ ├── episodes.py # Episode profile management
│ ├── state.py # State management
│ ├── validators.py # Validation utilities
│ └── resources/ # Bundled templates
│ ├── prompts/
│ ├── speakers_config.json
│ ├── episodes_config.json
│ ├── streamlit_app/ # Web interface
│ └── examples/
├── pyproject.toml # Package configuration
└── README.md
Testing
# Test the package
python -c "from podcast_creator import create_podcast; print('Import successful')"
# Test CLI
podcast-creator --help
# Test web interface
podcast-creator ui
# Test initialization
mkdir test_project
cd test_project
podcast-creator init
python example_usage.py
📝 Examples
Check the examples/ directory for:
- Episode Profiles: Comprehensive guide to streamlined podcast creation
- Basic usage examples
- Advanced configuration
- Custom speaker setups
- Multi-language podcasts
- Different content types
🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details on:
- 🚀 Getting started with development
- 📋 Our pull request process
- 🧪 Testing guidelines
- 🎨 Code style and standards
- 🐛 How to report bugs
- 💡 How to suggest new features
Quick links:
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🔗 Links
- Examples: Examples
Made with ❤️ for the AI community
Metadata
Release files for podcast-creator 0.13.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| podcast_creator-0.13.0.tar.gz | 497.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| podcast_creator-0.13.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 579.9 kB
Release files / podcast_creator-0.13.0.tar.gz
| Download URL | podcast_creator-0.13.0.tar.gz |
|---|---|
| Size | 497.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d9bab87506d9b42605c6b18f0ab3035ccd1fde744d1736e3cdb3cf5d283296bb
|
|
BLAKE2b-256 checksum How to use checksums |
3c23e3e5ba947a672cd39dab0b0a726dfd8ce76638bf71d1ffbfc356cb3b9733
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / podcast_creator-0.13.0-py3-none-any.whl
| Download URL | podcast_creator-0.13.0-py3-none-any.whl |
|---|---|
| Size | 82.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e67d30fa8f4049fa3b4653b009cab6f69a3d5ed5ec551decae0901ab1be5b665
|
|
BLAKE2b-256 checksum How to use checksums |
21927cfa0b0c7992ea91ee1398d2ebe84a566159219f773402bef600055915c6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|