magic_hour 0.36.2__py3-none-any.whl → 0.37.0__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.
- magic_hour/__init__.py +1 -1
- magic_hour/client.py +1 -1
- magic_hour/environment.py +1 -1
- magic_hour/resources/v1/ai_clothes_changer/client.py +8 -8
- magic_hour/resources/v1/ai_face_editor/client.py +8 -8
- magic_hour/resources/v1/ai_gif_generator/client.py +7 -7
- magic_hour/resources/v1/ai_headshot_generator/client.py +8 -8
- magic_hour/resources/v1/ai_image_editor/client.py +8 -8
- magic_hour/resources/v1/ai_image_generator/client.py +7 -7
- magic_hour/resources/v1/ai_image_upscaler/client.py +8 -8
- magic_hour/resources/v1/ai_meme_generator/client.py +7 -7
- magic_hour/resources/v1/ai_photo_editor/client.py +8 -8
- magic_hour/resources/v1/ai_qr_code_generator/client.py +7 -7
- magic_hour/resources/v1/ai_talking_photo/client.py +8 -8
- magic_hour/resources/v1/animation/client.py +8 -8
- magic_hour/resources/v1/auto_subtitle_generator/client.py +8 -8
- magic_hour/resources/v1/client.py +1 -1
- magic_hour/resources/v1/face_detection/client.py +5 -5
- magic_hour/resources/v1/face_swap/client.py +8 -8
- magic_hour/resources/v1/face_swap_photo/client.py +8 -8
- magic_hour/resources/v1/files/client.py +1 -1
- magic_hour/resources/v1/files/upload_urls/client.py +2 -2
- magic_hour/resources/v1/image_background_remover/client.py +8 -8
- magic_hour/resources/v1/image_projects/client.py +4 -4
- magic_hour/resources/v1/image_to_video/client.py +8 -8
- magic_hour/resources/v1/lip_sync/client.py +8 -8
- magic_hour/resources/v1/photo_colorizer/client.py +8 -8
- magic_hour/resources/v1/text_to_video/client.py +7 -7
- magic_hour/resources/v1/video_projects/client.py +4 -4
- magic_hour/resources/v1/video_to_video/client.py +8 -8
- magic_hour/types/params/v1_ai_talking_photo_create_body_style.py +4 -3
- {magic_hour-0.36.2.dist-info → magic_hour-0.37.0.dist-info}/METADATA +2 -5
- {magic_hour-0.36.2.dist-info → magic_hour-0.37.0.dist-info}/RECORD +35 -45
- magic_hour/core/__init__.py +0 -50
- magic_hour/core/api_error.py +0 -56
- magic_hour/core/auth.py +0 -354
- magic_hour/core/base_client.py +0 -627
- magic_hour/core/binary_response.py +0 -23
- magic_hour/core/query.py +0 -124
- magic_hour/core/request.py +0 -162
- magic_hour/core/response.py +0 -297
- magic_hour/core/type_utils.py +0 -28
- magic_hour/core/utils.py +0 -55
- {magic_hour-0.36.2.dist-info → magic_hour-0.37.0.dist-info}/LICENSE +0 -0
- {magic_hour-0.36.2.dist-info → magic_hour-0.37.0.dist-info}/WHEEL +0 -0
magic_hour/core/auth.py
DELETED
|
@@ -1,354 +0,0 @@
|
|
|
1
|
-
"""Generated by Sideko (sideko.dev)"""
|
|
2
|
-
|
|
3
|
-
import abc
|
|
4
|
-
import datetime
|
|
5
|
-
from typing import Any, Dict, TypedDict, Optional, List, Tuple, Literal, Union, cast
|
|
6
|
-
|
|
7
|
-
import jsonpointer # type: ignore
|
|
8
|
-
import httpx
|
|
9
|
-
from .request import RequestConfig
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class AuthProvider(abc.ABC):
|
|
13
|
-
"""
|
|
14
|
-
Abstract base class defining the interface for authentication providers.
|
|
15
|
-
|
|
16
|
-
Each concrete implementation handles a specific authentication method
|
|
17
|
-
and modifies the request configuration accordingly.
|
|
18
|
-
"""
|
|
19
|
-
|
|
20
|
-
@abc.abstractmethod
|
|
21
|
-
def add_to_request(self, cfg: RequestConfig) -> RequestConfig:
|
|
22
|
-
"""
|
|
23
|
-
Adds authentication details to the request configuration.
|
|
24
|
-
|
|
25
|
-
Args:
|
|
26
|
-
cfg: The request configuration to modify
|
|
27
|
-
|
|
28
|
-
Returns:
|
|
29
|
-
The modified request configuration with authentication details added
|
|
30
|
-
"""
|
|
31
|
-
|
|
32
|
-
@abc.abstractmethod
|
|
33
|
-
def set_value(self, val: Optional[str]) -> None:
|
|
34
|
-
"""
|
|
35
|
-
Generic method to set an auth value.
|
|
36
|
-
|
|
37
|
-
Args:
|
|
38
|
-
val: Authentication value to set
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
class AuthBasic(AuthProvider):
|
|
43
|
-
"""
|
|
44
|
-
Implements HTTP Basic Authentication.
|
|
45
|
-
|
|
46
|
-
Adds username and password credentials to the request using the standard
|
|
47
|
-
HTTP Basic Authentication scheme.
|
|
48
|
-
"""
|
|
49
|
-
|
|
50
|
-
username: Optional[str]
|
|
51
|
-
password: Optional[str]
|
|
52
|
-
|
|
53
|
-
def __init__(
|
|
54
|
-
self, *, username: Optional[str] = None, password: Optional[str] = None
|
|
55
|
-
):
|
|
56
|
-
super().__init__()
|
|
57
|
-
self.username = username
|
|
58
|
-
self.password = password
|
|
59
|
-
|
|
60
|
-
def add_to_request(self, cfg: RequestConfig) -> RequestConfig:
|
|
61
|
-
"""
|
|
62
|
-
Adds Basic Authentication credentials to the request configuration.
|
|
63
|
-
|
|
64
|
-
Only modifies the configuration if both username and password are provided.
|
|
65
|
-
"""
|
|
66
|
-
if self.username is not None and self.password is not None:
|
|
67
|
-
cfg["auth"] = (self.username, self.password)
|
|
68
|
-
return cfg
|
|
69
|
-
|
|
70
|
-
def set_value(self, val: Optional[str]) -> None:
|
|
71
|
-
"""
|
|
72
|
-
Sets value as the username
|
|
73
|
-
"""
|
|
74
|
-
self.username = val
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
class AuthBearer(AuthProvider):
|
|
78
|
-
"""
|
|
79
|
-
Implements Bearer token authentication.
|
|
80
|
-
|
|
81
|
-
Adds a Bearer token to the request's Authorization header following
|
|
82
|
-
a 'Bearer ' prefix
|
|
83
|
-
"""
|
|
84
|
-
|
|
85
|
-
token: Optional[str]
|
|
86
|
-
|
|
87
|
-
def __init__(self, *, token: Optional[str] = None):
|
|
88
|
-
super().__init__()
|
|
89
|
-
self.token = token
|
|
90
|
-
|
|
91
|
-
def add_to_request(self, cfg: RequestConfig) -> RequestConfig:
|
|
92
|
-
"""
|
|
93
|
-
Adds Bearer token to the Authorization header.
|
|
94
|
-
|
|
95
|
-
Only modifies the configuration if a token value is provided.
|
|
96
|
-
"""
|
|
97
|
-
if self.token is not None:
|
|
98
|
-
headers = cfg.get("headers", dict())
|
|
99
|
-
headers["Authorization"] = f"Bearer {self.token}"
|
|
100
|
-
cfg["headers"] = headers
|
|
101
|
-
return cfg
|
|
102
|
-
|
|
103
|
-
def set_value(self, val: Optional[str]) -> None:
|
|
104
|
-
"""
|
|
105
|
-
Sets value as the bearer token
|
|
106
|
-
"""
|
|
107
|
-
self.token = val
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
class AuthKey(AuthProvider):
|
|
111
|
-
"""
|
|
112
|
-
Implements query, header, or cookie based authentication.
|
|
113
|
-
|
|
114
|
-
Adds an authentication token to the request in the configured location
|
|
115
|
-
"""
|
|
116
|
-
|
|
117
|
-
name: str
|
|
118
|
-
location: Literal["query", "header", "cookie"]
|
|
119
|
-
val: Optional[str]
|
|
120
|
-
|
|
121
|
-
def __init__(
|
|
122
|
-
self,
|
|
123
|
-
*,
|
|
124
|
-
name: str,
|
|
125
|
-
location: Literal["query", "header", "cookie"],
|
|
126
|
-
val: Optional[str] = None,
|
|
127
|
-
):
|
|
128
|
-
super().__init__()
|
|
129
|
-
self.name = name
|
|
130
|
-
self.location = location
|
|
131
|
-
self.val = val
|
|
132
|
-
|
|
133
|
-
def add_to_request(self, cfg: RequestConfig) -> RequestConfig:
|
|
134
|
-
"""
|
|
135
|
-
Adds authentication value as a query/header/cookie parameter
|
|
136
|
-
"""
|
|
137
|
-
if self.val is None:
|
|
138
|
-
return cfg
|
|
139
|
-
elif self.location == "query":
|
|
140
|
-
params = cfg.get("params", dict())
|
|
141
|
-
params[self.name] = self.val
|
|
142
|
-
cfg["params"] = params
|
|
143
|
-
elif self.location == "header":
|
|
144
|
-
headers = cfg.get("headers", {})
|
|
145
|
-
headers[self.name] = self.val
|
|
146
|
-
cfg["headers"] = headers
|
|
147
|
-
else:
|
|
148
|
-
cookies = cfg.get("cookies", dict())
|
|
149
|
-
cookies[self.name] = self.val
|
|
150
|
-
cfg["cookies"] = cookies
|
|
151
|
-
|
|
152
|
-
return cfg
|
|
153
|
-
|
|
154
|
-
def set_value(self, val: Optional[str]) -> None:
|
|
155
|
-
"""
|
|
156
|
-
Sets value as the key
|
|
157
|
-
"""
|
|
158
|
-
self.val = val
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
GrantType = Literal["password", "client_credentials"]
|
|
162
|
-
CredentialsLocation = Literal["request_body", "basic_authorization_header"]
|
|
163
|
-
BodyContent = Literal["form", "json"]
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
class OAuth2Password(TypedDict, total=True):
|
|
167
|
-
"""
|
|
168
|
-
OAuth2 authentication form for a password flow
|
|
169
|
-
|
|
170
|
-
Details:
|
|
171
|
-
https://datatracker.ietf.org/doc/html/rfc6749#section-4.3
|
|
172
|
-
"""
|
|
173
|
-
|
|
174
|
-
username: str
|
|
175
|
-
password: str
|
|
176
|
-
client_id: Optional[str]
|
|
177
|
-
client_secret: Optional[str]
|
|
178
|
-
grant_type: Optional[Union[GrantType, str]]
|
|
179
|
-
scope: Optional[List[str]]
|
|
180
|
-
|
|
181
|
-
token_url: Optional[str]
|
|
182
|
-
"""
|
|
183
|
-
Overrides the default token url
|
|
184
|
-
"""
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
class OAuth2ClientCredentials(TypedDict, total=True):
|
|
188
|
-
"""
|
|
189
|
-
OAuth2 authentication form for a client credentials flow
|
|
190
|
-
|
|
191
|
-
Details:
|
|
192
|
-
https://datatracker.ietf.org/doc/html/rfc6749#section-4.4
|
|
193
|
-
"""
|
|
194
|
-
|
|
195
|
-
client_id: str
|
|
196
|
-
client_secret: str
|
|
197
|
-
grant_type: Optional[Union[GrantType, str]]
|
|
198
|
-
scope: Optional[List[str]]
|
|
199
|
-
|
|
200
|
-
token_url: Optional[str]
|
|
201
|
-
"""
|
|
202
|
-
Overrides the default token url
|
|
203
|
-
"""
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
class OAuth2(AuthProvider):
|
|
207
|
-
"""
|
|
208
|
-
Implements OAuth2 token retrieval and refreshing.
|
|
209
|
-
Currently supports `password` and `client_credentials`
|
|
210
|
-
grant types.
|
|
211
|
-
"""
|
|
212
|
-
|
|
213
|
-
# OAuth2 provider configuration
|
|
214
|
-
base_url: str
|
|
215
|
-
token_url: str
|
|
216
|
-
access_token_pointer: str
|
|
217
|
-
expires_in_pointer: str
|
|
218
|
-
credentials_location: CredentialsLocation
|
|
219
|
-
body_content: BodyContent
|
|
220
|
-
request_mutator: AuthProvider
|
|
221
|
-
|
|
222
|
-
# OAuth2 access token request values
|
|
223
|
-
grant_type: Union[GrantType, str]
|
|
224
|
-
username: Optional[str]
|
|
225
|
-
password: Optional[str]
|
|
226
|
-
client_id: Optional[str]
|
|
227
|
-
client_secret: Optional[str]
|
|
228
|
-
scope: Optional[List[str]]
|
|
229
|
-
|
|
230
|
-
# access_token storage
|
|
231
|
-
access_token: Optional[str]
|
|
232
|
-
expires_at: Optional[datetime.datetime]
|
|
233
|
-
|
|
234
|
-
def __init__(
|
|
235
|
-
self,
|
|
236
|
-
*,
|
|
237
|
-
base_url: str,
|
|
238
|
-
default_token_url: str,
|
|
239
|
-
access_token_pointer: str,
|
|
240
|
-
expires_in_pointer: str,
|
|
241
|
-
credentials_location: CredentialsLocation,
|
|
242
|
-
body_content: BodyContent,
|
|
243
|
-
request_mutator: AuthProvider,
|
|
244
|
-
form: Optional[Union[OAuth2Password, OAuth2ClientCredentials]] = None,
|
|
245
|
-
):
|
|
246
|
-
super().__init__()
|
|
247
|
-
|
|
248
|
-
form_data: Union[OAuth2Password, OAuth2ClientCredentials] = form or cast(
|
|
249
|
-
OAuth2ClientCredentials, {}
|
|
250
|
-
)
|
|
251
|
-
|
|
252
|
-
self.base_url = base_url
|
|
253
|
-
self.token_url = form_data.get("token_url") or default_token_url
|
|
254
|
-
self.access_token_pointer = access_token_pointer
|
|
255
|
-
self.expires_in_pointer = expires_in_pointer
|
|
256
|
-
self.credentials_location = credentials_location
|
|
257
|
-
self.body_content = body_content
|
|
258
|
-
self.request_mutator = request_mutator
|
|
259
|
-
|
|
260
|
-
default_grant_type: GrantType = (
|
|
261
|
-
"password"
|
|
262
|
-
if form_data.get("username") is not None
|
|
263
|
-
else "client_credentials"
|
|
264
|
-
)
|
|
265
|
-
self.grant_type = form_data.get("grant_type") or default_grant_type
|
|
266
|
-
self.username = cast(Optional[str], form_data.get("username"))
|
|
267
|
-
self.password = cast(Optional[str], form_data.get("password"))
|
|
268
|
-
self.client_id = form_data.get("client_id")
|
|
269
|
-
self.client_secret = form_data.get("client_secret")
|
|
270
|
-
self.scope = form_data.get("scope")
|
|
271
|
-
|
|
272
|
-
self.access_token = None
|
|
273
|
-
self.expires_at = None
|
|
274
|
-
|
|
275
|
-
def _refresh(self) -> Tuple[str, datetime.datetime]:
|
|
276
|
-
# build token url using base_url if relative
|
|
277
|
-
url = self.token_url
|
|
278
|
-
if url.startswith("/"):
|
|
279
|
-
url = f"{self.base_url.strip('/')}/{self.token_url.strip('/')}"
|
|
280
|
-
|
|
281
|
-
req_cfg: Dict[str, Any] = {"url": url}
|
|
282
|
-
req_data: Dict[str, str] = {"grant_type": self.grant_type}
|
|
283
|
-
|
|
284
|
-
# add client credentials
|
|
285
|
-
if self.client_id is not None or self.client_secret is not None:
|
|
286
|
-
if self.credentials_location == "basic_authorization_header":
|
|
287
|
-
req_cfg["auth"] = (self.client_id or "", self.client_secret or "")
|
|
288
|
-
else:
|
|
289
|
-
if self.client_id is not None:
|
|
290
|
-
req_data["client_id"] = self.client_id
|
|
291
|
-
if self.client_secret is not None:
|
|
292
|
-
req_data["client_secret"] = self.client_secret
|
|
293
|
-
|
|
294
|
-
# construct request data
|
|
295
|
-
if self.username is not None:
|
|
296
|
-
req_data["username"] = self.username
|
|
297
|
-
if self.password is not None:
|
|
298
|
-
req_data["password"] = self.password
|
|
299
|
-
if self.scope is not None:
|
|
300
|
-
req_data["scope"] = " ".join(self.scope)
|
|
301
|
-
|
|
302
|
-
if self.body_content == "json":
|
|
303
|
-
req_cfg["json"] = req_data
|
|
304
|
-
req_cfg["headers"] = {"content-type": "application/json"}
|
|
305
|
-
else:
|
|
306
|
-
req_cfg["data"] = req_data
|
|
307
|
-
req_cfg["headers"] = {"content-type": "application/x-www-form-urlencoded"}
|
|
308
|
-
|
|
309
|
-
# make access token request
|
|
310
|
-
token_res = httpx.post(**req_cfg)
|
|
311
|
-
token_res.raise_for_status()
|
|
312
|
-
|
|
313
|
-
# retrieve access token & optional expiry seconds
|
|
314
|
-
token_res_json: Dict[str, Any] = token_res.json()
|
|
315
|
-
access_token = str(
|
|
316
|
-
jsonpointer.resolve_pointer(token_res_json, self.access_token_pointer)
|
|
317
|
-
)
|
|
318
|
-
|
|
319
|
-
expires_in_secs = jsonpointer.resolve_pointer(
|
|
320
|
-
token_res_json, self.expires_in_pointer
|
|
321
|
-
)
|
|
322
|
-
if not isinstance(expires_in_secs, int):
|
|
323
|
-
expires_in_secs = 600
|
|
324
|
-
expires_at = datetime.datetime.now() + datetime.timedelta(
|
|
325
|
-
seconds=(
|
|
326
|
-
expires_in_secs - 60
|
|
327
|
-
) # subtract a minute from the expiry as a buffer
|
|
328
|
-
)
|
|
329
|
-
|
|
330
|
-
return (access_token, expires_at)
|
|
331
|
-
|
|
332
|
-
def add_to_request(self, cfg: RequestConfig) -> RequestConfig:
|
|
333
|
-
if (
|
|
334
|
-
self.username is None
|
|
335
|
-
and self.password is None
|
|
336
|
-
and self.client_id is None
|
|
337
|
-
and self.client_secret is None
|
|
338
|
-
):
|
|
339
|
-
# provider is not configured to make an oauth token request
|
|
340
|
-
return cfg
|
|
341
|
-
|
|
342
|
-
token_expired = (
|
|
343
|
-
self.expires_at is not None and self.expires_at <= datetime.datetime.now()
|
|
344
|
-
)
|
|
345
|
-
if self.access_token is None or token_expired:
|
|
346
|
-
access_token, expires_at = self._refresh()
|
|
347
|
-
self.expires_at = expires_at
|
|
348
|
-
self.access_token = access_token
|
|
349
|
-
|
|
350
|
-
self.request_mutator.set_value(self.access_token)
|
|
351
|
-
return self.request_mutator.add_to_request(cfg)
|
|
352
|
-
|
|
353
|
-
def set_value(self, _val: Optional[str]) -> None:
|
|
354
|
-
raise NotImplementedError("an OAuth2 auth provider cannot be a request_mutator")
|