diff --git a/src/elevenlabs/music_custom.py b/src/elevenlabs/music_custom.py index 7fb53eeb..4897aeb5 100644 --- a/src/elevenlabs/music_custom.py +++ b/src/elevenlabs/music_custom.py @@ -30,6 +30,7 @@ class MultipartResponse: json: typing.Dict[str, typing.Any] # Contains compositionPlan and songMetadata audio: bytes filename: str + song_id: typing.Optional[str] = None class MusicClient(AutogeneratedMusicClient): @@ -46,6 +47,8 @@ def compose_detailed( # type: ignore[override] composition_plan: typing.Optional[MusicPrompt] = OMIT, music_length_ms: typing.Optional[int] = OMIT, model_id: typing.Optional[typing.Literal["music_v1"]] = OMIT, + force_instrumental: typing.Optional[bool] = OMIT, + store_for_inpainting: typing.Optional[bool] = OMIT, with_timestamps: typing.Optional[bool] = OMIT, sign_with_c2pa: typing.Optional[bool] = OMIT, request_options: typing.Optional[RequestOptions] = None, @@ -55,23 +58,25 @@ def compose_detailed( # type: ignore[override] Compose a song from a prompt or a composition plan with detailed response parsing. This method calls the original compose_detailed and then parses the stream response. - Returns a MultipartResponse containing parsed JSON metadata, audio bytes, and filename. + Returns a MultipartResponse containing parsed JSON metadata, audio bytes, filename, + and song_id (if store_for_inpainting was True). """ - # Call the parent method to get the stream - stream = super().compose_detailed( + # Use the raw client directly to access response headers (for song_id) + with self._raw_client.compose_detailed( output_format=output_format, prompt=prompt, composition_plan=composition_plan, music_length_ms=music_length_ms, model_id=model_id, + force_instrumental=force_instrumental, + store_for_inpainting=store_for_inpainting, with_timestamps=with_timestamps, sign_with_c_2_pa=sign_with_c2pa, request_options=request_options, - **kwargs, - ) - - # Parse the stream using the parsing method - return self._parse_multipart(stream) + ) as r: + result = self._parse_multipart(r.data) + result.song_id = r.headers.get("song-id") + return result def _parse_multipart(self, stream: typing.Iterator[bytes]) -> MultipartResponse: """ @@ -178,6 +183,8 @@ async def compose_detailed( # type: ignore[override] composition_plan: typing.Optional[MusicPrompt] = OMIT, music_length_ms: typing.Optional[int] = OMIT, model_id: typing.Optional[typing.Literal["music_v1"]] = OMIT, + force_instrumental: typing.Optional[bool] = OMIT, + store_for_inpainting: typing.Optional[bool] = OMIT, with_timestamps: typing.Optional[bool] = OMIT, sign_with_c2pa: typing.Optional[bool] = OMIT, request_options: typing.Optional[RequestOptions] = None, @@ -187,23 +194,25 @@ async def compose_detailed( # type: ignore[override] Compose a song from a prompt or a composition plan with detailed response parsing. This method calls the original compose_detailed and then parses the stream response. - Returns a MultipartResponse containing parsed JSON metadata, audio bytes, and filename. + Returns a MultipartResponse containing parsed JSON metadata, audio bytes, filename, + and song_id (if store_for_inpainting was True). """ - # Call the parent method to get the stream - stream = super().compose_detailed( + # Use the raw client directly to access response headers (for song_id) + async with self._raw_client.compose_detailed( output_format=output_format, prompt=prompt, composition_plan=composition_plan, music_length_ms=music_length_ms, model_id=model_id, + force_instrumental=force_instrumental, + store_for_inpainting=store_for_inpainting, with_timestamps=with_timestamps, sign_with_c_2_pa=sign_with_c2pa, request_options=request_options, - **kwargs, - ) - - # Parse the stream using the parsing method - return await self._parse_multipart_async(stream) + ) as r: + result = await self._parse_multipart_async(r.data) + result.song_id = r.headers.get("song-id") + return result async def _parse_multipart_async(self, stream: typing.AsyncIterator[bytes]) -> MultipartResponse: """