eleven_rb 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 80d8ceea950da279e7f35661aa4597eef9f6ff51fb67e38e607f634eaaa54feb
4
- data.tar.gz: 8afa2939e1c267ee50c74ae2c6fe4fe8ea4f9fe0618ebb3d3cb52bc09f0c2683
3
+ metadata.gz: 8f8b7a2ab5d7ebe900552e83ae3e8499545f788da927ffc60d54185464d3bbc3
4
+ data.tar.gz: ee1b62923b6fc88304f4c78a60f68164617fd0f46d68cca09ad1496f14dda10d
5
5
  SHA512:
6
- metadata.gz: 0cb196287b9bad7eafa9e34ac6b3c30933f9d9e316d42728fd9a609cb83db95e0b63d307568a8000bfcaf1613d059026c63ff83e75cd70e9c2f686874be72d0a
7
- data.tar.gz: 22e29e9eab3a7e04a3ca040bf4cd564b70dd0077a777acbf095dc17eb5f64f3958578c457eb9f487c95a3194b7b62a9b97cf590bc8744c50f392c14af3532027
6
+ metadata.gz: 3079784a64fe6d3bff8e2c631d46763dcc6e352da16e9a7599d0ef0df7be45694c3b00279d3362f6459f7168c72f26b0e460eb80bd07239b675394979fd9cc59
7
+ data.tar.gz: 224530d093fdbd9b489adf6199cfd4cd19e22b2e9074bd42f59345fd0a5efcd06930c9ef9e9bfe9ab9034621c54eaf6c9443ba1b5aee91296c7365347fbcbe67
data/CHANGELOG.md CHANGED
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.0] - 2026-02-08
11
+
12
+ ### Added
13
+
14
+ - Music generation via `client.music.generate` (`POST /v1/music`)
15
+ - Music streaming via `client.music.stream` (`POST /v1/music/stream`)
16
+ - Composition plan creation via `client.music.create_plan` (`POST /v1/music/plan`)
17
+ - `Client#generate_music` convenience method
18
+
10
19
  ## [0.2.0] - 2026-02-07
11
20
 
12
21
  ### Added
data/README.md CHANGED
@@ -4,12 +4,13 @@
4
4
  [![CI](https://github.com/webventures/eleven_rb/actions/workflows/ci.yml/badge.svg)](https://github.com/webventures/eleven_rb/actions/workflows/ci.yml)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
- A Ruby client for the [ElevenLabs](https://try.elevenlabs.io/qyk2j8gumrjz) Text-to-Speech and Sound Effects API.
7
+ A Ruby client for the [ElevenLabs](https://try.elevenlabs.io/qyk2j8gumrjz) Text-to-Speech, Sound Effects, and Music API.
8
8
 
9
9
  ## Features
10
10
 
11
11
  - Text-to-Speech generation and streaming
12
12
  - Sound effects generation from text descriptions
13
+ - Music generation from prompts or composition plans
13
14
  - Voice management (list, get, create, update, delete)
14
15
  - Voice Library access (search 10,000+ community voices)
15
16
  - Voice Slot Manager for automatic slot management within account limits
@@ -109,6 +110,34 @@ audio = client.sound_effects.generate("gentle rain", loop: true)
109
110
  audio = client.generate_sound_effect("explosion")
110
111
  ```
111
112
 
113
+ ### Music
114
+
115
+ ```ruby
116
+ # Generate music from a text prompt
117
+ audio = client.music.generate("upbeat jazz piano solo")
118
+ audio.save_to_file("jazz.mp3")
119
+
120
+ # With options (duration, instrumental-only)
121
+ audio = client.music.generate(
122
+ "epic orchestral battle theme",
123
+ music_length_ms: 30_000,
124
+ force_instrumental: true
125
+ )
126
+
127
+ # Using a composition plan (create_plan is free, no credits used)
128
+ plan = client.music.create_plan("lo-fi hip hop beats", music_length_ms: 60_000)
129
+ audio = client.music.generate(composition_plan: plan)
130
+ audio.save_to_file("lo-fi.mp3")
131
+
132
+ # Streaming
133
+ File.open("song.mp3", "wb") do |file|
134
+ client.music.stream("ambient electronic") { |chunk| file.write(chunk) }
135
+ end
136
+
137
+ # Convenience method
138
+ audio = client.generate_music("chill acoustic guitar")
139
+ ```
140
+
112
141
  ### Voice Management
113
142
 
114
143
  ```ruby
@@ -86,6 +86,13 @@ module ElevenRb
86
86
  @sound_effects ||= Resources::SoundEffects.new(http_client)
87
87
  end
88
88
 
89
+ # Music generation resource
90
+ #
91
+ # @return [Resources::Music]
92
+ def music
93
+ @music ||= Resources::Music.new(http_client)
94
+ end
95
+
89
96
  # Voice slot manager
90
97
  #
91
98
  # @return [VoiceSlotManager]
@@ -112,6 +119,15 @@ module ElevenRb
112
119
  sound_effects.generate(text, **options)
113
120
  end
114
121
 
122
+ # Convenience method: generate music
123
+ #
124
+ # @param prompt [String] description of the music to generate
125
+ # @param options [Hash] additional options
126
+ # @return [Objects::Audio]
127
+ def generate_music(prompt, **options)
128
+ music.generate(prompt, **options)
129
+ end
130
+
115
131
  # Convenience method: stream speech
116
132
  #
117
133
  # @param text [String] the text to convert
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElevenRb
4
+ module Resources
5
+ # Music generation resource
6
+ #
7
+ # @example Generate music from a prompt
8
+ # audio = client.music.generate("upbeat jazz piano solo")
9
+ # audio.save_to_file("jazz.mp3")
10
+ #
11
+ # @example Generate music from a composition plan
12
+ # plan = client.music.create_plan("epic orchestral battle theme", music_length_ms: 30_000)
13
+ # audio = client.music.generate(composition_plan: plan)
14
+ # audio.save_to_file("battle.mp3")
15
+ #
16
+ # @example Stream music
17
+ # File.open("song.mp3", "wb") do |file|
18
+ # client.music.stream("lo-fi hip hop beats") { |chunk| file.write(chunk) }
19
+ # end
20
+ class Music < Base
21
+ DEFAULT_MODEL = 'music_v1'
22
+
23
+ # Generate music from a text prompt or composition plan
24
+ #
25
+ # @param prompt [String, nil] text description of the music to generate (mutually exclusive with composition_plan)
26
+ # @param composition_plan [Hash, nil] structured composition plan (mutually exclusive with prompt)
27
+ # @param music_length_ms [Integer, nil] duration in milliseconds (3000-600000, only with prompt)
28
+ # @param model_id [String] the model to use (default: music_v1)
29
+ # @param force_instrumental [Boolean, nil] whether to force instrumental output (only with prompt)
30
+ # @param respect_sections_durations [Boolean, nil] whether to respect section durations (only with compose)
31
+ # @param output_format [String] audio output format
32
+ # @return [Objects::Audio]
33
+ def generate(prompt = nil, composition_plan: nil, music_length_ms: nil,
34
+ model_id: DEFAULT_MODEL, force_instrumental: nil,
35
+ respect_sections_durations: nil, output_format: 'mp3_44100_128')
36
+ validate_prompt_or_plan!(prompt, composition_plan)
37
+
38
+ body = build_body(
39
+ prompt: prompt,
40
+ composition_plan: composition_plan,
41
+ music_length_ms: music_length_ms,
42
+ model_id: model_id,
43
+ force_instrumental: force_instrumental,
44
+ respect_sections_durations: respect_sections_durations
45
+ )
46
+
47
+ path = "/music?output_format=#{output_format}"
48
+ response = post_binary(path, body)
49
+
50
+ audio = Objects::Audio.new(
51
+ data: response,
52
+ format: output_format,
53
+ voice_id: nil,
54
+ text: prompt,
55
+ model_id: model_id
56
+ )
57
+
58
+ cost_info = Objects::CostInfo.new(text: prompt || '', voice_id: 'music', model_id: model_id)
59
+ http_client.config.trigger(
60
+ :on_audio_generated,
61
+ audio: audio,
62
+ voice_id: nil,
63
+ text: prompt,
64
+ cost_info: cost_info.to_h
65
+ )
66
+
67
+ audio
68
+ end
69
+
70
+ # Stream music from a text prompt or composition plan
71
+ #
72
+ # @param prompt [String, nil] text description of the music to generate
73
+ # @param composition_plan [Hash, nil] structured composition plan
74
+ # @param music_length_ms [Integer, nil] duration in milliseconds (3000-600000, only with prompt)
75
+ # @param model_id [String] the model to use
76
+ # @param force_instrumental [Boolean, nil] whether to force instrumental output (only with prompt)
77
+ # @param output_format [String] audio output format
78
+ # @yield [String] each chunk of audio data
79
+ # @return [void]
80
+ def stream(prompt = nil, composition_plan: nil, music_length_ms: nil,
81
+ model_id: DEFAULT_MODEL, force_instrumental: nil,
82
+ output_format: 'mp3_44100_128', &block)
83
+ validate_prompt_or_plan!(prompt, composition_plan)
84
+ raise ArgumentError, 'Block required for streaming' unless block_given?
85
+
86
+ body = build_body(
87
+ prompt: prompt,
88
+ composition_plan: composition_plan,
89
+ music_length_ms: music_length_ms,
90
+ model_id: model_id,
91
+ force_instrumental: force_instrumental
92
+ )
93
+
94
+ path = "/music/stream?output_format=#{output_format}"
95
+ post_stream(path, body, &block)
96
+
97
+ cost_info = Objects::CostInfo.new(text: prompt || '', voice_id: 'music', model_id: model_id)
98
+ http_client.config.trigger(
99
+ :on_audio_generated,
100
+ audio: nil,
101
+ voice_id: nil,
102
+ text: prompt,
103
+ cost_info: cost_info.to_h
104
+ )
105
+ end
106
+
107
+ # Create a composition plan from a text prompt (free, no credits used)
108
+ #
109
+ # @param prompt [String] text description of the music
110
+ # @param music_length_ms [Integer, nil] desired duration in milliseconds
111
+ # @param model_id [String] the model to use
112
+ # @return [Hash] structured composition plan
113
+ def create_plan(prompt, music_length_ms: nil, model_id: DEFAULT_MODEL)
114
+ validate_presence!(prompt, 'prompt')
115
+
116
+ body = {
117
+ prompt: prompt,
118
+ model_id: model_id
119
+ }
120
+ body[:music_length_ms] = music_length_ms unless music_length_ms.nil?
121
+
122
+ post('/music/plan', body)
123
+ end
124
+
125
+ private
126
+
127
+ def validate_prompt_or_plan!(prompt, composition_plan)
128
+ raise Errors::ValidationError, 'Either prompt or composition_plan must be provided' if prompt.nil? && composition_plan.nil?
129
+
130
+ return unless prompt && composition_plan
131
+
132
+ raise Errors::ValidationError, 'prompt and composition_plan are mutually exclusive'
133
+ end
134
+
135
+ def build_body(prompt:, composition_plan:, music_length_ms:, model_id:,
136
+ force_instrumental: nil, respect_sections_durations: nil)
137
+ body = { model_id: model_id }
138
+
139
+ if prompt
140
+ body[:prompt] = prompt
141
+ body[:music_length_ms] = music_length_ms unless music_length_ms.nil?
142
+ body[:force_instrumental] = force_instrumental unless force_instrumental.nil?
143
+ else
144
+ body[:composition_plan] = composition_plan
145
+ end
146
+
147
+ body[:respect_sections_durations] = respect_sections_durations unless respect_sections_durations.nil?
148
+
149
+ body
150
+ end
151
+ end
152
+ end
153
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ElevenRb
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
data/lib/eleven_rb.rb CHANGED
@@ -107,6 +107,7 @@ require_relative 'eleven_rb/resources/voice_library'
107
107
  require_relative 'eleven_rb/resources/models'
108
108
  require_relative 'eleven_rb/resources/user'
109
109
  require_relative 'eleven_rb/resources/sound_effects'
110
+ require_relative 'eleven_rb/resources/music'
110
111
 
111
112
  # High-level components
112
113
  require_relative 'eleven_rb/voice_slot_manager'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eleven_rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Web Ventures Ltd
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-02-07 00:00:00.000000000 Z
11
+ date: 2026-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -156,6 +156,7 @@ files:
156
156
  - lib/eleven_rb/objects/voice_settings.rb
157
157
  - lib/eleven_rb/resources/base.rb
158
158
  - lib/eleven_rb/resources/models.rb
159
+ - lib/eleven_rb/resources/music.rb
159
160
  - lib/eleven_rb/resources/sound_effects.rb
160
161
  - lib/eleven_rb/resources/text_to_speech.rb
161
162
  - lib/eleven_rb/resources/user.rb