Metadata-Version: 2.5
Name: golpo
Version: 2.0.0
Summary: Python SDK for Golpo AI-generated explainer videos and podcasts.
Project-URL: Homepage, https://video.golpoai.com
Author-email: Golpo <shreyas2@stanford.edu>
License: MIT
Requires-Python: >=3.8
Requires-Dist: requests>=2.31
Description-Content-Type: text/markdown

# Golpo Python SDK

Python SDK for [Golpo](https://video.golpoai.com) — generate AI-powered explainer videos and podcasts from text and documents.

## Installation

```bash
pip install golpo
```

## Quick start

```python
from golpo import Golpo

client = Golpo(api_key="your-api-key")

# Submit a video generation job
job = client.create_video(prompt="Explain quantum computing in 2 minutes")
print(job.video_id)  # Job ID

# Check status
status = client.get_video_status(job.video_id)
print(status.status)  # processing | completed | failed
if status.status == "completed":
    print(status.url)
    print(status.script)
```

---

## Client setup

```python
from golpo import Golpo

client = Golpo(api_key="your-api-key")
```

| Parameter   | Type | Description |
|------------|------|-------------|
| `api_key`  | `str` | Your Golpo API key (required). |

---

## API reference

### 1. `create_podcast` — Generate a podcast

Submits an AI-generated podcast job from a prompt and optional documents.
Returns immediately with a job ID.

**Returns:** `VideoJobResult` (`.video_id`, `.status`, optional `.variant_job_ids`)

**Example**

```python
from golpo import Golpo, VideoJobResult

client = Golpo(api_key="your-api-key")

# Prompt only
job: VideoJobResult = client.create_podcast(
    prompt="Create a 5-minute podcast about climate change and renewable energy."
)
print(job.video_id)  # Job ID
print(job.status)    # processing

# With document URLs (no upload)
job = client.create_podcast(
    prompt="Summarize this document and discuss key points.",
    uploads=["https://example.com/document.pdf"],
)

# With local files (uploaded to S3 automatically)
job = client.create_podcast(
    prompt="Turn this into an explainer podcast.",
    uploads=["./my-doc.pdf", Path("./other-doc.pdf")],
    style="solo-female-3",
    bg_music="lofi",
)

# Poll using unified status endpoint
status = client.get_video_status(job.video_id)
if status.status == "completed":
    print(status.url)     # podcast URL
    print(status.script)  # generated script
```

**Parameters**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `prompt` | `str` | required | Topic or instructions for the podcast. |
| `uploads` | `str` \| `Path` \| iterable | `None` | Document URL(s) or local file path(s). Local files are uploaded in parallel by the SDK. |
| `voice_instructions` | `str` | `None` | Instructions for voice style. |
| `language` | `str` | `None` | Language code (e.g. `"hindi"`, `"english"`). |
| `style` | `str` | `"solo-female-3"` | `"solo-female-3"`, `"solo-female-4"`, `"solo-male-3"`, `"solo-male-4"`. |
| `bg_music` | `str` | `None` | `"jazz"`, `"lofi"`, `"dramatic"`, `"whimsical"`, `"engaging"`, `"hyper"`, `"inspirational"`, `"documentary"`. |
| `new_script` | `str` | `None` | Use this script instead of generating one. Auto-estimates `timing` from word count. |
| `timing` | `str` or `float` | `"1"` | Duration in minutes. Auto-calculated when `new_script` is provided. |

---

### 2. `create_video` — Generate a video

Submits an AI-generated explainer video job from a prompt and optional documents.
Returns immediately with a job ID.

**Returns:** `VideoJobResult` (`.video_id`, `.status`, optional `.variant_job_ids`)

**Example**

```python
from golpo import Golpo, VideoJobResult

client = Golpo(api_key="your-api-key")

job: VideoJobResult = client.create_video(
    prompt="Create a 2-minute explainer on how photosynthesis works.",
)
print(job.video_id)  # Job ID
print(job.status)    # processing

# Golpo Canvas style
result = client.create_video(
    prompt="How solar panels convert sunlight to electricity",
    use_2_style=True,
    image_style="modern_minimal",
    pen_style="stylus",
    timing="2",
)

# Golpo Sketch style
result = client.create_video(
    prompt="Explain blockchain technology step by step",
    use_lineart_2_style="true",
    pacing="normal",
    timing="2",
)

# With user-provided images and videos
result = client.create_video(
    prompt="Create a product showcase video",
    user_images=["~/product_photo_1.png", "~/product_photo_2.png"],
    user_images_descriptions=["Front view of the product", "Side view with packaging"],
    user_videos=["~/demo_clip.mp4"],
    user_videos_description=["App walkthrough demo"],
    user_audio_in_video=[1],  # video 1 gets AI narration; video 2 keeps its original audio
    timing="2",
)

# Script only mode submission
job = client.create_video(
    prompt="Explain recursion in programming",
    just_return_script=True,
)
print(job.video_id)
```

**Parameters**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `prompt` | `str` | required | Topic or instructions for the video. |
| `uploads` | `str` \| `Path` \| iterable | `None` | Document URL(s) or local file path(s). |
| `voice_instructions` | `str` | `None` | Voice style instructions. |
| `language` | `str` | `None` | Language code (e.g. `"hindi"`, `"english"`). |
| `style` | `str` | `"solo-female-3"` | `"solo-male-3"`, `"solo-male-4"`, `"solo-female-3"`. |
| `bg_music` | `str` | `"engaging"` | `"jazz"`, `"lofi"`, `"dramatic"`, `"whimsical"`, `"engaging"`, `"hyper"`, `"inspirational"`, `"documentary"`. |
| `video_type` | `str` | `"long"` | `"short"` or `"long"`. |
| `include_watermark` | `bool` | `False` | Include watermark. |
| `new_script` | `str` | `None` | Override script; `timing` is auto-estimated from word count. |
| `just_return_script` | `bool` | `False` | Only generate script, no video. Poll `get_video_status(job_id)` and treat `script_ready` as completion. |
| `video_id` | `str` | `None` | Optional custom video ID to resume or target a specific backend video record. |
| `logo` | `str` \| `Path` | `None` | Logo URL or local path. Auto-enables watermark. |
| `logo_placement` | `str` | `None` | Logo placement position. |
| `timing` | `str` or `float` | `"1"` | Duration in minutes (0.25–10). Must be ≥ 0.25. |
| `video_instructions` | `str` | `None` | Custom instructions for video generation and visual style. |
| `use_color` | `bool` | `True` | Create color videos instead of black-and-white. |
| `use_lineart_2_style` | `str` | `None` | Golpo Sketch style: `"false"` (classic), `"true"` (improved), or `"advanced"` (formal). **Do not combine with `use_2_style`.** |
| `use_2_style` | `bool` | `False` | Enable Golpo Canvas visual style. **Do not combine with `use_lineart_2_style`.** |
| `image_style` | `str` | `None` | Golpo Canvas image style: `"chalkboard_white"`, `"neon"`, `"whiteboard"`, `"modern_minimal"`, `"playful"`, `"technical"`, `"editorial"`, `"marker"`. Only with `use_2_style=True`. |
| `input_images` | `str`, `Path`, or `List` | `None` | Reference input images for Golpo Canvas. Only with `use_2_style=True`. |
| `pen_style` | `str` | `None` | Drawing pen style: `"stylus"`, `"marker"`, or `"pen"`. Auto-enables `show_pencil_cursor`. Only with `use_2_style=True`. |
| `show_pencil_cursor` | `bool` | `False` | Show a pencil cursor during drawing animation. Auto-enabled when `pen_style` is set. Only with `use_2_style=True`. |
| `pacing` | `str` | `"normal"` | `"normal"` (15s max per frame) or `"fast"` (10s max per frame). |
| `display_language` | `str` | `None` | Language for on-screen text/captions. Defaults to `language` when omitted. |
| `onscreen_text_from_script_only` | `bool` | `False` | Restrict on-screen text to script-only verbatim excerpts. |
| `visual_animation_style` | `str` | `None` | Canvas visual animation policy: `"auto"`, `"fade_in"`, or `"sketch"`. |
| `language_variants` | `List[str]` | `None` | Generate additional language variants alongside the primary `language`. |
| `variant_visuals_mode` | `str` | `None` | Variant visuals mode: `"shared"` (reuse primary visuals) or `"per_language"` (generate per variant). |
| `own_narration_video_layout` | `str` | `None` | Own narration layout: `"round_overlay"` or `"side_by_side"` (requires `audio_clip`). |
| `own_narration_video_position` | `str` | `None` | Narration video position: `"left-top"`, `"right-top"`, `"left-bottom"`, `"right-bottom"`. |
| `own_narration_split_size` | `str` | `None` | Side-by-side split size: `"25%"`, `"30%"`, `"35%"`, `"40%"`, `"50%"`. |
| `own_narration_video_fit` | `str` | `None` | Side-by-side fit mode: `"crop"` or `"full"`. |
| `own_narration_focus_position` | `float` | `None` | Crop focus position from `0` to `100` for side-by-side mode. |
| `audio_clip` | `str` \| `Path` | `None` | Audio or video file path or URL. The audio will be extracted and included in the video generation. |
| `is_public` | `bool` | `None` | Set video visibility to public or private. |
| `use_as_is` | `str` or `List[bool]` | `None` | Per-frame flags to use generated images as-is without animation. |
| `skip_animation` | `str` or `List[bool]` | `None` | Per-frame flags to skip the drawing animation. |
| `user_images` | `str`, `Path`, or `List` | `None` | User-provided images (file paths or URLs). Local files are auto-uploaded to S3. |
| `user_images_descriptions` | `str` or `List[str]` | `None` | Descriptions for each user image. Strongly recommended for best visual results. |
| `user_videos` | `str`, `Path`, or `List` | `None` | User-provided video clips (file paths or URLs). Local files are auto-uploaded to S3. |
| `user_videos_description` | `str` or `List[str]` | `None` | Descriptions for each user video. |
| `user_audio_in_video` | `str` or `List[int]` | `None` | List of video numbers (1-indexed) that should use AI narration. Videos NOT in this list keep their original audio. |

---

### 3. `get_video_status` — Check video generation status

Checks one submitted generation job by `job_id`.
The SDK auto-selects the backend status endpoint from the submitted request payload:
- `just_return_script=True` -> `/script-status/{job_id}`
- otherwise -> `/status/{job_id}`

**Returns:** `VideoStatusResult` (`.video_id`, `.status`, `.url`, `.script`, `.error`)

**Example**

```python
from golpo import Golpo, VideoStatusResult

client = Golpo(api_key="your-api-key")

job = client.create_video(prompt="Explain recursion in programming")
status: VideoStatusResult = client.get_video_status(job.video_id)
print(status.status)

if status.status == "completed":
    print(status.url)
    print(status.script)
elif status.status == "failed":
    print(status.error)

# For script-only jobs, SDK auto-detects mode from request payload
script_job = client.create_video(prompt="...", just_return_script=True)
script_status = client.get_video_status(script_job.video_id)
if script_status.status in ("completed", "script_ready"):
    print(script_status.script)
```

**Parameters**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `job_id` | `str` | required | Job ID returned by `create_video`. |

---

### 4. `edit_video` — Edit specific frames

Edits selected frames of an existing video using text prompts, then regenerates the video. Optionally combines all frames into one final video.

**Returns:** `VideoEditResult` (`.video_url`, `.job_id`; also supports `result["video_url"]`, `result.get("job_id")`)

**Example**

```python
from golpo import Golpo, VideoEditResult

client = Golpo(api_key="your-api-key")

# Use video_id from create_video result
result: VideoEditResult = client.edit_video(
    video_id="your-video-id",
    frame_ids=["0", "2", "5"],
    edit_prompts=[
        "Add a chart showing growth",
        "Change background to blue",
        "Add a callout to this section",
    ],
    video_url="https://..../original-video.mp4",
)
print(result.video_url)
print(result.job_id)

# With auto_combine: merge all frames into one video with audio
result = client.edit_video(
    video_id="your-video-id",
    frame_ids=["1"],
    edit_prompts=["Make the diagram larger"],
    video_url="https://..../original-video.mp4",
    auto_combine=True,
    poll_interval_ms=3000,
)
```

**Parameters**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `video_id` | `str` | required | Video/job ID (e.g. from `create_video`). |
| `frame_ids` | `List[str]` | required | Frame indices to edit. |
| `edit_prompts` | `List[str]` | required | One prompt per frame; length must match `frame_ids`. |
| `video_url` | `str` | required | URL of the original video. |
| `reference_images` | `List[str]` | `None` | Optional reference image URLs. |
| `user_id` | `str` | `None` | Optional user ID. |
| `poll_interval_ms` | `int` | `2000` | Polling interval in milliseconds. |
| `auto_combine` | `bool` | `False` | If `True`, combine all frames into one video with audio. |

---

### 5. `combine_videos` — Combine multiple videos

Combines several MP4 URLs into a single video. Useful for merging frame-level animations (e.g. after editing) with optional audio from an original video.

**Returns:** `CombinedVideoResult` (`.url`)

**Example**

```python
from golpo import Golpo, CombinedVideoResult

client = Golpo(api_key="your-api-key")

result: CombinedVideoResult = client.combine_videos(
    mp4_urls=[
        "https://..../frame-0.mp4",
        "https://..../frame-1.mp4",
        "https://..../frame-2.mp4",
    ],
    video_url="https://..../original-with-audio.mp4",  # Optional: use for audio
    poll_interval_ms=2000,
)
print(result.url)  # Combined video URL
```

**Parameters**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `mp4_urls` | `List[str]` | required | At least one MP4 URL. |
| `video_url` | `str` | `None` | Optional source video URL for audio. |
| `poll_interval_ms` | `int` | `2000` | Polling interval in milliseconds. |

---

### 6. `get_frame_animations` — Get frame animation URLs

Returns a mapping of frame index → animation URL for a video. Use this to inspect frame-level outputs or to pass URLs into `combine_videos`.

**Returns:** `Dict[str, str]` (frame index → animation URL)

**Example**

```python
from golpo import Golpo

client = Golpo(api_key="your-api-key")

animations = client.get_frame_animations(video_id="your-video-id")
# e.g. {"0": "https://.../frame-0.mp4", "1": "https://.../frame-1.mp4", ...}

for frame_id, url in animations.items():
    print(f"Frame {frame_id}: {url}")

# Use with combine_videos
urls = list(animations.values())
combined = client.combine_videos(mp4_urls=urls, video_url=original_video_url)
```

**Parameters**

| Parameter | Type | Description |
|-----------|------|-------------|
| `video_id` | `str` | Video/job ID (e.g. from `create_video`). |

---

### 7. `update_video` — Update video metadata

Updates video metadata such as prompt, context, scenes, video URL, and frame animations. Requires JWT authentication.

**Returns:** `Dict[str, Any]`

**Example**

```python
from golpo import Golpo

client = Golpo(api_key="your-api-key")

result = client.update_video(
    video_id="your-video-id",
    prompt="Updated prompt text",
    scenes=8,
    jwt_token="your-jwt-token",
)
```

**Parameters**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `video_id` | `str` | required | The ID of the video to update. |
| `prompt` | `str` | `None` | New prompt. |
| `context` | `str` | `None` | New context. |
| `scenes` | `int` | `None` | Number of scenes. |
| `video_url` | `str` | `None` | New video URL. |
| `frame_animations` | `Dict[str, Any]` | `None` | Frame animations dictionary. |
| `jwt_token` | `str` | required | JWT token for authentication. |

---

## Result types

Import from `golpo`:

```python
from golpo import (
    GenerationResult,
    VideoJobResult,
    VideoStatusResult,
    VideoEditResult,
    CombinedVideoResult,
)
```

| Type | Attributes | Used by |
|------|------------|--------|
| `GenerationResult` | `url`, `script`, `video_id` | legacy compatibility type |
| `VideoJobResult` | `video_id`, `status`, `variant_job_ids` (optional) | `create_video`, `create_podcast` |
| `VideoStatusResult` | `video_id`, `status`, `url`, `script`, `error` | `get_video_status` |
| `VideoEditResult` | `video_url`, `job_id` (and `["video_url"]`, `.get("job_id")`) | `edit_video` |
| `CombinedVideoResult` | `url` | `combine_videos` |

---

## Uploads (local files vs URLs)

- **URLs** (e.g. `https://...` or `s3://...`): Sent as-is in the `upload_urls` form field.
- **Local paths** (`str` or `pathlib.Path`): Uploaded to S3 via `/upload-url`, then the resulting URLs are sent. Uploads run in parallel with a fixed internal worker pool in both `create_video` and `create_podcast`.

You can mix URLs and paths in a single list:

```python
client.create_video(
    prompt="...",
    uploads=[
        "https://example.com/doc.pdf",
        "./local-file.pdf",
        Path("~/Documents/other.pdf"),
    ],
)
```

---

## Errors

The SDK may raise:

- `ValueError` — Invalid arguments (e.g. empty prompt, mismatched `frame_ids`/`edit_prompts`, missing `video_url`, timing < 0.25).
- `FileNotFoundError` — A local path in `uploads` does not exist.
- `TimeoutError` — Edit/combine job did not complete within the allowed time.
- `requests.HTTPError` — API returned an error status (e.g. 4xx/5xx).

Handle these in your code as needed (retries, logging, user message).

---

## Links

- **Homepage:** [https://video.golpoai.com](https://video.golpoai.com)
- **Python:** Requires 3.8+
