Metadata-Version: 2.4
Name: pulze-renderflow
Version: 1.2.5
Summary: Official RenderFlow API client for Python
Project-URL: Homepage, https://pulze.io/products/renderflow
Author-email: Pulze <support@pulze.io>
License-Expression: MIT
License-File: LICENSE
Keywords: 3dsmax,arnold,blender,cinema4d,corona,fusion,houdini,maya,network-rendering,nuke,pulze,redshift,render-farm,renderflow,vray
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.10
Requires-Dist: httpx<1.0.0,>=0.23.0
Requires-Dist: pydantic<3.0.0,>=2.0.0
Description-Content-Type: text/markdown

# renderflow

Official Python client for the [RenderFlow](https://pulze.io/products/renderflow) API.

## Installation

```bash
pip install pulze-renderflow
```

## Quick Start

```python
from renderflow import RenderFlow

rf = RenderFlow(api_key="your_api_key")

jobs = rf.jobs.list()
print(f"Found {len(jobs)} jobs")
```

## Submitting a Job

```python
from renderflow import RenderFlow, IPublicJobCreate
from renderflow.models import ISoftwareValue, IEngineValue, SoftwareId, EngineId

rf = RenderFlow(api_key="your_api_key")

job = rf.jobs.create(IPublicJobCreate(
    name="Exterior Shot",
    file="C:/projects/scene.max",
    type="3dsmax.render",
    host=ISoftwareValue(id=SoftwareId._3dsmax, name="3ds Max", version="2026"),
    engine=IEngineValue(id=EngineId.vray, name="V-Ray", version="7.20.04"),
    frame="1-100",
    resolution="1920x1080",
    priority=75,
    status="pending"
))

rf.jobs.start(job.id)
```

## Managing Jobs

```python
# Get a specific job
job = rf.jobs.get("job_id")

# Update job settings
rf.jobs.update("job_id", IPublicJobUpdate(
    priority=100,
    limit=5,
    max_batch_size=10
))

# Control jobs
rf.jobs.stop("job_id")
rf.jobs.reset("job_id")
rf.jobs.archive("job_id")
rf.jobs.delete("job_id")
```

## Tasks

```python
# List tasks for a job (paginated)
tasks = rf.tasks.list("job_id", page=1, limit=50)

# Get a specific task
task = rf.tasks.get("task_id")

# Get task logs
logs = rf.tasks.logs("task_id", offset=0, limit=500)

# Get task thumbnail
thumbnail = rf.tasks.thumbnail("task_id")
```

## Nodes

```python
# List all nodes
nodes = rf.nodes.list()

# Get node details
node = rf.nodes.get("node_id")

# Update node status
rf.nodes.update_status("node_id", "suspended")

# Assign to a pool
rf.nodes.update_pool("node_id", "pool_id")

# Get node utilization
util = rf.nodes.utilization("node_id")

# Get benchmark rankings
benchmarks = rf.nodes.benchmarks("vray")
```

## Pools

```python
pools = rf.pools.list()
pool = rf.pools.get("pool_id")
```

## Users

```python
users = rf.users.list()
user = rf.users.get("user_id")
```

## Errors

```python
errors = rf.errors.list()
job_errors = rf.errors.by_job("job_id")
node_errors = rf.errors.by_node("node_id")
```

## Real-time Events

Subscribe to live updates using Server-Sent Events:

```python
# Listen to all job changes
listener = rf.jobs.on(lambda event: print(
    event.type,      # "insert" | "update" | "delete" | "replace"
    event.document,  # full job document
    event.updated,   # changed fields (on update)
    event.time       # event timestamp
))

# Listen to task events for a specific job
task_listener = rf.tasks.on("job_id", lambda event: print(
    f"Task {event.document['_id']}: {event.document['status']}"
))

# Listen to node events
node_listener = rf.nodes.on(lambda event: print(
    f"Node {event.document['name']}: {event.document['status']}"
))

# Error handling
listener = rf.jobs.on(
    lambda event: print(event),
    lambda error: print(f"Connection error: {error}")
)

# Stop listening
listener.close()

# Also works as a context manager
with rf.jobs.on(on_event) as listener:
    import time
    time.sleep(60)
```

## Service Info

```python
info = rf.info.get()
print(f"RenderFlow {info.version} ({info.node_type})")
```
