elevenlabs_client 0.1.0 → 0.3.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 +4 -4
- data/CHANGELOG.md +228 -10
- data/README.md +219 -90
- data/lib/elevenlabs_client/client.rb +126 -6
- data/lib/elevenlabs_client/endpoints/models.rb +26 -0
- data/lib/elevenlabs_client/endpoints/music.rb +127 -0
- data/lib/elevenlabs_client/endpoints/sound_generation.rb +46 -0
- data/lib/elevenlabs_client/endpoints/text_to_dialogue.rb +40 -0
- data/lib/elevenlabs_client/endpoints/text_to_speech.rb +50 -0
- data/lib/elevenlabs_client/endpoints/text_to_speech_stream.rb +42 -0
- data/lib/elevenlabs_client/endpoints/text_to_voice.rb +95 -0
- data/lib/elevenlabs_client/endpoints/voices.rb +147 -0
- data/lib/elevenlabs_client/errors.rb +3 -0
- data/lib/elevenlabs_client/version.rb +1 -1
- data/lib/elevenlabs_client.rb +9 -1
- metadata +10 -2
- /data/lib/elevenlabs_client/{dubs.rb → endpoints/dubs.rb} +0 -0
@@ -7,13 +7,21 @@ module ElevenlabsClient
|
|
7
7
|
class Client
|
8
8
|
DEFAULT_BASE_URL = "https://api.elevenlabs.io"
|
9
9
|
|
10
|
-
attr_reader :base_url, :api_key, :dubs
|
10
|
+
attr_reader :base_url, :api_key, :dubs, :text_to_speech, :text_to_speech_stream, :text_to_dialogue, :sound_generation, :text_to_voice, :models, :voices, :music
|
11
11
|
|
12
12
|
def initialize(api_key: nil, base_url: nil, api_key_env: "ELEVENLABS_API_KEY", base_url_env: "ELEVENLABS_BASE_URL")
|
13
13
|
@api_key = api_key || fetch_api_key(api_key_env)
|
14
14
|
@base_url = base_url || fetch_base_url(base_url_env)
|
15
15
|
@conn = build_connection
|
16
16
|
@dubs = Dubs.new(self)
|
17
|
+
@text_to_speech = TextToSpeech.new(self)
|
18
|
+
@text_to_speech_stream = TextToSpeechStream.new(self)
|
19
|
+
@text_to_dialogue = TextToDialogue.new(self)
|
20
|
+
@sound_generation = SoundGeneration.new(self)
|
21
|
+
@text_to_voice = TextToVoice.new(self)
|
22
|
+
@models = Models.new(self)
|
23
|
+
@voices = Voices.new(self)
|
24
|
+
@music = Endpoints::Music.new(self)
|
17
25
|
end
|
18
26
|
|
19
27
|
# Makes an authenticated GET request
|
@@ -35,7 +43,19 @@ module ElevenlabsClient
|
|
35
43
|
def post(path, body = nil)
|
36
44
|
response = @conn.post(path) do |req|
|
37
45
|
req.headers["xi-api-key"] = api_key
|
38
|
-
req.
|
46
|
+
req.headers["Content-Type"] = "application/json"
|
47
|
+
req.body = body.to_json if body
|
48
|
+
end
|
49
|
+
|
50
|
+
handle_response(response)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Makes an authenticated DELETE request
|
54
|
+
# @param path [String] API endpoint path
|
55
|
+
# @return [Hash] Response body
|
56
|
+
def delete(path)
|
57
|
+
response = @conn.delete(path) do |req|
|
58
|
+
req.headers["xi-api-key"] = api_key
|
39
59
|
end
|
40
60
|
|
41
61
|
handle_response(response)
|
@@ -54,6 +74,62 @@ module ElevenlabsClient
|
|
54
74
|
handle_response(response)
|
55
75
|
end
|
56
76
|
|
77
|
+
# Makes an authenticated POST request expecting binary response
|
78
|
+
# @param path [String] API endpoint path
|
79
|
+
# @param body [Hash, nil] Request body
|
80
|
+
# @return [String] Binary response body
|
81
|
+
def post_binary(path, body = nil)
|
82
|
+
response = @conn.post(path) do |req|
|
83
|
+
req.headers["xi-api-key"] = api_key
|
84
|
+
req.headers["Content-Type"] = "application/json"
|
85
|
+
req.body = body.to_json if body
|
86
|
+
end
|
87
|
+
|
88
|
+
handle_response(response)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Makes an authenticated POST request with custom headers
|
92
|
+
# @param path [String] API endpoint path
|
93
|
+
# @param body [Hash, nil] Request body
|
94
|
+
# @param custom_headers [Hash] Additional headers
|
95
|
+
# @return [String] Response body (binary or text)
|
96
|
+
def post_with_custom_headers(path, body = nil, custom_headers = {})
|
97
|
+
response = @conn.post(path) do |req|
|
98
|
+
req.headers["xi-api-key"] = api_key
|
99
|
+
req.headers["Content-Type"] = "application/json"
|
100
|
+
custom_headers.each { |key, value| req.headers[key] = value }
|
101
|
+
req.body = body.to_json if body
|
102
|
+
end
|
103
|
+
|
104
|
+
# For streaming/binary responses, return raw body
|
105
|
+
if custom_headers["Accept"]&.include?("audio") || custom_headers["Transfer-Encoding"] == "chunked"
|
106
|
+
handle_response(response)
|
107
|
+
else
|
108
|
+
handle_response(response)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Makes an authenticated POST request with streaming response
|
113
|
+
# @param path [String] API endpoint path
|
114
|
+
# @param body [Hash, nil] Request body
|
115
|
+
# @param block [Proc] Block to handle each chunk
|
116
|
+
# @return [Faraday::Response] Response object
|
117
|
+
def post_streaming(path, body = nil, &block)
|
118
|
+
response = @conn.post(path) do |req|
|
119
|
+
req.headers["xi-api-key"] = api_key
|
120
|
+
req.headers["Content-Type"] = "application/json"
|
121
|
+
req.headers["Accept"] = "audio/mpeg"
|
122
|
+
req.body = body.to_json if body
|
123
|
+
|
124
|
+
# Set up streaming callback
|
125
|
+
req.options.on_data = proc do |chunk, _|
|
126
|
+
block.call(chunk) if block_given?
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
handle_response(response)
|
131
|
+
end
|
132
|
+
|
57
133
|
# Helper method to create Faraday::Multipart::FilePart
|
58
134
|
# @param file_io [IO] File IO object
|
59
135
|
# @param filename [String] Original filename
|
@@ -97,14 +173,58 @@ module ElevenlabsClient
|
|
97
173
|
case response.status
|
98
174
|
when 200..299
|
99
175
|
response.body
|
176
|
+
when 400
|
177
|
+
error_message = extract_error_message(response.body)
|
178
|
+
raise BadRequestError, error_message.empty? ? "Bad request - invalid parameters" : error_message
|
100
179
|
when 401
|
101
|
-
|
180
|
+
error_message = extract_error_message(response.body)
|
181
|
+
raise AuthenticationError, error_message.empty? ? "Invalid API key or authentication failed" : error_message
|
182
|
+
when 404
|
183
|
+
error_message = extract_error_message(response.body)
|
184
|
+
raise NotFoundError, error_message.empty? ? "Resource not found" : error_message
|
185
|
+
when 422
|
186
|
+
error_message = extract_error_message(response.body)
|
187
|
+
raise UnprocessableEntityError, error_message.empty? ? "Unprocessable entity - invalid data" : error_message
|
102
188
|
when 429
|
103
|
-
|
189
|
+
error_message = extract_error_message(response.body)
|
190
|
+
raise RateLimitError, error_message.empty? ? "Rate limit exceeded" : error_message
|
104
191
|
when 400..499
|
105
|
-
|
192
|
+
error_message = extract_error_message(response.body)
|
193
|
+
raise ValidationError, error_message.empty? ? "Client error occurred with status #{response.status}" : error_message
|
106
194
|
else
|
107
|
-
|
195
|
+
error_message = extract_error_message(response.body)
|
196
|
+
raise APIError, error_message.empty? ? "API request failed with status #{response.status}" : error_message
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
|
202
|
+
def extract_error_message(response_body)
|
203
|
+
return "" if response_body.nil? || response_body.empty?
|
204
|
+
|
205
|
+
# Handle non-string response bodies
|
206
|
+
body_str = response_body.is_a?(String) ? response_body : response_body.to_s
|
207
|
+
|
208
|
+
begin
|
209
|
+
error_info = JSON.parse(body_str)
|
210
|
+
|
211
|
+
# Try different common error message fields
|
212
|
+
message = error_info["detail"] ||
|
213
|
+
error_info["message"] ||
|
214
|
+
error_info["error"] ||
|
215
|
+
error_info["errors"]
|
216
|
+
|
217
|
+
# Handle nested detail objects
|
218
|
+
if message.is_a?(Hash)
|
219
|
+
message = message["message"] || message.to_s
|
220
|
+
elsif message.is_a?(Array)
|
221
|
+
message = message.first.to_s
|
222
|
+
end
|
223
|
+
|
224
|
+
message.to_s
|
225
|
+
rescue JSON::ParserError, TypeError
|
226
|
+
# If not JSON or can't be parsed, return the raw body (truncated if too long)
|
227
|
+
body_str.length > 200 ? "#{body_str[0..200]}..." : body_str
|
108
228
|
end
|
109
229
|
end
|
110
230
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElevenlabsClient
|
4
|
+
class Models
|
5
|
+
def initialize(client)
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
# GET /v1/models
|
10
|
+
# Gets a list of available models
|
11
|
+
# Documentation: https://elevenlabs.io/docs/api-reference/models/list
|
12
|
+
#
|
13
|
+
# @return [Hash] The JSON response containing an array of models
|
14
|
+
def list
|
15
|
+
endpoint = "/v1/models"
|
16
|
+
@client.get(endpoint)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Alias for backward compatibility and convenience
|
20
|
+
alias_method :list_models, :list
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :client
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElevenlabsClient
|
4
|
+
module Endpoints
|
5
|
+
class Music
|
6
|
+
def initialize(client)
|
7
|
+
@client = client
|
8
|
+
end
|
9
|
+
|
10
|
+
# POST /v1/music
|
11
|
+
# Compose music and return binary audio data
|
12
|
+
# Documentation: https://elevenlabs.io/docs/api-reference/music/compose
|
13
|
+
#
|
14
|
+
# @param options [Hash] Music composition parameters
|
15
|
+
# @option options [String] :prompt Text description of the music to generate
|
16
|
+
# @option options [Hash] :composition_plan Detailed composition structure (optional)
|
17
|
+
# @option options [Integer] :music_length_ms Length of music in milliseconds (optional)
|
18
|
+
# @option options [String] :model_id Model to use for generation (default: "music_v1")
|
19
|
+
# @option options [String] :output_format Audio format (e.g., "mp3_44100_128")
|
20
|
+
# @return [String] Binary audio data
|
21
|
+
def compose(options = {})
|
22
|
+
endpoint = "/v1/music"
|
23
|
+
request_body = build_music_request_body(options)
|
24
|
+
|
25
|
+
query_params = {}
|
26
|
+
query_params[:output_format] = options[:output_format] if options[:output_format]
|
27
|
+
|
28
|
+
endpoint_with_query = query_params.empty? ? endpoint : "#{endpoint}?#{URI.encode_www_form(query_params)}"
|
29
|
+
|
30
|
+
@client.post_binary(endpoint_with_query, request_body)
|
31
|
+
end
|
32
|
+
|
33
|
+
# POST /v1/music/stream
|
34
|
+
# Compose music with streaming audio response
|
35
|
+
# Documentation: https://elevenlabs.io/docs/api-reference/music/compose-stream
|
36
|
+
#
|
37
|
+
# @param options [Hash] Music composition parameters
|
38
|
+
# @option options [String] :prompt Text description of the music to generate
|
39
|
+
# @option options [Hash] :composition_plan Detailed composition structure (optional)
|
40
|
+
# @option options [Integer] :music_length_ms Length of music in milliseconds (optional)
|
41
|
+
# @option options [String] :model_id Model to use for generation (default: "music_v1")
|
42
|
+
# @option options [String] :output_format Audio format (e.g., "mp3_44100_128")
|
43
|
+
# @param block [Proc] Block to handle streaming audio chunks
|
44
|
+
# @return [nil] Audio is streamed via the block
|
45
|
+
def compose_stream(options = {}, &block)
|
46
|
+
endpoint = "/v1/music/stream"
|
47
|
+
request_body = build_music_request_body(options)
|
48
|
+
|
49
|
+
query_params = {}
|
50
|
+
query_params[:output_format] = options[:output_format] if options[:output_format]
|
51
|
+
|
52
|
+
endpoint_with_query = query_params.empty? ? endpoint : "#{endpoint}?#{URI.encode_www_form(query_params)}"
|
53
|
+
|
54
|
+
@client.post_streaming(endpoint_with_query, request_body, &block)
|
55
|
+
end
|
56
|
+
|
57
|
+
# POST /v1/music/detailed
|
58
|
+
# Compose music and return detailed response with metadata and audio
|
59
|
+
# Documentation: https://elevenlabs.io/docs/api-reference/music/compose-detailed
|
60
|
+
#
|
61
|
+
# @param options [Hash] Music composition parameters
|
62
|
+
# @option options [String] :prompt Text description of the music to generate
|
63
|
+
# @option options [Hash] :composition_plan Detailed composition structure (optional)
|
64
|
+
# @option options [Integer] :music_length_ms Length of music in milliseconds (optional)
|
65
|
+
# @option options [String] :model_id Model to use for generation (default: "music_v1")
|
66
|
+
# @option options [String] :output_format Audio format (e.g., "mp3_44100_128")
|
67
|
+
# @return [String] Multipart response with JSON metadata and binary audio
|
68
|
+
def compose_detailed(options = {})
|
69
|
+
endpoint = "/v1/music/detailed"
|
70
|
+
request_body = build_music_request_body(options)
|
71
|
+
|
72
|
+
query_params = {}
|
73
|
+
query_params[:output_format] = options[:output_format] if options[:output_format]
|
74
|
+
|
75
|
+
endpoint_with_query = query_params.empty? ? endpoint : "#{endpoint}?#{URI.encode_www_form(query_params)}"
|
76
|
+
|
77
|
+
# Use post_with_custom_headers to handle multipart response
|
78
|
+
@client.post_with_custom_headers(
|
79
|
+
endpoint_with_query,
|
80
|
+
request_body,
|
81
|
+
{ "Accept" => "multipart/mixed" }
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
# POST /v1/music/plan
|
86
|
+
# Create a composition plan for music generation
|
87
|
+
# Documentation: https://elevenlabs.io/docs/api-reference/music/create-plan
|
88
|
+
#
|
89
|
+
# @param options [Hash] Plan creation parameters
|
90
|
+
# @option options [String] :prompt Text description of the music style/structure
|
91
|
+
# @option options [Integer] :music_length_ms Desired length of music in milliseconds
|
92
|
+
# @option options [Hash] :source_composition_plan Base plan to modify (optional)
|
93
|
+
# @option options [String] :model_id Model to use for plan generation (default: "music_v1")
|
94
|
+
# @return [Hash] JSON response containing the composition plan
|
95
|
+
def create_plan(options = {})
|
96
|
+
endpoint = "/v1/music/plan"
|
97
|
+
request_body = {
|
98
|
+
prompt: options[:prompt],
|
99
|
+
music_length_ms: options[:music_length_ms],
|
100
|
+
source_composition_plan: options[:source_composition_plan],
|
101
|
+
model_id: options[:model_id] || "music_v1"
|
102
|
+
}.compact
|
103
|
+
|
104
|
+
@client.post(endpoint, request_body)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Alias methods for convenience
|
108
|
+
alias_method :compose_music, :compose
|
109
|
+
alias_method :compose_music_stream, :compose_stream
|
110
|
+
alias_method :compose_music_detailed, :compose_detailed
|
111
|
+
alias_method :create_music_plan, :create_plan
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
attr_reader :client
|
116
|
+
|
117
|
+
def build_music_request_body(options)
|
118
|
+
{
|
119
|
+
prompt: options[:prompt],
|
120
|
+
composition_plan: options[:composition_plan],
|
121
|
+
music_length_ms: options[:music_length_ms],
|
122
|
+
model_id: options[:model_id] || "music_v1"
|
123
|
+
}.compact
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElevenlabsClient
|
4
|
+
class SoundGeneration
|
5
|
+
def initialize(client)
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
# POST /v1/sound-generation
|
10
|
+
# Convert text to sound effects and retrieve audio (binary data)
|
11
|
+
# Documentation: https://elevenlabs.io/docs/api-reference/sound-generation
|
12
|
+
#
|
13
|
+
# @param text [String] Text prompt describing the sound effect
|
14
|
+
# @param options [Hash] Optional parameters
|
15
|
+
# @option options [Boolean] :loop Whether to create a looping sound effect (default: false)
|
16
|
+
# @option options [Float] :duration_seconds Duration in seconds (0.5 to 30, default: nil for auto-detection)
|
17
|
+
# @option options [Float] :prompt_influence Prompt influence (0.0 to 1.0, default: 0.3)
|
18
|
+
# @option options [String] :output_format Output format (e.g., "mp3_22050_32", default: "mp3_44100_128")
|
19
|
+
# @return [String] The binary audio data (usually an MP3)
|
20
|
+
def generate(text, **options)
|
21
|
+
endpoint = "/v1/sound-generation"
|
22
|
+
request_body = { text: text }
|
23
|
+
|
24
|
+
# Add optional parameters if provided
|
25
|
+
request_body[:loop] = options[:loop] unless options[:loop].nil?
|
26
|
+
request_body[:duration_seconds] = options[:duration_seconds] if options[:duration_seconds]
|
27
|
+
request_body[:prompt_influence] = options[:prompt_influence] if options[:prompt_influence]
|
28
|
+
|
29
|
+
# Handle output_format as query parameter
|
30
|
+
query_params = {}
|
31
|
+
query_params[:output_format] = options[:output_format] if options[:output_format]
|
32
|
+
|
33
|
+
# Build endpoint with query parameters if any
|
34
|
+
full_endpoint = query_params.any? ? "#{endpoint}?#{URI.encode_www_form(query_params)}" : endpoint
|
35
|
+
|
36
|
+
@client.post_binary(full_endpoint, request_body)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Alias for backward compatibility and convenience
|
40
|
+
alias_method :sound_generation, :generate
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_reader :client
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElevenlabsClient
|
4
|
+
class TextToDialogue
|
5
|
+
def initialize(client)
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
# POST /v1/text-to-dialogue
|
10
|
+
# Converts a list of text and voice ID pairs into speech (dialogue) and returns audio.
|
11
|
+
# Documentation: https://elevenlabs.io/docs/api-reference/text-to-dialogue/convert
|
12
|
+
#
|
13
|
+
# @param inputs [Array<Hash>] A list of dialogue inputs, each containing text and a voice ID
|
14
|
+
# @option inputs [String] :text The text to be converted to speech
|
15
|
+
# @option inputs [String] :voice_id The voice ID to use for this text
|
16
|
+
# @param options [Hash] Optional parameters
|
17
|
+
# @option options [String] :model_id Identifier of the model to be used
|
18
|
+
# @option options [Hash] :settings Settings controlling the dialogue generation
|
19
|
+
# @option options [Integer] :seed Best effort to sample deterministically
|
20
|
+
# @return [String] The binary audio data (usually an MP3)
|
21
|
+
def convert(inputs, **options)
|
22
|
+
endpoint = "/v1/text-to-dialogue"
|
23
|
+
request_body = { inputs: inputs }
|
24
|
+
|
25
|
+
# Add optional parameters
|
26
|
+
request_body[:model_id] = options[:model_id] if options[:model_id]
|
27
|
+
request_body[:settings] = options[:settings] if options[:settings] && !options[:settings].empty?
|
28
|
+
request_body[:seed] = options[:seed] if options[:seed]
|
29
|
+
|
30
|
+
@client.post_binary(endpoint, request_body)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Alias for backward compatibility and convenience
|
34
|
+
alias_method :text_to_dialogue, :convert
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_reader :client
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElevenlabsClient
|
4
|
+
class TextToSpeech
|
5
|
+
def initialize(client)
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
# POST /v1/text-to-speech/{voice_id}
|
10
|
+
# Convert text to speech and retrieve audio (binary data)
|
11
|
+
# Documentation: https://elevenlabs.io/docs/api-reference/text-to-speech/convert
|
12
|
+
#
|
13
|
+
# @param voice_id [String] The ID of the voice to use
|
14
|
+
# @param text [String] Text to synthesize
|
15
|
+
# @param options [Hash] Optional TTS parameters
|
16
|
+
# @option options [String] :model_id Model to use (e.g. "eleven_monolingual_v1" or "eleven_multilingual_v1")
|
17
|
+
# @option options [Hash] :voice_settings Voice configuration (stability, similarity_boost, style, use_speaker_boost, etc.)
|
18
|
+
# @option options [Boolean] :optimize_streaming Whether to receive chunked streaming audio
|
19
|
+
# @return [String] The binary audio data (usually an MP3)
|
20
|
+
def convert(voice_id, text, **options)
|
21
|
+
endpoint = "/v1/text-to-speech/#{voice_id}"
|
22
|
+
request_body = { text: text }
|
23
|
+
|
24
|
+
# Add optional parameters
|
25
|
+
request_body[:model_id] = options[:model_id] if options[:model_id]
|
26
|
+
request_body[:voice_settings] = options[:voice_settings] if options[:voice_settings]
|
27
|
+
|
28
|
+
# Handle streaming optimization
|
29
|
+
if options[:optimize_streaming]
|
30
|
+
@client.post_with_custom_headers(endpoint, request_body, streaming_headers)
|
31
|
+
else
|
32
|
+
@client.post_binary(endpoint, request_body)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Alias for backward compatibility and convenience
|
37
|
+
alias_method :text_to_speech, :convert
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :client
|
42
|
+
|
43
|
+
def streaming_headers
|
44
|
+
{
|
45
|
+
"Accept" => "audio/mpeg",
|
46
|
+
"Transfer-Encoding" => "chunked"
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElevenlabsClient
|
4
|
+
class TextToSpeechStream
|
5
|
+
def initialize(client)
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
# POST /v1/text-to-speech/{voice_id}/stream
|
10
|
+
# Stream text-to-speech audio in real-time chunks
|
11
|
+
#
|
12
|
+
# @param voice_id [String] The ID of the voice to use
|
13
|
+
# @param text [String] Text to synthesize
|
14
|
+
# @param options [Hash] Optional TTS parameters
|
15
|
+
# @option options [String] :model_id Model to use (defaults to "eleven_multilingual_v2")
|
16
|
+
# @option options [String] :output_format Output format (defaults to "mp3_44100_128")
|
17
|
+
# @option options [Hash] :voice_settings Voice configuration
|
18
|
+
# @param block [Proc] Block to handle each audio chunk
|
19
|
+
# @return [Faraday::Response] The response object
|
20
|
+
def stream(voice_id, text, **options, &block)
|
21
|
+
output_format = options[:output_format] || "mp3_44100_128"
|
22
|
+
endpoint = "/v1/text-to-speech/#{voice_id}/stream?output_format=#{output_format}"
|
23
|
+
|
24
|
+
request_body = {
|
25
|
+
text: text,
|
26
|
+
model_id: options[:model_id] || "eleven_multilingual_v2"
|
27
|
+
}
|
28
|
+
|
29
|
+
# Add voice_settings if provided
|
30
|
+
request_body[:voice_settings] = options[:voice_settings] if options[:voice_settings]
|
31
|
+
|
32
|
+
@client.post_streaming(endpoint, request_body, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Alias for backward compatibility
|
36
|
+
alias_method :text_to_speech_stream, :stream
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
attr_reader :client
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElevenlabsClient
|
4
|
+
class TextToVoice
|
5
|
+
def initialize(client)
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
# POST /v1/text-to-voice/design
|
10
|
+
# Designs a voice based on a description
|
11
|
+
# Documentation: https://elevenlabs.io/docs/api-reference/text-to-voice/design
|
12
|
+
#
|
13
|
+
# @param voice_description [String] Description of the voice (20-1000 characters)
|
14
|
+
# @param options [Hash] Optional parameters
|
15
|
+
# @option options [String] :output_format Output format (e.g., "mp3_44100_192")
|
16
|
+
# @option options [String] :model_id Model to use (e.g., "eleven_multilingual_ttv_v2", "eleven_ttv_v3")
|
17
|
+
# @option options [String] :text Text to generate (100-1000 characters, optional)
|
18
|
+
# @option options [Boolean] :auto_generate_text Auto-generate text (default: false)
|
19
|
+
# @option options [Float] :loudness Loudness level (-1 to 1, default: 0.5)
|
20
|
+
# @option options [Integer] :seed Random seed (0 to 2147483647, optional)
|
21
|
+
# @option options [Float] :guidance_scale Guidance scale (0 to 100, default: 5)
|
22
|
+
# @option options [Boolean] :stream_previews Stream previews (default: false)
|
23
|
+
# @option options [String] :remixing_session_id Remixing session ID (optional)
|
24
|
+
# @option options [String] :remixing_session_iteration_id Remixing session iteration ID (optional)
|
25
|
+
# @option options [Float] :quality Quality level (-1 to 1, optional)
|
26
|
+
# @option options [String] :reference_audio_base64 Base64 encoded reference audio (optional, requires eleven_ttv_v3)
|
27
|
+
# @option options [Float] :prompt_strength Prompt strength (0 to 1, optional, requires eleven_ttv_v3)
|
28
|
+
# @return [Hash] JSON response containing previews and text
|
29
|
+
def design(voice_description, **options)
|
30
|
+
endpoint = "/v1/text-to-voice/design"
|
31
|
+
request_body = { voice_description: voice_description }
|
32
|
+
|
33
|
+
# Add optional parameters if provided
|
34
|
+
request_body[:output_format] = options[:output_format] if options[:output_format]
|
35
|
+
request_body[:model_id] = options[:model_id] if options[:model_id]
|
36
|
+
request_body[:text] = options[:text] if options[:text]
|
37
|
+
request_body[:auto_generate_text] = options[:auto_generate_text] unless options[:auto_generate_text].nil?
|
38
|
+
request_body[:loudness] = options[:loudness] if options[:loudness]
|
39
|
+
request_body[:seed] = options[:seed] if options[:seed]
|
40
|
+
request_body[:guidance_scale] = options[:guidance_scale] if options[:guidance_scale]
|
41
|
+
request_body[:stream_previews] = options[:stream_previews] unless options[:stream_previews].nil?
|
42
|
+
request_body[:remixing_session_id] = options[:remixing_session_id] if options[:remixing_session_id]
|
43
|
+
request_body[:remixing_session_iteration_id] = options[:remixing_session_iteration_id] if options[:remixing_session_iteration_id]
|
44
|
+
request_body[:quality] = options[:quality] if options[:quality]
|
45
|
+
request_body[:reference_audio_base64] = options[:reference_audio_base64] if options[:reference_audio_base64]
|
46
|
+
request_body[:prompt_strength] = options[:prompt_strength] if options[:prompt_strength]
|
47
|
+
|
48
|
+
@client.post(endpoint, request_body)
|
49
|
+
end
|
50
|
+
|
51
|
+
# POST /v1/text-to-voice
|
52
|
+
# Creates a voice from the designed voice generated_voice_id
|
53
|
+
# Documentation: https://elevenlabs.io/docs/api-reference/text-to-voice
|
54
|
+
#
|
55
|
+
# @param voice_name [String] Name of the voice
|
56
|
+
# @param voice_description [String] Description of the voice (20-1000 characters)
|
57
|
+
# @param generated_voice_id [String] The generated voice ID from design_voice
|
58
|
+
# @param options [Hash] Optional parameters
|
59
|
+
# @option options [Hash] :labels Optional metadata for the voice
|
60
|
+
# @option options [Array<String>] :played_not_selected_voice_ids Optional list of voice IDs played but not selected
|
61
|
+
# @return [Hash] JSON response containing voice_id and other voice details
|
62
|
+
def create(voice_name, voice_description, generated_voice_id, **options)
|
63
|
+
endpoint = "/v1/text-to-voice"
|
64
|
+
request_body = {
|
65
|
+
voice_name: voice_name,
|
66
|
+
voice_description: voice_description,
|
67
|
+
generated_voice_id: generated_voice_id
|
68
|
+
}
|
69
|
+
|
70
|
+
# Add optional parameters if provided
|
71
|
+
request_body[:labels] = options[:labels] if options[:labels]
|
72
|
+
request_body[:played_not_selected_voice_ids] = options[:played_not_selected_voice_ids] if options[:played_not_selected_voice_ids]
|
73
|
+
|
74
|
+
@client.post(endpoint, request_body)
|
75
|
+
end
|
76
|
+
|
77
|
+
# GET /v1/voices
|
78
|
+
# Retrieves all voices associated with your Elevenlabs account
|
79
|
+
# Documentation: https://elevenlabs.io/docs/api-reference/voices
|
80
|
+
#
|
81
|
+
# @return [Hash] The JSON response containing an array of voices
|
82
|
+
def list_voices
|
83
|
+
endpoint = "/v1/voices"
|
84
|
+
@client.get(endpoint)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Alias methods for backward compatibility and convenience
|
88
|
+
alias_method :design_voice, :design
|
89
|
+
alias_method :create_from_generated_voice, :create
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
attr_reader :client
|
94
|
+
end
|
95
|
+
end
|