runapi-suno 0.1.0
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 +7 -0
- data/LICENSE +201 -0
- data/lib/runapi/suno/client.rb +37 -0
- data/lib/runapi/suno/resources/add_instrumental.rb +40 -0
- data/lib/runapi/suno/resources/add_vocals.rb +40 -0
- data/lib/runapi/suno/resources/boost_style.rb +30 -0
- data/lib/runapi/suno/resources/convert_audio.rb +40 -0
- data/lib/runapi/suno/resources/cover_audio.rb +40 -0
- data/lib/runapi/suno/resources/create_mashup.rb +40 -0
- data/lib/runapi/suno/resources/extend_music.rb +40 -0
- data/lib/runapi/suno/resources/generate_artwork.rb +40 -0
- data/lib/runapi/suno/resources/generate_lyrics.rb +40 -0
- data/lib/runapi/suno/resources/generate_midi.rb +40 -0
- data/lib/runapi/suno/resources/generate_persona.rb +30 -0
- data/lib/runapi/suno/resources/get_timestamped_lyrics.rb +30 -0
- data/lib/runapi/suno/resources/replace_section.rb +40 -0
- data/lib/runapi/suno/resources/separate_audio_stems.rb +40 -0
- data/lib/runapi/suno/resources/text_to_music.rb +40 -0
- data/lib/runapi/suno/resources/text_to_sound.rb +40 -0
- data/lib/runapi/suno/resources/visualize_music.rb +40 -0
- data/lib/runapi/suno/types.rb +215 -0
- data/lib/runapi/suno/validators.rb +155 -0
- data/lib/runapi/suno.rb +35 -0
- data/lib/runapi-suno.rb +3 -0
- metadata +81 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RunApi
|
|
4
|
+
module Suno
|
|
5
|
+
module Resources
|
|
6
|
+
class GetTimestampedLyrics
|
|
7
|
+
include RunApi::Core::ResourceHelpers
|
|
8
|
+
|
|
9
|
+
ENDPOINT = "/api/v1/suno/get_timestamped_lyrics"
|
|
10
|
+
RESPONSE_CLASS = Types::GetTimestampedLyricsResponse
|
|
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_get_timestamped_lyrics!(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 ReplaceSection
|
|
7
|
+
include RunApi::Core::ResourceHelpers
|
|
8
|
+
|
|
9
|
+
ENDPOINT = "/api/v1/suno/replace_section"
|
|
10
|
+
RESPONSE_CLASS = Types::ReplaceSectionResponse
|
|
11
|
+
COMPLETED_RESPONSE_CLASS = Types::CompletedReplaceSectionResponse
|
|
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_replace_section!(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 SeparateAudioStems
|
|
7
|
+
include RunApi::Core::ResourceHelpers
|
|
8
|
+
|
|
9
|
+
ENDPOINT = "/api/v1/suno/separate_audio_stems"
|
|
10
|
+
RESPONSE_CLASS = Types::SeparateAudioStemsResponse
|
|
11
|
+
COMPLETED_RESPONSE_CLASS = Types::CompletedSeparateAudioStemsResponse
|
|
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_separate_audio_stems!(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 TextToMusic
|
|
7
|
+
include RunApi::Core::ResourceHelpers
|
|
8
|
+
|
|
9
|
+
ENDPOINT = "/api/v1/suno/text_to_music"
|
|
10
|
+
RESPONSE_CLASS = Types::TextToMusicResponse
|
|
11
|
+
COMPLETED_RESPONSE_CLASS = Types::CompletedTextToMusicResponse
|
|
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_text_to_music!(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 TextToSound
|
|
7
|
+
include RunApi::Core::ResourceHelpers
|
|
8
|
+
|
|
9
|
+
ENDPOINT = "/api/v1/suno/text_to_sound"
|
|
10
|
+
RESPONSE_CLASS = Types::TextToSoundResponse
|
|
11
|
+
COMPLETED_RESPONSE_CLASS = Types::CompletedTextToSoundResponse
|
|
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_text_to_sound!(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 VisualizeMusic
|
|
7
|
+
include RunApi::Core::ResourceHelpers
|
|
8
|
+
|
|
9
|
+
ENDPOINT = "/api/v1/suno/visualize_music"
|
|
10
|
+
RESPONSE_CLASS = Types::VisualizeMusicResponse
|
|
11
|
+
COMPLETED_RESPONSE_CLASS = Types::CompletedVisualizeMusicResponse
|
|
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_visualize_music!(params, self)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RunApi
|
|
4
|
+
module Suno
|
|
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
|
|
8
|
+
SOUND_KEYS = %w[
|
|
9
|
+
Cm C#m Dm D#m Em Fm F#m Gm G#m Am A#m Bm
|
|
10
|
+
C C# D D# E F F# G G# A A# B
|
|
11
|
+
].freeze
|
|
12
|
+
VOCAL_GENDERS = %w[f m].freeze
|
|
13
|
+
PERSONA_MODELS = %w[style_persona voice_persona].freeze
|
|
14
|
+
SEPARATE_AUDIO_STEMS_TYPES = %w[separate_vocal split_stem].freeze
|
|
15
|
+
|
|
16
|
+
class Audio < RunApi::Core::BaseModel
|
|
17
|
+
optional :id, String
|
|
18
|
+
optional :audio_url, String
|
|
19
|
+
optional :stream_audio_url, String
|
|
20
|
+
optional :image_url, String
|
|
21
|
+
optional :prompt, String
|
|
22
|
+
optional :model_name, String
|
|
23
|
+
optional :title, String
|
|
24
|
+
optional :tags, [ String ]
|
|
25
|
+
optional :duration, Numeric
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class Cover < RunApi::Core::BaseModel
|
|
29
|
+
required :url, String
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class AlignedWord < RunApi::Core::BaseModel
|
|
33
|
+
required :word, String
|
|
34
|
+
required :success
|
|
35
|
+
required :start_time, Numeric
|
|
36
|
+
required :end_time, Numeric
|
|
37
|
+
required :palign, Numeric
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class SeparatedAudio < RunApi::Core::BaseModel
|
|
41
|
+
optional :vocal_url, String
|
|
42
|
+
optional :instrumental_url, String
|
|
43
|
+
optional :backing_vocals_url, String
|
|
44
|
+
optional :bass_url, String
|
|
45
|
+
optional :brass_url, String
|
|
46
|
+
optional :drums_url, String
|
|
47
|
+
optional :fx_url, String
|
|
48
|
+
optional :guitar_url, String
|
|
49
|
+
optional :keyboard_url, String
|
|
50
|
+
optional :percussion_url, String
|
|
51
|
+
optional :piano_url, String
|
|
52
|
+
optional :strings_url, String
|
|
53
|
+
optional :synth_url, String
|
|
54
|
+
optional :woodwinds_url, String
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class MidiNote < RunApi::Core::BaseModel
|
|
58
|
+
required :pitch, Numeric
|
|
59
|
+
required :start_time, Numeric
|
|
60
|
+
required :end_time, Numeric
|
|
61
|
+
required :velocity, Numeric
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class MidiInstrument < RunApi::Core::BaseModel
|
|
65
|
+
required :name, String
|
|
66
|
+
optional :notes, [ -> { MidiNote } ]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class Lyric < RunApi::Core::BaseModel
|
|
70
|
+
optional :title, String
|
|
71
|
+
required :text, String
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
class Persona < RunApi::Core::BaseModel
|
|
75
|
+
required :id, String
|
|
76
|
+
required :name, String
|
|
77
|
+
required :description, String
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
class AsyncTaskResponse < RunApi::Core::TaskResponse
|
|
81
|
+
required :id, String
|
|
82
|
+
required :status, String, enum: -> { RunApi::Core::TaskResponse::Status::ALL }
|
|
83
|
+
optional :generation_stage, String
|
|
84
|
+
optional :error, String
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class TextToMusicResponse < AsyncTaskResponse
|
|
88
|
+
optional :audios, [ -> { Audio } ]
|
|
89
|
+
optional :audio_url, String
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
class ExtendMusicResponse < AsyncTaskResponse
|
|
93
|
+
optional :audios, [ -> { Audio } ]
|
|
94
|
+
optional :original_task_id, String
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
class GenerateArtworkResponse < AsyncTaskResponse
|
|
98
|
+
optional :covers, [ -> { Cover } ]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
class CoverAudioResponse < AsyncTaskResponse
|
|
102
|
+
optional :audios, [ -> { Audio } ]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class AddInstrumentalResponse < TextToMusicResponse; end
|
|
106
|
+
class AddVocalsResponse < TextToMusicResponse; end
|
|
107
|
+
class TextToSoundResponse < TextToMusicResponse; end
|
|
108
|
+
|
|
109
|
+
class SeparateAudioStemsResponse < AsyncTaskResponse
|
|
110
|
+
optional :separated_audios, -> { SeparatedAudio }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
class GenerateMidiResponse < AsyncTaskResponse
|
|
114
|
+
optional :instruments, [ -> { MidiInstrument } ]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
class ConvertAudioResponse < AsyncTaskResponse
|
|
118
|
+
optional :wav_url, String
|
|
119
|
+
optional :original_task_id, String
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
class VisualizeMusicResponse < AsyncTaskResponse
|
|
123
|
+
optional :video_url, String
|
|
124
|
+
optional :original_task_id, String
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
class GenerateLyricsResponse < AsyncTaskResponse
|
|
128
|
+
optional :lyrics, [ -> { Lyric } ]
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
class GetTimestampedLyricsResponse < RunApi::Core::BaseModel
|
|
132
|
+
optional :aligned_words, [ -> { AlignedWord } ]
|
|
133
|
+
optional :waveform_data, [ Numeric ]
|
|
134
|
+
optional :hoot_cer, Numeric
|
|
135
|
+
optional :is_streamed
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
class ReplaceSectionResponse < AsyncTaskResponse
|
|
139
|
+
optional :track, -> { Audio }
|
|
140
|
+
optional :audios, [ -> { Audio } ]
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
class GeneratePersonaResponse < RunApi::Core::BaseModel
|
|
144
|
+
required :persona, -> { Persona }
|
|
145
|
+
optional :error, String
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
class BoostStyleResponse < RunApi::Core::BaseModel
|
|
149
|
+
optional :style, String
|
|
150
|
+
optional :error, String
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
class CreateMashupResponse < AsyncTaskResponse
|
|
154
|
+
optional :audio, -> { Audio }
|
|
155
|
+
optional :audios, [ -> { Audio } ]
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
class CompletedTextToMusicResponse < TextToMusicResponse
|
|
159
|
+
required :audios, [ -> { Audio } ]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
class CompletedExtendMusicResponse < ExtendMusicResponse
|
|
163
|
+
required :audios, [ -> { Audio } ]
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
class CompletedGenerateArtworkResponse < GenerateArtworkResponse
|
|
167
|
+
required :covers, [ -> { Cover } ]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
class CompletedCoverAudioResponse < CoverAudioResponse
|
|
171
|
+
required :audios, [ -> { Audio } ]
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
class CompletedAddInstrumentalResponse < AddInstrumentalResponse
|
|
175
|
+
required :audios, [ -> { Audio } ]
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
class CompletedAddVocalsResponse < AddVocalsResponse
|
|
179
|
+
required :audios, [ -> { Audio } ]
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
class CompletedSeparateAudioStemsResponse < SeparateAudioStemsResponse
|
|
183
|
+
required :separated_audios, -> { SeparatedAudio }
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
class CompletedGenerateMidiResponse < GenerateMidiResponse
|
|
187
|
+
required :instruments, [ -> { MidiInstrument } ]
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
class CompletedConvertAudioResponse < ConvertAudioResponse
|
|
191
|
+
required :wav_url, String
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
class CompletedVisualizeMusicResponse < VisualizeMusicResponse
|
|
195
|
+
required :video_url, String
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
class CompletedGenerateLyricsResponse < GenerateLyricsResponse
|
|
199
|
+
required :lyrics, [ -> { Lyric } ]
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
class CompletedReplaceSectionResponse < ReplaceSectionResponse
|
|
203
|
+
required :track, -> { Audio }
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
class CompletedCreateMashupResponse < CreateMashupResponse
|
|
207
|
+
required :audios, [ -> { Audio } ]
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
class CompletedTextToSoundResponse < TextToSoundResponse
|
|
211
|
+
required :audios, [ -> { Audio } ]
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RunApi
|
|
4
|
+
module Suno
|
|
5
|
+
module Validators
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
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
|
|
15
|
+
require_param!(resource, params, :model)
|
|
16
|
+
validate_optional!(resource, params, :model, Types::MODELS)
|
|
17
|
+
validate_optional!(resource, params, :vocal_gender, Types::VOCAL_GENDERS)
|
|
18
|
+
validate_optional!(resource, params, :persona_model, Types::PERSONA_MODELS)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def validate_extend_music!(params, resource)
|
|
22
|
+
unless %i[task_id audio_id audio_url upload_url].any? { |key| param(resource, params, key) }
|
|
23
|
+
raise Core::ValidationError, "task_id, audio_id, audio_url, or upload_url is required"
|
|
24
|
+
end
|
|
25
|
+
require_param!(resource, params, :default_param_flag)
|
|
26
|
+
require_param!(resource, params, :model)
|
|
27
|
+
|
|
28
|
+
if truthy?(param(resource, params, :default_param_flag))
|
|
29
|
+
require_param!(resource, params, :style)
|
|
30
|
+
require_param!(resource, params, :title)
|
|
31
|
+
require_param!(resource, params, :continue_at)
|
|
32
|
+
end
|
|
33
|
+
validate_optional!(resource, params, :model, Types::MODELS)
|
|
34
|
+
validate_optional!(resource, params, :vocal_gender, Types::VOCAL_GENDERS)
|
|
35
|
+
validate_optional!(resource, params, :persona_model, Types::PERSONA_MODELS)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def validate_generate_artwork!(params, resource)
|
|
39
|
+
require_param!(resource, params, :task_id)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def validate_cover_audio!(params, resource)
|
|
43
|
+
require_param!(resource, params, :upload_url)
|
|
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
|
|
51
|
+
validate_optional!(resource, params, :model, Types::MODELS)
|
|
52
|
+
validate_optional!(resource, params, :vocal_gender, Types::VOCAL_GENDERS)
|
|
53
|
+
validate_optional!(resource, params, :persona_model, Types::PERSONA_MODELS)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def validate_add_instrumental!(params, resource)
|
|
57
|
+
require_all!(resource, params, :upload_url, :title, :negative_tags, :tags, :model)
|
|
58
|
+
validate_optional!(resource, params, :model, Types::MODELS)
|
|
59
|
+
validate_optional!(resource, params, :vocal_gender, Types::VOCAL_GENDERS)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def validate_add_vocals!(params, resource)
|
|
63
|
+
require_all!(resource, params, :upload_url, :prompt, :title, :negative_tags, :style, :model)
|
|
64
|
+
validate_optional!(resource, params, :model, Types::MODELS)
|
|
65
|
+
validate_optional!(resource, params, :vocal_gender, Types::VOCAL_GENDERS)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def validate_separate_audio_stems!(params, resource)
|
|
69
|
+
require_all!(resource, params, :task_id, :audio_id)
|
|
70
|
+
validate_optional!(resource, params, :type, Types::SEPARATE_AUDIO_STEMS_TYPES)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def validate_generate_midi!(params, resource)
|
|
74
|
+
require_param!(resource, params, :task_id)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def validate_convert_audio!(params, resource)
|
|
78
|
+
require_all!(resource, params, :task_id, :audio_id)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def validate_visualize_music!(params, resource)
|
|
82
|
+
require_all!(resource, params, :task_id, :audio_id)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def validate_generate_lyrics!(params, resource)
|
|
86
|
+
require_param!(resource, params, :prompt)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def validate_get_timestamped_lyrics!(params, resource)
|
|
90
|
+
require_all!(resource, params, :task_id, :audio_id)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def validate_replace_section!(params, resource)
|
|
94
|
+
require_all!(resource, params, :task_id, :audio_id, :prompt, :tags, :title, :infill_start_time, :infill_end_time)
|
|
95
|
+
if param(resource, params, :infill_end_time).to_f <= param(resource, params, :infill_start_time).to_f
|
|
96
|
+
raise Core::ValidationError, "infill_end_time must be greater than infill_start_time"
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def validate_create_mashup!(params, resource)
|
|
101
|
+
upload_url_list = param(resource, params, :upload_url_list)
|
|
102
|
+
unless upload_url_list.is_a?(Array) && upload_url_list.size == 2
|
|
103
|
+
raise Core::ValidationError, "upload_url_list must contain exactly 2 URLs"
|
|
104
|
+
end
|
|
105
|
+
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
|
|
112
|
+
validate_optional!(resource, params, :model, Types::MODELS)
|
|
113
|
+
validate_optional!(resource, params, :vocal_gender, Types::VOCAL_GENDERS)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def validate_text_to_sound!(params, resource)
|
|
117
|
+
require_all!(resource, params, :prompt, :model)
|
|
118
|
+
validate_optional!(resource, params, :model, Types::SOUND_MODELS)
|
|
119
|
+
validate_optional!(resource, params, :sound_key, Types::SOUND_KEYS)
|
|
120
|
+
tempo = param(resource, params, :sound_tempo)
|
|
121
|
+
if tempo && !(1..300).cover?(tempo.to_i)
|
|
122
|
+
raise Core::ValidationError, "sound_tempo must be between 1 and 300"
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def validate_generate_persona!(params, resource)
|
|
127
|
+
require_all!(resource, params, :task_id, :audio_id, :name, :description)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def validate_boost_style!(params, resource)
|
|
131
|
+
require_param!(resource, params, :description)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def require_all!(resource, params, *keys)
|
|
135
|
+
keys.each { |key| require_param!(resource, params, key) }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def require_param!(resource, params, key)
|
|
139
|
+
raise Core::ValidationError, "#{key} is required" if param(resource, params, key).nil?
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def param(resource, params, key)
|
|
143
|
+
resource.send(:param, params, key)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def validate_optional!(resource, params, key, allowed)
|
|
147
|
+
resource.send(:validate_optional!, params, key, allowed)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def truthy?(value)
|
|
151
|
+
[ true, 1, "1", "true", "TRUE", "True" ].include?(value)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
data/lib/runapi/suno.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "runapi/core"
|
|
4
|
+
require_relative "suno/types"
|
|
5
|
+
require_relative "suno/validators"
|
|
6
|
+
require_relative "suno/resources/text_to_music"
|
|
7
|
+
require_relative "suno/resources/extend_music"
|
|
8
|
+
require_relative "suno/resources/generate_artwork"
|
|
9
|
+
require_relative "suno/resources/cover_audio"
|
|
10
|
+
require_relative "suno/resources/add_instrumental"
|
|
11
|
+
require_relative "suno/resources/add_vocals"
|
|
12
|
+
require_relative "suno/resources/separate_audio_stems"
|
|
13
|
+
require_relative "suno/resources/generate_midi"
|
|
14
|
+
require_relative "suno/resources/convert_audio"
|
|
15
|
+
require_relative "suno/resources/visualize_music"
|
|
16
|
+
require_relative "suno/resources/generate_lyrics"
|
|
17
|
+
require_relative "suno/resources/get_timestamped_lyrics"
|
|
18
|
+
require_relative "suno/resources/replace_section"
|
|
19
|
+
require_relative "suno/resources/create_mashup"
|
|
20
|
+
require_relative "suno/resources/text_to_sound"
|
|
21
|
+
require_relative "suno/resources/generate_persona"
|
|
22
|
+
require_relative "suno/resources/boost_style"
|
|
23
|
+
require_relative "suno/client"
|
|
24
|
+
|
|
25
|
+
module RunApi
|
|
26
|
+
module Suno
|
|
27
|
+
AuthenticationError = RunApi::Core::AuthenticationError
|
|
28
|
+
RateLimitError = RunApi::Core::RateLimitError
|
|
29
|
+
InsufficientCreditsError = RunApi::Core::InsufficientCreditsError
|
|
30
|
+
ValidationError = RunApi::Core::ValidationError
|
|
31
|
+
NotFoundError = RunApi::Core::NotFoundError
|
|
32
|
+
TaskFailedError = RunApi::Core::TaskFailedError
|
|
33
|
+
TaskTimeoutError = RunApi::Core::TaskTimeoutError
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/runapi-suno.rb
ADDED