magic_hour 0.35.0__py3-none-any.whl → 0.36.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of magic_hour might be problematic. Click here for more details.

Files changed (94) hide show
  1. magic_hour/README.md +35 -0
  2. magic_hour/core/base_client.py +6 -5
  3. magic_hour/core/query.py +12 -6
  4. magic_hour/core/request.py +3 -3
  5. magic_hour/core/response.py +18 -14
  6. magic_hour/core/utils.py +3 -3
  7. magic_hour/environment.py +1 -1
  8. magic_hour/helpers/__init__.py +3 -0
  9. magic_hour/helpers/download.py +75 -0
  10. magic_hour/resources/v1/README.md +33 -0
  11. magic_hour/resources/v1/ai_clothes_changer/README.md +73 -0
  12. magic_hour/resources/v1/ai_clothes_changer/client.py +146 -0
  13. magic_hour/resources/v1/ai_face_editor/README.md +110 -0
  14. magic_hour/resources/v1/ai_face_editor/client.py +168 -0
  15. magic_hour/resources/v1/ai_gif_generator/README.md +59 -0
  16. magic_hour/resources/v1/ai_gif_generator/client.py +119 -0
  17. magic_hour/resources/v1/ai_headshot_generator/README.md +60 -0
  18. magic_hour/resources/v1/ai_headshot_generator/client.py +140 -0
  19. magic_hour/resources/v1/ai_image_editor/README.md +64 -0
  20. magic_hour/resources/v1/ai_image_editor/client.py +136 -0
  21. magic_hour/resources/v1/ai_image_generator/README.md +66 -0
  22. magic_hour/resources/v1/ai_image_generator/client.py +139 -0
  23. magic_hour/resources/v1/ai_image_upscaler/README.md +67 -0
  24. magic_hour/resources/v1/ai_image_upscaler/client.py +150 -0
  25. magic_hour/resources/v1/ai_meme_generator/README.md +71 -0
  26. magic_hour/resources/v1/ai_meme_generator/client.py +127 -0
  27. magic_hour/resources/v1/ai_photo_editor/README.md +98 -7
  28. magic_hour/resources/v1/ai_photo_editor/client.py +174 -0
  29. magic_hour/resources/v1/ai_qr_code_generator/README.md +63 -0
  30. magic_hour/resources/v1/ai_qr_code_generator/client.py +123 -0
  31. magic_hour/resources/v1/ai_talking_photo/README.md +74 -0
  32. magic_hour/resources/v1/ai_talking_photo/client.py +170 -0
  33. magic_hour/resources/v1/animation/README.md +100 -0
  34. magic_hour/resources/v1/animation/client.py +218 -0
  35. magic_hour/resources/v1/auto_subtitle_generator/README.md +69 -0
  36. magic_hour/resources/v1/auto_subtitle_generator/client.py +178 -0
  37. magic_hour/resources/v1/face_detection/README.md +59 -0
  38. magic_hour/resources/v1/face_detection/__init__.py +10 -2
  39. magic_hour/resources/v1/face_detection/client.py +179 -0
  40. magic_hour/resources/v1/face_swap/README.md +105 -8
  41. magic_hour/resources/v1/face_swap/client.py +242 -0
  42. magic_hour/resources/v1/face_swap_photo/README.md +84 -0
  43. magic_hour/resources/v1/face_swap_photo/client.py +172 -0
  44. magic_hour/resources/v1/files/README.md +40 -0
  45. magic_hour/resources/v1/files/client.py +350 -0
  46. magic_hour/resources/v1/files/client_test.py +414 -0
  47. magic_hour/resources/v1/files/upload_urls/README.md +8 -0
  48. magic_hour/resources/v1/image_background_remover/README.md +68 -0
  49. magic_hour/resources/v1/image_background_remover/client.py +130 -0
  50. magic_hour/resources/v1/image_projects/README.md +52 -0
  51. magic_hour/resources/v1/image_projects/__init__.py +10 -2
  52. magic_hour/resources/v1/image_projects/client.py +138 -0
  53. magic_hour/resources/v1/image_projects/client_test.py +527 -0
  54. magic_hour/resources/v1/image_to_video/README.md +77 -9
  55. magic_hour/resources/v1/image_to_video/client.py +186 -0
  56. magic_hour/resources/v1/lip_sync/README.md +87 -9
  57. magic_hour/resources/v1/lip_sync/client.py +210 -0
  58. magic_hour/resources/v1/photo_colorizer/README.md +59 -0
  59. magic_hour/resources/v1/photo_colorizer/client.py +130 -0
  60. magic_hour/resources/v1/text_to_video/README.md +68 -0
  61. magic_hour/resources/v1/text_to_video/client.py +151 -0
  62. magic_hour/resources/v1/video_projects/README.md +52 -0
  63. magic_hour/resources/v1/video_projects/__init__.py +10 -2
  64. magic_hour/resources/v1/video_projects/client.py +137 -0
  65. magic_hour/resources/v1/video_projects/client_test.py +527 -0
  66. magic_hour/resources/v1/video_to_video/README.md +98 -10
  67. magic_hour/resources/v1/video_to_video/client.py +222 -0
  68. magic_hour/types/params/__init__.py +58 -0
  69. magic_hour/types/params/v1_ai_clothes_changer_generate_body_assets.py +33 -0
  70. magic_hour/types/params/v1_ai_face_editor_generate_body_assets.py +17 -0
  71. magic_hour/types/params/v1_ai_headshot_generator_generate_body_assets.py +17 -0
  72. magic_hour/types/params/v1_ai_image_editor_generate_body_assets.py +17 -0
  73. magic_hour/types/params/v1_ai_image_upscaler_generate_body_assets.py +17 -0
  74. magic_hour/types/params/v1_ai_photo_editor_generate_body_assets.py +17 -0
  75. magic_hour/types/params/v1_ai_talking_photo_generate_body_assets.py +26 -0
  76. magic_hour/types/params/v1_animation_generate_body_assets.py +39 -0
  77. magic_hour/types/params/v1_auto_subtitle_generator_generate_body_assets.py +17 -0
  78. magic_hour/types/params/v1_face_detection_generate_body_assets.py +17 -0
  79. magic_hour/types/params/v1_face_swap_create_body.py +12 -0
  80. magic_hour/types/params/v1_face_swap_create_body_style.py +33 -0
  81. magic_hour/types/params/v1_face_swap_generate_body_assets.py +56 -0
  82. magic_hour/types/params/v1_face_swap_generate_body_assets_face_mappings_item.py +25 -0
  83. magic_hour/types/params/v1_face_swap_photo_generate_body_assets.py +47 -0
  84. magic_hour/types/params/v1_face_swap_photo_generate_body_assets_face_mappings_item.py +25 -0
  85. magic_hour/types/params/v1_image_background_remover_generate_body_assets.py +27 -0
  86. magic_hour/types/params/v1_image_to_video_generate_body_assets.py +17 -0
  87. magic_hour/types/params/v1_lip_sync_generate_body_assets.py +36 -0
  88. magic_hour/types/params/v1_photo_colorizer_generate_body_assets.py +17 -0
  89. magic_hour/types/params/v1_video_to_video_generate_body_assets.py +27 -0
  90. magic_hour-0.36.1.dist-info/METADATA +306 -0
  91. {magic_hour-0.35.0.dist-info → magic_hour-0.36.1.dist-info}/RECORD +93 -65
  92. magic_hour-0.35.0.dist-info/METADATA +0 -166
  93. {magic_hour-0.35.0.dist-info → magic_hour-0.36.1.dist-info}/LICENSE +0 -0
  94. {magic_hour-0.35.0.dist-info → magic_hour-0.36.1.dist-info}/WHEEL +0 -0
@@ -1,3 +1,54 @@
1
+ # v1_image_projects
2
+
3
+ ## Module Functions
4
+
5
+ <!-- CUSTOM DOCS START -->
6
+
7
+ ### Check results <a name="check-result"></a>
8
+
9
+ Poll the details API to check on the status of the rendering. Optionally can also download the output
10
+
11
+ #### Parameters
12
+
13
+ | Parameter | Required | Description | Example |
14
+ | --------------------- | :------: | ---------------------------------------------------------------------------------------------------- | ---------------- |
15
+ | `id` | ✓ | Unique ID of the image project. This value is returned by all of the POST APIs that create an image. | `"cuid-example"` |
16
+ | `wait_for_completion` | ✗ | Whether to wait for the project to complete. | `True` |
17
+ | `download_outputs` | ✗ | Whether to download the generated files | `True` |
18
+ | `download_directory` | ✗ | Directory to save downloaded files (defaults to current directory) | `"./outputs"` |
19
+
20
+ #### Synchronous Client
21
+
22
+ ```python
23
+ from magic_hour import Client
24
+ from os import getenv
25
+
26
+ client = Client(token=getenv("API_TOKEN"))
27
+ res = client.v1.image_projects.check_result(
28
+ id="cuid-example",
29
+ wait_for_completion=True,
30
+ download_outputs=True,
31
+ download_directory="outputs",
32
+ )
33
+
34
+ ```
35
+
36
+ #### Asynchronous Client
37
+
38
+ ```python
39
+ from magic_hour import AsyncClient
40
+ from os import getenv
41
+
42
+ client = AsyncClient(token=getenv("API_TOKEN"))
43
+ res = await client.v1.image_projects.check_result(
44
+ id="cuid-example",
45
+ wait_for_completion=True,
46
+ download_outputs=True,
47
+ download_directory="outputs",
48
+ )
49
+ ```
50
+
51
+ <!-- CUSTOM DOCS END -->
1
52
 
2
53
  ### Delete image <a name="delete"></a>
3
54
 
@@ -83,3 +134,4 @@ res = await client.v1.image_projects.get(id="cuid-example")
83
134
 
84
135
  ##### Example
85
136
  `{"created_at": "1970-01-01T00:00:00", "credits_charged": 5, "downloads": [{"expires_at": "2024-10-19T05:16:19.027Z", "url": "https://videos.magichour.ai/id/output.png"}], "enabled": True, "error": {"code": "no_source_face", "message": "Please use an image with a detectable face"}, "id": "cuid-example", "image_count": 1, "name": "Example Name", "status": "complete", "total_frame_cost": 5, "type_": "AI_IMAGE"}`
137
+
@@ -1,4 +1,12 @@
1
- from .client import AsyncImageProjectsClient, ImageProjectsClient
1
+ from .client import (
2
+ AsyncImageProjectsClient,
3
+ ImageProjectsClient,
4
+ V1ImageProjectsGetResponseWithDownloads,
5
+ )
2
6
 
3
7
 
4
- __all__ = ["AsyncImageProjectsClient", "ImageProjectsClient"]
8
+ __all__ = [
9
+ "AsyncImageProjectsClient",
10
+ "ImageProjectsClient",
11
+ "V1ImageProjectsGetResponseWithDownloads",
12
+ ]
@@ -1,3 +1,8 @@
1
+ import asyncio
2
+ import logging
3
+ import os
4
+ import pydantic
5
+ import time
1
6
  import typing
2
7
 
3
8
  from magic_hour.core import (
@@ -6,13 +11,87 @@ from magic_hour.core import (
6
11
  SyncBaseClient,
7
12
  default_request_options,
8
13
  )
14
+ from magic_hour.helpers.download import download_files_async, download_files_sync
9
15
  from magic_hour.types import models
10
16
 
11
17
 
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class V1ImageProjectsGetResponseWithDownloads(models.V1ImageProjectsGetResponse):
22
+ downloaded_paths: typing.Optional[typing.List[str]] = pydantic.Field(
23
+ default=None, alias="downloaded_paths"
24
+ )
25
+ """
26
+ The paths to the downloaded files.
27
+
28
+ This field is only populated if `download_outputs` is True and the image project is complete.
29
+ """
30
+
31
+
12
32
  class ImageProjectsClient:
13
33
  def __init__(self, *, base_client: SyncBaseClient):
14
34
  self._base_client = base_client
15
35
 
36
+ def check_result(
37
+ self,
38
+ id: str,
39
+ wait_for_completion: bool,
40
+ download_outputs: bool,
41
+ download_directory: typing.Optional[str] = None,
42
+ ) -> V1ImageProjectsGetResponseWithDownloads:
43
+ """
44
+ Check the result of an image project with optional waiting and downloading.
45
+
46
+ This method retrieves the status of an image project and optionally waits for completion
47
+ and downloads the output files.
48
+
49
+ Args:
50
+ id: Unique ID of the image project
51
+ wait_for_completion: Whether to wait for the image project to complete
52
+ download_outputs: Whether to download the outputs
53
+ download_directory: The directory to download the outputs to. If not provided,
54
+ the outputs will be downloaded to the current working directory
55
+
56
+ Returns:
57
+ V1ImageProjectsGetResponseWithDownloads: The image project response with optional
58
+ downloaded file paths included
59
+ """
60
+ api_response = self.get(id=id)
61
+ if not wait_for_completion:
62
+ response = V1ImageProjectsGetResponseWithDownloads(
63
+ **api_response.model_dump()
64
+ )
65
+ return response
66
+
67
+ poll_interval = float(os.getenv("MAGIC_HOUR_POLL_INTERVAL", "0.5"))
68
+
69
+ status = api_response.status
70
+
71
+ while status not in ["complete", "error", "canceled"]:
72
+ api_response = self.get(id=id)
73
+ status = api_response.status
74
+ time.sleep(poll_interval)
75
+
76
+ if api_response.status != "complete":
77
+ log = logger.error if api_response.status == "error" else logger.info
78
+ log(
79
+ f"Image project {id} has status {api_response.status}: {api_response.error}"
80
+ )
81
+ return V1ImageProjectsGetResponseWithDownloads(**api_response.model_dump())
82
+
83
+ if not download_outputs:
84
+ return V1ImageProjectsGetResponseWithDownloads(**api_response.model_dump())
85
+
86
+ downloaded_paths = download_files_sync(
87
+ downloads=api_response.downloads,
88
+ download_directory=download_directory,
89
+ )
90
+
91
+ return V1ImageProjectsGetResponseWithDownloads(
92
+ **api_response.model_dump(), downloaded_paths=downloaded_paths
93
+ )
94
+
16
95
  def delete(
17
96
  self, *, id: str, request_options: typing.Optional[RequestOptions] = None
18
97
  ) -> None:
@@ -95,6 +174,65 @@ class AsyncImageProjectsClient:
95
174
  def __init__(self, *, base_client: AsyncBaseClient):
96
175
  self._base_client = base_client
97
176
 
177
+ async def check_result(
178
+ self,
179
+ id: str,
180
+ wait_for_completion: bool,
181
+ download_outputs: bool,
182
+ download_directory: typing.Optional[str] = None,
183
+ ) -> V1ImageProjectsGetResponseWithDownloads:
184
+ """
185
+ Check the result of an image project with optional waiting and downloading.
186
+
187
+ This method retrieves the status of an image project and optionally waits for completion
188
+ and downloads the output files.
189
+
190
+ Args:
191
+ id: Unique ID of the image project
192
+ wait_for_completion: Whether to wait for the image project to complete
193
+ download_outputs: Whether to download the outputs
194
+ download_directory: The directory to download the outputs to. If not provided,
195
+ the outputs will be downloaded to the current working directory
196
+
197
+ Returns:
198
+ V1ImageProjectsGetResponseWithDownloads: The image project response with optional
199
+ downloaded file paths included
200
+ """
201
+ api_response = await self.get(id=id)
202
+ if not wait_for_completion:
203
+ response = V1ImageProjectsGetResponseWithDownloads(
204
+ **api_response.model_dump()
205
+ )
206
+ return response
207
+
208
+ poll_interval = float(os.getenv("MAGIC_HOUR_POLL_INTERVAL", "0.5"))
209
+
210
+ status = api_response.status
211
+
212
+ while status not in ["complete", "error", "canceled"]:
213
+ api_response = await self.get(id=id)
214
+ status = api_response.status
215
+ await asyncio.sleep(poll_interval)
216
+
217
+ if api_response.status != "complete":
218
+ log = logger.error if api_response.status == "error" else logger.info
219
+ log(
220
+ f"Image project {id} has status {api_response.status}: {api_response.error}"
221
+ )
222
+ return V1ImageProjectsGetResponseWithDownloads(**api_response.model_dump())
223
+
224
+ if not download_outputs:
225
+ return V1ImageProjectsGetResponseWithDownloads(**api_response.model_dump())
226
+
227
+ downloaded_paths = await download_files_async(
228
+ downloads=api_response.downloads,
229
+ download_directory=download_directory,
230
+ )
231
+
232
+ return V1ImageProjectsGetResponseWithDownloads(
233
+ **api_response.model_dump(), downloaded_paths=downloaded_paths
234
+ )
235
+
98
236
  async def delete(
99
237
  self, *, id: str, request_options: typing.Optional[RequestOptions] = None
100
238
  ) -> None: