elevenlabs 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c2b887df23210fbd0b7c0d2e75e667ffb46ab4c47400f8db1915b60b8984adca
4
+ data.tar.gz: 51287a55127cfa42b10d0e01a5463a6bd14db42bae6e6f16a36ee6884ba359a7
5
+ SHA512:
6
+ metadata.gz: '048e8939495f6e7e25920c3bcf88d9b17779435f79f56df03f057a86eec7069211eafafce8fc329046121b6df764ef8e595f45c6e5068d546654e4b29593b517'
7
+ data.tar.gz: 9e72052b4ef1371c3ba9790df2716c9a3d55645f40b58c39295ef01fb0e7f26c6420517f7b8e65af9aa92c64f0222ca36077d92595818ac2c7f7576acaff37d9
data/README.md ADDED
@@ -0,0 +1,234 @@
1
+ # Elevenlabs Ruby Gem
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/elevenlabs.svg)](https://badge.fury.io/rb/elevenlabs)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ A **Ruby client** for the [ElevenLabs](https://elevenlabs.io/) **Text-to-Speech API**.
7
+ This gem provides an easy-to-use interface for:
8
+
9
+ - **Listing available voices**
10
+ - **Fetching details about a voice**
11
+ - **Creating a custom voice** (with uploaded sample files)
12
+ - **Editing an existing voice**
13
+ - **Deleting a voice**
14
+ - **Converting text to speech** and retrieving the generated audio
15
+
16
+ All requests are handled via [Faraday](https://github.com/lostisland/faraday).
17
+
18
+ ---
19
+
20
+ ## Table of Contents
21
+
22
+ - [Features](#features)
23
+ - [Installation](#installation)
24
+ - [Usage](#usage)
25
+ - [Basic Example](#basic-example)
26
+ - [Rails Integration](#rails-integration)
27
+ - [Store API Key in Rails Credentials](#store-api-key-in-rails-credentials)
28
+ - [Rails Initializer](#rails-initializer)
29
+ - [Controller Example](#controller-example)
30
+ - [Endpoints](#endpoints)
31
+ - [Error Handling](#error-handling)
32
+ - [Development](#development)
33
+ - [Contributing](#contributing)
34
+ - [License](#license)
35
+
36
+ ---
37
+
38
+ ## Features
39
+
40
+ - **Simple and intuitive API client** for ElevenLabs.
41
+ - **Multipart file uploads** for training custom voices.
42
+ - **Automatic authentication** via API key configuration.
43
+ - **Error handling** with custom exceptions.
44
+ - **Rails integration support** (including credentials storage).
45
+
46
+ ---
47
+
48
+ ## Installation
49
+
50
+ Add the gem to your `Gemfile`:
51
+
52
+ ```ruby
53
+ gem "elevenlabs", "~> 0.0.1"
54
+ ```
55
+ Then run:
56
+ ```ruby
57
+ bundle install
58
+ ```
59
+ Or install it directly using:
60
+ ```ruby
61
+ gem install elevenlabs
62
+ ```
63
+ Usage
64
+ Basic Example (Standalone Ruby)
65
+ ```ruby
66
+ require "elevenlabs"
67
+
68
+ # 1. Configure the gem globally (Optional)
69
+ Elevenlabs.configure do |config|
70
+ config.api_key = "YOUR_API_KEY"
71
+ end
72
+
73
+ # 2. Initialize a client (will use configured API key)
74
+ client = Elevenlabs::Client.new
75
+
76
+ # 3. List available voices
77
+ voices = client.list_voices
78
+ puts voices # JSON response with voices
79
+
80
+ # 4. Convert text to speech
81
+ voice_id = "YOUR_VOICE_ID"
82
+ text = "Hello from Elevenlabs!"
83
+ audio_data = client.text_to_speech(voice_id, text)
84
+
85
+ # 5. Save the audio file
86
+ File.open("output.mp3", "wb") { |f| f.write(audio_data) }
87
+ puts "Audio file saved to output.mp3"
88
+ ```
89
+ Note: You can override the API key per request:
90
+ ```ruby
91
+ client = Elevenlabs::Client.new(api_key: "DIFFERENT_API_KEY")
92
+ ```
93
+ Rails Integration
94
+ Store API Key in Rails Credentials
95
+ 1. Open your encrypted credentials:
96
+ ```ruby
97
+ EDITOR=vim rails credentials:edit
98
+ ```
99
+
100
+ 2. Add the ElevenLabs API key:
101
+ ```ruby
102
+ eleven_labs:
103
+ api_key: YOUR_SECURE_KEY
104
+ ```
105
+ 3. Save and exit. Rails will securely encrypt your API key.
106
+
107
+ Rails Initializer
108
+ Create an initializer file: config/initializers/elevenlabs.rb
109
+ ```ruby
110
+ # config/initializers/elevenlabs.rb
111
+ require "elevenlabs"
112
+
113
+ Rails.application.config.to_prepare do
114
+ Elevenlabs.configure do |config|
115
+ config.api_key = Rails.application.credentials.dig(:eleven_labs, :api_key)
116
+ end
117
+ end
118
+ ```
119
+ Now you can simply call:
120
+ ```ruby
121
+ client = Elevenlabs::Client.new
122
+ ```
123
+ without manually providing an API key.
124
+
125
+ Endpoints
126
+ 1. List Voices
127
+ ```ruby
128
+ client.list_voices
129
+ # => { "voices" => [...] }
130
+ ```
131
+ 2. Get Voice Details
132
+ ```ruby
133
+ client.get_voice("VOICE_ID")
134
+ # => { "voice_id" => "...", "name" => "...", ... }
135
+ ```
136
+ 3. Create a Custom Voice
137
+ ```ruby
138
+ sample_files = [File.open("sample1.mp3", "rb")]
139
+ client.create_voice("Custom Voice", sample_files, description: "My custom AI voice")
140
+ # => JSON response with new voice details
141
+ ```
142
+ 4. Check if a voice is banned?
143
+ ```ruby
144
+ sample_files = [File.open("trump.mp3", "rb")]
145
+ client.create_voice("Donald Trump", sample_files, description: "My Trump Voice")
146
+ => {"voice_id"=>"<RETURNED_VOICE_ID>", "requires_verification"=>false}
147
+ trump= "<RETURNED_VOICE_ID>"
148
+ client.banned? trump
149
+ => true
150
+ ```
151
+ 5. Edit a Voice
152
+ ```ruby
153
+ client.edit_voice("VOICE_ID", name: "Updated Voice Name")
154
+ # => JSON response with updated details
155
+ ```
156
+ 6. Delete a Voice
157
+ ```ruby
158
+ client.delete_voice("VOICE_ID")
159
+ # => JSON response acknowledging deletion
160
+ ```
161
+ 7. Convert Text to Speech
162
+ ```ruby
163
+ audio_data = client.text_to_speech("VOICE_ID", "Hello world!")
164
+ File.open("output.mp3", "wb") { |f| f.write(audio_data) }
165
+ ```
166
+ 8 Stream Text to Speech
167
+ stream from terminal
168
+ ```ruby
169
+ Mac: brew install sox
170
+ Linux: sudo apt install sox
171
+
172
+ IO.popen("play -t mp3 -", "wb") do |audio_pipe| # Notice "wb" (write binary)
173
+ client.text_to_speech_stream("VOICE_ID", "Some text to stream back in chunks") do |chunk|
174
+ audio_pipe.write(chunk.b) # Ensure chunk is written as binary
175
+ end
176
+ end
177
+ ```
178
+
179
+ Error Handling
180
+ When the API returns an error, the gem raises specific exceptions:
181
+
182
+ Exception Meaning
183
+ Elevenlabs::BadRequestError Invalid request parameters
184
+ Elevenlabs::AuthenticationError Invalid API key
185
+ Elevenlabs::NotFoundError Resource (voice) not found
186
+ Elevenlabs::APIError General API failure
187
+ Example:
188
+
189
+ ```ruby
190
+ begin
191
+ client.text_to_speech("INVALID_VOICE_ID", "Test")
192
+ rescue Elevenlabs::AuthenticationError => e
193
+ puts "Invalid API key: #{e.message}"
194
+ rescue Elevenlabs::NotFoundError => e
195
+ puts "Voice not found: #{e.message}"
196
+ rescue Elevenlabs::APIError => e
197
+ puts "General error: #{e.message}"
198
+ end
199
+ ```
200
+
201
+ Development
202
+ Clone this repository
203
+ ```bash
204
+ git clone https://github.com/your-username/elevenlabs.git
205
+ cd elevenlabs
206
+ ```
207
+ Install dependencies
208
+ ```bash
209
+ bundle install
210
+ ```
211
+ Build the gem
212
+ ```bash
213
+ gem build elevenlabs.gemspec
214
+ ```
215
+ Install the gem locally
216
+ ```bash
217
+ gem install ./elevenlabs-0.0.1.gem
218
+ ```
219
+ Contributing
220
+ Contributions are welcome! Please follow these steps:
221
+
222
+ Fork the repository
223
+ Create a feature branch (git checkout -b feature/my-new-feature)
224
+ Commit your changes (git commit -am 'Add new feature')
225
+ Push to your branch (git push origin feature/my-new-feature)
226
+ Create a Pull Request describing your changes
227
+ For bug reports, please open an issue with details.
228
+
229
+ License
230
+ This project is licensed under the MIT License. See the LICENSE file for details.
231
+
232
+ ⭐ Thank you for using the Elevenlabs Ruby Gem!
233
+ If you have any questions or suggestions, feel free to open an issue or submit a Pull Request!
234
+
@@ -0,0 +1,291 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "faraday/multipart"
5
+ require "json"
6
+
7
+ module Elevenlabs
8
+ class Client
9
+ BASE_URL = "https://api.elevenlabs.io"
10
+
11
+ # Note the default param: `api_key: nil`
12
+ def initialize(api_key: nil)
13
+ # If the caller doesn’t provide an api_key, use the gem-wide config
14
+ @api_key = api_key || Elevenlabs.configuration&.api_key
15
+
16
+ @connection = Faraday.new(url: BASE_URL) do |conn|
17
+ conn.request :url_encoded
18
+ conn.response :raise_error
19
+ conn.adapter Faraday.default_adapter
20
+ end
21
+ end
22
+
23
+ #####################################################
24
+ # Text-to-Speech #
25
+ # (POST /v1/text-to-speech/{voice_id}) #
26
+ #####################################################
27
+
28
+ # Convert text to speech and retrieve audio (binary data)
29
+ # Documentation: https://elevenlabs.io/docs/api-reference/text-to-speech/convert
30
+ #
31
+ # @param [String] voice_id - the ID of the voice to use
32
+ # @param [String] text - text to synthesize
33
+ # @param [Hash] options - optional TTS parameters
34
+ # :model_id => String (e.g. "eleven_monolingual_v1" or "eleven_multilingual_v1")
35
+ # :voice_settings => Hash (stability, similarity_boost, style, use_speaker_boost, etc.)
36
+ # :optimize_streaming => Boolean (whether to receive chunked streaming audio)
37
+ #
38
+ # @return [String] The binary audio data (usually an MP3).
39
+ def text_to_speech(voice_id, text, options = {})
40
+ endpoint = "/v1/text-to-speech/#{voice_id}"
41
+ request_body = { text: text }
42
+
43
+ # If user provided voice_settings, add them
44
+ if options[:voice_settings]
45
+ request_body[:voice_settings] = options[:voice_settings]
46
+ end
47
+
48
+ # If user specified a model_id, add it
49
+ request_body[:model_id] = options[:model_id] if options[:model_id]
50
+
51
+ # If user wants streaming optimization
52
+ headers = default_headers
53
+ if options[:optimize_streaming]
54
+ headers["Accept"] = "audio/mpeg"
55
+ headers["Transfer-Encoding"] = "chunked"
56
+ end
57
+
58
+ response = @connection.post(endpoint) do |req|
59
+ req.headers = headers
60
+ req.body = request_body.to_json
61
+ end
62
+
63
+ # Returns raw binary data (often MP3)
64
+ response.body
65
+ rescue Faraday::ClientError => e
66
+ handle_error(e)
67
+ end
68
+
69
+ #####################################################
70
+ # Text-to-Speech-Stream #
71
+ # (POST /v1/text-to-speech/{voice_id})/stream #
72
+ #####################################################
73
+ def text_to_speech_stream(voice_id, text, options = {}, &block)
74
+ endpoint = "/v1/text-to-speech/#{voice_id}/stream?output_format=mp3_44100_128"
75
+ request_body = { text: text, model_id: options[:model_id] || "eleven_multilingual_v2" }
76
+
77
+ headers = default_headers
78
+ headers["Accept"] = "audio/mpeg"
79
+
80
+ response = @connection.post(endpoint, request_body.to_json, headers) do |req|
81
+ req.options.on_data = Proc.new do |chunk, _|
82
+ block.call(chunk) if block_given?
83
+ end
84
+ end
85
+
86
+ response
87
+ rescue Faraday::ClientError => e
88
+ handle_error(e)
89
+ end
90
+
91
+
92
+ #####################################################
93
+ # GET Voices #
94
+ # (GET /v1/voices) #
95
+ #####################################################
96
+
97
+ # Retrieves all voices associated with your Elevenlabs account
98
+ # Documentation: https://elevenlabs.io/docs/api-reference/voices
99
+ #
100
+ # @return [Hash] The JSON response containing an array of voices
101
+ def list_voices
102
+ endpoint = "/v1/voices"
103
+ response = @connection.get(endpoint) do |req|
104
+ req.headers = default_headers
105
+ end
106
+ JSON.parse(response.body)
107
+ rescue Faraday::ClientError => e
108
+ handle_error(e)
109
+ end
110
+
111
+ #####################################################
112
+ # GET a Single Voice #
113
+ # (GET /v1/voices/{voice_id}) #
114
+ #####################################################
115
+
116
+ # Retrieves details about a single voice
117
+ #
118
+ # @param [String] voice_id
119
+ # @return [Hash] Details of the voice
120
+ def get_voice(voice_id)
121
+ endpoint = "/v1/voices/#{voice_id}"
122
+ response = @connection.get(endpoint) do |req|
123
+ req.headers = default_headers
124
+ end
125
+ JSON.parse(response.body)
126
+ rescue Faraday::ClientError => e
127
+ handle_error(e)
128
+ end
129
+
130
+ #####################################################
131
+ # Create a Voice #
132
+ # (POST /v1/voices/add) #
133
+ #####################################################
134
+
135
+ # Creates a new voice
136
+ # @param [String] name - name of the voice
137
+ # @param [File] samples - array of files to train the voice
138
+ # @param [Hash] options - additional parameters
139
+ # :description => String
140
+ #
141
+ # NOTE: This method may require a multipart form request
142
+ # if you are uploading sample audio files.
143
+ def create_voice(name, samples = [], options = {})
144
+ endpoint = "/v1/voices/add"
145
+
146
+ # Ensure Faraday handles multipart form data
147
+ mp_connection = Faraday.new(url: BASE_URL) do |conn|
148
+ conn.request :multipart
149
+ conn.response :raise_error
150
+ conn.adapter Faraday.default_adapter
151
+ end
152
+
153
+ # Build multipart form parameters
154
+ form_params = {
155
+ "name" => name,
156
+ "description" => options[:description] || ""
157
+ }
158
+
159
+ # Convert File objects to multipart upload format
160
+ sample_files = []
161
+ samples.each_with_index do |sample_file, i|
162
+ sample_files << ["files", Faraday::UploadIO.new(sample_file.path, "audio/mpeg")]
163
+ end
164
+
165
+ # Perform the POST request
166
+ response = mp_connection.post(endpoint) do |req|
167
+ req.headers["xi-api-key"] = @api_key
168
+ req.body = form_params.merge(sample_files.to_h)
169
+ end
170
+
171
+ JSON.parse(response.body)
172
+ rescue Faraday::ClientError => e
173
+ handle_error(e)
174
+ end
175
+
176
+
177
+ #####################################################
178
+ # Edit a Voice #
179
+ # (POST /v1/voices/{voice_id}/edit) #
180
+ #####################################################
181
+ # Updates an existing voice
182
+ # @param [String] voice_id
183
+ # @param [Array<File>] samples
184
+ # @param [Hash] options
185
+ # options[:name] [String] name
186
+ # options[:description] [String] description
187
+
188
+ def edit_voice(voice_id, samples = [], options = {})
189
+ endpoint = "/v1/voices/#{voice_id}/edit"
190
+
191
+ # Force text fields to be strings.
192
+ form_params = {
193
+ "name" => options[:name].to_s,
194
+ "description" => (options[:description] || "").to_s
195
+ }
196
+
197
+ form_params["files[]"] = samples.map do |sample_file|
198
+ Faraday::UploadIO.new(sample_file.path, "audio/mpeg", File.basename(sample_file.path))
199
+ end
200
+
201
+ mp_connection = Faraday.new(url: BASE_URL) do |conn|
202
+ conn.request :multipart
203
+ conn.response :raise_error
204
+ conn.adapter Faraday.default_adapter
205
+ end
206
+
207
+ response = mp_connection.post(endpoint) do |req|
208
+ req.headers["xi-api-key"] = @api_key
209
+ req.body = form_params
210
+ end
211
+
212
+ JSON.parse(response.body)
213
+ rescue Faraday::ClientError => e
214
+ handle_error(e)
215
+ end
216
+
217
+ #####################################################
218
+ # Delete a Voice #
219
+ # (DELETE /v1/voices/{voice_id}) #
220
+ #####################################################
221
+
222
+ # Deletes a voice from your account
223
+ # @param [String] voice_id
224
+ # @return [Hash] response
225
+ def delete_voice(voice_id)
226
+ endpoint = "/v1/voices/#{voice_id}"
227
+ response = @connection.delete(endpoint) do |req|
228
+ req.headers = default_headers
229
+ end
230
+
231
+ JSON.parse(response.body)
232
+ rescue Faraday::ClientError => e
233
+ handle_error(e)
234
+ end
235
+
236
+ #####################################################
237
+ # Banned Voice Check #
238
+ #####################################################
239
+
240
+ # Checks safety control on a single voice for "BAN"
241
+ #
242
+ # @param [String] voice_id
243
+ # @return [Boolean]
244
+ def banned?(voice_id)
245
+ voice = get_voice(voice_id)
246
+ voice["safety_control"] == "BAN"
247
+ end
248
+
249
+ #####################################################
250
+ # Active Voice Check #
251
+ #####################################################
252
+
253
+ # Checks if a voice_id is in list_voices
254
+ #
255
+ # @param [String] voice_id
256
+ # @return [Boolean]
257
+ def active?(voice_id)
258
+ active_voices = list_voices["voices"].map{|voice| voice["voice_id"]}
259
+ voice_id.in?(active_voices)
260
+ end
261
+
262
+ private
263
+
264
+ # Common headers needed by Elevenlabs
265
+ def default_headers
266
+ {
267
+ "xi-api-key" => @api_key,
268
+ "Content-Type" => "application/json"
269
+ }
270
+ end
271
+
272
+ # Error handling
273
+ def handle_error(exception)
274
+ status = exception.response[:status] rescue nil
275
+ body = exception.response[:body] rescue "{}"
276
+ error_info = JSON.parse(body) rescue {}
277
+
278
+ detail = error_info["detail"]
279
+ simple_message = detail.is_a?(Hash) ? detail["message"] || detail.to_s : detail.to_s
280
+
281
+ case status
282
+ when 400 then raise BadRequestError, simple_message
283
+ when 401 then raise AuthenticationError, simple_message
284
+ when 404 then raise NotFoundError, simple_message
285
+ else
286
+ raise APIError, simple_message
287
+ end
288
+ end
289
+ end
290
+ end
291
+
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Elevenlabs
4
+ class Error < StandardError; end
5
+ class APIError < Error; end
6
+ class AuthenticationError < Error; end
7
+ class NotFoundError < Error; end
8
+ class BadRequestError < Error; end
9
+ # ... add more as needed ...
10
+ end
11
+
data/lib/elevenlabs.rb ADDED
@@ -0,0 +1,24 @@
1
+ # lib/elevenlabs.rb
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "elevenlabs/client"
5
+ require_relative "elevenlabs/errors"
6
+
7
+ module Elevenlabs
8
+ VERSION = "0.0.1"
9
+
10
+ # Optional global configuration
11
+ class << self
12
+ attr_accessor :configuration
13
+ end
14
+
15
+ def self.configure
16
+ self.configuration ||= Configuration.new
17
+ yield(configuration)
18
+ end
19
+
20
+ class Configuration
21
+ attr_accessor :api_key
22
+ end
23
+ end
24
+
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elevenlabs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - hackliteracy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-02-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ description: This gem provides a convenient Ruby interface to the ElevenLabs TTS,
28
+ Voice Cloning, and Streaming endpoints.
29
+ email:
30
+ - hackliteracy@gmail.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - README.md
36
+ - lib/elevenlabs.rb
37
+ - lib/elevenlabs/client.rb
38
+ - lib/elevenlabs/errors.rb
39
+ homepage: https://github.com/ktamulonis/elevenlabs
40
+ licenses:
41
+ - MIT
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '2.5'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubygems_version: 3.5.23
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: A Ruby client for the ElevenLabs Text-to-Speech API
62
+ test_files: []