runapi-suno 0.2.1 → 0.2.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9da988a431cb6531396024b3c3456a4451a9a13f9ddd67d6a0266e9e49e2220a
4
- data.tar.gz: c83064427d9daa4ab9f2388606723d9e38616372ccfa7c51257015edc7122307
3
+ metadata.gz: f676e53a763fe863f61cd2fbd16f0702c93f2d189f7369d1471e819dd01bfa79
4
+ data.tar.gz: 76e18fcc6c46fe6b0f97be04949db28868d9f4ea3bfe7efcf5aa5296ca9a6ff7
5
5
  SHA512:
6
- metadata.gz: 14794ffced77ba1adf102c9f4cb67cb0d385226f0ba0b97803f88cc0cbede5fdb1fb6655e8506d90df3a99936271f561e909ec9827959f3e811f26ac31234543
7
- data.tar.gz: 8df719d435a2c6047bae4ba3d8bf7b6d33bba262bcfddb60bd81f4880b70116fc7a1c66409a0aa154c04720ca62f3c818685fcb13c0affe0c209d0fb6753e120
6
+ metadata.gz: 83832d65fe97b8d8bfa5eae30c1f095fe8ea13368f06242588d89e67aa31013e2cd9ef3b5b7ba14e760430b13d76cc83a942dafd7878b654f58d6a3ff0736b5a
7
+ data.tar.gz: 5973d5187e4c761c056f1cde61a2e68f6fab5be8d42988e66194989eaddaafda9e77b6b481d89ee5eea5d84997f86def8a6c57ac8ceebd34bd570701dbcb1866
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # Suno AI API Ruby SDK for RunAPI
2
+
3
+ The suno ai api Ruby SDK is the language-specific package for Suno on RunAPI. Use this suno ai api package for song generation, lyrics, vocal, extension, and audio transformation flows when your application needs JSON request bodies, task status lookup, and consistent RunAPI errors in Ruby.
4
+
5
+ This suno ai api README is the Ruby package guide inside the public `suno-sdk` repository. For the repository overview, start at `../README.md`; for model details, use https://runapi.ai/models/suno; for API reference, use https://runapi.ai/docs#suno; for SDK docs, use https://runapi.ai/docs#sdk-suno.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ gem install runapi-suno
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```ruby
16
+ require "runapi-suno"
17
+
18
+ client = RunApi::Suno::Client.new
19
+ task = client.generations.create(
20
+ # Pass the Suno JSON request body from https://runapi.ai/docs#suno.
21
+ )
22
+ status = client.generations.get(task.id)
23
+ ```
24
+
25
+ Use `create` when you want to submit a task and return quickly, `get` when you need the latest task state, and `run` when a script should create and poll until completion. In web request handlers, prefer `create` plus webhook or later `get` polling so a worker is not held open.
26
+
27
+ ## Language notes
28
+
29
+ Use Ruby keyword arguments and the `RunApi::Suno` error classes when building music jobs, Rails workers, or scripts. The available resources include generations, extensions, upload and extensions, covers, upload and covers, instrumentals, vocals, vocal removals, midi, wav conversions, music videos, lyrics, timestamped lyrics, section replacements, mashups, sounds, personas, and styles. Keep `RUNAPI_API_KEY` in the environment or your secret manager; never commit API keys or callback secrets.
30
+
31
+ ## Links
32
+
33
+ - Model page: https://runapi.ai/models/suno
34
+ - SDK docs: https://runapi.ai/docs#sdk-suno
35
+ - Product docs: https://runapi.ai/docs#suno
36
+ - Pricing and rate limits: https://runapi.ai/models/suno/v3.5
37
+ - Provider comparison: https://runapi.ai/providers/suno
38
+ - Full catalog: https://runapi.ai/models
39
+ - Repository: https://github.com/runapi-ai/suno-sdk
40
+
41
+ ## License
42
+
43
+ Licensed under the Apache License, Version 2.0.
@@ -6,7 +6,9 @@ module RunApi
6
6
  attr_reader :text_to_music, :extend_music, :generate_artwork, :cover_audio,
7
7
  :add_instrumental, :add_vocals, :separate_audio_stems, :generate_midi,
8
8
  :convert_audio, :visualize_music, :generate_lyrics, :get_timestamped_lyrics,
9
- :replace_section, :create_mashup, :text_to_sound, :generate_persona, :boost_style
9
+ :replace_section, :create_mashup, :text_to_sound, :voice_to_validation_phrase,
10
+ :regenerate_validation_phrase, :generate_voice, :check_voice, :generate_persona,
11
+ :boost_style
10
12
 
11
13
  def initialize(api_key: nil, **options)
12
14
  @api_key = Core::Auth.resolve_api_key(api_key)
@@ -29,6 +31,10 @@ module RunApi
29
31
  @replace_section = Resources::ReplaceSection.new(http)
30
32
  @create_mashup = Resources::CreateMashup.new(http)
31
33
  @text_to_sound = Resources::TextToSound.new(http)
34
+ @voice_to_validation_phrase = Resources::VoiceToValidationPhrase.new(http)
35
+ @regenerate_validation_phrase = Resources::RegenerateValidationPhrase.new(http)
36
+ @generate_voice = Resources::GenerateVoice.new(http)
37
+ @check_voice = Resources::CheckVoice.new(http)
32
38
  @generate_persona = Resources::GeneratePersona.new(http)
33
39
  @boost_style = Resources::BoostStyle.new(http)
34
40
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RunApi
4
+ module Suno
5
+ module Resources
6
+ class CheckVoice
7
+ include RunApi::Core::ResourceHelpers
8
+
9
+ ENDPOINT = "/api/v1/suno/check_voice"
10
+ RESPONSE_CLASS = Types::CheckVoiceResponse
11
+
12
+ def initialize(http)
13
+ @http = http
14
+ end
15
+
16
+ def run(**params)
17
+ params = compact_params(params)
18
+ validate_params!(params)
19
+ request(:post, ENDPOINT, body: params)
20
+ end
21
+
22
+ private
23
+
24
+ def validate_params!(params)
25
+ Validators.validate_check_voice!(params, self)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RunApi
4
+ module Suno
5
+ module Resources
6
+ class GenerateVoice
7
+ include RunApi::Core::ResourceHelpers
8
+
9
+ ENDPOINT = "/api/v1/suno/generate_voice"
10
+ RESPONSE_CLASS = Types::VoiceGenerationResponse
11
+ COMPLETED_RESPONSE_CLASS = Types::CompletedVoiceGenerationResponse
12
+
13
+ def initialize(http)
14
+ @http = http
15
+ end
16
+
17
+ def run(**params)
18
+ task = create(**params)
19
+ poll_until_complete { get(task.id) }
20
+ end
21
+
22
+ def create(**params)
23
+ params = compact_params(params)
24
+ validate_params!(params)
25
+ request(:post, ENDPOINT, body: params)
26
+ end
27
+
28
+ def get(id)
29
+ request(:get, "#{ENDPOINT}/#{id}")
30
+ end
31
+
32
+ private
33
+
34
+ def validate_params!(params)
35
+ Validators.validate_generate_voice!(params, self)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RunApi
4
+ module Suno
5
+ module Resources
6
+ class RegenerateValidationPhrase
7
+ include RunApi::Core::ResourceHelpers
8
+
9
+ ENDPOINT = "/api/v1/suno/regenerate_validation_phrase"
10
+ RESPONSE_CLASS = Types::ValidationPhraseResponse
11
+ COMPLETED_RESPONSE_CLASS = Types::CompletedValidationPhraseResponse
12
+
13
+ def initialize(http)
14
+ @http = http
15
+ end
16
+
17
+ def run(**params)
18
+ task = create(**params)
19
+ poll_until_complete { get(task.id) }
20
+ end
21
+
22
+ def create(**params)
23
+ params = compact_params(params)
24
+ validate_params!(params)
25
+ request(:post, ENDPOINT, body: params)
26
+ end
27
+
28
+ def get(id)
29
+ request(:get, "#{ENDPOINT}/#{id}")
30
+ end
31
+
32
+ private
33
+
34
+ def validate_params!(params)
35
+ Validators.validate_regenerate_validation_phrase!(params, self)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RunApi
4
+ module Suno
5
+ module Resources
6
+ class VoiceToValidationPhrase
7
+ include RunApi::Core::ResourceHelpers
8
+
9
+ ENDPOINT = "/api/v1/suno/voice_to_validation_phrase"
10
+ RESPONSE_CLASS = Types::ValidationPhraseResponse
11
+ COMPLETED_RESPONSE_CLASS = Types::CompletedValidationPhraseResponse
12
+
13
+ def initialize(http)
14
+ @http = http
15
+ end
16
+
17
+ def run(**params)
18
+ task = create(**params)
19
+ poll_until_complete { get(task.id) }
20
+ end
21
+
22
+ def create(**params)
23
+ params = compact_params(params)
24
+ validate_params!(params)
25
+ request(:post, ENDPOINT, body: params)
26
+ end
27
+
28
+ def get(id)
29
+ request(:get, "#{ENDPOINT}/#{id}")
30
+ end
31
+
32
+ private
33
+
34
+ def validate_params!(params)
35
+ Validators.validate_voice_to_validation_phrase!(params, self)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -3,17 +3,33 @@
3
3
  module RunApi
4
4
  module Suno
5
5
  module Types
6
- MODELS = %w[V5_5 V5 V4_5PLUS V4_5ALL V4_5 V4 V3_5].freeze
7
- SOUND_MODELS = %w[V5 V5_5].freeze
6
+ MODELS = %w[suno-v5.5 suno-v5 suno-v4.5-plus suno-v4.5-all suno-v4.5 suno-v4].freeze
7
+ SOUND_MODELS = %w[suno-v5 suno-v5.5].freeze
8
8
  SOUND_KEYS = %w[
9
9
  Cm C#m Dm D#m Em Fm F#m Gm G#m Am A#m Bm
10
10
  C C# D D# E F F# G G# A A# B
11
11
  ].freeze
12
- VOCAL_GENDERS = %w[f m].freeze
13
- PERSONA_MODELS = %w[style_persona voice_persona].freeze
12
+ VOCAL_GENDERS = %w[female male].freeze
13
+ PERSONA_TYPES = %w[style voice].freeze
14
+ PARAMETER_MODES = %w[source custom].freeze
15
+ VOCAL_MODES = %w[auto_lyrics exact_lyrics instrumental].freeze
14
16
  SEPARATE_AUDIO_STEMS_TYPES = %w[separate_vocal split_stem].freeze
17
+ VALIDATION_PHRASE_LANGUAGES = %w[en zh es fr pt de ja ko hi ru].freeze
18
+ SINGER_SKILL_LEVELS = %w[beginner intermediate advanced professional].freeze
15
19
 
16
20
  class Audio < RunApi::Core::BaseModel
21
+ optional :id, String
22
+ optional :audio_url, String
23
+ optional :stream_audio_url, String
24
+ optional :image_url, String
25
+ optional :lyrics, String
26
+ optional :model_name, String
27
+ optional :title, String
28
+ optional :tags, [String]
29
+ optional :duration, Numeric
30
+ end
31
+
32
+ class SoundAudio < RunApi::Core::BaseModel
17
33
  optional :id, String
18
34
  optional :audio_url, String
19
35
  optional :stream_audio_url, String
@@ -21,7 +37,7 @@ module RunApi
21
37
  optional :prompt, String
22
38
  optional :model_name, String
23
39
  optional :title, String
24
- optional :tags, [ String ]
40
+ optional :tags, [String]
25
41
  optional :duration, Numeric
26
42
  end
27
43
 
@@ -63,7 +79,7 @@ module RunApi
63
79
 
64
80
  class MidiInstrument < RunApi::Core::BaseModel
65
81
  required :name, String
66
- optional :notes, [ -> { MidiNote } ]
82
+ optional :notes, [-> { MidiNote }]
67
83
  end
68
84
 
69
85
  class Lyric < RunApi::Core::BaseModel
@@ -85,33 +101,36 @@ module RunApi
85
101
  end
86
102
 
87
103
  class TextToMusicResponse < AsyncTaskResponse
88
- optional :audios, [ -> { Audio } ]
104
+ optional :audios, [-> { Audio }]
89
105
  optional :audio_url, String
90
106
  end
91
107
 
92
108
  class ExtendMusicResponse < AsyncTaskResponse
93
- optional :audios, [ -> { Audio } ]
109
+ optional :audios, [-> { Audio }]
94
110
  optional :original_task_id, String
95
111
  end
96
112
 
97
113
  class GenerateArtworkResponse < AsyncTaskResponse
98
- optional :covers, [ -> { Cover } ]
114
+ optional :covers, [-> { Cover }]
99
115
  end
100
116
 
101
117
  class CoverAudioResponse < AsyncTaskResponse
102
- optional :audios, [ -> { Audio } ]
118
+ optional :audios, [-> { Audio }]
103
119
  end
104
120
 
105
121
  class AddInstrumentalResponse < TextToMusicResponse; end
106
122
  class AddVocalsResponse < TextToMusicResponse; end
107
- class TextToSoundResponse < TextToMusicResponse; end
123
+
124
+ class TextToSoundResponse < AsyncTaskResponse
125
+ optional :audios, [-> { SoundAudio }]
126
+ end
108
127
 
109
128
  class SeparateAudioStemsResponse < AsyncTaskResponse
110
129
  optional :separated_audios, -> { SeparatedAudio }
111
130
  end
112
131
 
113
132
  class GenerateMidiResponse < AsyncTaskResponse
114
- optional :instruments, [ -> { MidiInstrument } ]
133
+ optional :instruments, [-> { MidiInstrument }]
115
134
  end
116
135
 
117
136
  class ConvertAudioResponse < AsyncTaskResponse
@@ -125,19 +144,19 @@ module RunApi
125
144
  end
126
145
 
127
146
  class GenerateLyricsResponse < AsyncTaskResponse
128
- optional :lyrics, [ -> { Lyric } ]
147
+ optional :lyrics, [-> { Lyric }]
129
148
  end
130
149
 
131
150
  class GetTimestampedLyricsResponse < RunApi::Core::BaseModel
132
- optional :aligned_words, [ -> { AlignedWord } ]
133
- optional :waveform_data, [ Numeric ]
151
+ optional :aligned_words, [-> { AlignedWord }]
152
+ optional :waveform_data, [Numeric]
134
153
  optional :hoot_cer, Numeric
135
154
  optional :is_streamed
136
155
  end
137
156
 
138
157
  class ReplaceSectionResponse < AsyncTaskResponse
139
158
  optional :track, -> { Audio }
140
- optional :audios, [ -> { Audio } ]
159
+ optional :audios, [-> { Audio }]
141
160
  end
142
161
 
143
162
  class GeneratePersonaResponse < RunApi::Core::BaseModel
@@ -152,31 +171,46 @@ module RunApi
152
171
 
153
172
  class CreateMashupResponse < AsyncTaskResponse
154
173
  optional :audio, -> { Audio }
155
- optional :audios, [ -> { Audio } ]
174
+ optional :audios, [-> { Audio }]
175
+ end
176
+
177
+ class ValidationPhraseResponse < AsyncTaskResponse
178
+ optional :provider_status, String
179
+ optional :validation_phrase, String
180
+ end
181
+
182
+ class VoiceGenerationResponse < AsyncTaskResponse
183
+ optional :provider_status, String
184
+ optional :voice_id, String
185
+ end
186
+
187
+ class CheckVoiceResponse < RunApi::Core::BaseModel
188
+ optional :is_available
189
+ optional :error, String
156
190
  end
157
191
 
158
192
  class CompletedTextToMusicResponse < TextToMusicResponse
159
- required :audios, [ -> { Audio } ]
193
+ required :audios, [-> { Audio }]
160
194
  end
161
195
 
162
196
  class CompletedExtendMusicResponse < ExtendMusicResponse
163
- required :audios, [ -> { Audio } ]
197
+ required :audios, [-> { Audio }]
164
198
  end
165
199
 
166
200
  class CompletedGenerateArtworkResponse < GenerateArtworkResponse
167
- required :covers, [ -> { Cover } ]
201
+ required :covers, [-> { Cover }]
168
202
  end
169
203
 
170
204
  class CompletedCoverAudioResponse < CoverAudioResponse
171
- required :audios, [ -> { Audio } ]
205
+ required :audios, [-> { Audio }]
172
206
  end
173
207
 
174
208
  class CompletedAddInstrumentalResponse < AddInstrumentalResponse
175
- required :audios, [ -> { Audio } ]
209
+ required :audios, [-> { Audio }]
176
210
  end
177
211
 
178
212
  class CompletedAddVocalsResponse < AddVocalsResponse
179
- required :audios, [ -> { Audio } ]
213
+ required :audios, [-> { Audio }]
180
214
  end
181
215
 
182
216
  class CompletedSeparateAudioStemsResponse < SeparateAudioStemsResponse
@@ -184,7 +218,7 @@ module RunApi
184
218
  end
185
219
 
186
220
  class CompletedGenerateMidiResponse < GenerateMidiResponse
187
- required :instruments, [ -> { MidiInstrument } ]
221
+ required :instruments, [-> { MidiInstrument }]
188
222
  end
189
223
 
190
224
  class CompletedConvertAudioResponse < ConvertAudioResponse
@@ -196,7 +230,7 @@ module RunApi
196
230
  end
197
231
 
198
232
  class CompletedGenerateLyricsResponse < GenerateLyricsResponse
199
- required :lyrics, [ -> { Lyric } ]
233
+ required :lyrics, [-> { Lyric }]
200
234
  end
201
235
 
202
236
  class CompletedReplaceSectionResponse < ReplaceSectionResponse
@@ -204,11 +238,19 @@ module RunApi
204
238
  end
205
239
 
206
240
  class CompletedCreateMashupResponse < CreateMashupResponse
207
- required :audios, [ -> { Audio } ]
241
+ required :audios, [-> { Audio }]
208
242
  end
209
243
 
210
244
  class CompletedTextToSoundResponse < TextToSoundResponse
211
- required :audios, [ -> { Audio } ]
245
+ required :audios, [-> { SoundAudio }]
246
+ end
247
+
248
+ class CompletedValidationPhraseResponse < ValidationPhraseResponse
249
+ required :validation_phrase, String
250
+ end
251
+
252
+ class CompletedVoiceGenerationResponse < VoiceGenerationResponse
253
+ required :voice_id, String
212
254
  end
213
255
  end
214
256
  end
@@ -5,34 +5,34 @@ module RunApi
5
5
  module Validators
6
6
  module_function
7
7
 
8
+ MUSIC_PROMPT_SHAPE_ERROR = "choose a valid vocal_mode: auto_lyrics, exact_lyrics, or instrumental"
9
+
8
10
  def validate_text_to_music!(params, resource)
9
- if param(resource, params, :custom_mode)
10
- require_param!(resource, params, :style)
11
- require_param!(resource, params, :title)
12
- else
13
- require_param!(resource, params, :prompt)
14
- end
11
+ validate_music_prompt_shape!(params, resource)
15
12
  require_param!(resource, params, :model)
13
+ validate_optional!(resource, params, :vocal_mode, Types::VOCAL_MODES)
16
14
  validate_optional!(resource, params, :model, Types::MODELS)
17
15
  validate_optional!(resource, params, :vocal_gender, Types::VOCAL_GENDERS)
18
- validate_optional!(resource, params, :persona_model, Types::PERSONA_MODELS)
16
+ validate_optional!(resource, params, :persona_type, Types::PERSONA_TYPES)
19
17
  end
20
18
 
21
19
  def validate_extend_music!(params, resource)
22
20
  unless %i[task_id audio_id audio_url upload_url].any? { |key| param(resource, params, key) }
23
21
  raise Core::ValidationError, "task_id, audio_id, audio_url, or upload_url is required"
24
22
  end
25
- require_param!(resource, params, :default_param_flag)
23
+ require_param!(resource, params, :parameter_mode)
26
24
  require_param!(resource, params, :model)
27
25
 
28
- if truthy?(param(resource, params, :default_param_flag))
26
+ validate_optional!(resource, params, :parameter_mode, Types::PARAMETER_MODES)
27
+ if param(resource, params, :parameter_mode) == "custom"
29
28
  require_param!(resource, params, :style)
30
29
  require_param!(resource, params, :title)
31
30
  require_param!(resource, params, :continue_at)
32
31
  end
32
+ validate_extend_music_prompt_shape!(params, resource)
33
33
  validate_optional!(resource, params, :model, Types::MODELS)
34
34
  validate_optional!(resource, params, :vocal_gender, Types::VOCAL_GENDERS)
35
- validate_optional!(resource, params, :persona_model, Types::PERSONA_MODELS)
35
+ validate_optional!(resource, params, :persona_type, Types::PERSONA_TYPES)
36
36
  end
37
37
 
38
38
  def validate_generate_artwork!(params, resource)
@@ -42,15 +42,11 @@ module RunApi
42
42
  def validate_cover_audio!(params, resource)
43
43
  require_param!(resource, params, :upload_url)
44
44
  require_param!(resource, params, :model)
45
- if param(resource, params, :custom_mode)
46
- require_param!(resource, params, :style)
47
- require_param!(resource, params, :title)
48
- else
49
- require_param!(resource, params, :prompt)
50
- end
45
+ validate_music_prompt_shape!(params, resource)
46
+ validate_optional!(resource, params, :vocal_mode, Types::VOCAL_MODES)
51
47
  validate_optional!(resource, params, :model, Types::MODELS)
52
48
  validate_optional!(resource, params, :vocal_gender, Types::VOCAL_GENDERS)
53
- validate_optional!(resource, params, :persona_model, Types::PERSONA_MODELS)
49
+ validate_optional!(resource, params, :persona_type, Types::PERSONA_TYPES)
54
50
  end
55
51
 
56
52
  def validate_add_instrumental!(params, resource)
@@ -60,7 +56,7 @@ module RunApi
60
56
  end
61
57
 
62
58
  def validate_add_vocals!(params, resource)
63
- require_all!(resource, params, :upload_url, :prompt, :title, :negative_tags, :style, :model)
59
+ require_all!(resource, params, :upload_url, :lyrics, :title, :negative_tags, :style, :model)
64
60
  validate_optional!(resource, params, :model, Types::MODELS)
65
61
  validate_optional!(resource, params, :vocal_gender, Types::VOCAL_GENDERS)
66
62
  end
@@ -91,7 +87,7 @@ module RunApi
91
87
  end
92
88
 
93
89
  def validate_replace_section!(params, resource)
94
- require_all!(resource, params, :task_id, :audio_id, :prompt, :tags, :title, :infill_start_time, :infill_end_time)
90
+ require_all!(resource, params, :task_id, :audio_id, :lyrics, :tags, :title, :infill_start_time, :infill_end_time)
95
91
  if param(resource, params, :infill_end_time).to_f <= param(resource, params, :infill_start_time).to_f
96
92
  raise Core::ValidationError, "infill_end_time must be greater than infill_start_time"
97
93
  end
@@ -103,14 +99,11 @@ module RunApi
103
99
  raise Core::ValidationError, "upload_url_list must contain exactly 2 URLs"
104
100
  end
105
101
  require_param!(resource, params, :model)
106
- if param(resource, params, :custom_mode)
107
- require_all!(resource, params, :style, :title)
108
- require_param!(resource, params, :prompt) unless truthy?(param(resource, params, :instrumental))
109
- else
110
- require_param!(resource, params, :prompt)
111
- end
102
+ validate_music_prompt_shape!(params, resource)
103
+ validate_optional!(resource, params, :vocal_mode, Types::VOCAL_MODES)
112
104
  validate_optional!(resource, params, :model, Types::MODELS)
113
105
  validate_optional!(resource, params, :vocal_gender, Types::VOCAL_GENDERS)
106
+ validate_optional!(resource, params, :persona_type, Types::PERSONA_TYPES)
114
107
  end
115
108
 
116
109
  def validate_text_to_sound!(params, resource)
@@ -123,6 +116,30 @@ module RunApi
123
116
  end
124
117
  end
125
118
 
119
+ def validate_voice_to_validation_phrase!(params, resource)
120
+ require_all!(resource, params, :voice_url, :vocal_start_seconds, :vocal_end_seconds)
121
+ validate_optional!(resource, params, :language, Types::VALIDATION_PHRASE_LANGUAGES)
122
+
123
+ start_seconds = param(resource, params, :vocal_start_seconds).to_i
124
+ end_seconds = param(resource, params, :vocal_end_seconds).to_i
125
+ return if end_seconds > start_seconds
126
+
127
+ raise Core::ValidationError, "vocal_end_seconds must be greater than vocal_start_seconds"
128
+ end
129
+
130
+ def validate_regenerate_validation_phrase!(params, resource)
131
+ require_param!(resource, params, :task_id)
132
+ end
133
+
134
+ def validate_generate_voice!(params, resource)
135
+ require_all!(resource, params, :task_id, :verify_url)
136
+ validate_optional!(resource, params, :singer_skill_level, Types::SINGER_SKILL_LEVELS)
137
+ end
138
+
139
+ def validate_check_voice!(params, resource)
140
+ require_param!(resource, params, :task_id)
141
+ end
142
+
126
143
  def validate_generate_persona!(params, resource)
127
144
  require_all!(resource, params, :task_id, :audio_id, :name, :description)
128
145
  end
@@ -131,6 +148,45 @@ module RunApi
131
148
  require_param!(resource, params, :description)
132
149
  end
133
150
 
151
+ def validate_music_prompt_shape!(params, resource)
152
+ mode = param(resource, params, :vocal_mode).to_s
153
+ has_prompt = truthy_presence?(param(resource, params, :prompt))
154
+ has_lyrics = truthy_presence?(param(resource, params, :lyrics))
155
+ has_style = truthy_presence?(param(resource, params, :style))
156
+ has_title = truthy_presence?(param(resource, params, :title))
157
+
158
+ valid_shape = case mode
159
+ when "auto_lyrics"
160
+ has_prompt && !has_lyrics && !has_style && !has_title
161
+ when "exact_lyrics"
162
+ !has_prompt && has_lyrics && has_style && has_title
163
+ when "instrumental"
164
+ !has_prompt && !has_lyrics && has_style && has_title
165
+ else
166
+ false
167
+ end
168
+ return if valid_shape
169
+
170
+ raise Core::ValidationError, MUSIC_PROMPT_SHAPE_ERROR
171
+ end
172
+
173
+ def validate_extend_music_prompt_shape!(params, resource)
174
+ return unless truthy_presence?(param(resource, params, :lyrics))
175
+
176
+ if truthy_presence?(param(resource, params, :prompt))
177
+ raise Core::ValidationError, "prompt cannot be combined with lyrics"
178
+ end
179
+
180
+ if truthy?(param(resource, params, :instrumental))
181
+ raise Core::ValidationError, "lyrics cannot be used when instrumental is true"
182
+ end
183
+
184
+ upload_mode = %i[audio_url upload_url].any? { |key| truthy_presence?(param(resource, params, key)) }
185
+ return if param(resource, params, :parameter_mode) == "custom" && upload_mode
186
+
187
+ raise Core::ValidationError, "lyrics can only be used when extending uploaded audio with custom parameters"
188
+ end
189
+
134
190
  def require_all!(resource, params, *keys)
135
191
  keys.each { |key| require_param!(resource, params, key) }
136
192
  end
@@ -148,7 +204,13 @@ module RunApi
148
204
  end
149
205
 
150
206
  def truthy?(value)
151
- [ true, 1, "1", "true", "TRUE", "True" ].include?(value)
207
+ [true, 1, "1", "true", "TRUE", "True"].include?(value)
208
+ end
209
+
210
+ def truthy_presence?(value)
211
+ return !value.empty? if value.respond_to?(:empty?)
212
+
213
+ !value.nil?
152
214
  end
153
215
  end
154
216
  end
data/lib/runapi/suno.rb CHANGED
@@ -18,6 +18,10 @@ require_relative "suno/resources/get_timestamped_lyrics"
18
18
  require_relative "suno/resources/replace_section"
19
19
  require_relative "suno/resources/create_mashup"
20
20
  require_relative "suno/resources/text_to_sound"
21
+ require_relative "suno/resources/voice_to_validation_phrase"
22
+ require_relative "suno/resources/regenerate_validation_phrase"
23
+ require_relative "suno/resources/generate_voice"
24
+ require_relative "suno/resources/check_voice"
21
25
  require_relative "suno/resources/generate_persona"
22
26
  require_relative "suno/resources/boost_style"
23
27
  require_relative "suno/client"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: runapi-suno
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - RunAPI
@@ -15,28 +15,34 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '0.1'
18
+ version: 0.2.5
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '0.1'
26
- description: RunAPI Suno SDK for JavaScript, Ruby, and Go
25
+ version: 0.2.5
26
+ description: The suno ai api Ruby SDK is the language-specific package for Suno on
27
+ RunAPI. Use this suno ai api package for song generation, lyrics, vocal, extension,
28
+ and audio transformation flows when your application needs JSON request bodies,
29
+ task status lookup, and consistent RunAPI errors in Ruby.
27
30
  email:
28
31
  - contact@runapi.ai
29
32
  executables: []
30
33
  extensions: []
31
- extra_rdoc_files: []
34
+ extra_rdoc_files:
35
+ - README.md
32
36
  files:
33
37
  - LICENSE
38
+ - README.md
34
39
  - lib/runapi-suno.rb
35
40
  - lib/runapi/suno.rb
36
41
  - lib/runapi/suno/client.rb
37
42
  - lib/runapi/suno/resources/add_instrumental.rb
38
43
  - lib/runapi/suno/resources/add_vocals.rb
39
44
  - lib/runapi/suno/resources/boost_style.rb
45
+ - lib/runapi/suno/resources/check_voice.rb
40
46
  - lib/runapi/suno/resources/convert_audio.rb
41
47
  - lib/runapi/suno/resources/cover_audio.rb
42
48
  - lib/runapi/suno/resources/create_mashup.rb
@@ -45,12 +51,15 @@ files:
45
51
  - lib/runapi/suno/resources/generate_lyrics.rb
46
52
  - lib/runapi/suno/resources/generate_midi.rb
47
53
  - lib/runapi/suno/resources/generate_persona.rb
54
+ - lib/runapi/suno/resources/generate_voice.rb
48
55
  - lib/runapi/suno/resources/get_timestamped_lyrics.rb
56
+ - lib/runapi/suno/resources/regenerate_validation_phrase.rb
49
57
  - lib/runapi/suno/resources/replace_section.rb
50
58
  - lib/runapi/suno/resources/separate_audio_stems.rb
51
59
  - lib/runapi/suno/resources/text_to_music.rb
52
60
  - lib/runapi/suno/resources/text_to_sound.rb
53
61
  - lib/runapi/suno/resources/visualize_music.rb
62
+ - lib/runapi/suno/resources/voice_to_validation_phrase.rb
54
63
  - lib/runapi/suno/types.rb
55
64
  - lib/runapi/suno/validators.rb
56
65
  homepage: https://runapi.ai/models/suno
@@ -58,7 +67,7 @@ licenses:
58
67
  - Apache-2.0
59
68
  metadata:
60
69
  homepage_uri: https://runapi.ai/models/suno
61
- documentation_uri: https://runapi.ai/models/suno
70
+ documentation_uri: https://github.com/runapi-ai/suno-sdk/blob/main/ruby/README.md
62
71
  source_code_uri: https://github.com/runapi-ai/suno-sdk
63
72
  changelog_uri: https://github.com/runapi-ai/suno-sdk/blob/main/CHANGELOG.md
64
73
  rdoc_options: []
@@ -75,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
84
  - !ruby/object:Gem::Version
76
85
  version: '0'
77
86
  requirements: []
78
- rubygems_version: 4.0.6
87
+ rubygems_version: 4.0.10
79
88
  specification_version: 4
80
- summary: Suno API SDKs for JavaScript, Ruby, and Go on RunAPI.
89
+ summary: Suno AI API Ruby SDK for RunAPI
81
90
  test_files: []