Metadata-Version: 2.4
Name: cutrix-video-translate-sdk
Version: 0.0.3
Summary: Python SDK for the Cutrix API.
Author: Cutrix
License-Expression: MIT
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: cos-python-sdk-v5<2.0,>=1.9
Requires-Dist: httpx<1.0,>=0.27
Requires-Dist: pydantic<3.0,>=2.7
Provides-Extra: dev
Requires-Dist: setuptools-scm[toml]<9,>=8; extra == "dev"
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Requires-Dist: ruff>=0.11; extra == "dev"
Requires-Dist: mypy>=1.10; extra == "dev"
Provides-Extra: publish
Requires-Dist: build<2.0,>=1.2; extra == "publish"
Requires-Dist: setuptools-scm[toml]<9,>=8; extra == "publish"
Requires-Dist: twine<7.0,>=5.1; extra == "publish"

# Cutrix Python SDK

The official Python SDK for the [Cutrix](https://www.cutrix.cc) AI video platform.
Translate, dub, and add subtitles to videos with a few lines of Python.

```python
from cutrix import Client

with Client(api_key="sk-...") as client:
    result = client.video.translate(
        file_path="./demo.mp4",
        target_lang="zh",
    )
    print(result.task_id)
```

## Requirements

- Python 3.9+
- A Cutrix API key — register at [www.cutrix.cc](https://www.cutrix.cc) to get one

## Installation

```bash
pip install cutrix-video-translate-sdk
```

## Authentication

Sign up at [www.cutrix.cc](https://www.cutrix.cc) to get your API key, then pass it
when constructing the client:

```python
from cutrix import Client

client = Client(api_key="sk-...")
```

Or read it from an environment variable to avoid hard-coding secrets:

```python
import os
from cutrix import Client

client = Client(api_key=os.environ["CUTRIX_API_KEY"])
```

## Translate a video

Submit a translation task from either a local file or a public URL (exactly one
must be provided):

```python
from cutrix import Client

with Client(api_key="sk-...") as client:
    result = client.video.translate(
        file_path="./demo.mp4",   # optional — local file path (exclusive with url)
        # url="https://example.com/demo.mp4",  # optional — public video URL
        target_lang="zh",          # required — target language (BCP-47)
        source_lang="auto",        # optional — default: "auto" (auto-detect)
        task_name="my task",       # optional — human-readable label
        add_subtitle=True,         # optional — burn subtitles into output, default True
        erase_original_subtitle=False,  # optional — erase hard-coded source subtitles
        remove_cutrix_logo=True,   # optional — maps to API field is_logo
        remove_background_audio=False,  # optional — maps to API field remove_background_audio
        # progress_callback=on_progress,  # optional — receives upload progress 0-100
    )

    print(result.task_id)                   # int
    print(result.required_minutes)          # int   — estimated quota cost
    print(result.video_duration_seconds)    # float — source video duration
```

Supported URL platforms include YouTube, TikTok, Bilibili, and X.

For URL-based tasks, the maximum supported source video duration is 15 minutes
(900 seconds). Longer videos are rejected by the service.

`translate()` returns immediately with a `task_id`. The actual translation runs
asynchronously — use `get_task()` to poll for completion.

## Supported languages

You can inspect supported language codes at runtime:

```python
from cutrix import SOURCE_LANGUAGES, TARGET_LANGUAGES

print(sorted(SOURCE_LANGUAGES))
print(sorted(TARGET_LANGUAGES))
```

Supported source languages (`source_lang`):

| Code | Language |
|---|---|
| `auto` | Auto-detect |
| `zh` | Chinese (Mandarin) |
| `en` | English |
| `yue` | Cantonese |
| `ar` | Arabic |
| `cs` | Czech |
| `da` | Danish |
| `de` | German |
| `el` | Greek |
| `es` | Spanish |
| `fa` | Persian |
| `fi` | Finnish |
| `fil` | Filipino |
| `fr` | French |
| `hi` | Hindi |
| `hu` | Hungarian |
| `id` | Indonesian |
| `it` | Italian |
| `ja` | Japanese |
| `ko` | Korean |
| `mk` | Macedonian |
| `ms` | Malay |
| `nl` | Dutch |
| `pl` | Polish |
| `pt` | Portuguese |
| `ro` | Romanian |
| `ru` | Russian |
| `sv` | Swedish |
| `th` | Thai |
| `tr` | Turkish |
| `vi` | Vietnamese |

Supported target languages (`target_lang`):

| Code | Language |
|---|---|
| `zh` | Chinese (Mandarin) |
| `en` | English |
| `ar` | Arabic |
| `da` | Danish |
| `de` | German |
| `el` | Greek |
| `es` | Spanish |
| `fi` | Finnish |
| `fr` | French |
| `he` | Hebrew |
| `hi` | Hindi |
| `id` | Indonesian |
| `it` | Italian |
| `ja` | Japanese |
| `ko` | Korean |
| `ms` | Malay |
| `nl` | Dutch |
| `no` | Norwegian |
| `pl` | Polish |
| `pt` | Portuguese |
| `ru` | Russian |
| `sv` | Swedish |
| `sw` | Swahili |
| `th` | Thai |
| `tr` | Turkish |
| `vi` | Vietnamese |

## Poll task status

Translation tasks run asynchronously. After submitting, poll `get_task()` until
the task reaches a terminal state:

| `status` | Meaning |
|---|---|
| `pending` | Task is queued, waiting to be picked up |
| `started` | Processing is in progress |
| `succeed` | Translation finished successfully — `output_video_path` is available |
| `failed` | Task failed — check `failed_code` for the reason |

```python
import time
from cutrix import Client

SUCCESS = {"succeed"}
FAILURE = {"failed"}

with Client(api_key="sk-...") as client:
    # Submit
    result = client.video.translate(
        file_path="./demo.mp4",
        target_lang="zh",
    )

    # Poll until done
    while True:
        task = client.video.get_task(task_id=result.task_id)
        print(f"status: {task.status}")

        if task.status in SUCCESS:
            print("output:", task.output_video_path)
            break
        if task.status in FAILURE:
            print("failed, code:", task.failed_code)
            print("failed, message:", task.failed_message)
            break

        time.sleep(5)
```

`get_task()` response fields:

| Field | Type | Description |
|---|---|---|
| `id` / `task_id` | `int` | Task identifier |
| `status` | `str \| None` | Current status (see table above) |
| `output_video_path` | `str` | Download URL of the translated video (populated on success) |
| `input_video_path` | `str` | URL of the original uploaded video |
| `source_lang` | `str \| None` | Detected or specified source language |
| `target_lang` | `str \| None` | Target language |
| `input_video_duration` | `float \| None` | Source video length in seconds |
| `name` | `str \| None` | Task label |
| `failed_code` | `int \| str \| None` | Error code when `status` is `failed` |
| `failed_message` | `str \| None` | SDK-provided human-readable message for known `failed_code` values |
| `created_at` | `datetime \| None` | Task creation time (UTC) |
| `estimate_finish_time` | `datetime \| None` | Estimated completion time (UTC) |
| `finished_at` | `datetime \| None` | Actual completion time (UTC) |

> **Note:** `get_task()` has a built-in soft rate limit (1 call/second per client
> instance) to prevent accidental hot-loop polling.

Known `failed_code` values:

| Code | Message |
|---|---|
| `2001` | Automatic language detection failed. Please select the source language manually and try again. |
| `2002` | No audio was detected. Please make sure the video contains an audio track. |
| `2003` | No video was detected. Please make sure the file contains video content. |
| `2004` | Unknown error. Please try again later or contact support. |
| `2005` | The source and target languages are the same, so translation is not needed. |

You can also access the raw mapping at runtime:

```python
from cutrix import FAILED_CODE_MESSAGES

print(FAILED_CODE_MESSAGES["2002"])
```

## Compatibility alias

`client.video.translate_file(...)` is still available as a thin alias of
`client.video.translate(...)`. The SDK handles the full upload flow for you:
init -> upload to cloud storage -> complete -> create task.

```python
from cutrix import Client

def on_progress(percent: int) -> None:
    print(f"\rupload: {percent}%", end="", flush=True)

with Client(api_key="sk-...") as client:
    result = client.video.translate_file(
        file_path="./demo.mp4",
        target_lang="zh",
        source_lang="auto",
        task_name="local upload demo",
        progress_callback=on_progress,   # optional — receives 0–100
    )

    print(f"\ntask_id: {result.task_id}")
```

## Error handling

All SDK errors inherit from `SDKError`. HTTP errors inherit from `APIError`.

```python
from cutrix import Client, AuthenticationError, RateLimitError
from cutrix.exceptions import APIError, SDKError

with Client(api_key="sk-...") as client:
    try:
        result = client.video.translate(
            file_path="./demo.mp4",
            target_lang="zh",
        )
    except AuthenticationError:
        print("Invalid API key")
    except RateLimitError:
        print("Rate limit hit — slow down")
    except APIError as e:
        print(f"API error {e.status_code}: {e.message}")
    except SDKError as e:
        print(f"SDK error: {e}")
```

| Exception | When |
|---|---|
| `AuthenticationError` | HTTP 401 — invalid or missing API key |
| `RateLimitError` | HTTP 429 — too many requests |
| `APIError` | Any other HTTP error or non-zero `code` in API response |
| `SDKError` | Local errors (e.g. file not found, missing required dependency) |

`APIError` attributes: `message` (str), `status_code` (int | None), `raw_response` (any).

## Advanced options

```python
from cutrix import Client

client = Client(
    api_key="sk-...",
    base_url="https://www.cutrix.cc/v1",  # default
    timeout=60.0,                          # request timeout in seconds, default 30.0
    headers={"X-Custom-Header": "value"}, # extra headers merged into every request
)
```

You can also inject a pre-configured `httpx.Client` for full control over
connection pooling, proxies, or retry policies:

```python
import httpx
from cutrix import Client

transport = httpx.HTTPTransport(retries=3)
with Client(api_key="sk-...", http_client=httpx.Client(transport=transport)) as client:
    ...
```
