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
2 changes: 1 addition & 1 deletion .sdk.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"id": "91afb71a-62ad-4b8e-8919-8ae69f24d91a"
"id": "3964f5ce-f6bd-4480-bed6-618618e6acb8"
}
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.28.0"
MOCK_SERVER = "https://api.sideko.dev/v1/mock/magichour/magic-hour/0.29.0"


def _get_base_url(
Expand Down
16 changes: 15 additions & 1 deletion magic_hour/resources/v1/face_swap_photo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Create a face swap photo. Each photo costs 5 credits. The height/width of the ou

| Parameter | Required | Description | Example |
|-----------|:--------:|-------------|--------|
| `assets` | ✓ | Provide the assets for face swap photo | `{"source_file_path": "api-assets/id/1234.png", "target_file_path": "api-assets/id/1234.png"}` |
| `assets` | ✓ | Provide the assets for face swap photo | `{"face_mappings": [{"new_face": "api-assets/id/1234.png", "original_face": "api-assets/id/0-0.png"}], "face_swap_mode": "all-faces", "source_file_path": "api-assets/id/1234.png", "target_file_path": "api-assets/id/1234.png"}` |
| `name` | ✗ | The name of image | `"Face Swap image"` |

#### Synchronous Client
Expand All @@ -21,6 +21,13 @@ from os import getenv
client = Client(token=getenv("API_TOKEN"))
res = client.v1.face_swap_photo.create(
assets={
"face_mappings": [
{
"new_face": "api-assets/id/1234.png",
"original_face": "api-assets/id/0-0.png",
}
],
"face_swap_mode": "all-faces",
"source_file_path": "api-assets/id/1234.png",
"target_file_path": "api-assets/id/1234.png",
},
Expand All @@ -38,6 +45,13 @@ from os import getenv
client = AsyncClient(token=getenv("API_TOKEN"))
res = await client.v1.face_swap_photo.create(
assets={
"face_mappings": [
{
"new_face": "api-assets/id/1234.png",
"original_face": "api-assets/id/0-0.png",
}
],
"face_swap_mode": "all-faces",
"source_file_path": "api-assets/id/1234.png",
"target_file_path": "api-assets/id/1234.png",
},
Expand Down
14 changes: 14 additions & 0 deletions magic_hour/resources/v1/face_swap_photo/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ def create(
```py
client.v1.face_swap_photo.create(
assets={
"face_mappings": [
{
"new_face": "api-assets/id/1234.png",
"original_face": "api-assets/id/0-0.png",
}
],
"face_swap_mode": "all-faces",
"source_file_path": "api-assets/id/1234.png",
"target_file_path": "api-assets/id/1234.png",
},
Expand Down Expand Up @@ -104,6 +111,13 @@ async def create(
```py
await client.v1.face_swap_photo.create(
assets={
"face_mappings": [
{
"new_face": "api-assets/id/1234.png",
"original_face": "api-assets/id/0-0.png",
}
],
"face_swap_mode": "all-faces",
"source_file_path": "api-assets/id/1234.png",
"target_file_path": "api-assets/id/1234.png",
},
Expand Down
6 changes: 6 additions & 0 deletions magic_hour/types/params/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@
V1FaceSwapPhotoCreateBodyAssets,
_SerializerV1FaceSwapPhotoCreateBodyAssets,
)
from .v1_face_swap_photo_create_body_assets_face_mappings_item import (
V1FaceSwapPhotoCreateBodyAssetsFaceMappingsItem,
_SerializerV1FaceSwapPhotoCreateBodyAssetsFaceMappingsItem,
)
from .v1_files_upload_urls_create_body import (
V1FilesUploadUrlsCreateBody,
_SerializerV1FilesUploadUrlsCreateBody,
Expand Down Expand Up @@ -257,6 +261,7 @@
"V1FaceSwapCreateBodyAssets",
"V1FaceSwapPhotoCreateBody",
"V1FaceSwapPhotoCreateBodyAssets",
"V1FaceSwapPhotoCreateBodyAssetsFaceMappingsItem",
"V1FilesUploadUrlsCreateBody",
"V1FilesUploadUrlsCreateBodyItemsItem",
"V1ImageBackgroundRemoverCreateBody",
Expand Down Expand Up @@ -312,6 +317,7 @@
"_SerializerV1FaceSwapCreateBodyAssets",
"_SerializerV1FaceSwapPhotoCreateBody",
"_SerializerV1FaceSwapPhotoCreateBodyAssets",
"_SerializerV1FaceSwapPhotoCreateBodyAssetsFaceMappingsItem",
"_SerializerV1FilesUploadUrlsCreateBody",
"_SerializerV1FilesUploadUrlsCreateBodyItemsItem",
"_SerializerV1ImageBackgroundRemoverCreateBody",
Expand Down
38 changes: 34 additions & 4 deletions magic_hour/types/params/v1_face_swap_photo_create_body_assets.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,39 @@
import pydantic
import typing
import typing_extensions

from .v1_face_swap_photo_create_body_assets_face_mappings_item import (
V1FaceSwapPhotoCreateBodyAssetsFaceMappingsItem,
_SerializerV1FaceSwapPhotoCreateBodyAssetsFaceMappingsItem,
)


class V1FaceSwapPhotoCreateBodyAssets(typing_extensions.TypedDict):
"""
Provide the assets for face swap photo
"""

source_file_path: typing_extensions.Required[str]
face_mappings: typing_extensions.NotRequired[
typing.List[V1FaceSwapPhotoCreateBodyAssetsFaceMappingsItem]
]
"""
This is the array of face mappings used for multiple face swap. The value is required if `face_swap_mode` is `individual-faces`.
"""

face_swap_mode: typing_extensions.NotRequired[
typing_extensions.Literal["all-faces", "individual-faces"]
]
"""
The mode of face swap.
* `all-faces` - Swap all faces in the target image. `source_file_path` is required.
* `individual-faces` - Swap individual faces in the target image. `source_faces` is required.
"""

source_file_path: typing_extensions.NotRequired[str]
"""
This is the image from which the face is extracted. This value can be either the `file_path` field from the response of the [upload urls API](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls), or the url of the file.
This is the image from which the face is extracted. The value is required if `face_swap_mode` is `all-faces`.

This value can be either the `file_path` field from the response of the [upload urls API](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls), or the url of the file.
"""

target_file_path: typing_extensions.Required[str]
Expand All @@ -28,8 +52,14 @@ class _SerializerV1FaceSwapPhotoCreateBodyAssets(pydantic.BaseModel):
populate_by_name=True,
)

source_file_path: str = pydantic.Field(
alias="source_file_path",
face_mappings: typing.Optional[
typing.List[_SerializerV1FaceSwapPhotoCreateBodyAssetsFaceMappingsItem]
] = pydantic.Field(alias="face_mappings", default=None)
face_swap_mode: typing.Optional[
typing_extensions.Literal["all-faces", "individual-faces"]
] = pydantic.Field(alias="face_swap_mode", default=None)
source_file_path: typing.Optional[str] = pydantic.Field(
alias="source_file_path", default=None
)
target_file_path: str = pydantic.Field(
alias="target_file_path",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import pydantic
import typing_extensions


class V1FaceSwapPhotoCreateBodyAssetsFaceMappingsItem(typing_extensions.TypedDict):
"""
V1FaceSwapPhotoCreateBodyAssetsFaceMappingsItem
"""

new_face: typing_extensions.Required[str]
"""
The face image that will be used to replace the face in the `original_face`. This value can be either the `file_path` field from the response of the [upload urls API](https://docs.magichour.ai/api-reference/files/generate-asset-upload-urls), or the url of the file.
"""

original_face: typing_extensions.Required[str]
"""
The face detected from the image in `target_file_path`. The file name is in the format of `<face_frame>-<face_index>.png`. This value is corresponds to the response in the face detection API [COMING SOON].

* The face_frame is the frame number of the face in the target image. For images, the frame number is always 0.
* The face_index is the index of the face in the target image, starting from 0 going left to right.
"""


class _SerializerV1FaceSwapPhotoCreateBodyAssetsFaceMappingsItem(pydantic.BaseModel):
"""
Serializer for V1FaceSwapPhotoCreateBodyAssetsFaceMappingsItem handling case conversions
and file omissions as dictated by the API
"""

model_config = pydantic.ConfigDict(
populate_by_name=True,
)

new_face: str = pydantic.Field(
alias="new_face",
)
original_face: str = pydantic.Field(
alias="original_face",
)
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "magic_hour"
version = "0.28.0"
version = "0.29.0"
description = "Python SDK for Magic Hour API"
readme = "README.md"
authors = []
Expand Down
14 changes: 14 additions & 0 deletions tests/test_v1_face_swap_photo_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ def test_create_200_success_all_params():
client = Client(token="API_TOKEN", environment=Environment.MOCK_SERVER)
response = client.v1.face_swap_photo.create(
assets={
"face_mappings": [
{
"new_face": "api-assets/id/1234.png",
"original_face": "api-assets/id/0-0.png",
}
],
"face_swap_mode": "all-faces",
"source_file_path": "api-assets/id/1234.png",
"target_file_path": "api-assets/id/1234.png",
},
Expand Down Expand Up @@ -66,6 +73,13 @@ async def test_await_create_200_success_all_params():
client = AsyncClient(token="API_TOKEN", environment=Environment.MOCK_SERVER)
response = await client.v1.face_swap_photo.create(
assets={
"face_mappings": [
{
"new_face": "api-assets/id/1234.png",
"original_face": "api-assets/id/0-0.png",
}
],
"face_swap_mode": "all-faces",
"source_file_path": "api-assets/id/1234.png",
"target_file_path": "api-assets/id/1234.png",
},
Expand Down