magic_hour 0.36.1__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.

Files changed (47) hide show
  1. magic_hour/__init__.py +1 -1
  2. magic_hour/client.py +1 -1
  3. magic_hour/environment.py +1 -1
  4. magic_hour/helpers/__init__.py +2 -1
  5. magic_hour/helpers/logger.py +8 -0
  6. magic_hour/resources/v1/ai_clothes_changer/client.py +9 -10
  7. magic_hour/resources/v1/ai_face_editor/client.py +9 -10
  8. magic_hour/resources/v1/ai_gif_generator/client.py +8 -9
  9. magic_hour/resources/v1/ai_headshot_generator/client.py +9 -10
  10. magic_hour/resources/v1/ai_image_editor/client.py +9 -10
  11. magic_hour/resources/v1/ai_image_generator/client.py +8 -9
  12. magic_hour/resources/v1/ai_image_upscaler/client.py +9 -10
  13. magic_hour/resources/v1/ai_meme_generator/client.py +8 -9
  14. magic_hour/resources/v1/ai_photo_editor/client.py +9 -10
  15. magic_hour/resources/v1/ai_qr_code_generator/client.py +8 -9
  16. magic_hour/resources/v1/ai_talking_photo/client.py +9 -10
  17. magic_hour/resources/v1/animation/client.py +9 -10
  18. magic_hour/resources/v1/auto_subtitle_generator/client.py +9 -10
  19. magic_hour/resources/v1/client.py +1 -1
  20. magic_hour/resources/v1/face_detection/client.py +6 -6
  21. magic_hour/resources/v1/face_swap/client.py +9 -10
  22. magic_hour/resources/v1/face_swap_photo/client.py +9 -10
  23. magic_hour/resources/v1/files/client.py +3 -3
  24. magic_hour/resources/v1/files/upload_urls/client.py +2 -2
  25. magic_hour/resources/v1/image_background_remover/client.py +9 -10
  26. magic_hour/resources/v1/image_projects/client.py +5 -5
  27. magic_hour/resources/v1/image_to_video/client.py +9 -10
  28. magic_hour/resources/v1/lip_sync/client.py +9 -10
  29. magic_hour/resources/v1/photo_colorizer/client.py +9 -10
  30. magic_hour/resources/v1/text_to_video/client.py +8 -9
  31. magic_hour/resources/v1/video_projects/client.py +5 -5
  32. magic_hour/resources/v1/video_to_video/client.py +9 -10
  33. magic_hour/types/params/v1_ai_talking_photo_create_body_style.py +4 -3
  34. {magic_hour-0.36.1.dist-info → magic_hour-0.37.0.dist-info}/METADATA +2 -5
  35. {magic_hour-0.36.1.dist-info → magic_hour-0.37.0.dist-info}/RECORD +37 -46
  36. magic_hour/core/__init__.py +0 -50
  37. magic_hour/core/api_error.py +0 -56
  38. magic_hour/core/auth.py +0 -354
  39. magic_hour/core/base_client.py +0 -627
  40. magic_hour/core/binary_response.py +0 -23
  41. magic_hour/core/query.py +0 -124
  42. magic_hour/core/request.py +0 -162
  43. magic_hour/core/response.py +0 -297
  44. magic_hour/core/type_utils.py +0 -28
  45. magic_hour/core/utils.py +0 -55
  46. {magic_hour-0.36.1.dist-info → magic_hour-0.37.0.dist-info}/LICENSE +0 -0
  47. {magic_hour-0.36.1.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")