Metadata-Version: 2.1
Name: bithuman
Version: 3.0.2
Summary: Run a bitHuman avatar on this machine: `bithuman.open(avatar).render(audio)`.
Keywords: bithuman,avatar,essence,lipsync,pybind11
Author-Email: bitHuman <hello@bithuman.ai>
License: Commercial — see LICENSE file
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: MacOS
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Programming Language :: Python
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
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: C++
Classifier: Topic :: Multimedia
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Multimedia :: Video
Project-URL: Homepage, https://bithuman.ai
Project-URL: Documentation, https://docs.bithuman.ai
Project-URL: Source, https://github.com/bithuman-product/homebrew-bithuman
Requires-Python: <3.15,>=3.10
Requires-Dist: numpy>=1.26.0
Requires-Dist: loguru~=0.7
Requires-Dist: soundfile>=0.13
Requires-Dist: pydantic~=2.10
Requires-Dist: pydantic-settings~=2.8
Requires-Dist: av>=12.0
Requires-Dist: opencv-python-headless>=4.8
Provides-Extra: test
Requires-Dist: pytest>=7; extra == "test"
Requires-Dist: psutil>=5.9; extra == "test"
Provides-Extra: offline
Requires-Dist: torch>=2.1; extra == "offline"
Requires-Dist: onnx>=1.15; extra == "offline"
Requires-Dist: onnxruntime>=1.17; extra == "offline"
Provides-Extra: tessera
Requires-Dist: torch>=2.1; extra == "tessera"
Requires-Dist: onnx>=1.15; extra == "tessera"
Requires-Dist: onnxruntime>=1.17; extra == "tessera"
Provides-Extra: expression-2
Requires-Dist: ai-edge-litert>=2.1.5; extra == "expression-2"
Description-Content-Type: text/markdown

# bithuman

Run a bitHuman avatar on your own machine, in your own process.

```bash
pip install bithuman
```

```python
import bithuman

avatar = bithuman.open("A63GVG1577.imx")
for image in avatar.render("hello.wav"):
    show(image)
```

That is the whole thing: **open an avatar, then render audio through it.**

### both families, the same two lines

An **essence-2** avatar and an **expression-2** avatar are opened and rendered
by the code above, unchanged. Nothing you write says which one you have, and
you do not have to know.

expression-2 needs one extra package on the machine:

```bash
pip install "bithuman[expression-2]"
```

Open an expression-2 avatar without it and the refusal says so, and says that
line. Nothing else differs.

---

## The surface — eight names

| you write | it means |
|---|---|
| `bithuman.open(source)` | open the avatar file on this machine; returns an `Avatar` |
| `avatar.render(audio)` | yield the frames for that audio |
| `Avatar` | what `open` gives you |
| `AvatarError` | catch this for any refusal |
| `InvalidAvatar` | we cannot find it, or it is not a usable avatar |
| `NotSupported` | this avatar cannot run here |
| `NotAuthorised` | the key is missing, invalid, or out of credit |
| `Failed` | we could not do it — the message says which |

There is nothing else, and nothing to configure. This package runs the avatar
on this machine, so there is no choice left about where or how it runs.

### audio in

`audio` is 16 kHz mono, and it is either a buffer or a stream — the same call:

```python
avatar.render("hello.wav")                 # an audio file
avatar.render(samples)                     # int16 or float32 in [-1, 1]
avatar.render(raw_bytes)                   # 16 kHz mono, signed 16-bit
avatar.render(microphone())                # any iterable of the above
```

### frames out

Each frame is a `(height, width, 3)` uint8 array in **RGB** order, in order, at
the avatar's own frame rate — which is a property of the avatar, not something
to choose. (This line read "one per 40 ms of speech" until 2026-09-06, which
was true of every avatar the package could open at the time and is not true of
an expression-2 one.)

```python
import cv2
for image in avatar.render("hello.wav"):
    cv2.imshow("avatar", image[:, :, ::-1])   # OpenCV wants BGR
    cv2.waitKey(1)
```

### stopping early

Someone interrupting the avatar is "stop consuming and close the iterator":

```python
frames = avatar.render(speech)
for image in frames:
    if interrupted:
        frames.close()
        break
    show(image)
```

### releasing it

`with` frees everything at the end of the block; without it, the avatar is
freed when it is garbage collected.

```python
with bithuman.open("A63GVG1577.imx") as avatar:
    for image in avatar.render("hello.wav"):
        show(image)
```

---

## The four refusals

Each one leads to a different fix, and none of them asks you to know anything
about how we are built.

```python
try:
    avatar = bithuman.open(source)
    for image in avatar.render(audio):
        show(image)
except bithuman.InvalidAvatar:
    ...   # fix the path or the code, or fetch the avatar again
except bithuman.NotSupported:
    ...   # use the cloud package, or another device
except bithuman.NotAuthorised:
    ...   # fix the credential
except bithuman.Failed:
    ...   # retry, then report it
```

Every one of them is an `AvatarError`, so `except bithuman.AvatarError` catches
all four.

---

## The key

Rendering is metered, and the key belongs in the environment rather than in
your code:

```bash
export BITHUMAN_API_SECRET=...
```

Without one, `render` refuses with `NotAuthorised` before it hands you a
frame. Get a key at <https://www.bithuman.ai/#developer>.

---

## Where it runs

| | |
|---|---|
| Python | 3.10 – 3.14 |
| macOS | Apple silicon |
| Linux | x86-64 and arm64 |
| Windows, Intel Macs | not built — `pip install` refuses loudly rather than quietly giving you an old release |

`ffmpeg` must be on your PATH to read an audio file or to prepare an avatar
for its first run. Pass 16 kHz mono samples and it is not needed.

Two environment variables exist for hosts that need them, and neither is
required for a working result:

| | |
|---|---|
| `BITHUMAN_API_SECRET` | your key |
| `BITHUMAN_CACHE_DIR` | where a prepared avatar is kept (default `~/.cache/bithuman`) |

---

## This package never puts a command on your PATH

`pip install bithuman` installs a library and nothing else. The `bithuman`
command-line tool is a different artifact and is **not** installed with pip:

```bash
curl -fsSL https://raw.githubusercontent.com/bithuman-product/homebrew-bithuman/main/install.sh | sh
brew install bithuman-product/bithuman/bithuman-cli      # macOS, equivalently
```

That is an invariant, not an accident: a pip-installed command named
`bithuman` would overwrite the one Homebrew put at the same path, and every
check would still report success. `tests/test_no_console_script.py` fails if a
release ever grows one — on **every** push and pull request (the source side,
with three firing controls) and again inside each publish job, run directly
against the wheels being uploaded. A directory that is declared and holds no
`bithuman` wheel exits **2**: a publish that cannot be graded is refused, not
passed.

---

## Coming from 2.10.0?

3.0.0 is a clean break. Thirty-two names became eight, and fourteen error
classes became four.

| if you see | do this |
|---|---|
| `cannot import name 'AsyncBithuman'` (or `Bithuman`, `AudioChunk`, `VideoFrame`, `VideoControl`) | `bithuman.open(...)` and `avatar.render(audio)` replace all of them |
| `cannot import name 'Fixture'` (or `Runtime`, `EP_AUTO`, `ComposedFrame`) | same: they were the layer under `render`, and there is no layer to reach for now |
| `no module named 'bithuman.api'` (or `.models`, `.exceptions`, `.config`, `.bhci`) | the values they held are gone from the surface; the four refusals replace the error classes |
| a `DeprecationWarning` when you import the 2.x offline-render module | it still works until 4.0.0; the warning names the module and the class names to write instead (`bithuman.offline`, `OfflineRenderer`, `OfflineRenderError`) |
| `module 'bithuman' has no attribute '__version__'` | `importlib.metadata.version("bithuman")` |
| you install the 2.x extra for offline rendering | it still installs the same three packages until 4.0.0; the extra is now `bithuman[offline]` |
| your frames look blue | frames are RGB now, not BGR — `image[:, :, ::-1]` if you feed OpenCV |
| `except BithumanError` never fires | `except bithuman.AvatarError` |

Frames are still `(height, width, 3)` uint8 arrays, still 25 per second, still
in order.

2.10.0 is on PyPI forever and keeps resolving exactly as it does today. Pin
`bithuman<3` to stay on it.

---

## Licence

Proprietary — this package carries the runtime. See `LICENSE`.
