Metadata-Version: 2.4
Name: whiteframes
Version: 0.1.4
Summary: Python SDK for Whiteframes AI — create AI-powered videos programmatically
License-Expression: MIT
Project-URL: Homepage, https://whiteframes.ai
Project-URL: Documentation, https://whiteframes.ai/docs
Project-URL: Repository, https://github.com/whiteframes/whiteframes-sdk
Keywords: ai,video,whiteboard,text-to-video,whiteframes
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28.0

# Whiteframes AI Python SDK

Create AI-powered videos programmatically - whiteboard explainers, social shorts, news broadcasts, podcasts, and more.

## Installation

```bash
pip install whiteframes
```

## Quick Start

```python
from whiteframes import WhiteframesAI

client = WhiteframesAI(api_key="wfai_your_key_here")

# Create a video and wait until ready (~2 minutes)
video = client.create_video("Explain how neural networks work").wait()

print(video.title)           # "How Neural Networks Work"
print(video.frame_count)     # 4
print(video.frames[0].narration)
```

Get your API key at [whiteframes.ai](https://whiteframes.ai) → Settings → API Keys.

---

## Creating Videos

`create_video()` returns a `VideoJob`. Call `.wait()` to block until ready.

### From a text prompt

```python
video = client.create_video(
    prompt="Explain how neural networks work",
    voice="Stephen",         # default
    quality="enhanced",      # default - or "basic", "premium"
    frames=4,                # default - or 8, 12
    video_type="whiteboard", # default - see Video Types below
    language="en-US",        # default - see Languages below
    aspect_ratio="16:9",     # default - or "9:16" for vertical (TikTok/Reels/Shorts)
).wait()
```

### From a document (PDF, image, etc.)

```python
video = client.create_video(
    prompt="Create a video summary of this report",
    file_path="quarterly-report.pdf",  # uploaded automatically
    frames=8,
    video_type="corporate",
).wait()
```

The file is uploaded and its content extracted automatically. Max file size: ~4.5 MB.

### Multi-speaker videocast

```python
video = client.create_video(
    prompt="Discuss the future of AI",
    video_type="videocast",
    frames=8,
    speaker_voices={
        "speaker_1": "Stephen",
        "speaker_2": "Ruth",
        "speaker_3": "Brian",
    },
).wait()
```

### With progress callback

```python
def on_progress(progress, message):
    print(f"[{progress}%] {message}")

video = client.create_video(prompt="Explain blockchain").wait(
    on_progress=on_progress
)
```

### Non-blocking

```python
job = client.create_video(prompt="Explain blockchain")

# Do other work...
job.refresh()
print(job.status, job.progress)  # "generating", 45

# Block when ready
video = job.wait()
```

### Enhance a prompt

Use AI to expand a short prompt into a detailed video description before creating.

```python
enhanced = client.enhance_prompt("explain blockchain")
# "Explore the revolutionary world of blockchain technology..."

video = client.create_video(prompt=enhanced).wait()
```

### Manual job polling

Poll job status yourself instead of using `.wait()`.

```python
import time

job = client.create_video(prompt="Explain quantum computing")

while True:
    status = client.get_job_status(job.job_id)
    print(f"[{status.get('progress', 0)}%] {status['status']}")
    if status["status"] == "ready":
        video = client.get_video(status["projectId"])
        break
    if status["status"] == "error":
        print(f"Failed: {status.get('message')}")
        break
    time.sleep(5)
```

---

## Managing Videos

### Get, list, delete

```python
# Get a single video
video = client.get_video("my-project-id")

# List all videos (paginated)
result = client.list_videos()
for video in result["videos"]:
    print(video.title, video.frame_count, "frames")

# Paginate
result2 = client.list_videos(cursor=result["next_cursor"])

# Delete
client.delete_video("my-project-id")
```

### Edit a video

Use the SDK methods to modify videos programmatically.

```python
video = client.get_video("my-project-id")

# Update narration (triggers audio re-synthesis)
client.update_frame(video.id, frame_num=1, narration="Updated narration text here.")

# Update visual description and keywords
client.update_frame(video.id, frame_num=2,
    visual_description="A detailed flowchart showing the process",
    keywords=["flowchart", "process", "steps"])

# Override one frame to use a premium voice
client.update_frame(video.id, frame_num=1,
    voice_id="Mark", voice_tier="premium")

# Update project-level settings
client.update_project(video.id, voice_id="Amy", video_type="educational")

# Add background music
client.update_project(video.id, music_track_id="track-02", music_volume=0.3)

# Remove background music
client.update_project(video.id, music_track_id="")

# Adjust music volume
client.update_project(video.id, music_volume=0.7)

# Change language (narration metadata only - does not re-generate audio)
client.update_project(video.id, language="fr-FR")

# Switch project to a premium voice
voices = client.list_premium_voices()
client.update_project(video.id,
    voice_id=voices[0]["voiceId"], voice_tier="premium")

# Delete a frame
client.delete_frame(video.id, frame_num=3)

# Reorder frames
client.reorder_frames(video.id, frame_order=[2, 1, 3, 4])
```

---

## Frame Operations

### Add a new frame

```python
# Manual narration
video = client.add_frame(
    project_id="my-project-id",
    frame_description="A world map showing global internet usage",
    narration="Over 5 billion people now have internet access.",
    keywords=["internet", "global", "map"],
    position=2,  # insert as 3rd frame (0-based)
    quality="enhanced",
    video_type="whiteboard",
)
print(f"Now has {video.frame_count} frames")

# AI-generated narration (leave narration empty)
video = client.add_frame(
    project_id="my-project-id",
    frame_description="A timeline of major AI breakthroughs",
    narration="",  # AI generates narration
    keywords=["AI", "timeline"],
)

# Premium model + premium voice
voices = client.list_premium_voices()
video = client.add_frame(
    project_id="my-project-id",
    frame_description="A futuristic city skyline",
    narration="",
    quality="premium",
    voice=voices[0]["voiceId"],
    voice_tier="premium",
)
```

### Regenerate a frame's visual

```python
video = client.regenerate_frame(
    project_id="my-project-id",
    frame_num=2,
    frame_description="A bar chart comparing CPU vs GPU performance",
    narration="GPUs outperform CPUs for parallel workloads by 10x.",
    keywords=["GPU", "CPU", "performance"],
    quality="premium",
    video_type="educational",
    voice_tier="premium",   # optional - "standard" (default) or "premium"
)

# Also regenerate narration with AI (+1 credit)
video = client.regenerate_frame(
    project_id="my-project-id",
    frame_num=1,
    frame_description="A diagram showing neural network layers",
    quality="enhanced",
    video_type="educational",
    regenerate_narration=True,
)
```

### Regenerate narration only

Regenerate narration text without changing the visual. Costs 1 credit (+1 if premium voice).

```python
video = client.regenerate_narration(
    project_id="my-project-id",
    frame_num=2,
    frame_description="A bar chart comparing CPU vs GPU performance",
    keywords=["GPU", "CPU", "performance"],
)
print(video.frames[1].narration)
```

### Fix a frame with AI

Apply targeted fixes without regenerating the entire visual.

```python
video = client.fix_frame(
    project_id="my-project-id",
    frame_num=2,
    instruction="Make the title text larger and change the color to blue",
)

# Use the premium model for more complex fixes
video = client.fix_frame(
    project_id="my-project-id",
    frame_num=2,
    instruction="Redesign the chart with gradient fills and 3D perspective",
    quality="premium",
)
```

### Frame version management

Each regeneration or fix creates a new version. You can list, switch, or delete versions.

```python
# List all versions of a frame
versions = client.list_frame_versions("my-project-id", frame_num=1)
for v in versions:
    active = " (active)" if v["active"] else ""
    print(f"v{v['index'] + 1}: {v['generatedAt']}{active}")

# Switch to a previous version
video = client.switch_frame_version("my-project-id", frame_num=1, version_index=0)

# Delete a version (cannot delete the last remaining one)
video = client.delete_frame_version("my-project-id", frame_num=1, version_index=2)
```

### Social media metadata

Every video includes AI-generated social media metadata — ready to copy and paste.

```python
video = client.create_video("Top 10 programming languages in 2025").wait()

# Access social media metadata
social = video._data.get("socialMedia", {})
print(social.get("title"))           # Viral-optimized title
print(social.get("altTitles"))       # 2 alternative titles for A/B testing
print(social.get("description"))     # Description with hook
print(social.get("hashtags"))        # 10-15 relevant hashtags
print(social.get("hookLine"))        # Hook for first 3 seconds
print(social.get("thumbnailText"))   # Suggested thumbnail text
```

---

## API Key Management

```python
# Create a new key (save it - shown once only!)
result = client.create_api_key("My Production App")
print(result["key"])

# List all keys
keys = client.list_api_keys()
for key in keys:
    print(key["name"], key["totalCalls"], "calls")

# Revoke a key
client.revoke_api_key("key_a1b2c3d4")
```

---

## Usage & Statistics

```python
# Get full API call history
calls = client.get_usage()
total = sum(c.get("creditsUsed", 0) for c in calls)
print(f"Total credits used: {total}")

# Get account usage statistics
stats = client.get_stats()
print(f"Total videos: {stats.total_videos}")
print(f"Downloads: {stats.downloads_count}")
print(f"Credits: {stats.credits}")

# Quality breakdown
print(f"Basic: {stats.videos_by_quality.get('basic', 0)}")
print(f"Enhanced: {stats.videos_by_quality.get('enhanced', 0)}")
print(f"Premium: {stats.videos_by_quality.get('premium', 0)}")

# Edits
print(f"Frame regenerations: {stats.frame_regenerations}")
print(f"Frames fixed: {stats.frames_fixed}")

# Monthly breakdown
for m in stats.monthly:
    print(f"{m.month}: {m.videos_created} videos, {m.downloads_count} downloads")

# User profile
profile = client.get_profile()
print(f"Credits remaining: {profile['credits']}")
print(f"Name: {profile['displayName']}")
```

### UsageStats fields

```python
stats.total_videos        # active (non-deleted) videos
stats.trashed_videos      # soft-deleted videos
stats.favorited_videos    # favorited count
stats.frames_generated    # total frames across all videos
stats.downloads_count     # total downloads
stats.reels_created       # reel exports
stats.frame_regenerations # frame regenerations
stats.frames_fixed        # frames fixed via AI
stats.videos_by_quality   # {"basic": 50, "enhanced": 120, "premium": 30}
stats.credits             # remaining credits (None = unlimited)
stats.credits_used        # total credits consumed via API
stats.credits_via_ui      # credits consumed via the web app
stats.credits_via_api     # credits consumed via the API/SDK
stats.last_video_at       # timestamp of most recent video
stats.monthly             # list of MonthlyStats (see below)
```

### MonthlyStats fields

```python
m.month              # "2026-03"
m.videos_created     # videos created that month
m.frames_generated   # frames in videos created that month
m.downloads_count    # downloads for videos created that month
m.favorited_videos   # favorited videos created that month
m.videos_by_quality  # {"basic": 5, "enhanced": 10, "premium": 5}
```

---

## Languages

Create videos in 26 languages. Narration text is generated in the specified language.

```python
# List all supported languages
languages = client.list_languages()
for lang in languages:
    print(f"{lang['code']}: {lang['name']} ({lang['voices']} voices)")

# Create a video in Hindi
video = client.create_video(
    prompt="Explain how neural networks work",
    voice="Kajal",
    language="hi-IN",
).wait()
print(video.language)  # "hi-IN"

# Create a video in French
video = client.create_video(
    prompt="Explain quantum computing",
    voice="Lea",
    language="fr-FR",
    frames=8,
).wait()

# Filter voices by language
french_voices = client.list_voices(language="fr-FR")
# [{"id": "Lea", "language": "fr-FR", ...}, {"id": "Remi", "language": "fr-FR", ...}]

# Change language on an existing video
client.update_project("my-project-id", language="de-DE")
```

### Supported languages

`en-US`, `en-GB`, `en-AU`, `fr-FR`, `de-DE`, `es-ES`, `es-MX`, `it-IT`, `pt-BR`, `ja-JP`, `ko-KR`, `cmn-CN`, `hi-IN`, `nl-NL`, `pl-PL`, `ar-AE`, `tr-TR`, `sv-SE`, `nb-NO`, `da-DK`, `fi-FI`, `ca-ES`, `cy-GB`, `ro-RO`, `ru-RU`, `he-IL`

---

## Voices

### Standard voices (included)

35+ built-in voices across 24 languages (Russian and Hebrew available via premium voices only):

```python
# List all voices
voices = client.list_voices()
for v in voices:
    print(f"{v['id']} - {v['language_name']} ({v['language']})")

# Filter by language
hindi_voices = client.list_voices(language="hi-IN")
german_voices = client.list_voices(language="de-DE")
```

### Premium voices (+1 credit per video)

A larger catalog of high-quality voices. Browse and use them with `voice_tier="premium"`.

```python
# List all available premium voices
voices = client.list_premium_voices()
for v in voices:
    print(f"{v['voiceId']}: {v.get('name', v['voiceId'])}")

# Create a video with a premium voice
video = client.create_video(
    prompt="Explain quantum computing",
    voice=voices[0]["voiceId"],
    voice_tier="premium",
    quality="premium",
    frames=8,
).wait()
print(video.voice_tier)  # "premium"

# Switch an existing project to a premium voice
video = client.update_project(
    "my-project-id",
    voice_id="Dennis",
    voice_tier="premium",
)
```

---

## Supported Video Types

| videoType     | Description |
|---------------|-------------|
| whiteboard    | Cinematic whiteboard - bold illustrations on white |
| saas-demo     | Product demo style - clean light UI |
| infographic   | Bold saturated flat design - data as landscape |
| corporate     | Professional visual style - metaphorical illustrations |
| architecture  | Technical system diagrams - service nodes, data flow lines |
| educational   | Visual proofs and discoveries - warm light backgrounds |
| social-short  | Vertical 9:16 format - massive typography |
| pitch         | High-impact pitch style - hero metrics, premium canvas |
| storytelling  | Cinematic narrative style - atmospheric scenes |
| news          | Professional TV news - headline banners, lower-thirds |
| videocast     | Animated podcast - multiple speakers with avatars |
| quiz          | Trivia questions with 4 options and answer reveals |
| timeline      | Chronological events along a visual timeline |
| comparison    | Side-by-side versus comparison with verdict |
| geo-map       | Map-based geographic visuals with regional data |
| top-10        | Countdown-style ranked lists with visual numbers |
| did-you-know  | Surprising facts with bold visual hooks |
| flashcard     | Term and definition study cards for learning |
| how-to        | Numbered step-by-step guides with progress tracking |
| myth-vs-fact  | Debunk myths with visual side-by-side comparison |

---

## Credit Costs

New accounts receive **30 free credits**.

| Action | Basic | Enhanced | Premium |
|--------|-------|----------|---------|
| Video Generation (4 frames) | 7 | 15 | 20 |
| Video Generation (8 frames) | 15 | 30 | 45 |
| Video Generation (12 frames) | 22 | 45 | 68 |
| Frame Regeneration | 2 | — | 6 |
| Fix Frame (AI edit) | 1 | — | 3 |
| Regenerate Narration | 1 | 1 | 1 |
| Premium Voice Surcharge | +1 per video | +1 per video | +1 per video |
| Everything else | Free | Free | Free |

> **Note:** Frame Regeneration and Fix Frame only have Basic and Premium tiers. Passing `quality="enhanced"` (the default) uses Basic pricing (2 credits / 1 credit respectively).

---

## Error Handling

```python
from whiteframes import WhiteframesAI, InsufficientCreditsError, ContentRejectedError, WhiteframesError

try:
    video = client.create_video(prompt="...")
except InsufficientCreditsError:
    print("Not enough credits")
except ContentRejectedError:
    print("Content was rejected by moderation")
except WhiteframesError as e:
    print(f"API error {e.status_code}: {e}")
```

---

## Video & Frame Objects

```python
video.id            # project ID
video.title         # generated title
video.status        # "ready"
video.video_type    # e.g. "educational"
video.voice_id      # e.g. "Stephen"
video.voice_tier    # "standard" or "premium"
video.language      # BCP-47 code, e.g. "en-US", "hi-IN"
video.frame_count   # number of frames
video.created_at    # timestamp
video.frames        # list of Frame objects

frame.id                  # frame number
frame.narration           # spoken text
frame.visual_description  # what the visual depicts
frame.duration            # seconds
frame.keywords            # list of keywords
frame.has_visual          # bool - has generated visual
frame.has_audio           # bool - has generated audio
frame.speaker_id          # for videocast: "speaker_1", etc.
frame.status              # "ready", "generating", or "error"
```

---

## Self-Hosting

```python
client = WhiteframesAI(
    api_key="wfai_xxxx",
    base_url="https://your-domain.com"
)
```
