magic_hour 0.9.3__tar.gz → 0.9.5__tar.gz

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 (132) hide show
  1. {magic_hour-0.9.3 → magic_hour-0.9.5}/PKG-INFO +1 -1
  2. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/client.py +10 -12
  3. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/core/__init__.py +2 -3
  4. magic_hour-0.9.5/magic_hour/core/query.py +106 -0
  5. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/core/request.py +3 -5
  6. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/core/utils.py +1 -1
  7. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/environment.py +3 -1
  8. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_clothes_changer/client.py +0 -2
  9. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_headshot_generator/client.py +0 -2
  10. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_image_generator/client.py +1 -3
  11. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_image_upscaler/client.py +0 -2
  12. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_photo_editor/client.py +0 -2
  13. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_qr_code_generator/client.py +0 -2
  14. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/animation/client.py +0 -2
  15. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/client.py +8 -42
  16. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/face_swap/client.py +0 -2
  17. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/face_swap_photo/client.py +0 -2
  18. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/files/client.py +0 -2
  19. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/files/upload_urls/client.py +0 -2
  20. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/image_background_remover/client.py +0 -2
  21. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/image_projects/README.md +1 -1
  22. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/image_projects/client.py +2 -6
  23. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/image_to_video/client.py +0 -2
  24. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/lip_sync/client.py +0 -2
  25. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/text_to_video/client.py +1 -3
  26. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/video_projects/README.md +1 -1
  27. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/video_projects/client.py +2 -6
  28. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/video_to_video/client.py +0 -2
  29. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/get_v1_image_projects_id_response.py +2 -2
  30. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/get_v1_video_projects_id_response.py +2 -2
  31. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/post_v1_ai_clothes_changer_response.py +1 -1
  32. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/post_v1_ai_headshot_generator_response.py +1 -1
  33. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/post_v1_ai_image_generator_response.py +1 -1
  34. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/post_v1_ai_image_upscaler_response.py +1 -1
  35. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/post_v1_ai_photo_editor_response.py +1 -1
  36. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/post_v1_ai_qr_code_generator_response.py +1 -1
  37. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/post_v1_animation_response.py +1 -1
  38. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/post_v1_face_swap_photo_response.py +1 -1
  39. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/post_v1_face_swap_response.py +1 -1
  40. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/post_v1_image_background_remover_response.py +1 -1
  41. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/post_v1_image_to_video_response.py +1 -1
  42. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/post_v1_lip_sync_response.py +1 -1
  43. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/post_v1_text_to_video_response.py +1 -1
  44. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/post_v1_video_to_video_response.py +1 -1
  45. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_ai_clothes_changer_body_assets.py +3 -3
  46. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_ai_headshot_generator_body_assets.py +2 -2
  47. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_ai_image_upscaler_body_assets.py +2 -2
  48. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_ai_photo_editor_body_assets.py +2 -2
  49. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_animation_body_assets.py +3 -3
  50. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_face_swap_body_assets.py +3 -3
  51. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_face_swap_photo_body_assets.py +3 -3
  52. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_image_background_remover_body_assets.py +2 -2
  53. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_image_to_video_body_assets.py +2 -2
  54. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_lip_sync_body_assets.py +3 -3
  55. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_video_to_video_body_assets.py +2 -2
  56. {magic_hour-0.9.3 → magic_hour-0.9.5}/pyproject.toml +1 -1
  57. {magic_hour-0.9.3 → magic_hour-0.9.5}/LICENSE +0 -0
  58. {magic_hour-0.9.3 → magic_hour-0.9.5}/README.md +0 -0
  59. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/__init__.py +1 -1
  60. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/core/api_error.py +0 -0
  61. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/core/auth.py +0 -0
  62. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/core/base_client.py +0 -0
  63. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/core/binary_response.py +0 -0
  64. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/core/response.py +0 -0
  65. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/core/type_utils.py +0 -0
  66. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/__init__.py +0 -0
  67. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_clothes_changer/README.md +0 -0
  68. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_clothes_changer/__init__.py +0 -0
  69. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_headshot_generator/README.md +0 -0
  70. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_headshot_generator/__init__.py +0 -0
  71. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_image_generator/README.md +0 -0
  72. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_image_generator/__init__.py +0 -0
  73. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_image_upscaler/README.md +0 -0
  74. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_image_upscaler/__init__.py +0 -0
  75. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_photo_editor/README.md +0 -0
  76. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_photo_editor/__init__.py +0 -0
  77. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_qr_code_generator/README.md +0 -0
  78. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/ai_qr_code_generator/__init__.py +0 -0
  79. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/animation/README.md +0 -0
  80. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/animation/__init__.py +0 -0
  81. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/face_swap/README.md +0 -0
  82. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/face_swap/__init__.py +0 -0
  83. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/face_swap_photo/README.md +0 -0
  84. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/face_swap_photo/__init__.py +0 -0
  85. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/files/__init__.py +0 -0
  86. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/files/upload_urls/README.md +0 -0
  87. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/files/upload_urls/__init__.py +0 -0
  88. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/image_background_remover/README.md +0 -0
  89. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/image_background_remover/__init__.py +0 -0
  90. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/image_projects/__init__.py +0 -0
  91. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/image_to_video/README.md +0 -0
  92. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/image_to_video/__init__.py +0 -0
  93. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/lip_sync/README.md +0 -0
  94. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/lip_sync/__init__.py +0 -0
  95. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/text_to_video/README.md +0 -0
  96. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/text_to_video/__init__.py +0 -0
  97. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/video_projects/__init__.py +0 -0
  98. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/video_to_video/README.md +0 -0
  99. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/resources/v1/video_to_video/__init__.py +0 -0
  100. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/__init__.py +4 -4
  101. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/get_v1_image_projects_id_response_downloads_item.py +0 -0
  102. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/get_v1_image_projects_id_response_error.py +0 -0
  103. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/get_v1_video_projects_id_response_download.py +0 -0
  104. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/get_v1_video_projects_id_response_downloads_item.py +0 -0
  105. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/get_v1_video_projects_id_response_error.py +0 -0
  106. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/post_v1_files_upload_urls_response.py +1 -1
  107. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/models/post_v1_files_upload_urls_response_items_item.py +0 -0
  108. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/__init__.py +51 -51
  109. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_ai_clothes_changer_body.py +1 -1
  110. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_ai_headshot_generator_body.py +1 -1
  111. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_ai_image_generator_body.py +1 -1
  112. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_ai_image_generator_body_style.py +1 -1
  113. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_ai_image_upscaler_body.py +1 -1
  114. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_ai_image_upscaler_body_style.py +1 -1
  115. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_ai_photo_editor_body.py +1 -1
  116. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_ai_photo_editor_body_style.py +1 -1
  117. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_ai_qr_code_generator_body.py +1 -1
  118. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_ai_qr_code_generator_body_style.py +1 -1
  119. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_animation_body.py +1 -1
  120. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_animation_body_style.py +1 -1
  121. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_face_swap_body.py +1 -1
  122. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_face_swap_photo_body.py +1 -1
  123. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_files_upload_urls_body.py +1 -1
  124. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_files_upload_urls_body_items_item.py +1 -1
  125. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_image_background_remover_body.py +1 -1
  126. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_image_to_video_body.py +1 -1
  127. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_image_to_video_body_style.py +1 -1
  128. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_lip_sync_body.py +1 -1
  129. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_text_to_video_body.py +1 -1
  130. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_text_to_video_body_style.py +1 -1
  131. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_video_to_video_body.py +1 -1
  132. {magic_hour-0.9.3 → magic_hour-0.9.5}/magic_hour/types/params/post_v1_video_to_video_body_style.py +1 -1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: magic_hour
3
- Version: 0.9.3
3
+ Version: 0.9.5
4
4
  Summary: Python SDK for Magic Hour API
5
5
  Requires-Python: >=3.8,<4.0
6
6
  Classifier: Programming Language :: Python :: 3
@@ -1,9 +1,9 @@
1
1
  import httpx
2
2
  import typing
3
3
 
4
- from magic_hour.resources.v1 import AsyncV1Client, V1Client
5
- from magic_hour.environment import Environment
6
4
  from magic_hour.core import AsyncBaseClient, AuthBearer, SyncBaseClient
5
+ from magic_hour.environment import Environment
6
+ from magic_hour.resources.v1 import AsyncV1Client, V1Client
7
7
 
8
8
 
9
9
  class Client:
@@ -16,14 +16,14 @@ class Client:
16
16
  environment: Environment = Environment.ENVIRONMENT,
17
17
  token: typing.Optional[str] = None,
18
18
  ):
19
+ """Initialize root client"""
19
20
  self._base_client = SyncBaseClient(
20
21
  base_url=_get_base_url(base_url=base_url, environment=environment),
21
- httpx_client=(
22
- httpx.Client(timeout=timeout) if httpx_client is None else httpx_client
23
- ),
22
+ httpx_client=httpx.Client(timeout=timeout)
23
+ if httpx_client is None
24
+ else httpx_client,
24
25
  )
25
26
  self._base_client.register_auth("bearerAuth", AuthBearer(val=token))
26
-
27
27
  self.v1 = V1Client(base_client=self._base_client)
28
28
 
29
29
 
@@ -37,16 +37,14 @@ class AsyncClient:
37
37
  environment: Environment = Environment.ENVIRONMENT,
38
38
  token: typing.Optional[str] = None,
39
39
  ):
40
+ """Initialize root client"""
40
41
  self._base_client = AsyncBaseClient(
41
42
  base_url=_get_base_url(base_url=base_url, environment=environment),
42
- httpx_client=(
43
- httpx.AsyncClient(timeout=timeout)
44
- if httpx_client is None
45
- else httpx_client
46
- ),
43
+ httpx_client=httpx.AsyncClient(timeout=timeout)
44
+ if httpx_client is None
45
+ else httpx_client,
47
46
  )
48
47
  self._base_client.register_auth("bearerAuth", AuthBearer(val=token))
49
-
50
48
  self.v1 = AsyncV1Client(base_client=self._base_client)
51
49
 
52
50
 
@@ -13,14 +13,13 @@ from .auth import (
13
13
  )
14
14
  from .base_client import AsyncBaseClient, BaseClient, SyncBaseClient
15
15
  from .binary_response import BinaryResponse
16
+ from .query import encode_query_param, QueryParams
16
17
  from .request import (
17
- encode_param,
18
18
  filter_not_given,
19
19
  to_content,
20
20
  to_encodable,
21
21
  RequestOptions,
22
22
  default_request_options,
23
- QueryParams,
24
23
  )
25
24
  from .response import from_encodable, AsyncStreamResponse, StreamResponse
26
25
 
@@ -45,7 +44,7 @@ __all__ = [
45
44
  "to_encodable",
46
45
  "filter_not_given",
47
46
  "to_content",
48
- "encode_param",
47
+ "encode_query_param",
49
48
  "from_encodable",
50
49
  "AsyncStreamResponse",
51
50
  "StreamResponse",
@@ -0,0 +1,106 @@
1
+ from typing import Any, Dict, Union
2
+ from typing_extensions import Literal, Sequence
3
+ from urllib.parse import quote_plus, quote
4
+
5
+ import httpx
6
+
7
+
8
+ # Type alias for query parameters that can handle both primitive data and sequences
9
+ QueryParams = Dict[
10
+ str, Union[httpx._types.PrimitiveData, Sequence[httpx._types.PrimitiveData]]
11
+ ]
12
+
13
+
14
+ def encode_query_param(
15
+ params: QueryParams,
16
+ name: str,
17
+ value: Any,
18
+ style: Literal["form", "spaceDelimited", "pipeDelimited", "deepObject"] = "form",
19
+ explode: bool = True,
20
+ ):
21
+ if style == "form":
22
+ _encode_form(params, name, value, explode)
23
+ elif style == "spaceDelimited":
24
+ _encode_spaced_delimited(params, name, value, explode)
25
+ elif style == "pipeDelimited":
26
+ _encode_pipe_delimited(params, name, value, explode)
27
+ elif style == "deepObject":
28
+ _encode_deep_object(params, name, value, explode)
29
+ else:
30
+ raise NotImplementedError(f"query param style '{style}' not implemented")
31
+
32
+
33
+ def _encode_form(params: QueryParams, name: str, value: Any, explode: bool):
34
+ """
35
+ Encodes query params in the `form` style as defined by OpenAPI with both explode and non-explode
36
+ variants.
37
+ """
38
+ if isinstance(value, list) and not explode:
39
+ # non-explode form lists should be encoded like /users?id=3,4,5
40
+ params[name] = quote_plus(",".join(map(str, value)))
41
+ elif isinstance(value, dict):
42
+ if explode:
43
+ # explode form objects should be encoded like /users?key0=val0&key1=val1
44
+ # the input param name will be omitted
45
+ for k, v in value.items():
46
+ params[k] = quote_plus(str(v))
47
+ else:
48
+ # non-explode form objects should be encoded like /users?id=key0,val0,key1,val1
49
+ encoded_chunks = []
50
+ for k, v in value.items():
51
+ encoded_chunks.extend([str(k), str(v)])
52
+ params[name] = quote_plus(",".join(encoded_chunks))
53
+ else:
54
+ params[name] = value
55
+
56
+
57
+ def _encode_spaced_delimited(params: QueryParams, name: str, value: Any, explode: bool):
58
+ """
59
+ Encodes query params in the `spaceDelimited` style as defined by OpenAPI with both explode and non-explode
60
+ variants.
61
+ """
62
+ if isinstance(value, list) and not explode:
63
+ # non-explode spaceDelimited lists should be encoded like /users?id=3%204%205
64
+ params[name] = quote(" ".join(map(str, value)))
65
+ else:
66
+ # according to the docs, spaceDelimited + explode=false only effects lists,
67
+ # all other encodings are marked as n/a or are the same as `form` style
68
+ # fall back on form style as it is the default for query params
69
+ _encode_form(params, name, value, explode)
70
+
71
+
72
+ def _encode_pipe_delimited(params: QueryParams, name: str, value: Any, explode: bool):
73
+ """
74
+ Encodes query params in the `pipeDelimited` style as defined by OpenAPI with both explode and non-explode
75
+ variants.
76
+ """
77
+ if isinstance(value, list) and not explode:
78
+ # non-explode pipeDelimited lists should be encoded like /users?id=3|4|5
79
+ params[name] = quote("|".join(map(str, value)))
80
+ else:
81
+ # according to the docs, pipeDelimited + explode=false only effects lists,
82
+ # all other encodings are marked as n/a or are the same as `form` style
83
+ # fall back on form style as it is the default for query params
84
+ _encode_form(params, name, value, explode)
85
+
86
+
87
+ def _encode_deep_object(params: QueryParams, name: str, value: Any, explode: bool):
88
+ """ """
89
+ if isinstance(value, dict):
90
+ _encode_deep_object_key(params, name, value)
91
+ else:
92
+ # according to the docs, deepObject style only applies to
93
+ # object encodes, encodings for primitives & arrays are listed as n/a,
94
+ # fall back on form style as it is the default for query params
95
+ _encode_form(params, name, value, explode)
96
+
97
+
98
+ def _encode_deep_object_key(params: QueryParams, key: str, value: Any):
99
+ if isinstance(value, dict):
100
+ for k, v in value.items():
101
+ _encode_deep_object_key(params, f"{key}[{k}]", v)
102
+ elif isinstance(value, list):
103
+ for i, v in enumerate(value):
104
+ _encode_deep_object_key(params, f"{key}[{i}]", v)
105
+ else:
106
+ params[key] = value
@@ -1,9 +1,12 @@
1
1
  from typing import Any, Dict, Type, Union, Sequence, List
2
2
  from urllib.parse import quote_plus
3
+
3
4
  import httpx
4
5
  from typing_extensions import TypedDict, Required, NotRequired
5
6
  from pydantic import TypeAdapter, BaseModel
7
+
6
8
  from .type_utils import NotGiven
9
+ from .query import QueryParams
7
10
 
8
11
  """
9
12
  Request configuration and utility functions for handling HTTP requests.
@@ -11,11 +14,6 @@ This module provides type definitions and helper functions for building
11
14
  and processing HTTP requests in a type-safe manner.
12
15
  """
13
16
 
14
- # Type alias for query parameters that can handle both primitive data and sequences
15
- QueryParams = Dict[
16
- str, Union[httpx._types.PrimitiveData, Sequence[httpx._types.PrimitiveData]]
17
- ]
18
-
19
17
 
20
18
  class RequestConfig(TypedDict):
21
19
  """
@@ -21,7 +21,7 @@ def get_response_type(headers: httpx.Headers) -> Literal["json", "text", "binary
21
21
  """Check response type based on content type"""
22
22
  content_type = headers.get("content-type")
23
23
 
24
- if re.search("^application/(.+\+)?json", content_type):
24
+ if re.search("^application/(.+[+])?json", content_type):
25
25
  return "json"
26
26
  elif re.search("^text/(.+)", content_type):
27
27
  return "text"
@@ -2,5 +2,7 @@ import enum
2
2
 
3
3
 
4
4
  class Environment(enum.Enum):
5
+ """Pre-defined base URLs for the API"""
6
+
5
7
  ENVIRONMENT = "https://api.magichour.ai"
6
- MOCK_SERVER = "https://api.sideko.dev/v1/mock/magichour/magic-hour/0.9.3"
8
+ MOCK_SERVER = "https://api.sideko.dev/v1/mock/magichour/magic-hour/0.9.5"
@@ -54,7 +54,6 @@ class AiClothesChangerClient:
54
54
  name="Clothes Changer image",
55
55
  )
56
56
  ```
57
-
58
57
  """
59
58
  _json = to_encodable(
60
59
  item={"name": name, "assets": assets},
@@ -113,7 +112,6 @@ class AsyncAiClothesChangerClient:
113
112
  name="Clothes Changer image",
114
113
  )
115
114
  ```
116
-
117
115
  """
118
116
  _json = to_encodable(
119
117
  item={"name": name, "assets": assets},
@@ -50,7 +50,6 @@ class AiHeadshotGeneratorClient:
50
50
  name="Ai Headshot image",
51
51
  )
52
52
  ```
53
-
54
53
  """
55
54
  _json = to_encodable(
56
55
  item={"name": name, "assets": assets},
@@ -105,7 +104,6 @@ class AsyncAiHeadshotGeneratorClient:
105
104
  name="Ai Headshot image",
106
105
  )
107
106
  ```
108
-
109
107
  """
110
108
  _json = to_encodable(
111
109
  item={"name": name, "assets": assets},
@@ -1,5 +1,5 @@
1
- import typing_extensions
2
1
  import typing
2
+ import typing_extensions
3
3
 
4
4
  from magic_hour.core import (
5
5
  AsyncBaseClient,
@@ -57,7 +57,6 @@ class AiImageGeneratorClient:
57
57
  name="Ai Image image",
58
58
  )
59
59
  ```
60
-
61
60
  """
62
61
  _json = to_encodable(
63
62
  item={
@@ -123,7 +122,6 @@ class AsyncAiImageGeneratorClient:
123
122
  name="Ai Image image",
124
123
  )
125
124
  ```
126
-
127
125
  """
128
126
  _json = to_encodable(
129
127
  item={
@@ -56,7 +56,6 @@ class AiImageUpscalerClient:
56
56
  name="Image Upscaler image",
57
57
  )
58
58
  ```
59
-
60
59
  """
61
60
  _json = to_encodable(
62
61
  item={
@@ -122,7 +121,6 @@ class AsyncAiImageUpscalerClient:
122
121
  name="Image Upscaler image",
123
122
  )
124
123
  ```
125
-
126
124
  """
127
125
  _json = to_encodable(
128
126
  item={
@@ -67,7 +67,6 @@ class AiPhotoEditorClient:
67
67
  name="Photo Editor image",
68
68
  )
69
69
  ```
70
-
71
70
  """
72
71
  _json = to_encodable(
73
72
  item={
@@ -145,7 +144,6 @@ class AsyncAiPhotoEditorClient:
145
144
  name="Photo Editor image",
146
145
  )
147
146
  ```
148
-
149
147
  """
150
148
  _json = to_encodable(
151
149
  item={
@@ -53,7 +53,6 @@ class AiQrCodeGeneratorClient:
53
53
  name="Qr Code image",
54
54
  )
55
55
  ```
56
-
57
56
  """
58
57
  _json = to_encodable(
59
58
  item={"name": name, "content": content, "style": style},
@@ -111,7 +110,6 @@ class AsyncAiQrCodeGeneratorClient:
111
110
  name="Qr Code image",
112
111
  )
113
112
  ```
114
-
115
113
  """
116
114
  _json = to_encodable(
117
115
  item={"name": name, "content": content, "style": style},
@@ -71,7 +71,6 @@ class AnimationClient:
71
71
  name="Animation video",
72
72
  )
73
73
  ```
74
-
75
74
  """
76
75
  _json = to_encodable(
77
76
  item={
@@ -155,7 +154,6 @@ class AsyncAnimationClient:
155
154
  name="Animation video",
156
155
  )
157
156
  ```
158
-
159
157
  """
160
158
  _json = to_encodable(
161
159
  item={
@@ -1,12 +1,4 @@
1
1
  from magic_hour.core import AsyncBaseClient, SyncBaseClient
2
- from magic_hour.resources.v1.image_projects import (
3
- AsyncImageProjectsClient,
4
- ImageProjectsClient,
5
- )
6
- from magic_hour.resources.v1.video_projects import (
7
- AsyncVideoProjectsClient,
8
- VideoProjectsClient,
9
- )
10
2
  from magic_hour.resources.v1.ai_clothes_changer import (
11
3
  AiClothesChangerClient,
12
4
  AsyncAiClothesChangerClient,
@@ -42,6 +34,10 @@ from magic_hour.resources.v1.image_background_remover import (
42
34
  AsyncImageBackgroundRemoverClient,
43
35
  ImageBackgroundRemoverClient,
44
36
  )
37
+ from magic_hour.resources.v1.image_projects import (
38
+ AsyncImageProjectsClient,
39
+ ImageProjectsClient,
40
+ )
45
41
  from magic_hour.resources.v1.image_to_video import (
46
42
  AsyncImageToVideoClient,
47
43
  ImageToVideoClient,
@@ -51,6 +47,10 @@ from magic_hour.resources.v1.text_to_video import (
51
47
  AsyncTextToVideoClient,
52
48
  TextToVideoClient,
53
49
  )
50
+ from magic_hour.resources.v1.video_projects import (
51
+ AsyncVideoProjectsClient,
52
+ VideoProjectsClient,
53
+ )
54
54
  from magic_hour.resources.v1.video_to_video import (
55
55
  AsyncVideoToVideoClient,
56
56
  VideoToVideoClient,
@@ -60,94 +60,60 @@ from magic_hour.resources.v1.video_to_video import (
60
60
  class V1Client:
61
61
  def __init__(self, *, base_client: SyncBaseClient):
62
62
  self._base_client = base_client
63
-
64
63
  self.image_projects = ImageProjectsClient(base_client=self._base_client)
65
-
66
64
  self.video_projects = VideoProjectsClient(base_client=self._base_client)
67
-
68
65
  self.ai_clothes_changer = AiClothesChangerClient(base_client=self._base_client)
69
-
70
66
  self.ai_headshot_generator = AiHeadshotGeneratorClient(
71
67
  base_client=self._base_client
72
68
  )
73
-
74
69
  self.ai_image_generator = AiImageGeneratorClient(base_client=self._base_client)
75
-
76
70
  self.ai_image_upscaler = AiImageUpscalerClient(base_client=self._base_client)
77
-
78
71
  self.ai_photo_editor = AiPhotoEditorClient(base_client=self._base_client)
79
-
80
72
  self.ai_qr_code_generator = AiQrCodeGeneratorClient(
81
73
  base_client=self._base_client
82
74
  )
83
-
84
75
  self.animation = AnimationClient(base_client=self._base_client)
85
-
86
76
  self.face_swap = FaceSwapClient(base_client=self._base_client)
87
-
88
77
  self.face_swap_photo = FaceSwapPhotoClient(base_client=self._base_client)
89
-
90
78
  self.files = FilesClient(base_client=self._base_client)
91
-
92
79
  self.image_background_remover = ImageBackgroundRemoverClient(
93
80
  base_client=self._base_client
94
81
  )
95
-
96
82
  self.image_to_video = ImageToVideoClient(base_client=self._base_client)
97
-
98
83
  self.lip_sync = LipSyncClient(base_client=self._base_client)
99
-
100
84
  self.text_to_video = TextToVideoClient(base_client=self._base_client)
101
-
102
85
  self.video_to_video = VideoToVideoClient(base_client=self._base_client)
103
86
 
104
87
 
105
88
  class AsyncV1Client:
106
89
  def __init__(self, *, base_client: AsyncBaseClient):
107
90
  self._base_client = base_client
108
-
109
91
  self.image_projects = AsyncImageProjectsClient(base_client=self._base_client)
110
-
111
92
  self.video_projects = AsyncVideoProjectsClient(base_client=self._base_client)
112
-
113
93
  self.ai_clothes_changer = AsyncAiClothesChangerClient(
114
94
  base_client=self._base_client
115
95
  )
116
-
117
96
  self.ai_headshot_generator = AsyncAiHeadshotGeneratorClient(
118
97
  base_client=self._base_client
119
98
  )
120
-
121
99
  self.ai_image_generator = AsyncAiImageGeneratorClient(
122
100
  base_client=self._base_client
123
101
  )
124
-
125
102
  self.ai_image_upscaler = AsyncAiImageUpscalerClient(
126
103
  base_client=self._base_client
127
104
  )
128
-
129
105
  self.ai_photo_editor = AsyncAiPhotoEditorClient(base_client=self._base_client)
130
-
131
106
  self.ai_qr_code_generator = AsyncAiQrCodeGeneratorClient(
132
107
  base_client=self._base_client
133
108
  )
134
-
135
109
  self.animation = AsyncAnimationClient(base_client=self._base_client)
136
-
137
110
  self.face_swap = AsyncFaceSwapClient(base_client=self._base_client)
138
-
139
111
  self.face_swap_photo = AsyncFaceSwapPhotoClient(base_client=self._base_client)
140
-
141
112
  self.files = AsyncFilesClient(base_client=self._base_client)
142
-
143
113
  self.image_background_remover = AsyncImageBackgroundRemoverClient(
144
114
  base_client=self._base_client
145
115
  )
146
-
147
116
  self.image_to_video = AsyncImageToVideoClient(base_client=self._base_client)
148
-
149
117
  self.lip_sync = AsyncLipSyncClient(base_client=self._base_client)
150
-
151
118
  self.text_to_video = AsyncTextToVideoClient(base_client=self._base_client)
152
-
153
119
  self.video_to_video = AsyncVideoToVideoClient(base_client=self._base_client)
@@ -65,7 +65,6 @@ class FaceSwapClient:
65
65
  name="Face Swap video",
66
66
  )
67
67
  ```
68
-
69
68
  """
70
69
  _json = to_encodable(
71
70
  item={
@@ -142,7 +141,6 @@ class AsyncFaceSwapClient:
142
141
  name="Face Swap video",
143
142
  )
144
143
  ```
145
-
146
144
  """
147
145
  _json = to_encodable(
148
146
  item={
@@ -53,7 +53,6 @@ class FaceSwapPhotoClient:
53
53
  name="Face Swap image",
54
54
  )
55
55
  ```
56
-
57
56
  """
58
57
  _json = to_encodable(
59
58
  item={"name": name, "assets": assets},
@@ -111,7 +110,6 @@ class AsyncFaceSwapPhotoClient:
111
110
  name="Face Swap image",
112
111
  )
113
112
  ```
114
-
115
113
  """
116
114
  _json = to_encodable(
117
115
  item={"name": name, "assets": assets},
@@ -8,12 +8,10 @@ from magic_hour.resources.v1.files.upload_urls import (
8
8
  class FilesClient:
9
9
  def __init__(self, *, base_client: SyncBaseClient):
10
10
  self._base_client = base_client
11
-
12
11
  self.upload_urls = UploadUrlsClient(base_client=self._base_client)
13
12
 
14
13
 
15
14
  class AsyncFilesClient:
16
15
  def __init__(self, *, base_client: AsyncBaseClient):
17
16
  self._base_client = base_client
18
-
19
17
  self.upload_urls = AsyncUploadUrlsClient(base_client=self._base_client)
@@ -61,7 +61,6 @@ class UploadUrlsClient:
61
61
  ```py
62
62
  client.v1.files.upload_urls.create(items=[{"extension": "mp4", "type_field": "video"}, {"extension": "mp3", "type_field": "audio"}])
63
63
  ```
64
-
65
64
  """
66
65
  _json = to_encodable(
67
66
  item={"items": items}, dump_with=params._SerializerPostV1FilesUploadUrlsBody
@@ -127,7 +126,6 @@ class AsyncUploadUrlsClient:
127
126
  ```py
128
127
  await client.v1.files.upload_urls.create(items=[{"extension": "mp4", "type_field": "video"}, {"extension": "mp3", "type_field": "audio"}])
129
128
  ```
130
-
131
129
  """
132
130
  _json = to_encodable(
133
131
  item={"items": items}, dump_with=params._SerializerPostV1FilesUploadUrlsBody
@@ -50,7 +50,6 @@ class ImageBackgroundRemoverClient:
50
50
  name="Background Remover image",
51
51
  )
52
52
  ```
53
-
54
53
  """
55
54
  _json = to_encodable(
56
55
  item={"name": name, "assets": assets},
@@ -105,7 +104,6 @@ class AsyncImageBackgroundRemoverClient:
105
104
  name="Background Remover image",
106
105
  )
107
106
  ```
108
-
109
107
  """
110
108
  _json = to_encodable(
111
109
  item={"name": name, "assets": assets},
@@ -29,7 +29,7 @@ res = await client.v1.image_projects.delete(id="cm6pvghix03bvyz0zwash6noj")
29
29
  ### get <a name="get"></a>
30
30
  Get image details
31
31
 
32
- Get the details of a image project. The `download` field will be `null` unless the image was successfully rendered.
32
+ Get the details of a image project. The `downloads` field will be empty unless the image was successfully rendered.
33
33
 
34
34
  The image can be one of the following status
35
35
  - `draft` - not currently used
@@ -38,7 +38,6 @@ class ImageProjectsClient:
38
38
  ```py
39
39
  client.v1.image_projects.delete(id="cm6pvghix03bvyz0zwash6noj")
40
40
  ```
41
-
42
41
  """
43
42
  self._base_client.request(
44
43
  method="DELETE",
@@ -54,7 +53,7 @@ class ImageProjectsClient:
54
53
  """
55
54
  Get image details
56
55
 
57
- Get the details of a image project. The `download` field will be `null` unless the image was successfully rendered.
56
+ Get the details of a image project. The `downloads` field will be empty unless the image was successfully rendered.
58
57
 
59
58
  The image can be one of the following status
60
59
  - `draft` - not currently used
@@ -82,7 +81,6 @@ class ImageProjectsClient:
82
81
  ```py
83
82
  client.v1.image_projects.get(id="cm6pvghix03bvyz0zwash6noj")
84
83
  ```
85
-
86
84
  """
87
85
  return self._base_client.request(
88
86
  method="GET",
@@ -122,7 +120,6 @@ class AsyncImageProjectsClient:
122
120
  ```py
123
121
  await client.v1.image_projects.delete(id="cm6pvghix03bvyz0zwash6noj")
124
122
  ```
125
-
126
123
  """
127
124
  await self._base_client.request(
128
125
  method="DELETE",
@@ -138,7 +135,7 @@ class AsyncImageProjectsClient:
138
135
  """
139
136
  Get image details
140
137
 
141
- Get the details of a image project. The `download` field will be `null` unless the image was successfully rendered.
138
+ Get the details of a image project. The `downloads` field will be empty unless the image was successfully rendered.
142
139
 
143
140
  The image can be one of the following status
144
141
  - `draft` - not currently used
@@ -166,7 +163,6 @@ class AsyncImageProjectsClient:
166
163
  ```py
167
164
  await client.v1.image_projects.get(id="cm6pvghix03bvyz0zwash6noj")
168
165
  ```
169
-
170
166
  """
171
167
  return await self._base_client.request(
172
168
  method="GET",
@@ -65,7 +65,6 @@ class ImageToVideoClient:
65
65
  name="Image To Video video",
66
66
  )
67
67
  ```
68
-
69
68
  """
70
69
  _json = to_encodable(
71
70
  item={
@@ -142,7 +141,6 @@ class AsyncImageToVideoClient:
142
141
  name="Image To Video video",
143
142
  )
144
143
  ```
145
-
146
144
  """
147
145
  _json = to_encodable(
148
146
  item={
@@ -73,7 +73,6 @@ class LipSyncClient:
73
73
  name="Lip Sync video",
74
74
  )
75
75
  ```
76
-
77
76
  """
78
77
  _json = to_encodable(
79
78
  item={
@@ -159,7 +158,6 @@ class AsyncLipSyncClient:
159
158
  name="Lip Sync video",
160
159
  )
161
160
  ```
162
-
163
161
  """
164
162
  _json = to_encodable(
165
163
  item={