elevenlabs_client 0.2.0 → 0.4.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 +169 -2
- data/README.md +50 -3
- data/lib/elevenlabs_client/client.rb +79 -35
- data/lib/elevenlabs_client/endpoints/dubs.rb +156 -0
- data/lib/elevenlabs_client/endpoints/models.rb +26 -0
- data/lib/elevenlabs_client/endpoints/music.rb +127 -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 +4 -0
- metadata +5 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b65be08b17b9ae232158f2c004511a7840688e76853c014f9be37984a9639d1
|
4
|
+
data.tar.gz: 2de88d74e59af044cfe943e32f0d2f2d8b45f71200469a69b3f95fc7605436d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f919ebdf7090d2f4cdd812589eccd1425e8be5e86615c04f3bf2882c7e8e8e07058db4f37cdf420e6b1948c668c3acbb177fe796b48c7f23563fa41a7204f4ce
|
7
|
+
data.tar.gz: 23b7dc77bb3ca90e2019d4098887b8555d103d66c1fdf259f883f7952b4c26f57671f9bd2e250e27491acc42c2518e85b37bcb48cdb678fd163f9b3be9b1d7e4
|
data/CHANGELOG.md
CHANGED
@@ -5,7 +5,174 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
-
## [
|
8
|
+
## [0.4.0] - 2025-09-12
|
9
|
+
|
10
|
+
### Added
|
11
|
+
|
12
|
+
- **🎵 Dubbing Generation API**
|
13
|
+
- `delete(dubbing_id)` - Delete dubbing projects
|
14
|
+
- `get_resource(dubbing_id)` - Get detailed resource information
|
15
|
+
- `create_segment(options)` - Create new segments
|
16
|
+
- `delete_segment(options)` - Delete segments
|
17
|
+
- `update_segment(options)` - Update segment text/timing
|
18
|
+
- `transcribe_segment(options)` - Regenerate transcriptions
|
19
|
+
- `translate_segment(options)` - Regenerate translations
|
20
|
+
- `dub_segment(options)` - Regenerate dubs
|
21
|
+
- `render_project(options)` - Render output media
|
22
|
+
- `update_speaker(options)` - Update speaker voices
|
23
|
+
- `get_similar_voices(options)` - Get voice recommendations
|
24
|
+
- **🔧 HTTP Client Improvements** - Added HTTP method
|
25
|
+
- Added `patch` method for PATCH requests
|
26
|
+
|
27
|
+
## [0.3.0] - 2025-09-12
|
28
|
+
|
29
|
+
### Added
|
30
|
+
- **🎵 Music Generation API** - AI-powered music composition and streaming
|
31
|
+
- `client.music.compose(options)` - Generate music from text prompts
|
32
|
+
- `client.music.compose_stream(options, &block)` - Real-time music streaming
|
33
|
+
- `client.music.compose_detailed(options)` - Generate music with metadata
|
34
|
+
- `client.music.create_plan(options)` - Create structured composition plans
|
35
|
+
- **🎭 Voice Management API** - Complete CRUD operations for individual voices
|
36
|
+
- `client.voices.get(voice_id)` - Get detailed voice information
|
37
|
+
- `client.voices.list()` - List all voices in account
|
38
|
+
- `client.voices.create(name, samples, **options)` - Create custom voices from audio samples
|
39
|
+
- `client.voices.edit(voice_id, samples, **options)` - Edit existing voices
|
40
|
+
- `client.voices.delete(voice_id)` - Delete voices from account
|
41
|
+
- `client.voices.banned?(voice_id)` - Check voice safety status
|
42
|
+
- `client.voices.active?(voice_id)` - Check voice availability
|
43
|
+
- **📋 Enhanced Rakefile** - Comprehensive gem management and development tasks
|
44
|
+
- Build, install, push, and clean gem operations
|
45
|
+
- Development tools (linting, testing, security audit)
|
46
|
+
- Documentation generation and serving
|
47
|
+
- Release preparation and management
|
48
|
+
- Maintenance and cleanup tasks
|
49
|
+
|
50
|
+
### Enhanced
|
51
|
+
- **🚨 Consolidated Error Handling** - Unified error handling across all endpoints
|
52
|
+
- Merged `handle_response`, `handle_binary_response`, and `handle_streaming_response` into single method
|
53
|
+
- Enhanced error message extraction from JSON, nested objects, arrays, and plain text
|
54
|
+
- More specific error types: `BadRequestError`, `NotFoundError`, `UnprocessableEntityError`
|
55
|
+
- Better error messages extracted from actual API responses instead of generic fallbacks
|
56
|
+
- **🔧 HTTP Client Improvements** - Added missing HTTP methods and consolidated functionality
|
57
|
+
- Added `delete` method for DELETE requests
|
58
|
+
- Enhanced `post_with_custom_headers` for flexible header management
|
59
|
+
- Consistent error handling across all HTTP methods (GET, POST, DELETE, multipart, binary, streaming)
|
60
|
+
- **📚 Documentation Organization** - Comprehensive documentation for all new features
|
61
|
+
- [MUSIC.md](docs/MUSIC.md) - Complete music generation guide (570 lines)
|
62
|
+
- [VOICES.md](docs/VOICES.md) - Voice management documentation (519 lines)
|
63
|
+
- Enhanced README with music capabilities and updated feature list
|
64
|
+
- Professional Rails integration examples
|
65
|
+
|
66
|
+
### New Error Classes
|
67
|
+
- `ElevenlabsClient::BadRequestError` (400) - Invalid parameters or malformed requests
|
68
|
+
- `ElevenlabsClient::NotFoundError` (404) - Resource not found
|
69
|
+
- `ElevenlabsClient::UnprocessableEntityError` (422) - Valid request but invalid data
|
70
|
+
|
71
|
+
### Music Generation Features
|
72
|
+
- **🎼 Composition Styles** - Support for all major music genres
|
73
|
+
- Electronic: EDM, House, Techno, Ambient, Synthwave
|
74
|
+
- Orchestral: Classical, Film Score, Epic Orchestral
|
75
|
+
- Popular: Pop, Rock, Hip-Hop, Country, Folk
|
76
|
+
- Jazz & Blues: Traditional Jazz, Smooth Jazz, Blues
|
77
|
+
- World Music: Celtic, Medieval, New Age, Ethnic
|
78
|
+
- **🎛️ Advanced Controls** - Detailed composition parameters
|
79
|
+
- Custom composition plans with sections, tempo, key, instruments
|
80
|
+
- Multiple output formats (MP3, WAV) with quality settings
|
81
|
+
- Music length control (5 seconds to 5 minutes)
|
82
|
+
- Model selection for different generation approaches
|
83
|
+
- **📡 Streaming Support** - Real-time music generation and playback
|
84
|
+
- Chunk-based streaming for immediate playback
|
85
|
+
- Memory-efficient processing for long compositions
|
86
|
+
- WebSocket integration for live applications
|
87
|
+
|
88
|
+
### Voice Management Features
|
89
|
+
- **🎤 Voice Creation** - Create custom voices from audio samples
|
90
|
+
- Multiple sample upload support for better quality
|
91
|
+
- Voice metadata and labeling system
|
92
|
+
- Quality validation and optimization
|
93
|
+
- **🔧 Voice Editing** - Modify existing voices
|
94
|
+
- Add new samples to improve voice quality
|
95
|
+
- Update voice metadata and descriptions
|
96
|
+
- Batch voice operations
|
97
|
+
- **🔍 Voice Discovery** - Advanced voice management
|
98
|
+
- Search and filter voices by category, labels, quality
|
99
|
+
- Voice status checking (active, banned, available)
|
100
|
+
- Voice analytics and usage tracking
|
101
|
+
|
102
|
+
### Rails Integration Examples
|
103
|
+
- **[MusicController](examples/music_controller.rb)** - Complete music generation implementation
|
104
|
+
- Basic and advanced music generation endpoints
|
105
|
+
- Streaming music with real-time playback
|
106
|
+
- Composition planning and structured music creation
|
107
|
+
- Batch generation and music library management
|
108
|
+
- Interactive music generation with user preferences
|
109
|
+
- **[VoicesController](examples/voices_controller.rb)** - Voice management implementation
|
110
|
+
- Full CRUD operations for voice management
|
111
|
+
- File upload handling for voice samples
|
112
|
+
- Voice search and filtering capabilities
|
113
|
+
- Batch voice operations and management workflows
|
114
|
+
|
115
|
+
### Technical Improvements
|
116
|
+
- **🧪 Comprehensive Testing** - Expanded test coverage
|
117
|
+
- **57 new music tests** (24 unit + 33 integration)
|
118
|
+
- **Enhanced error handling tests** across all endpoints
|
119
|
+
- **Total test coverage**: 300+ tests with consistent passing
|
120
|
+
- **🏗️ Architecture Consolidation** - Cleaner codebase
|
121
|
+
- Removed duplicate error handling methods
|
122
|
+
- Consolidated HTTP response processing
|
123
|
+
- Enhanced error message extraction with fallback handling
|
124
|
+
- Improved code organization and maintainability
|
125
|
+
- **📦 Release Management** - Professional release workflow
|
126
|
+
- Automated release preparation tasks
|
127
|
+
- Version management and changelog automation
|
128
|
+
- Security auditing and dependency management
|
129
|
+
- Documentation generation and validation
|
130
|
+
|
131
|
+
### Breaking Changes
|
132
|
+
- **Error Handling** - More specific error types may require catch block updates
|
133
|
+
```ruby
|
134
|
+
# Before (v0.2.0)
|
135
|
+
rescue ElevenlabsClient::ValidationError => e
|
136
|
+
# Handle all 4xx errors
|
137
|
+
end
|
138
|
+
|
139
|
+
# After (v0.3.0) - More specific handling
|
140
|
+
rescue ElevenlabsClient::BadRequestError => e
|
141
|
+
# Handle 400 Bad Request
|
142
|
+
rescue ElevenlabsClient::NotFoundError => e
|
143
|
+
# Handle 404 Not Found
|
144
|
+
rescue ElevenlabsClient::UnprocessableEntityError => e
|
145
|
+
# Handle 422 Unprocessable Entity
|
146
|
+
rescue ElevenlabsClient::ValidationError => e
|
147
|
+
# Handle other 4xx errors
|
148
|
+
end
|
149
|
+
```
|
150
|
+
|
151
|
+
### Migration Guide
|
152
|
+
```ruby
|
153
|
+
# New Music API Usage
|
154
|
+
client = ElevenlabsClient.new
|
155
|
+
|
156
|
+
# Generate music
|
157
|
+
music_data = client.music.compose(
|
158
|
+
prompt: "Upbeat electronic dance track",
|
159
|
+
music_length_ms: 30000
|
160
|
+
)
|
161
|
+
|
162
|
+
# Stream music generation
|
163
|
+
client.music.compose_stream(prompt: "Relaxing ambient") do |chunk|
|
164
|
+
# Process audio chunk in real-time
|
165
|
+
end
|
166
|
+
|
167
|
+
# Voice management
|
168
|
+
voices = client.voices.list
|
169
|
+
voice = client.voices.get("voice_id")
|
170
|
+
|
171
|
+
# Create custom voice
|
172
|
+
File.open("sample.mp3", "rb") do |sample|
|
173
|
+
voice = client.voices.create("My Voice", [sample])
|
174
|
+
end
|
175
|
+
```
|
9
176
|
|
10
177
|
## [0.2.0] - 2025-09-12
|
11
178
|
|
@@ -104,4 +271,4 @@ client.dubs.create(file_io: file, filename: "video.mp4", target_languages: ["es"
|
|
104
271
|
- **File Support**: Multiple video and audio formats (MP4, MOV, MP3, WAV, etc.)
|
105
272
|
- **Language Support**: Multiple target languages for dubbing
|
106
273
|
- **Configuration**: Flexible API key and endpoint configuration
|
107
|
-
- **Testing**: Comprehensive test suite with integration tests
|
274
|
+
- **Testing**: Comprehensive test suite with integration tests
|
data/README.md
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
# ElevenlabsClient
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/elevenlabs_client)
|
4
|
-
[](https://github.com/yourusername/elevenlabs_client/actions)
|
5
4
|
|
6
|
-
A comprehensive Ruby client library for the ElevenLabs API, supporting voice synthesis, dubbing, dialogue generation, and
|
5
|
+
A comprehensive Ruby client library for the ElevenLabs API, supporting voice synthesis, dubbing, dialogue generation, sound effects, and AI music composition.
|
7
6
|
|
8
7
|
## Features
|
9
8
|
|
@@ -11,6 +10,10 @@ A comprehensive Ruby client library for the ElevenLabs API, supporting voice syn
|
|
11
10
|
🎬 **Dubbing** - Create dubbed versions of audio/video content
|
12
11
|
💬 **Dialogue Generation** - Multi-speaker conversations
|
13
12
|
🔊 **Sound Generation** - AI-generated sound effects and ambient audio
|
13
|
+
🎵 **Music Generation** - AI-powered music composition and streaming
|
14
|
+
🎨 **Voice Design** - Create custom voices from text descriptions
|
15
|
+
🎭 **Voice Management** - Create, edit, and manage individual voices
|
16
|
+
🤖 **Models** - List available models and their capabilities
|
14
17
|
📡 **Streaming** - Real-time audio streaming
|
15
18
|
⚙️ **Configurable** - Flexible configuration options
|
16
19
|
🧪 **Well-tested** - Comprehensive test coverage
|
@@ -106,6 +109,38 @@ audio_data = client.text_to_dialogue.convert(dialogue)
|
|
106
109
|
# Sound Generation
|
107
110
|
audio_data = client.sound_generation.generate("Ocean waves crashing on rocks")
|
108
111
|
|
112
|
+
# Voice Design
|
113
|
+
design_result = client.text_to_voice.design("Warm, professional female voice")
|
114
|
+
generated_voice_id = design_result["previews"].first["generated_voice_id"]
|
115
|
+
|
116
|
+
voice_result = client.text_to_voice.create(
|
117
|
+
"Professional Voice",
|
118
|
+
"Warm, professional female voice",
|
119
|
+
generated_voice_id
|
120
|
+
)
|
121
|
+
|
122
|
+
# List Available Models
|
123
|
+
models = client.models.list
|
124
|
+
fastest_model = models["models"].min_by { |m| m["token_cost_factor"] }
|
125
|
+
puts "Fastest model: #{fastest_model['name']}"
|
126
|
+
|
127
|
+
# Voice Management
|
128
|
+
voices = client.voices.list
|
129
|
+
puts "Total voices: #{voices['voices'].length}"
|
130
|
+
|
131
|
+
# Create custom voice from audio samples
|
132
|
+
File.open("sample1.mp3", "rb") do |sample|
|
133
|
+
voice = client.voices.create("My Voice", [sample], description: "Custom narrator voice")
|
134
|
+
puts "Created voice: #{voice['voice_id']}"
|
135
|
+
end
|
136
|
+
|
137
|
+
# Music Generation
|
138
|
+
music_data = client.music.compose(
|
139
|
+
prompt: "Upbeat electronic dance track with synthesizers",
|
140
|
+
music_length_ms: 30000
|
141
|
+
)
|
142
|
+
File.open("generated_music.mp3", "wb") { |f| f.write(music_data) }
|
143
|
+
|
109
144
|
# Streaming Text-to-Speech
|
110
145
|
client.text_to_speech_stream.stream("voice_id", "Streaming text") do |chunk|
|
111
146
|
# Process audio chunk in real-time
|
@@ -122,6 +157,10 @@ end
|
|
122
157
|
- **[Text-to-Speech Streaming API](docs/TEXT_TO_SPEECH_STREAMING.md)** - Real-time audio streaming
|
123
158
|
- **[Text-to-Dialogue API](docs/TEXT_TO_DIALOGUE.md)** - Multi-speaker conversations
|
124
159
|
- **[Sound Generation API](docs/SOUND_GENERATION.md)** - AI-generated sound effects
|
160
|
+
- **[Music Generation API](docs/MUSIC.md)** - AI-powered music composition and streaming
|
161
|
+
- **[Text-to-Voice API](docs/TEXT_TO_VOICE.md)** - Design and create custom voices
|
162
|
+
- **[Voice Management API](docs/VOICES.md)** - Manage individual voices (CRUD operations)
|
163
|
+
- **[Models API](docs/MODELS.md)** - List available models and capabilities
|
125
164
|
|
126
165
|
### Available Endpoints
|
127
166
|
|
@@ -132,6 +171,10 @@ end
|
|
132
171
|
| `client.text_to_speech_stream.*` | Streaming TTS | [TEXT_TO_SPEECH_STREAMING.md](docs/TEXT_TO_SPEECH_STREAMING.md) |
|
133
172
|
| `client.text_to_dialogue.*` | Dialogue generation | [TEXT_TO_DIALOGUE.md](docs/TEXT_TO_DIALOGUE.md) |
|
134
173
|
| `client.sound_generation.*` | Sound effect generation | [SOUND_GENERATION.md](docs/SOUND_GENERATION.md) |
|
174
|
+
| `client.music.*` | AI music composition and streaming | [MUSIC.md](docs/MUSIC.md) |
|
175
|
+
| `client.text_to_voice.*` | Voice design and creation | [TEXT_TO_VOICE.md](docs/TEXT_TO_VOICE.md) |
|
176
|
+
| `client.voices.*` | Voice management (CRUD) | [VOICES.md](docs/VOICES.md) |
|
177
|
+
| `client.models.*` | Model information and capabilities | [MODELS.md](docs/MODELS.md) |
|
135
178
|
|
136
179
|
## Configuration Options
|
137
180
|
|
@@ -189,13 +232,16 @@ The gem is designed to work seamlessly with Rails applications. See the [example
|
|
189
232
|
- [StreamingAudioController](examples/streaming_audio_controller.rb) - Real-time streaming
|
190
233
|
- [TextToDialogueController](examples/text_to_dialogue_controller.rb) - Dialogue generation
|
191
234
|
- [SoundGenerationController](examples/sound_generation_controller.rb) - Sound effects
|
235
|
+
- [MusicController](examples/music_controller.rb) - AI music composition and streaming
|
236
|
+
- [TextToVoiceController](examples/text_to_voice_controller.rb) - Voice design and creation
|
237
|
+
- [VoicesController](examples/voices_controller.rb) - Voice management (CRUD operations)
|
192
238
|
|
193
239
|
## Development
|
194
240
|
|
195
241
|
After checking out the repo, run:
|
196
242
|
|
197
243
|
```bash
|
198
|
-
bin/setup
|
244
|
+
bin/setup # Install dependencies
|
199
245
|
bundle exec rspec # Run tests
|
200
246
|
```
|
201
247
|
|
@@ -221,6 +267,7 @@ bundle exec rspec
|
|
221
267
|
|
222
268
|
# Run specific test files
|
223
269
|
bundle exec rspec spec/elevenlabs_client/endpoints/
|
270
|
+
bundle exec rspec spec/elevenlabs_client/client
|
224
271
|
bundle exec rspec spec/integration/
|
225
272
|
|
226
273
|
# Run with documentation format
|
@@ -7,7 +7,7 @@ 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, :text_to_speech, :text_to_speech_stream, :text_to_dialogue, :sound_generation
|
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)
|
@@ -18,6 +18,10 @@ module ElevenlabsClient
|
|
18
18
|
@text_to_speech_stream = TextToSpeechStream.new(self)
|
19
19
|
@text_to_dialogue = TextToDialogue.new(self)
|
20
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)
|
21
25
|
end
|
22
26
|
|
23
27
|
# Makes an authenticated GET request
|
@@ -39,7 +43,33 @@ module ElevenlabsClient
|
|
39
43
|
def post(path, body = nil)
|
40
44
|
response = @conn.post(path) do |req|
|
41
45
|
req.headers["xi-api-key"] = api_key
|
42
|
-
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
|
59
|
+
end
|
60
|
+
|
61
|
+
handle_response(response)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Makes an authenticated PATCH request
|
65
|
+
# @param path [String] API endpoint path
|
66
|
+
# @param body [Hash, nil] Request body
|
67
|
+
# @return [Hash] Response body
|
68
|
+
def patch(path, body = nil)
|
69
|
+
response = @conn.patch(path) do |req|
|
70
|
+
req.headers["xi-api-key"] = api_key
|
71
|
+
req.headers["Content-Type"] = "application/json"
|
72
|
+
req.body = body.to_json if body
|
43
73
|
end
|
44
74
|
|
45
75
|
handle_response(response)
|
@@ -69,7 +99,7 @@ module ElevenlabsClient
|
|
69
99
|
req.body = body.to_json if body
|
70
100
|
end
|
71
101
|
|
72
|
-
|
102
|
+
handle_response(response)
|
73
103
|
end
|
74
104
|
|
75
105
|
# Makes an authenticated POST request with custom headers
|
@@ -87,7 +117,7 @@ module ElevenlabsClient
|
|
87
117
|
|
88
118
|
# For streaming/binary responses, return raw body
|
89
119
|
if custom_headers["Accept"]&.include?("audio") || custom_headers["Transfer-Encoding"] == "chunked"
|
90
|
-
|
120
|
+
handle_response(response)
|
91
121
|
else
|
92
122
|
handle_response(response)
|
93
123
|
end
|
@@ -111,7 +141,7 @@ module ElevenlabsClient
|
|
111
141
|
end
|
112
142
|
end
|
113
143
|
|
114
|
-
|
144
|
+
handle_response(response)
|
115
145
|
end
|
116
146
|
|
117
147
|
# Helper method to create Faraday::Multipart::FilePart
|
@@ -157,44 +187,58 @@ module ElevenlabsClient
|
|
157
187
|
case response.status
|
158
188
|
when 200..299
|
159
189
|
response.body
|
190
|
+
when 400
|
191
|
+
error_message = extract_error_message(response.body)
|
192
|
+
raise BadRequestError, error_message.empty? ? "Bad request - invalid parameters" : error_message
|
160
193
|
when 401
|
161
|
-
|
194
|
+
error_message = extract_error_message(response.body)
|
195
|
+
raise AuthenticationError, error_message.empty? ? "Invalid API key or authentication failed" : error_message
|
196
|
+
when 404
|
197
|
+
error_message = extract_error_message(response.body)
|
198
|
+
raise NotFoundError, error_message.empty? ? "Resource not found" : error_message
|
199
|
+
when 422
|
200
|
+
error_message = extract_error_message(response.body)
|
201
|
+
raise UnprocessableEntityError, error_message.empty? ? "Unprocessable entity - invalid data" : error_message
|
162
202
|
when 429
|
163
|
-
|
203
|
+
error_message = extract_error_message(response.body)
|
204
|
+
raise RateLimitError, error_message.empty? ? "Rate limit exceeded" : error_message
|
164
205
|
when 400..499
|
165
|
-
|
206
|
+
error_message = extract_error_message(response.body)
|
207
|
+
raise ValidationError, error_message.empty? ? "Client error occurred with status #{response.status}" : error_message
|
166
208
|
else
|
167
|
-
|
209
|
+
error_message = extract_error_message(response.body)
|
210
|
+
raise APIError, error_message.empty? ? "API request failed with status #{response.status}" : error_message
|
168
211
|
end
|
169
212
|
end
|
170
213
|
|
171
|
-
|
172
|
-
case response.status
|
173
|
-
when 200..299
|
174
|
-
response.body
|
175
|
-
when 401
|
176
|
-
raise AuthenticationError, "Invalid API key or authentication failed"
|
177
|
-
when 429
|
178
|
-
raise RateLimitError, "Rate limit exceeded"
|
179
|
-
when 400..499
|
180
|
-
raise ValidationError, "API request failed with status #{response.status}"
|
181
|
-
else
|
182
|
-
raise APIError, "API request failed with status #{response.status}"
|
183
|
-
end
|
184
|
-
end
|
214
|
+
private
|
185
215
|
|
186
|
-
def
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
216
|
+
def extract_error_message(response_body)
|
217
|
+
return "" if response_body.nil? || response_body.empty?
|
218
|
+
|
219
|
+
# Handle non-string response bodies
|
220
|
+
body_str = response_body.is_a?(String) ? response_body : response_body.to_s
|
221
|
+
|
222
|
+
begin
|
223
|
+
error_info = JSON.parse(body_str)
|
224
|
+
|
225
|
+
# Try different common error message fields
|
226
|
+
message = error_info["detail"] ||
|
227
|
+
error_info["message"] ||
|
228
|
+
error_info["error"] ||
|
229
|
+
error_info["errors"]
|
230
|
+
|
231
|
+
# Handle nested detail objects
|
232
|
+
if message.is_a?(Hash)
|
233
|
+
message = message["message"] || message.to_s
|
234
|
+
elsif message.is_a?(Array)
|
235
|
+
message = message.first.to_s
|
236
|
+
end
|
237
|
+
|
238
|
+
message.to_s
|
239
|
+
rescue JSON::ParserError, TypeError
|
240
|
+
# If not JSON or can't be parsed, return the raw body (truncated if too long)
|
241
|
+
body_str.length > 200 ? "#{body_str[0..200]}..." : body_str
|
198
242
|
end
|
199
243
|
end
|
200
244
|
|
@@ -53,6 +53,162 @@ module ElevenlabsClient
|
|
53
53
|
@client.get("/v1/dubbing/#{dubbing_id}/resources")
|
54
54
|
end
|
55
55
|
|
56
|
+
# DELETE /v1/dubbing/{id}
|
57
|
+
# Deletes a dubbing project
|
58
|
+
#
|
59
|
+
# @param dubbing_id [String] The dubbing job ID
|
60
|
+
# @return [Hash] Response with status
|
61
|
+
def delete(dubbing_id)
|
62
|
+
@client.delete("/v1/dubbing/#{dubbing_id}")
|
63
|
+
end
|
64
|
+
|
65
|
+
# GET /v1/dubbing/resource/{dubbing_id}
|
66
|
+
# Gets dubbing resource with detailed information including segments, speakers, etc.
|
67
|
+
#
|
68
|
+
# @param dubbing_id [String] The dubbing job ID
|
69
|
+
# @return [Hash] Detailed dubbing resource information
|
70
|
+
def get_resource(dubbing_id)
|
71
|
+
@client.get("/v1/dubbing/resource/#{dubbing_id}")
|
72
|
+
end
|
73
|
+
|
74
|
+
# POST /v1/dubbing/resource/{dubbing_id}/speaker/{speaker_id}/segment
|
75
|
+
# Creates a new segment in dubbing resource
|
76
|
+
#
|
77
|
+
# @param dubbing_id [String] The dubbing job ID
|
78
|
+
# @param speaker_id [String] The speaker ID
|
79
|
+
# @param start_time [Float] Start time of the segment
|
80
|
+
# @param end_time [Float] End time of the segment
|
81
|
+
# @param text [String, nil] Optional text for the segment
|
82
|
+
# @param translations [Hash, nil] Optional translations map
|
83
|
+
# @return [Hash] Response with version and new segment ID
|
84
|
+
def create_segment(dubbing_id:, speaker_id:, start_time:, end_time:, text: nil, translations: nil)
|
85
|
+
payload = {
|
86
|
+
start_time: start_time,
|
87
|
+
end_time: end_time,
|
88
|
+
text: text,
|
89
|
+
translations: translations
|
90
|
+
}.compact
|
91
|
+
|
92
|
+
@client.post("/v1/dubbing/resource/#{dubbing_id}/speaker/#{speaker_id}/segment", payload)
|
93
|
+
end
|
94
|
+
|
95
|
+
# DELETE /v1/dubbing/resource/{dubbing_id}/segment/{segment_id}
|
96
|
+
# Deletes a single segment from the dubbing
|
97
|
+
#
|
98
|
+
# @param dubbing_id [String] The dubbing job ID
|
99
|
+
# @param segment_id [String] The segment ID
|
100
|
+
# @return [Hash] Response with version
|
101
|
+
def delete_segment(dubbing_id, segment_id)
|
102
|
+
@client.delete("/v1/dubbing/resource/#{dubbing_id}/segment/#{segment_id}")
|
103
|
+
end
|
104
|
+
|
105
|
+
# PATCH /v1/dubbing/resource/{dubbing_id}/segment/{segment_id}/{language}
|
106
|
+
# Updates a single segment with new text and/or start/end times
|
107
|
+
#
|
108
|
+
# @param dubbing_id [String] The dubbing job ID
|
109
|
+
# @param segment_id [String] The segment ID
|
110
|
+
# @param language [String] The language ID
|
111
|
+
# @param start_time [Float, nil] Optional new start time
|
112
|
+
# @param end_time [Float, nil] Optional new end time
|
113
|
+
# @param text [String, nil] Optional new text
|
114
|
+
# @return [Hash] Response with version
|
115
|
+
def update_segment(dubbing_id:, segment_id:, language:, start_time: nil, end_time: nil, text: nil)
|
116
|
+
payload = {
|
117
|
+
start_time: start_time,
|
118
|
+
end_time: end_time,
|
119
|
+
text: text
|
120
|
+
}.compact
|
121
|
+
|
122
|
+
@client.patch("/v1/dubbing/resource/#{dubbing_id}/segment/#{segment_id}/#{language}", payload)
|
123
|
+
end
|
124
|
+
|
125
|
+
# POST /v1/dubbing/resource/{dubbing_id}/transcribe
|
126
|
+
# Regenerates transcriptions for specified segments
|
127
|
+
#
|
128
|
+
# @param dubbing_id [String] The dubbing job ID
|
129
|
+
# @param segments [Array<String>] List of segment IDs to transcribe
|
130
|
+
# @return [Hash] Response with version
|
131
|
+
def transcribe_segment(dubbing_id, segments)
|
132
|
+
payload = { segments: segments }
|
133
|
+
@client.post("/v1/dubbing/resource/#{dubbing_id}/transcribe", payload)
|
134
|
+
end
|
135
|
+
|
136
|
+
# POST /v1/dubbing/resource/{dubbing_id}/translate
|
137
|
+
# Regenerates translations for specified segments/languages
|
138
|
+
#
|
139
|
+
# @param dubbing_id [String] The dubbing job ID
|
140
|
+
# @param segments [Array<String>] List of segment IDs to translate
|
141
|
+
# @param languages [Array<String>, nil] Optional list of languages to translate
|
142
|
+
# @return [Hash] Response with version
|
143
|
+
def translate_segment(dubbing_id, segments, languages = nil)
|
144
|
+
payload = {
|
145
|
+
segments: segments,
|
146
|
+
languages: languages
|
147
|
+
}.compact
|
148
|
+
|
149
|
+
@client.post("/v1/dubbing/resource/#{dubbing_id}/translate", payload)
|
150
|
+
end
|
151
|
+
|
152
|
+
# POST /v1/dubbing/resource/{dubbing_id}/dub
|
153
|
+
# Regenerates dubs for specified segments/languages
|
154
|
+
#
|
155
|
+
# @param dubbing_id [String] The dubbing job ID
|
156
|
+
# @param segments [Array<String>] List of segment IDs to dub
|
157
|
+
# @param languages [Array<String>, nil] Optional list of languages to dub
|
158
|
+
# @return [Hash] Response with version
|
159
|
+
def dub_segment(dubbing_id, segments, languages = nil)
|
160
|
+
payload = {
|
161
|
+
segments: segments,
|
162
|
+
languages: languages
|
163
|
+
}.compact
|
164
|
+
|
165
|
+
@client.post("/v1/dubbing/resource/#{dubbing_id}/dub", payload)
|
166
|
+
end
|
167
|
+
|
168
|
+
# POST /v1/dubbing/resource/{dubbing_id}/render/{language}
|
169
|
+
# Renders the output media for a language
|
170
|
+
#
|
171
|
+
# @param dubbing_id [String] The dubbing job ID
|
172
|
+
# @param language [String] The language to render
|
173
|
+
# @param render_type [String] The type of render (mp4, aac, mp3, wav, aaf, tracks_zip, clips_zip)
|
174
|
+
# @param normalize_volume [Boolean, nil] Whether to normalize volume (defaults to false)
|
175
|
+
# @return [Hash] Response with version and render_id
|
176
|
+
def render_project(dubbing_id:, language:, render_type:, normalize_volume: nil)
|
177
|
+
payload = {
|
178
|
+
render_type: render_type,
|
179
|
+
normalize_volume: normalize_volume
|
180
|
+
}.compact
|
181
|
+
|
182
|
+
@client.post("/v1/dubbing/resource/#{dubbing_id}/render/#{language}", payload)
|
183
|
+
end
|
184
|
+
|
185
|
+
# PATCH /v1/dubbing/resource/{dubbing_id}/speaker/{speaker_id}
|
186
|
+
# Updates speaker metadata such as voice
|
187
|
+
#
|
188
|
+
# @param dubbing_id [String] The dubbing job ID
|
189
|
+
# @param speaker_id [String] The speaker ID
|
190
|
+
# @param voice_id [String, nil] Voice ID from library or 'track-clone'/'clip-clone'
|
191
|
+
# @param languages [Array<String>, nil] Languages to apply changes to
|
192
|
+
# @return [Hash] Response with version
|
193
|
+
def update_speaker(dubbing_id:, speaker_id:, voice_id: nil, languages: nil)
|
194
|
+
payload = {
|
195
|
+
voice_id: voice_id,
|
196
|
+
languages: languages
|
197
|
+
}.compact
|
198
|
+
|
199
|
+
@client.patch("/v1/dubbing/resource/#{dubbing_id}/speaker/#{speaker_id}", payload)
|
200
|
+
end
|
201
|
+
|
202
|
+
# GET /v1/dubbing/resource/{dubbing_id}/speaker/{speaker_id}/similar-voices
|
203
|
+
# Gets similar voices for a speaker
|
204
|
+
#
|
205
|
+
# @param dubbing_id [String] The dubbing job ID
|
206
|
+
# @param speaker_id [String] The speaker ID
|
207
|
+
# @return [Hash] Response with list of similar voices
|
208
|
+
def get_similar_voices(dubbing_id, speaker_id)
|
209
|
+
@client.get("/v1/dubbing/resource/#{dubbing_id}/speaker/#{speaker_id}/similar-voices")
|
210
|
+
end
|
211
|
+
|
56
212
|
private
|
57
213
|
|
58
214
|
attr_reader :client
|
@@ -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,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
|
@@ -0,0 +1,147 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElevenlabsClient
|
4
|
+
class Voices
|
5
|
+
def initialize(client)
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
# GET /v1/voices/{voice_id}
|
10
|
+
# Retrieves details about a single voice
|
11
|
+
# Documentation: https://elevenlabs.io/docs/api-reference/voices/get-voice
|
12
|
+
#
|
13
|
+
# @param voice_id [String] The ID of the voice to retrieve
|
14
|
+
# @return [Hash] Details of the voice
|
15
|
+
def get(voice_id)
|
16
|
+
endpoint = "/v1/voices/#{voice_id}"
|
17
|
+
@client.get(endpoint)
|
18
|
+
end
|
19
|
+
|
20
|
+
# GET /v1/voices
|
21
|
+
# Retrieves all voices associated with your Elevenlabs account
|
22
|
+
# Documentation: https://elevenlabs.io/docs/api-reference/voices
|
23
|
+
#
|
24
|
+
# @return [Hash] The JSON response containing an array of voices
|
25
|
+
def list
|
26
|
+
endpoint = "/v1/voices"
|
27
|
+
@client.get(endpoint)
|
28
|
+
end
|
29
|
+
|
30
|
+
# POST /v1/voices/add
|
31
|
+
# Creates a new voice by cloning from audio samples
|
32
|
+
# Documentation: https://elevenlabs.io/docs/api-reference/voices/add-voice
|
33
|
+
#
|
34
|
+
# @param name [String] Name of the voice
|
35
|
+
# @param samples [Array<File, IO>] Array of audio files to train the voice
|
36
|
+
# @param options [Hash] Additional parameters
|
37
|
+
# @option options [String] :description Description of the voice
|
38
|
+
# @option options [Hash] :labels Metadata labels for the voice
|
39
|
+
# @return [Hash] Response containing the new voice details
|
40
|
+
def create(name, samples = [], **options)
|
41
|
+
endpoint = "/v1/voices/add"
|
42
|
+
|
43
|
+
# Build multipart payload
|
44
|
+
payload = {
|
45
|
+
"name" => name,
|
46
|
+
"description" => options[:description] || ""
|
47
|
+
}
|
48
|
+
|
49
|
+
# Add labels if provided
|
50
|
+
if options[:labels]
|
51
|
+
options[:labels].each do |key, value|
|
52
|
+
payload["labels[#{key}]"] = value.to_s
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Add sample files
|
57
|
+
samples.each_with_index do |sample, index|
|
58
|
+
payload["files"] = @client.file_part(sample, "audio/mpeg")
|
59
|
+
end
|
60
|
+
|
61
|
+
@client.post_multipart(endpoint, payload)
|
62
|
+
end
|
63
|
+
|
64
|
+
# POST /v1/voices/{voice_id}/edit
|
65
|
+
# Updates an existing voice
|
66
|
+
# Documentation: https://elevenlabs.io/docs/api-reference/voices/edit-voice
|
67
|
+
#
|
68
|
+
# @param voice_id [String] The ID of the voice to edit
|
69
|
+
# @param samples [Array<File, IO>] Array of audio files (optional)
|
70
|
+
# @param options [Hash] Voice parameters to update
|
71
|
+
# @option options [String] :name New name for the voice
|
72
|
+
# @option options [String] :description New description for the voice
|
73
|
+
# @option options [Hash] :labels New labels for the voice
|
74
|
+
# @return [Hash] Response containing the updated voice details
|
75
|
+
def edit(voice_id, samples = [], **options)
|
76
|
+
endpoint = "/v1/voices/#{voice_id}/edit"
|
77
|
+
|
78
|
+
# Build multipart payload
|
79
|
+
payload = {}
|
80
|
+
|
81
|
+
# Add text fields if provided
|
82
|
+
payload["name"] = options[:name] if options[:name]
|
83
|
+
payload["description"] = options[:description] if options[:description]
|
84
|
+
|
85
|
+
# Add labels if provided
|
86
|
+
if options[:labels]
|
87
|
+
options[:labels].each do |key, value|
|
88
|
+
payload["labels[#{key}]"] = value.to_s
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Add sample files if provided
|
93
|
+
if samples && !samples.empty?
|
94
|
+
samples.each_with_index do |sample, index|
|
95
|
+
payload["files"] = @client.file_part(sample, "audio/mpeg")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
@client.post_multipart(endpoint, payload)
|
100
|
+
end
|
101
|
+
|
102
|
+
# DELETE /v1/voices/{voice_id}
|
103
|
+
# Deletes a voice from your account
|
104
|
+
# Documentation: https://elevenlabs.io/docs/api-reference/voices/delete-voice
|
105
|
+
#
|
106
|
+
# @param voice_id [String] The ID of the voice to delete
|
107
|
+
# @return [Hash] Response confirming deletion
|
108
|
+
def delete(voice_id)
|
109
|
+
endpoint = "/v1/voices/#{voice_id}"
|
110
|
+
@client.delete(endpoint)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Check if a voice is banned (safety control)
|
114
|
+
# @param voice_id [String] The ID of the voice to check
|
115
|
+
# @return [Boolean] True if the voice is banned
|
116
|
+
def banned?(voice_id)
|
117
|
+
voice = get(voice_id)
|
118
|
+
voice["safety_control"] == "BAN"
|
119
|
+
rescue ElevenlabsClient::ValidationError, ElevenlabsClient::APIError, ElevenlabsClient::NotFoundError
|
120
|
+
# If we can't get the voice, assume it's not banned
|
121
|
+
false
|
122
|
+
end
|
123
|
+
|
124
|
+
# Check if a voice is active (exists in the voice list)
|
125
|
+
# @param voice_id [String] The ID of the voice to check
|
126
|
+
# @return [Boolean] True if the voice is active
|
127
|
+
def active?(voice_id)
|
128
|
+
voices = list
|
129
|
+
active_voice_ids = voices["voices"].map { |voice| voice["voice_id"] }
|
130
|
+
active_voice_ids.include?(voice_id)
|
131
|
+
rescue ElevenlabsClient::ValidationError, ElevenlabsClient::APIError, ElevenlabsClient::NotFoundError
|
132
|
+
# If we can't get the voice list, assume it's not active
|
133
|
+
false
|
134
|
+
end
|
135
|
+
|
136
|
+
# Alias methods for backward compatibility and convenience
|
137
|
+
alias_method :get_voice, :get
|
138
|
+
alias_method :list_voices, :list
|
139
|
+
alias_method :create_voice, :create
|
140
|
+
alias_method :edit_voice, :edit
|
141
|
+
alias_method :delete_voice, :delete
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
attr_reader :client
|
146
|
+
end
|
147
|
+
end
|
data/lib/elevenlabs_client.rb
CHANGED
@@ -8,6 +8,10 @@ require_relative "elevenlabs_client/endpoints/text_to_speech"
|
|
8
8
|
require_relative "elevenlabs_client/endpoints/text_to_speech_stream"
|
9
9
|
require_relative "elevenlabs_client/endpoints/text_to_dialogue"
|
10
10
|
require_relative "elevenlabs_client/endpoints/sound_generation"
|
11
|
+
require_relative "elevenlabs_client/endpoints/text_to_voice"
|
12
|
+
require_relative "elevenlabs_client/endpoints/models"
|
13
|
+
require_relative "elevenlabs_client/endpoints/voices"
|
14
|
+
require_relative "elevenlabs_client/endpoints/music"
|
11
15
|
require_relative "elevenlabs_client/client"
|
12
16
|
|
13
17
|
module ElevenlabsClient
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elevenlabs_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vitor Oliveira
|
@@ -122,10 +122,14 @@ files:
|
|
122
122
|
- lib/elevenlabs_client.rb
|
123
123
|
- lib/elevenlabs_client/client.rb
|
124
124
|
- lib/elevenlabs_client/endpoints/dubs.rb
|
125
|
+
- lib/elevenlabs_client/endpoints/models.rb
|
126
|
+
- lib/elevenlabs_client/endpoints/music.rb
|
125
127
|
- lib/elevenlabs_client/endpoints/sound_generation.rb
|
126
128
|
- lib/elevenlabs_client/endpoints/text_to_dialogue.rb
|
127
129
|
- lib/elevenlabs_client/endpoints/text_to_speech.rb
|
128
130
|
- lib/elevenlabs_client/endpoints/text_to_speech_stream.rb
|
131
|
+
- lib/elevenlabs_client/endpoints/text_to_voice.rb
|
132
|
+
- lib/elevenlabs_client/endpoints/voices.rb
|
129
133
|
- lib/elevenlabs_client/errors.rb
|
130
134
|
- lib/elevenlabs_client/settings.rb
|
131
135
|
- lib/elevenlabs_client/version.rb
|