Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion .sdk.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"id": "b2d6ef4f-f33d-419f-85e6-a388096b49c1",
"id": "6b61dfff-c839-4c96-affc-fe4f3f004aff",
"tracked_paths": [
{
"editable": true,
Expand Down Expand Up @@ -273,6 +273,18 @@
"editable": true,
"path": "magic_hour/resources/v1/files/upload_urls/client.py"
},
{
"editable": true,
"path": "magic_hour/resources/v1/head_swap/README.md"
},
{
"editable": true,
"path": "magic_hour/resources/v1/head_swap/__init__.py"
},
{
"editable": true,
"path": "magic_hour/resources/v1/head_swap/client.py"
},
{
"editable": true,
"path": "magic_hour/resources/v1/image_background_remover/README.md"
Expand Down Expand Up @@ -469,6 +481,10 @@
"editable": false,
"path": "magic_hour/types/models/v1_files_upload_urls_create_response_items_item.py"
},
{
"editable": false,
"path": "magic_hour/types/models/v1_head_swap_create_response.py"
},
{
"editable": false,
"path": "magic_hour/types/models/v1_image_background_remover_create_response.py"
Expand Down Expand Up @@ -717,6 +733,14 @@
"editable": false,
"path": "magic_hour/types/params/v1_files_upload_urls_create_body_items_item.py"
},
{
"editable": false,
"path": "magic_hour/types/params/v1_head_swap_create_body.py"
},
{
"editable": false,
"path": "magic_hour/types/params/v1_head_swap_create_body_assets.py"
},
{
"editable": false,
"path": "magic_hour/types/params/v1_image_background_remover_create_body.py"
Expand Down Expand Up @@ -857,6 +881,10 @@
"editable": false,
"path": "tests/test_v1_files_upload_urls_client.py"
},
{
"editable": false,
"path": "tests/test_v1_head_swap_client.py"
},
{
"editable": false,
"path": "tests/test_v1_image_background_remover_client.py"
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,11 @@ download_urls = result.downloads

- [create](magic_hour/resources/v1/files/upload_urls/README.md#create) - Generate asset upload urls

### [v1.head_swap](magic_hour/resources/v1/head_swap/README.md)

- [create](magic_hour/resources/v1/head_swap/README.md#create) - Head Swap
- [generate](magic_hour/resources/v1/head_swap/README.md#generate) - Head Swap Generate Workflow

### [v1.image_background_remover](magic_hour/resources/v1/image_background_remover/README.md)

- [create](magic_hour/resources/v1/image_background_remover/README.md#create) - Image Background Remover
Expand Down
1 change: 1 addition & 0 deletions magic_hour/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
- [face_swap_photo](resources/v1/face_swap_photo/README.md) - face_swap_photo
- [upload_urls](resources/v1/files/upload_urls/README.md) - upload_urls
- [files](resources/v1/files/README.md) - files
- [head_swap](resources/v1/head_swap/README.md) - head_swap
- [image_background_remover](resources/v1/image_background_remover/README.md) - image_background_remover
- [image_projects](resources/v1/image_projects/README.md) - image_projects
- [image_to_video](resources/v1/image_to_video/README.md) - image_to_video
Expand Down
2 changes: 1 addition & 1 deletion magic_hour/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class Environment(enum.Enum):
"""Pre-defined base URLs for the API"""

ENVIRONMENT = "https://api.magichour.ai"
MOCK_SERVER = "https://api.sideko.dev/v1/mock/magichour/magic-hour/0.57.1"
MOCK_SERVER = "https://api.sideko.dev/v1/mock/magichour/magic-hour/0.58.0"


def _get_base_url(
Expand Down
1 change: 1 addition & 0 deletions magic_hour/resources/v1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- [face_swap](face_swap/README.md) - face_swap
- [face_swap_photo](face_swap_photo/README.md) - face_swap_photo
- [files](files/README.md) - files
- [head_swap](head_swap/README.md) - head_swap
- [image_background_remover](image_background_remover/README.md) - image_background_remover
- [image_projects](image_projects/README.md) - image_projects
- [image_to_video](image_to_video/README.md) - image_to_video
Expand Down
3 changes: 3 additions & 0 deletions magic_hour/resources/v1/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
FaceSwapPhotoClient,
)
from magic_hour.resources.v1.files import AsyncFilesClient, FilesClient
from magic_hour.resources.v1.head_swap import AsyncHeadSwapClient, HeadSwapClient
from magic_hour.resources.v1.image_background_remover import (
AsyncImageBackgroundRemoverClient,
ImageBackgroundRemoverClient,
Expand Down Expand Up @@ -136,6 +137,7 @@ def __init__(self, *, base_client: SyncBaseClient):
self.audio_projects = AudioProjectsClient(base_client=self._base_client)
self.ai_voice_generator = AiVoiceGeneratorClient(base_client=self._base_client)
self.ai_voice_cloner = AiVoiceClonerClient(base_client=self._base_client)
self.head_swap = HeadSwapClient(base_client=self._base_client)


class AsyncV1Client:
Expand Down Expand Up @@ -187,3 +189,4 @@ def __init__(self, *, base_client: AsyncBaseClient):
base_client=self._base_client
)
self.ai_voice_cloner = AsyncAiVoiceClonerClient(base_client=self._base_client)
self.head_swap = AsyncHeadSwapClient(base_client=self._base_client)
129 changes: 129 additions & 0 deletions magic_hour/resources/v1/head_swap/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# v1.head_swap

## Module Functions

<!-- CUSTOM DOCS START -->

### Head Swap Generate Workflow <a name="generate"></a>

The workflow performs the following action

1. upload local assets to Magic Hour storage. So you can pass in a local path instead of having to upload files yourself
2. trigger a generation
3. poll for a completion status. This is configurable
4. if success, download the output to local directory

> [!TIP]
> This is the recommended way to use the SDK unless you have specific needs where it is necessary to split up the actions.

#### Parameters

In addition to the parameters listed in the `.create` section below, `.generate` introduces 3 new parameters:

- `wait_for_completion` (bool, default True): Whether to wait for the project to complete.
- `download_outputs` (bool, default True): Whether to download the generated files
- `download_directory` (str, optional): Directory to save downloaded files (defaults to current directory)

#### Synchronous Client

```python
from magic_hour import Client
from os import getenv

client = Client(token=getenv("API_TOKEN"))
res = client.v1.head_swap.generate(
assets={
"body_file_path": "/path/to/body.png",
"head_file_path": "/path/to/head.png",
},
max_resolution=1024,
name="My Head Swap image",
wait_for_completion=True,
download_outputs=True,
download_directory=".",
)
```

#### Asynchronous Client

```python
from magic_hour import AsyncClient
from os import getenv

client = AsyncClient(token=getenv("API_TOKEN"))
res = await client.v1.head_swap.generate(
assets={
"body_file_path": "/path/to/body.png",
"head_file_path": "/path/to/head.png",
},
max_resolution=1024,
name="My Head Swap image",
wait_for_completion=True,
download_outputs=True,
download_directory=".",
)
```

<!-- CUSTOM DOCS END -->

### Head Swap <a name="create"></a>

Swap a head onto a body image. Each image costs 10 credits. Output resolution depends on your subscription; you may set `max_resolution` lower than your plan maximum if desired.

**API Endpoint**: `POST /v1/head-swap`

#### Parameters

| Parameter | Required | Description | Example |
| ------------------- | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `assets` | ✓ | Provide the body and head images for head swap | `{"body_file_path": "api-assets/id/1234.png", "head_file_path": "api-assets/id/5678.png"}` |
| `└─ body_file_path` | ✓ | Image that receives the swapped head. This value is either - a direct URL to the video file - `file_path` field from the response of the [upload urls API](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls). See the [file upload guide](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls#input-file) for details. | `"api-assets/id/1234.png"` |
| `└─ head_file_path` | ✓ | Image of the head to place on the body. This value is either - a direct URL to the video file - `file_path` field from the response of the [upload urls API](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls). See the [file upload guide](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls#input-file) for details. | `"api-assets/id/5678.png"` |
| `max_resolution` | ✗ | Constrains the larger dimension (height or width) of the output. Omit to use the maximum allowed for your plan (capped at 2048px). Values above your plan maximum are clamped down to your plan's maximum. | `1024` |
| `name` | ✗ | Give your image a custom name for easy identification. | `"My Head Swap image"` |

#### Synchronous Client

```python
from magic_hour import Client
from os import getenv

client = Client(token=getenv("API_TOKEN"))
res = client.v1.head_swap.create(
assets={
"body_file_path": "api-assets/id/1234.png",
"head_file_path": "api-assets/id/5678.png",
},
max_resolution=1024,
name="My Head Swap image",
)
```

#### Asynchronous Client

```python
from magic_hour import AsyncClient
from os import getenv

client = AsyncClient(token=getenv("API_TOKEN"))
res = await client.v1.head_swap.create(
assets={
"body_file_path": "api-assets/id/1234.png",
"head_file_path": "api-assets/id/5678.png",
},
max_resolution=1024,
name="My Head Swap image",
)
```

#### Response

##### Type

[V1HeadSwapCreateResponse](/magic_hour/types/models/v1_head_swap_create_response.py)

##### Example

```python
{"credits_charged": 10, "frame_cost": 10, "id": "cuid-example"}
```
4 changes: 4 additions & 0 deletions magic_hour/resources/v1/head_swap/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .client import AsyncHeadSwapClient, HeadSwapClient


__all__ = ["AsyncHeadSwapClient", "HeadSwapClient"]
Loading
Loading