runapi-suno 0.1.2__tar.gz → 0.2.0__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.
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/PKG-INFO +11 -5
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/README.md +7 -1
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/pyproject.toml +4 -4
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/client.py +2 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/contract_gen.py +36 -7
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/__init__.py +2 -0
- runapi_suno-0.2.0/src/runapi/suno/resources/blend_lyrics.py +30 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/create_mashup.py +1 -4
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/types.py +12 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/tests/test_client.py +21 -2
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/__init__.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/_validators.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/py.typed +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/add_instrumental.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/add_vocals.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/boost_style.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/check_voice.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/convert_audio.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/cover_audio.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/extend_music.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/generate_artwork.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/generate_lyrics.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/generate_midi.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/generate_persona.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/generate_voice.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/get_timestamped_lyrics.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/regenerate_validation_phrase.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/replace_section.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/separate_audio_stems.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/text_to_music.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/text_to_sound.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/visualize_music.py +0 -0
- {runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/voice_to_validation_phrase.py +0 -0
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: runapi-suno
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: RunAPI Suno SDK for text-to-music, cover audio, music extension, stem separation, voice validation phrase, custom voice, and related audio workflows in JavaScript, Python, Ruby, Go, Java, and PHP
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: RunAPI Suno SDK for text-to-music, lyric generation and blending, cover audio, music extension, stem separation, voice validation phrase, custom voice, and related audio workflows in JavaScript, Python, Ruby, Go, Java, and PHP
|
|
5
5
|
Project-URL: Homepage, https://runapi.ai/models/suno
|
|
6
6
|
Project-URL: Documentation, https://runapi.ai/docs#sdk-suno
|
|
7
7
|
Project-URL: Source, https://github.com/runapi-ai/suno-sdk
|
|
8
8
|
Project-URL: Issues, https://github.com/runapi-ai/suno-sdk/issues
|
|
9
9
|
Project-URL: Changelog, https://github.com/runapi-ai/suno-sdk/blob/main/CHANGELOG.md
|
|
10
|
-
Project-URL: Release Notes, https://github.com/runapi-ai/suno-sdk/releases/tag/python%2Fv0.
|
|
10
|
+
Project-URL: Release Notes, https://github.com/runapi-ai/suno-sdk/releases/tag/python%2Fv0.2.0
|
|
11
11
|
Author-email: RunAPI <contact@runapi.ai>
|
|
12
12
|
License-Expression: Apache-2.0
|
|
13
13
|
Keywords: api,audio-generation,golang,gradle,java,maven,music-api,music-generation,python,ruby,runapi,runapi-ai,sdk,suno,suno-ai-api,typescript
|
|
14
14
|
Requires-Python: >=3.9
|
|
15
|
-
Requires-Dist: runapi-core>=0.2.
|
|
15
|
+
Requires-Dist: runapi-core>=0.2.2
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
|
|
18
18
|
# Suno API Python SDK for RunAPI
|
|
@@ -45,6 +45,12 @@ print(result.audios[0].audio_url)
|
|
|
45
45
|
# Generate lyrics from a prompt.
|
|
46
46
|
lyrics = client.generate_lyrics.run(prompt="a song about the ocean at night")
|
|
47
47
|
print(lyrics.lyrics[0].text)
|
|
48
|
+
|
|
49
|
+
blended = client.blend_lyrics.run(
|
|
50
|
+
lyrics_a="[Verse]\nCity lights fade into dawn",
|
|
51
|
+
lyrics_b="[Chorus]\nWe rise with the morning sun",
|
|
52
|
+
)
|
|
53
|
+
print(blended.lyrics[0].text)
|
|
48
54
|
```
|
|
49
55
|
|
|
50
56
|
Use `create` to submit a task and return quickly, `get` to fetch the latest task state, and `run` when a script should create and poll until completion:
|
|
@@ -65,7 +71,7 @@ RunAPI-generated file URLs are temporary. Download and store generated images, v
|
|
|
65
71
|
|
|
66
72
|
## Language notes
|
|
67
73
|
|
|
68
|
-
Pass parameters as keyword arguments and catch the `runapi.suno` error classes when building music jobs or scripts. The available resources are `text_to_music`, `extend_music`, `generate_artwork`, `cover_audio`, `add_instrumental`, `add_vocals`, `separate_audio_stems`, `generate_midi`, `convert_audio`, `visualize_music`, `generate_lyrics`, `get_timestamped_lyrics`, `replace_section`, `create_mashup`, `text_to_sound`, `voice_to_validation_phrase`, `regenerate_validation_phrase`, `generate_voice`, `check_voice`, `generate_persona`, and `boost_style`. Keep `RUNAPI_API_KEY` in the environment or your secret manager; never commit API keys or callback secrets.
|
|
74
|
+
Pass parameters as keyword arguments and catch the `runapi.suno` error classes when building music jobs or scripts. The available resources are `text_to_music`, `extend_music`, `generate_artwork`, `cover_audio`, `add_instrumental`, `add_vocals`, `separate_audio_stems`, `generate_midi`, `convert_audio`, `visualize_music`, `generate_lyrics`, `blend_lyrics`, `get_timestamped_lyrics`, `replace_section`, `create_mashup`, `text_to_sound`, `voice_to_validation_phrase`, `regenerate_validation_phrase`, `generate_voice`, `check_voice`, `generate_persona`, and `boost_style`. Keep `RUNAPI_API_KEY` in the environment or your secret manager; never commit API keys or callback secrets.
|
|
69
75
|
|
|
70
76
|
## Links
|
|
71
77
|
|
|
@@ -28,6 +28,12 @@ print(result.audios[0].audio_url)
|
|
|
28
28
|
# Generate lyrics from a prompt.
|
|
29
29
|
lyrics = client.generate_lyrics.run(prompt="a song about the ocean at night")
|
|
30
30
|
print(lyrics.lyrics[0].text)
|
|
31
|
+
|
|
32
|
+
blended = client.blend_lyrics.run(
|
|
33
|
+
lyrics_a="[Verse]\nCity lights fade into dawn",
|
|
34
|
+
lyrics_b="[Chorus]\nWe rise with the morning sun",
|
|
35
|
+
)
|
|
36
|
+
print(blended.lyrics[0].text)
|
|
31
37
|
```
|
|
32
38
|
|
|
33
39
|
Use `create` to submit a task and return quickly, `get` to fetch the latest task state, and `run` when a script should create and poll until completion:
|
|
@@ -48,7 +54,7 @@ RunAPI-generated file URLs are temporary. Download and store generated images, v
|
|
|
48
54
|
|
|
49
55
|
## Language notes
|
|
50
56
|
|
|
51
|
-
Pass parameters as keyword arguments and catch the `runapi.suno` error classes when building music jobs or scripts. The available resources are `text_to_music`, `extend_music`, `generate_artwork`, `cover_audio`, `add_instrumental`, `add_vocals`, `separate_audio_stems`, `generate_midi`, `convert_audio`, `visualize_music`, `generate_lyrics`, `get_timestamped_lyrics`, `replace_section`, `create_mashup`, `text_to_sound`, `voice_to_validation_phrase`, `regenerate_validation_phrase`, `generate_voice`, `check_voice`, `generate_persona`, and `boost_style`. Keep `RUNAPI_API_KEY` in the environment or your secret manager; never commit API keys or callback secrets.
|
|
57
|
+
Pass parameters as keyword arguments and catch the `runapi.suno` error classes when building music jobs or scripts. The available resources are `text_to_music`, `extend_music`, `generate_artwork`, `cover_audio`, `add_instrumental`, `add_vocals`, `separate_audio_stems`, `generate_midi`, `convert_audio`, `visualize_music`, `generate_lyrics`, `blend_lyrics`, `get_timestamped_lyrics`, `replace_section`, `create_mashup`, `text_to_sound`, `voice_to_validation_phrase`, `regenerate_validation_phrase`, `generate_voice`, `check_voice`, `generate_persona`, and `boost_style`. Keep `RUNAPI_API_KEY` in the environment or your secret manager; never commit API keys or callback secrets.
|
|
52
58
|
|
|
53
59
|
## Links
|
|
54
60
|
|
|
@@ -4,14 +4,14 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "runapi-suno"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "RunAPI Suno SDK for text-to-music, cover audio, music extension, stem separation, voice validation phrase, custom voice, and related audio workflows in JavaScript, Python, Ruby, Go, Java, and PHP"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "RunAPI Suno SDK for text-to-music, lyric generation and blending, cover audio, music extension, stem separation, voice validation phrase, custom voice, and related audio workflows in JavaScript, Python, Ruby, Go, Java, and PHP"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
11
11
|
license = "Apache-2.0"
|
|
12
12
|
authors = [{ name = "RunAPI", email = "contact@runapi.ai" }]
|
|
13
13
|
keywords = ["runapi", "runapi-ai", "suno", "api", "sdk", "typescript", "python", "ruby", "golang", "java", "maven", "gradle", "music-generation", "audio-generation", "music-api", "suno-ai-api"]
|
|
14
|
-
dependencies = ["runapi-core>=0.2.
|
|
14
|
+
dependencies = ["runapi-core>=0.2.2"]
|
|
15
15
|
|
|
16
16
|
[project.urls]
|
|
17
17
|
Homepage = "https://runapi.ai/models/suno"
|
|
@@ -19,7 +19,7 @@ Documentation = "https://runapi.ai/docs#sdk-suno"
|
|
|
19
19
|
Source = "https://github.com/runapi-ai/suno-sdk"
|
|
20
20
|
Issues = "https://github.com/runapi-ai/suno-sdk/issues"
|
|
21
21
|
Changelog = "https://github.com/runapi-ai/suno-sdk/blob/main/CHANGELOG.md"
|
|
22
|
-
"Release Notes" = "https://github.com/runapi-ai/suno-sdk/releases/tag/python%2Fv0.
|
|
22
|
+
"Release Notes" = "https://github.com/runapi-ai/suno-sdk/releases/tag/python%2Fv0.2.0"
|
|
23
23
|
|
|
24
24
|
[tool.hatch.build.targets.wheel]
|
|
25
25
|
packages = ["src/runapi"]
|
|
@@ -9,6 +9,7 @@ from runapi.core import ClientOptions, HttpClient, resolve_api_key
|
|
|
9
9
|
from .resources.add_instrumental import AddInstrumental
|
|
10
10
|
from .resources.add_vocals import AddVocals
|
|
11
11
|
from .resources.boost_style import BoostStyle
|
|
12
|
+
from .resources.blend_lyrics import BlendLyrics
|
|
12
13
|
from .resources.check_voice import CheckVoice
|
|
13
14
|
from .resources.convert_audio import ConvertAudio
|
|
14
15
|
from .resources.cover_audio import CoverAudio
|
|
@@ -58,6 +59,7 @@ class SunoClient:
|
|
|
58
59
|
self.convert_audio = ConvertAudio(http)
|
|
59
60
|
self.visualize_music = VisualizeMusic(http)
|
|
60
61
|
self.generate_lyrics = GenerateLyrics(http)
|
|
62
|
+
self.blend_lyrics = BlendLyrics(http)
|
|
61
63
|
self.get_timestamped_lyrics = GetTimestampedLyrics(http)
|
|
62
64
|
self.replace_section = ReplaceSection(http)
|
|
63
65
|
self.create_mashup = CreateMashup(http)
|
|
@@ -70,6 +70,19 @@ CONTRACT = {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
},
|
|
73
|
+
"blend-lyrics": {
|
|
74
|
+
"models": [],
|
|
75
|
+
"fields_by_model": {
|
|
76
|
+
"_": {
|
|
77
|
+
"lyrics_a": {
|
|
78
|
+
"required": True
|
|
79
|
+
},
|
|
80
|
+
"lyrics_b": {
|
|
81
|
+
"required": True
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
73
86
|
"boost-style": {
|
|
74
87
|
"models": [],
|
|
75
88
|
"fields_by_model": {
|
|
@@ -235,7 +248,9 @@ CONTRACT = {
|
|
|
235
248
|
"enum": ["style", "voice"]
|
|
236
249
|
},
|
|
237
250
|
"upload_url_list": {
|
|
238
|
-
"required": True
|
|
251
|
+
"required": True,
|
|
252
|
+
"min_items": 2,
|
|
253
|
+
"max_items": 2
|
|
239
254
|
},
|
|
240
255
|
"vocal_gender": {
|
|
241
256
|
"enum": ["male", "female"]
|
|
@@ -253,7 +268,9 @@ CONTRACT = {
|
|
|
253
268
|
"enum": ["style", "voice"]
|
|
254
269
|
},
|
|
255
270
|
"upload_url_list": {
|
|
256
|
-
"required": True
|
|
271
|
+
"required": True,
|
|
272
|
+
"min_items": 2,
|
|
273
|
+
"max_items": 2
|
|
257
274
|
},
|
|
258
275
|
"vocal_gender": {
|
|
259
276
|
"enum": ["male", "female"]
|
|
@@ -271,7 +288,9 @@ CONTRACT = {
|
|
|
271
288
|
"enum": ["style", "voice"]
|
|
272
289
|
},
|
|
273
290
|
"upload_url_list": {
|
|
274
|
-
"required": True
|
|
291
|
+
"required": True,
|
|
292
|
+
"min_items": 2,
|
|
293
|
+
"max_items": 2
|
|
275
294
|
},
|
|
276
295
|
"vocal_gender": {
|
|
277
296
|
"enum": ["male", "female"]
|
|
@@ -289,7 +308,9 @@ CONTRACT = {
|
|
|
289
308
|
"enum": ["style", "voice"]
|
|
290
309
|
},
|
|
291
310
|
"upload_url_list": {
|
|
292
|
-
"required": True
|
|
311
|
+
"required": True,
|
|
312
|
+
"min_items": 2,
|
|
313
|
+
"max_items": 2
|
|
293
314
|
},
|
|
294
315
|
"vocal_gender": {
|
|
295
316
|
"enum": ["male", "female"]
|
|
@@ -307,7 +328,9 @@ CONTRACT = {
|
|
|
307
328
|
"enum": ["style", "voice"]
|
|
308
329
|
},
|
|
309
330
|
"upload_url_list": {
|
|
310
|
-
"required": True
|
|
331
|
+
"required": True,
|
|
332
|
+
"min_items": 2,
|
|
333
|
+
"max_items": 2
|
|
311
334
|
},
|
|
312
335
|
"vocal_gender": {
|
|
313
336
|
"enum": ["male", "female"]
|
|
@@ -325,7 +348,9 @@ CONTRACT = {
|
|
|
325
348
|
"enum": ["style", "voice"]
|
|
326
349
|
},
|
|
327
350
|
"upload_url_list": {
|
|
328
|
-
"required": True
|
|
351
|
+
"required": True,
|
|
352
|
+
"min_items": 2,
|
|
353
|
+
"max_items": 2
|
|
329
354
|
},
|
|
330
355
|
"vocal_gender": {
|
|
331
356
|
"enum": ["male", "female"]
|
|
@@ -458,7 +483,11 @@ CONTRACT = {
|
|
|
458
483
|
"generate-lyrics": {
|
|
459
484
|
"models": [],
|
|
460
485
|
"fields_by_model": {
|
|
461
|
-
"_": {
|
|
486
|
+
"_": {
|
|
487
|
+
"prompt": {
|
|
488
|
+
"required": True
|
|
489
|
+
}
|
|
490
|
+
}
|
|
462
491
|
}
|
|
463
492
|
},
|
|
464
493
|
"generate-midi": {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from .add_instrumental import AddInstrumental
|
|
2
2
|
from .add_vocals import AddVocals
|
|
3
3
|
from .boost_style import BoostStyle
|
|
4
|
+
from .blend_lyrics import BlendLyrics
|
|
4
5
|
from .check_voice import CheckVoice
|
|
5
6
|
from .convert_audio import ConvertAudio
|
|
6
7
|
from .cover_audio import CoverAudio
|
|
@@ -32,6 +33,7 @@ __all__ = [
|
|
|
32
33
|
"ConvertAudio",
|
|
33
34
|
"VisualizeMusic",
|
|
34
35
|
"GenerateLyrics",
|
|
36
|
+
"BlendLyrics",
|
|
35
37
|
"GetTimestampedLyrics",
|
|
36
38
|
"ReplaceSection",
|
|
37
39
|
"CreateMashup",
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Suno blend_lyrics resource."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
|
|
7
|
+
from runapi.core import RequestOptions, Resource
|
|
8
|
+
|
|
9
|
+
from ..contract_gen import CONTRACT
|
|
10
|
+
from ..types import BlendLyricsResponse, CompletedBlendLyricsResponse
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BlendLyrics(Resource):
|
|
14
|
+
"""Blend two caller-authored lyrics texts."""
|
|
15
|
+
|
|
16
|
+
ENDPOINT = "/api/v1/suno/blend_lyrics"
|
|
17
|
+
RESPONSE_CLASS = BlendLyricsResponse
|
|
18
|
+
COMPLETED_RESPONSE_CLASS = CompletedBlendLyricsResponse
|
|
19
|
+
|
|
20
|
+
def run(self, options: Optional[RequestOptions] = None, **params: Any) -> Any:
|
|
21
|
+
task = self.create(options=options, **params)
|
|
22
|
+
return self._poll_until_complete(lambda: self.get(task.id, options=options))
|
|
23
|
+
|
|
24
|
+
def create(self, options: Optional[RequestOptions] = None, **params: Any) -> Any:
|
|
25
|
+
compacted = self._compact_params(params)
|
|
26
|
+
self._validate_contract(CONTRACT["blend-lyrics"], compacted)
|
|
27
|
+
return self._request("post", self.ENDPOINT, body=compacted, options=options)
|
|
28
|
+
|
|
29
|
+
def get(self, id: str, options: Optional[RequestOptions] = None) -> Any:
|
|
30
|
+
return self._request("get", f"{self.ENDPOINT}/{id}", options=options)
|
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from typing import Any, Dict, Optional
|
|
6
6
|
|
|
7
|
-
from runapi.core import Resource,
|
|
7
|
+
from runapi.core import Resource, RequestOptions
|
|
8
8
|
|
|
9
9
|
from ..contract_gen import CONTRACT
|
|
10
10
|
from ..types import CompletedCreateMashupResponse, CreateMashupResponse
|
|
@@ -55,7 +55,4 @@ class CreateMashup(Resource):
|
|
|
55
55
|
return self._request("get", f"{self.ENDPOINT}/{id}", options=options)
|
|
56
56
|
|
|
57
57
|
def _validate_params(self, params: Dict[str, Any]) -> None:
|
|
58
|
-
upload_url_list = params.get("upload_url_list")
|
|
59
|
-
if not (isinstance(upload_url_list, list) and len(upload_url_list) == 2):
|
|
60
|
-
raise ValidationError("upload_url_list must contain exactly 2 URLs")
|
|
61
58
|
self._validate_contract(CONTRACT["create-mashup"], params)
|
|
@@ -228,6 +228,12 @@ class GenerateLyricsResponse(AsyncTaskResponse):
|
|
|
228
228
|
lyrics = optional([lambda: Lyric])
|
|
229
229
|
|
|
230
230
|
|
|
231
|
+
class BlendLyricsResponse(AsyncTaskResponse):
|
|
232
|
+
"""Suno lyrics blending task status response."""
|
|
233
|
+
|
|
234
|
+
lyrics = optional([lambda: Lyric])
|
|
235
|
+
|
|
236
|
+
|
|
231
237
|
class GetTimestampedLyricsResponse(BaseModel):
|
|
232
238
|
"""Suno timestamped-lyrics result."""
|
|
233
239
|
|
|
@@ -352,6 +358,12 @@ class CompletedGenerateLyricsResponse(GenerateLyricsResponse):
|
|
|
352
358
|
lyrics = required([lambda: Lyric])
|
|
353
359
|
|
|
354
360
|
|
|
361
|
+
class CompletedBlendLyricsResponse(BlendLyricsResponse):
|
|
362
|
+
"""Narrowed lyrics blending response once polling observes completion."""
|
|
363
|
+
|
|
364
|
+
lyrics = required([lambda: Lyric])
|
|
365
|
+
|
|
366
|
+
|
|
355
367
|
class CompletedReplaceSectionResponse(ReplaceSectionResponse):
|
|
356
368
|
"""Narrowed replace-section response once polling observes completion."""
|
|
357
369
|
|
|
@@ -80,6 +80,7 @@ def test_exposes_all_resource_accessors():
|
|
|
80
80
|
"convert_audio": R.ConvertAudio,
|
|
81
81
|
"visualize_music": R.VisualizeMusic,
|
|
82
82
|
"generate_lyrics": R.GenerateLyrics,
|
|
83
|
+
"blend_lyrics": R.BlendLyrics,
|
|
83
84
|
"get_timestamped_lyrics": R.GetTimestampedLyrics,
|
|
84
85
|
"replace_section": R.ReplaceSection,
|
|
85
86
|
"create_mashup": R.CreateMashup,
|
|
@@ -91,7 +92,7 @@ def test_exposes_all_resource_accessors():
|
|
|
91
92
|
"generate_persona": R.GeneratePersona,
|
|
92
93
|
"boost_style": R.BoostStyle,
|
|
93
94
|
}
|
|
94
|
-
assert len(expected) ==
|
|
95
|
+
assert len(expected) == 22
|
|
95
96
|
for name, cls in expected.items():
|
|
96
97
|
assert isinstance(getattr(client, name), cls), name
|
|
97
98
|
|
|
@@ -122,6 +123,24 @@ def test_get_fetches_by_id():
|
|
|
122
123
|
assert fake.calls == [("get", "/api/v1/suno/text_to_music/t1", None)]
|
|
123
124
|
|
|
124
125
|
|
|
126
|
+
def test_blend_lyrics_posts_flat_lyrics_pair():
|
|
127
|
+
fake = FakeHttp({"id": "blend_1", "status": "processing"})
|
|
128
|
+
client = SunoClient(api_key="k", http_client=fake)
|
|
129
|
+
|
|
130
|
+
client.blend_lyrics.create(lyrics_a="First verse", lyrics_b="Second verse")
|
|
131
|
+
|
|
132
|
+
assert fake.calls == [
|
|
133
|
+
("post", "/api/v1/suno/blend_lyrics", {"lyrics_a": "First verse", "lyrics_b": "Second verse"}),
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def test_blend_lyrics_requires_both_lyrics_texts():
|
|
138
|
+
client = SunoClient(api_key="k", http_client=FakeHttp())
|
|
139
|
+
|
|
140
|
+
with pytest.raises(ValidationError, match="lyrics_b"):
|
|
141
|
+
client.blend_lyrics.create(lyrics_a="First verse")
|
|
142
|
+
|
|
143
|
+
|
|
125
144
|
def test_run_narrows_completed_type():
|
|
126
145
|
fake = FakeHttp(
|
|
127
146
|
{"id": "t1", "status": "pending"},
|
|
@@ -326,7 +345,7 @@ def test_replace_section_rejects_mixed_sources():
|
|
|
326
345
|
|
|
327
346
|
def test_create_mashup_requires_two_urls():
|
|
328
347
|
client = SunoClient(api_key="k", http_client=FakeHttp())
|
|
329
|
-
with pytest.raises(ValidationError, match="upload_url_list must contain
|
|
348
|
+
with pytest.raises(ValidationError, match="upload_url_list must contain between 2 and 2 items"):
|
|
330
349
|
client.create_mashup.create(upload_url_list=["only-one"], model="suno-v5")
|
|
331
350
|
|
|
332
351
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/regenerate_validation_phrase.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{runapi_suno-0.1.2 → runapi_suno-0.2.0}/src/runapi/suno/resources/voice_to_validation_phrase.py
RENAMED
|
File without changes
|