Metadata-Version: 2.4
Name: manim-studio-sdk
Version: 0.1.0
Summary: Python SDK for ManimStudio — render Manim animations via API
Project-URL: Homepage, https://manimstudio.me
Project-URL: Documentation, https://manimstudio.me/docs
Project-URL: Repository, https://github.com/Luna7282/Manim-online
License: MIT
License-File: LICENSE
Keywords: animation,manim,math,rendering
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.8
Description-Content-Type: text/markdown

# manimstudio

Python SDK for [ManimStudio](https://manimstudio.me) — render [Manim](https://www.manim.community/) animations via API, no local Manim install required.

## Installation

```bash
pip install manim-studio-sdk
```

No external dependencies — the SDK is stdlib-only (`urllib`, `json`).

Requires Python 3.8+.

## Getting an API key

Create an API key from your dashboard: **https://manimstudio.me/dashboard**

## Quick start

```python
from manimstudio import ManimStudio

client = ManimStudio(api_key="msk_xxx")

code = """
from manim import *

class Hello(Scene):
    def construct(self):
        self.play(Write(Text("Hello, ManimStudio!")))
"""

job = client.render(code, quality="medium")
job.wait()
job.download("hello.mp4")

print(f"Saved to hello.mp4 — {job.url}")
```

## Client

### `ManimStudio(api_key, base_url="https://manimstudio.me", timeout=30.0)`

```python
from manimstudio import ManimStudio

client = ManimStudio(api_key="msk_xxx")

# Point at a self-hosted instance or local dev server:
client = ManimStudio(api_key="msk_xxx", base_url="http://localhost:8000")
```

Raises `ValueError` if `api_key` is empty.

### `client.render(code, quality="medium", scene=None, asset_keys=None)`

Submit a Manim scene for rendering. Returns a `RenderJob` immediately — the render happens asynchronously.

```python
job = client.render(
    code=my_scene_code,
    quality="high",        # "low" (480p), "medium" (720p), or "high" (1080p)
    scene="MyScene",       # optional: render one specific Scene class; omit to render all
    asset_keys=["assets/u1/logo.png"],  # optional: pre-uploaded asset keys referenced by the scene
)
```

### `client.get_usage()`

Get current quota usage for your account.

```python
usage = client.get_usage()
print(usage["tier"], usage["renders"])
```

## RenderJob

Returned by `client.render()`. Represents an in-progress or completed render.

### `job.wait(poll_interval=2.0, timeout=1800.0)`

Blocks until the job finishes, polling every `poll_interval` seconds. Raises `RenderError` if the render fails, or `RenderTimeoutError` if `timeout` seconds pass first.

```python
job = client.render(code)
job.wait(poll_interval=1.0, timeout=300.0)
```

### `job.poll()`

Fetch the job's current status once, without blocking. Returns a `RenderResult`.

```python
result = job.poll()
print(result.status)  # "queued" | "running" | "done" | "error"
```

### `job.status`

The job's last-known status (`"queued"` until you `poll()` or `wait()`).

### `job.url`

The primary output URL, once the job is done (relative `/api/media/...` paths are resolved against the client's `base_url`).

### `job.output_urls`

All output URLs, for multi-scene jobs.

### `job.logs`

Render logs, once available.

### `job.download(path, scene_index=0)`

Download a finished render to a local file. Raises `RenderError` if the job isn't done yet — call `wait()` first.

```python
job.wait()
job.download("scene_0.mp4")           # first/only scene
job.download("scene_1.mp4", scene_index=1)  # second scene, for multi-scene jobs
```

## Error handling

All SDK errors subclass `ManimStudioError`.

```python
from manimstudio import (
    ManimStudio,
    ManimStudioError,
    AuthenticationError,
    QuotaExceededError,
    QualityNotAvailableError,
    RenderError,
    RenderTimeoutError,
    APIError,
)

client = ManimStudio(api_key="msk_xxx")

try:
    job = client.render(code, quality="high")
    job.wait()
    job.download("output.mp4")
except AuthenticationError:
    # API key is missing, invalid, or revoked.
    # Get/rotate your key at https://manimstudio.me/dashboard
    ...
except QuotaExceededError as e:
    # Render limit reached for your plan.
    print(f"Used {e.used}/{e.limit}, resets in {e.resets_in}s")
except QualityNotAvailableError:
    # Requested quality tier isn't available on your plan.
    ...
except RenderTimeoutError:
    # wait() gave up before the render finished — it may still complete;
    # poll the job again later.
    ...
except RenderError as e:
    # The render itself failed (bad scene code, runtime error, etc).
    print(e)
    print(e.logs)
except APIError as e:
    # Any other non-2xx response from the API.
    print(e.status_code, e)
except ManimStudioError:
    # Catch-all for any SDK error.
    ...
```

| Exception | Raised when |
|---|---|
| `AuthenticationError` | API key missing, invalid, or revoked (HTTP 401) |
| `QuotaExceededError` | Render limit reached for your plan (HTTP 429). Has `.limit`, `.used`, `.resets_in` |
| `QualityNotAvailableError` | Requested `quality` isn't available on your plan (HTTP 403) |
| `RenderTimeoutError` | `job.wait()` exceeded its `timeout` before the job finished |
| `RenderError` | The render job itself failed. Has `.logs` |
| `APIError` | Any other non-2xx API response. Has `.status_code` |

## License

MIT
