Skip to content

Commit b99c032

Browse files
authored
feat: add check results function to audio projects (#101)
1 parent 5e6ce02 commit b99c032

File tree

6 files changed

+717
-2
lines changed

6 files changed

+717
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ download_urls = result.downloads
219219

220220
### [v1.audio_projects](magic_hour/resources/v1/audio_projects/README.md)
221221

222+
* [check-result](magic_hour/resources/v1/audio_projects/README.md#check-result) - Check results
222223
* [delete](magic_hour/resources/v1/audio_projects/README.md#delete) - Delete audio
223224
* [get](magic_hour/resources/v1/audio_projects/README.md#get) - Get audio details
224225

magic_hour/helpers/download.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def download_files_sync(
2323
downloads: Union[
2424
List[models.V1ImageProjectsGetResponseDownloadsItem],
2525
List[models.V1VideoProjectsGetResponseDownloadsItem],
26+
List[models.V1AudioProjectsGetResponseDownloadsItem],
2627
],
2728
download_directory: Union[str, None] = None,
2829
) -> List[str]:
@@ -51,6 +52,7 @@ async def download_files_async(
5152
downloads: Union[
5253
List[models.V1ImageProjectsGetResponseDownloadsItem],
5354
List[models.V1VideoProjectsGetResponseDownloadsItem],
55+
List[models.V1AudioProjectsGetResponseDownloadsItem],
5456
],
5557
download_directory: Union[str, None] = None,
5658
) -> List[str]:

magic_hour/resources/v1/audio_projects/README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,53 @@
22

33
## Module Functions
44

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 audio project. This value is returned by all of the POST APIs that create an audio. | `"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.audio_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.audio_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 -->
552
### Delete audio <a name="delete"></a>
653

754
Permanently delete the rendered audio file(s). This action is not reversible, please be sure before deleting.
Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
from .client import AsyncAudioProjectsClient, AudioProjectsClient
1+
from .client import (
2+
AsyncAudioProjectsClient,
3+
V1AudioProjectsGetResponseWithDownloads,
4+
AudioProjectsClient,
5+
)
26

37

4-
__all__ = ["AsyncAudioProjectsClient", "AudioProjectsClient"]
8+
__all__ = [
9+
"AsyncAudioProjectsClient",
10+
"V1AudioProjectsGetResponseWithDownloads",
11+
"AudioProjectsClient",
12+
]

magic_hour/resources/v1/audio_projects/client.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
import os
2+
import pydantic
3+
import time
14
import typing
25

6+
from magic_hour.helpers.download import download_files_async, download_files_sync
7+
from magic_hour.helpers.logger import get_sdk_logger
38
from magic_hour.types import models
49
from make_api_request import (
510
AsyncBaseClient,
@@ -9,10 +14,83 @@
914
)
1015

1116

17+
logger = get_sdk_logger(__name__)
18+
19+
20+
class V1AudioProjectsGetResponseWithDownloads(models.V1AudioProjectsGetResponse):
21+
downloaded_paths: typing.Optional[typing.List[str]] = pydantic.Field(
22+
default=None, alias="downloaded_paths"
23+
)
24+
"""
25+
The paths to the downloaded files.
26+
27+
This field is only populated if `download_outputs` is True and the audio project is complete.
28+
"""
29+
30+
1231
class AudioProjectsClient:
1332
def __init__(self, *, base_client: SyncBaseClient):
1433
self._base_client = base_client
1534

35+
def check_result(
36+
self,
37+
id: str,
38+
wait_for_completion: bool,
39+
download_outputs: bool,
40+
download_directory: typing.Optional[str] = None,
41+
) -> V1AudioProjectsGetResponseWithDownloads:
42+
"""
43+
Check the result of an audio project with optional waiting and downloading.
44+
45+
This method retrieves the status of an audio project and optionally waits for completion
46+
and downloads the output files.
47+
48+
Args:
49+
id: Unique ID of the audio project
50+
wait_for_completion: Whether to wait for the audio project to complete
51+
download_outputs: Whether to download the outputs
52+
download_directory: The directory to download the outputs to. If not provided,
53+
the outputs will be downloaded to the current working directory
54+
55+
Returns:
56+
V1AudioProjectsGetResponseWithDownloads: The audio project response with optional
57+
downloaded file paths included
58+
"""
59+
api_response = self.get(id=id)
60+
if not wait_for_completion:
61+
response = V1AudioProjectsGetResponseWithDownloads(
62+
**api_response.model_dump()
63+
)
64+
return response
65+
66+
poll_interval = float(os.getenv("MAGIC_HOUR_POLL_INTERVAL", "0.5"))
67+
68+
status = api_response.status
69+
70+
while status not in ["complete", "error", "canceled"]:
71+
api_response = self.get(id=id)
72+
status = api_response.status
73+
time.sleep(poll_interval)
74+
75+
if api_response.status != "complete":
76+
log = logger.error if api_response.status == "error" else logger.info
77+
log(
78+
f"Audio project {id} has status {api_response.status}: {api_response.error}"
79+
)
80+
return V1AudioProjectsGetResponseWithDownloads(**api_response.model_dump())
81+
82+
if not download_outputs:
83+
return V1AudioProjectsGetResponseWithDownloads(**api_response.model_dump())
84+
85+
downloaded_paths = download_files_sync(
86+
downloads=api_response.downloads,
87+
download_directory=download_directory,
88+
)
89+
90+
return V1AudioProjectsGetResponseWithDownloads(
91+
**api_response.model_dump(), downloaded_paths=downloaded_paths
92+
)
93+
1694
def delete(
1795
self, *, id: str, request_options: typing.Optional[RequestOptions] = None
1896
) -> None:
@@ -95,6 +173,65 @@ class AsyncAudioProjectsClient:
95173
def __init__(self, *, base_client: AsyncBaseClient):
96174
self._base_client = base_client
97175

176+
async def check_result(
177+
self,
178+
id: str,
179+
wait_for_completion: bool,
180+
download_outputs: bool,
181+
download_directory: typing.Optional[str] = None,
182+
) -> V1AudioProjectsGetResponseWithDownloads:
183+
"""
184+
Check the result of an audio project with optional waiting and downloading.
185+
186+
This method retrieves the status of an audio project and optionally waits for completion
187+
and downloads the output files.
188+
189+
Args:
190+
id: Unique ID of the audio project
191+
wait_for_completion: Whether to wait for the audio project to complete
192+
download_outputs: Whether to download the outputs
193+
download_directory: The directory to download the outputs to. If not provided,
194+
the outputs will be downloaded to the current working directory
195+
196+
Returns:
197+
V1AudioProjectsGetResponseWithDownloads: The audio project response with optional
198+
downloaded file paths included
199+
"""
200+
api_response = await self.get(id=id)
201+
if not wait_for_completion:
202+
response = V1AudioProjectsGetResponseWithDownloads(
203+
**api_response.model_dump()
204+
)
205+
return response
206+
207+
poll_interval = float(os.getenv("MAGIC_HOUR_POLL_INTERVAL", "0.5"))
208+
209+
status = api_response.status
210+
211+
while status not in ["complete", "error", "canceled"]:
212+
api_response = await self.get(id=id)
213+
status = api_response.status
214+
time.sleep(poll_interval)
215+
216+
if api_response.status != "complete":
217+
log = logger.error if api_response.status == "error" else logger.info
218+
log(
219+
f"Audio project {id} has status {api_response.status}: {api_response.error}"
220+
)
221+
return V1AudioProjectsGetResponseWithDownloads(**api_response.model_dump())
222+
223+
if not download_outputs:
224+
return V1AudioProjectsGetResponseWithDownloads(**api_response.model_dump())
225+
226+
downloaded_paths = await download_files_async(
227+
downloads=api_response.downloads,
228+
download_directory=download_directory,
229+
)
230+
231+
return V1AudioProjectsGetResponseWithDownloads(
232+
**api_response.model_dump(), downloaded_paths=downloaded_paths
233+
)
234+
98235
async def delete(
99236
self, *, id: str, request_options: typing.Optional[RequestOptions] = None
100237
) -> None:

0 commit comments

Comments
 (0)