diff --git a/ElevenLabs-DotNet-Proxy/ElevenLabs-DotNet-Proxy.csproj b/ElevenLabs-DotNet-Proxy/ElevenLabs-DotNet-Proxy.csproj
index 8265666..cd8ed77 100644
--- a/ElevenLabs-DotNet-Proxy/ElevenLabs-DotNet-Proxy.csproj
+++ b/ElevenLabs-DotNet-Proxy/ElevenLabs-DotNet-Proxy.csproj
@@ -3,32 +3,33 @@
net6.0
disable
disable
- True
- false
- false
Stephen Hodgson
+
ElevenLabs API Proxy
ElevenLabs-DotNet-Proxy
+ ElevenLabs-DotNet-Proxy
+ ElevenLabs.Proxy
A simple Proxy API gateway for ElevenLabs-DotNet to make authenticated requests from a front end application without exposing your API keys.
2024
https://github.com/RageAgainstThePixel/ElevenLabs-DotNet
https://github.com/RageAgainstThePixel/ElevenLabs-DotNet
ElevenLabs, AI, ML, API, api-proxy, proxy, gateway
- ElevenLabs API Proxy
- ElevenLabs-DotNet-Proxy
- 2.2.2
- ElevenLabs.Proxy
- Version 2.2.2
+ ElevenLabsIcon.png
+ Readme.md
+ LICENSE
+ true
+ true
+ false
+ false
+ 3.0.0
+
+Version 3.0.0
+- Renamed ElevenLabsProxyStartup to ElevenLabsProxy
+Version 2.2.2
- Updated EndpointRouteBuilder with optional route prefix parameter
Version 2.2.1
- Refactor with modern WebApplication builder
- Added ElevenLabs.Proxy.EndpointRouteBuilder
- True
- false
- Readme.md
- True
- ElevenLabsIcon.png
- LICENSE
@@ -36,17 +37,17 @@ Version 2.2.1
- True
+ true
\
- True
+ true
\
- True
+ true
\
diff --git a/ElevenLabs-DotNet-Proxy/Proxy/ElevenLabsProxy.cs b/ElevenLabs-DotNet-Proxy/Proxy/ElevenLabsProxy.cs
new file mode 100644
index 0000000..89014eb
--- /dev/null
+++ b/ElevenLabs-DotNet-Proxy/Proxy/ElevenLabsProxy.cs
@@ -0,0 +1,114 @@
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Server.Kestrel.Core;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using System;
+using System.Threading.Tasks;
+
+namespace ElevenLabs.Proxy
+{
+ ///
+ /// Used in ASP.NET Core WebApps to start your own ElevenLabs web api proxy.
+ ///
+ public class ElevenLabsProxy
+ {
+ private ElevenLabsClient elevenLabsClient;
+ private IAuthenticationFilter authenticationFilter;
+
+ ///
+ /// Configures the and services.
+ ///
+ ///
+ public void ConfigureServices(IServiceCollection services)
+ => SetupServices(services.BuildServiceProvider());
+
+ ///
+ /// Configures the to handle requests and forward them to OpenAI API.
+ ///
+ /// .
+ /// .
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+
+ SetupServices(app.ApplicationServices);
+
+ app.UseHttpsRedirection();
+ app.UseRouting();
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapGet("/health", HealthEndpoint);
+ endpoints.MapElevenLabsEndpoints(elevenLabsClient, authenticationFilter);
+ });
+ }
+
+ ///
+ /// Creates a new that acts as a proxy web api for ElevenLabs.
+ ///
+ /// type to use to validate your custom issued tokens.
+ /// Startup args.
+ /// with configured and .
+ public static IHost CreateDefaultHost(string[] args, ElevenLabsClient elevenLabsClient) where T : class, IAuthenticationFilter
+ => Host.CreateDefaultBuilder(args)
+ .ConfigureWebHostDefaults(webBuilder =>
+ {
+ webBuilder.UseStartup();
+ webBuilder.ConfigureKestrel(ConfigureKestrel);
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddSingleton(elevenLabsClient);
+ services.AddSingleton();
+ }).Build();
+
+ ///
+ /// Creates a new that acts as a proxy web api for OpenAI.
+ ///
+ /// type to use to validate your custom issued tokens.
+ /// Startup args.
+ /// with configured and .
+ public static WebApplication CreateWebApplication(string[] args, ElevenLabsClient elevenLabsClient) where T : class, IAuthenticationFilter
+ {
+ var builder = WebApplication.CreateBuilder(args);
+ builder.WebHost.ConfigureKestrel(ConfigureKestrel);
+ builder.Services.AddSingleton(elevenLabsClient);
+ builder.Services.AddSingleton();
+ var app = builder.Build();
+ var startup = new ElevenLabsProxy();
+ startup.Configure(app, app.Environment);
+ return app;
+ }
+
+ private static void ConfigureKestrel(KestrelServerOptions options)
+ {
+ options.AllowSynchronousIO = false;
+ options.Limits.MinRequestBodyDataRate = null;
+ options.Limits.MinResponseDataRate = null;
+ options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(10);
+ options.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(2);
+ }
+
+ private void SetupServices(IServiceProvider serviceProvider)
+ {
+ elevenLabsClient = serviceProvider.GetRequiredService();
+ authenticationFilter = serviceProvider.GetRequiredService();
+ }
+
+ private static async Task HealthEndpoint(HttpContext context)
+ {
+ // Respond with a 200 OK status code and a plain text message
+ context.Response.StatusCode = StatusCodes.Status200OK;
+ const string contentType = "text/plain";
+ context.Response.ContentType = contentType;
+ const string content = "OK";
+ await context.Response.WriteAsync(content);
+ }
+ }
+}
diff --git a/ElevenLabs-DotNet-Proxy/Proxy/ElevenLabsProxyStartup.cs b/ElevenLabs-DotNet-Proxy/Proxy/ElevenLabsProxyStartup.cs
index e8fab3f..1ec478f 100644
--- a/ElevenLabs-DotNet-Proxy/Proxy/ElevenLabsProxyStartup.cs
+++ b/ElevenLabs-DotNet-Proxy/Proxy/ElevenLabsProxyStartup.cs
@@ -2,113 +2,22 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
-using System.Threading.Tasks;
namespace ElevenLabs.Proxy
{
- ///
- /// Used in ASP.NET Core WebApps to start your own ElevenLabs web api proxy.
- ///
+ [Obsolete("Use ElevenLabsProxy")]
public class ElevenLabsProxyStartup
{
- private ElevenLabsClient elevenLabsClient;
- private IAuthenticationFilter authenticationFilter;
+ public void ConfigureServices(IServiceCollection services) { }
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { }
- ///
- /// Configures the and services.
- ///
- ///
- public void ConfigureServices(IServiceCollection services)
- => SetupServices(services.BuildServiceProvider());
+ public static IHost CreateDefaultHost(string[] args, ElevenLabsClient elevenLabsClient)
+ where T : class, IAuthenticationFilter => null;
- ///
- /// Configures the to handle requests and forward them to OpenAI API.
- ///
- /// .
- /// .
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
-
- SetupServices(app.ApplicationServices);
-
- app.UseHttpsRedirection();
- app.UseRouting();
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapGet("/health", HealthEndpoint);
- endpoints.MapElevenLabsEndpoints(elevenLabsClient, authenticationFilter);
- });
- }
-
- ///
- /// Creates a new that acts as a proxy web api for ElevenLabs.
- ///
- /// type to use to validate your custom issued tokens.
- /// Startup args.
- /// with configured and .
- public static IHost CreateDefaultHost(string[] args, ElevenLabsClient elevenLabsClient) where T : class, IAuthenticationFilter
- => Host.CreateDefaultBuilder(args)
- .ConfigureWebHostDefaults(webBuilder =>
- {
- webBuilder.UseStartup();
- webBuilder.ConfigureKestrel(ConfigureKestrel);
- })
- .ConfigureServices(services =>
- {
- services.AddSingleton(elevenLabsClient);
- services.AddSingleton();
- }).Build();
-
- ///
- /// Creates a new that acts as a proxy web api for OpenAI.
- ///
- /// type to use to validate your custom issued tokens.
- /// Startup args.
- /// with configured and .
- public static WebApplication CreateWebApplication(string[] args, ElevenLabsClient elevenLabsClient) where T : class, IAuthenticationFilter
- {
- var builder = WebApplication.CreateBuilder(args);
- builder.WebHost.ConfigureKestrel(ConfigureKestrel);
- builder.Services.AddSingleton(elevenLabsClient);
- builder.Services.AddSingleton();
- var app = builder.Build();
- var startup = new ElevenLabsProxyStartup();
- startup.Configure(app, app.Environment);
- return app;
- }
-
- private static void ConfigureKestrel(KestrelServerOptions options)
- {
- options.AllowSynchronousIO = false;
- options.Limits.MinRequestBodyDataRate = null;
- options.Limits.MinResponseDataRate = null;
- options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(10);
- options.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(2);
- }
-
- private void SetupServices(IServiceProvider serviceProvider)
- {
- elevenLabsClient = serviceProvider.GetRequiredService();
- authenticationFilter = serviceProvider.GetRequiredService();
- }
-
- private static async Task HealthEndpoint(HttpContext context)
- {
- // Respond with a 200 OK status code and a plain text message
- context.Response.StatusCode = StatusCodes.Status200OK;
- const string contentType = "text/plain";
- context.Response.ContentType = contentType;
- const string content = "OK";
- await context.Response.WriteAsync(content);
- }
+ public static WebApplication CreateWebApplication(string[] args, ElevenLabsClient elevenLabsClient)
+ where T : class, IAuthenticationFilter => null;
}
-}
+}
\ No newline at end of file
diff --git a/ElevenLabs-DotNet-Tests-Proxy/Program.cs b/ElevenLabs-DotNet-Tests-Proxy/Program.cs
index 37d5d1d..97bfc42 100644
--- a/ElevenLabs-DotNet-Tests-Proxy/Program.cs
+++ b/ElevenLabs-DotNet-Tests-Proxy/Program.cs
@@ -43,9 +43,9 @@ public override async Task ValidateAuthenticationAsync(IHeaderDictionary request
public static void Main(string[] args)
{
- var auth = ElevenLabsAuthentication.LoadFromEnv();
+ var auth = ElevenLabsAuthentication.LoadFromEnvironment();
var client = new ElevenLabsClient(auth);
- ElevenLabsProxyStartup.CreateWebApplication(args, client).Run();
+ ElevenLabsProxy.CreateWebApplication(args, client).Run();
}
}
}
\ No newline at end of file
diff --git a/ElevenLabs-DotNet-Tests/AbstractTestFixture.cs b/ElevenLabs-DotNet-Tests/AbstractTestFixture.cs
index 8681f7f..61b7217 100644
--- a/ElevenLabs-DotNet-Tests/AbstractTestFixture.cs
+++ b/ElevenLabs-DotNet-Tests/AbstractTestFixture.cs
@@ -31,6 +31,7 @@ protected AbstractTestFixture()
var settings = new ElevenLabsClientSettings(domain: domain);
var auth = new ElevenLabsAuthentication(TestUserToken);
ElevenLabsClient = new ElevenLabsClient(auth, settings, HttpClient);
+ ElevenLabsClient.EnableDebug = true;
}
}
}
\ No newline at end of file
diff --git a/ElevenLabs-DotNet-Tests/Test_Fixture_000_Proxy.cs b/ElevenLabs-DotNet-Tests/TestFixture_000_Proxy.cs
similarity index 96%
rename from ElevenLabs-DotNet-Tests/Test_Fixture_000_Proxy.cs
rename to ElevenLabs-DotNet-Tests/TestFixture_000_Proxy.cs
index 6bbd79d..a9590b3 100644
--- a/ElevenLabs-DotNet-Tests/Test_Fixture_000_Proxy.cs
+++ b/ElevenLabs-DotNet-Tests/TestFixture_000_Proxy.cs
@@ -8,7 +8,7 @@
namespace ElevenLabs.Tests
{
- internal class Test_Fixture_000_Proxy : AbstractTestFixture
+ internal class TestFixture_000_Proxy : AbstractTestFixture
{
[Test]
public async Task Test_01_Health()
diff --git a/ElevenLabs-DotNet-Tests/Test_Fixture_00_Authentication.cs b/ElevenLabs-DotNet-Tests/TestFixture_00_Authentication.cs
similarity index 87%
rename from ElevenLabs-DotNet-Tests/Test_Fixture_00_Authentication.cs
rename to ElevenLabs-DotNet-Tests/TestFixture_00_Authentication.cs
index 51f6c68..e068dce 100644
--- a/ElevenLabs-DotNet-Tests/Test_Fixture_00_Authentication.cs
+++ b/ElevenLabs-DotNet-Tests/TestFixture_00_Authentication.cs
@@ -8,29 +8,29 @@
namespace ElevenLabs.Tests
{
- internal class Test_Fixture_00_Authentication
+ internal class TestFixture_00_Authentication
{
[SetUp]
public void Setup()
{
var authJson = new AuthInfo("key-test12");
var authText = JsonSerializer.Serialize(authJson);
- File.WriteAllText(".elevenlabs", authText);
+ File.WriteAllText(ElevenLabsAuthentication.CONFIG_FILE, authText);
}
[Test]
- public void Test_01_GetAuthFromEnv()
+ public void Test_01_GetAuthFromEnvironment()
{
- var auth = ElevenLabsAuthentication.LoadFromEnv();
+ var auth = ElevenLabsAuthentication.LoadFromEnvironment();
Assert.IsNotNull(auth);
Assert.IsNotNull(auth.ApiKey);
Assert.IsNotEmpty(auth.ApiKey);
}
[Test]
- public void Test_02_GetAuthFromFile()
+ public void Test_02_GetAuthFromPath()
{
- var auth = ElevenLabsAuthentication.LoadFromDirectory();
+ var auth = ElevenLabsAuthentication.LoadFromPath(ElevenLabsAuthentication.CONFIG_FILE);
Assert.IsNotNull(auth);
Assert.IsNotNull(auth.ApiKey);
Assert.AreEqual("key-test12", auth.ApiKey);
@@ -119,9 +119,9 @@ public void Test_09_CustomDomainConfigurationSettings()
[TearDown]
public void TearDown()
{
- if (File.Exists(".elevenlabs"))
+ if (File.Exists(ElevenLabsAuthentication.CONFIG_FILE))
{
- File.Delete(".elevenlabs");
+ File.Delete(ElevenLabsAuthentication.CONFIG_FILE);
}
}
}
diff --git a/ElevenLabs-DotNet-Tests/Test_Fixture_01_UserEndpoint.cs b/ElevenLabs-DotNet-Tests/TestFixture_01_UserEndpoint.cs
similarity index 91%
rename from ElevenLabs-DotNet-Tests/Test_Fixture_01_UserEndpoint.cs
rename to ElevenLabs-DotNet-Tests/TestFixture_01_UserEndpoint.cs
index 82609d4..b4fc2d6 100644
--- a/ElevenLabs-DotNet-Tests/Test_Fixture_01_UserEndpoint.cs
+++ b/ElevenLabs-DotNet-Tests/TestFixture_01_UserEndpoint.cs
@@ -5,7 +5,7 @@
namespace ElevenLabs.Tests
{
- internal class Test_Fixture_01_UserEndpoint : AbstractTestFixture
+ internal class TestFixture_01_UserEndpoint : AbstractTestFixture
{
[Test]
public async Task Test_01_GetUserInfo()
diff --git a/ElevenLabs-DotNet-Tests/Test_Fixture_06_Models.cs b/ElevenLabs-DotNet-Tests/TestFixture_02_Models.cs
similarity index 92%
rename from ElevenLabs-DotNet-Tests/Test_Fixture_06_Models.cs
rename to ElevenLabs-DotNet-Tests/TestFixture_02_Models.cs
index 4114ba3..2d9b8a2 100644
--- a/ElevenLabs-DotNet-Tests/Test_Fixture_06_Models.cs
+++ b/ElevenLabs-DotNet-Tests/TestFixture_02_Models.cs
@@ -6,7 +6,7 @@
namespace ElevenLabs.Tests
{
- internal class Test_Fixture_06_Models : AbstractTestFixture
+ internal class TestFixture_02_Models : AbstractTestFixture
{
[Test]
public async Task Test_01_GetModels()
diff --git a/ElevenLabs-DotNet-Tests/Test_Fixture_02_VoicesEndpoint.cs b/ElevenLabs-DotNet-Tests/TestFixture_03_VoicesEndpoint.cs
similarity index 92%
rename from ElevenLabs-DotNet-Tests/Test_Fixture_02_VoicesEndpoint.cs
rename to ElevenLabs-DotNet-Tests/TestFixture_03_VoicesEndpoint.cs
index 9d0f4e4..a3cbaf0 100644
--- a/ElevenLabs-DotNet-Tests/Test_Fixture_02_VoicesEndpoint.cs
+++ b/ElevenLabs-DotNet-Tests/TestFixture_03_VoicesEndpoint.cs
@@ -10,10 +10,10 @@
namespace ElevenLabs.Tests
{
- internal class Test_Fixture_02_VoicesEndpoint : AbstractTestFixture
+ internal class TestFixture_03_VoicesEndpoint : AbstractTestFixture
{
[Test]
- public async Task Test_01_GetVoices()
+ public async Task Test_01_01_GetVoices()
{
Assert.NotNull(ElevenLabsClient.VoicesEndpoint);
var results = await ElevenLabsClient.VoicesEndpoint.GetAllVoicesAsync();
@@ -26,6 +26,20 @@ public async Task Test_01_GetVoices()
}
}
+ [Test]
+ public async Task Test_01_02_GetSharedVoices()
+ {
+ Assert.NotNull(ElevenLabsClient.SharedVoicesEndpoint);
+ var results = await ElevenLabsClient.SharedVoicesEndpoint.GetSharedVoicesAsync();
+ Assert.NotNull(results);
+ Assert.IsNotEmpty(results.Voices);
+
+ foreach (var voice in results.Voices)
+ {
+ Console.WriteLine($"{voice.OwnerId} | {voice.VoiceId} | {voice.Date} | {voice.Name}");
+ }
+ }
+
[Test]
public async Task Test_02_GetDefaultVoiceSettings()
{
diff --git a/ElevenLabs-DotNet-Tests/Test_Fixture_03_TextToSpeechEndpoint.cs b/ElevenLabs-DotNet-Tests/TestFixture_04_TextToSpeechEndpoint.cs
similarity index 95%
rename from ElevenLabs-DotNet-Tests/Test_Fixture_03_TextToSpeechEndpoint.cs
rename to ElevenLabs-DotNet-Tests/TestFixture_04_TextToSpeechEndpoint.cs
index dbd31d8..545f487 100644
--- a/ElevenLabs-DotNet-Tests/Test_Fixture_03_TextToSpeechEndpoint.cs
+++ b/ElevenLabs-DotNet-Tests/TestFixture_04_TextToSpeechEndpoint.cs
@@ -8,7 +8,7 @@
namespace ElevenLabs.Tests
{
- internal class Test_Fixture_03_TextToSpeechEndpoint : AbstractTestFixture
+ internal class TestFixture_04_TextToSpeechEndpoint : AbstractTestFixture
{
[Test]
public async Task Test_01_TextToSpeech()
diff --git a/ElevenLabs-DotNet-Tests/Test_Fixture_05_VoiceGeneration.cs b/ElevenLabs-DotNet-Tests/TestFixture_05_VoiceGeneration.cs
similarity index 96%
rename from ElevenLabs-DotNet-Tests/Test_Fixture_05_VoiceGeneration.cs
rename to ElevenLabs-DotNet-Tests/TestFixture_05_VoiceGeneration.cs
index dd67898..87ce52d 100644
--- a/ElevenLabs-DotNet-Tests/Test_Fixture_05_VoiceGeneration.cs
+++ b/ElevenLabs-DotNet-Tests/TestFixture_05_VoiceGeneration.cs
@@ -9,7 +9,7 @@
namespace ElevenLabs.Tests
{
- internal class Test_Fixture_05_VoiceGeneration : AbstractTestFixture
+ internal class TestFixture_05_VoiceGeneration : AbstractTestFixture
{
[Test]
public async Task Test_01_GetVoiceGenerationOptions()
diff --git a/ElevenLabs-DotNet-Tests/TestFixture_06_SoundGeneration.cs b/ElevenLabs-DotNet-Tests/TestFixture_06_SoundGeneration.cs
new file mode 100644
index 0000000..472f5b2
--- /dev/null
+++ b/ElevenLabs-DotNet-Tests/TestFixture_06_SoundGeneration.cs
@@ -0,0 +1,96 @@
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using ElevenLabs.SoundGeneration;
+using NUnit.Framework;
+using System.Threading.Tasks;
+
+namespace ElevenLabs.Tests
+{
+ internal class TestFixture_06_SoundGeneration : AbstractTestFixture
+ {
+ [Test]
+ public async Task Test_01_GenerateSound()
+ {
+ Assert.NotNull(ElevenLabsClient.SoundGenerationEndpoint);
+ var request = new SoundGenerationRequest("Star Wars Light Saber parry");
+ var clip = await ElevenLabsClient.SoundGenerationEndpoint.GenerateSoundAsync(request);
+ Assert.NotNull(clip);
+ Assert.IsFalse(clip.ClipData.IsEmpty);
+ Assert.IsFalse(string.IsNullOrWhiteSpace(clip.Text));
+ }
+
+ //[Test]
+ //public async Task Test_02_01_GetSoundGenerationHistory()
+ //{
+ // Assert.NotNull(ElevenLabsClient.SoundGenerationEndpoint);
+ // var historyInfo = await ElevenLabsClient.SoundGenerationEndpoint.GetHistoryAsync(pageSize: 20);
+ // Assert.NotNull(historyInfo);
+ // Assert.IsNotEmpty(historyInfo.HistoryItems);
+
+ // foreach (var item in historyInfo.HistoryItems)
+ // {
+ // Console.WriteLine($"{item.Id} | {item.Text} | {item.CreatedAt}");
+ // }
+ //}
+
+ //[Test]
+ //public async Task Test_02_02_GetHistoryAudio()
+ //{
+ // Assert.NotNull(ElevenLabsClient.SoundGenerationEndpoint);
+ // var historyInfo = await ElevenLabsClient.SoundGenerationEndpoint.GetHistoryAsync(pageSize: 20);
+ // Assert.NotNull(historyInfo);
+ // Assert.IsNotEmpty(historyInfo.HistoryItems);
+ // var downloadItem = historyInfo.HistoryItems.OrderByDescending(item => item.CreatedAt).FirstOrDefault();
+ // Assert.NotNull(downloadItem);
+ // Console.WriteLine($"Downloading {downloadItem!.Id}...");
+ // var soundClip = await ElevenLabsClient.SoundGenerationEndpoint.DownloadSoundAudioAsync(downloadItem);
+ // Assert.NotNull(soundClip);
+ // Assert.IsFalse(soundClip.ClipData.IsEmpty);
+ //}
+
+ //[Test]
+ //public async Task Test_02_03_DownloadAllHistoryItems()
+ //{
+ // Assert.NotNull(ElevenLabsClient.SoundGenerationEndpoint);
+ // var historyInfo = await ElevenLabsClient.SoundGenerationEndpoint.GetHistoryAsync(pageSize: 20);
+ // Assert.NotNull(historyInfo);
+ // Assert.IsNotEmpty(historyInfo.HistoryItems);
+ // var singleItem = historyInfo.HistoryItems.FirstOrDefault();
+ // var singleItemResult = await ElevenLabsClient.SoundGenerationEndpoint.DownloadHistoryItemsAsync(new List { singleItem });
+ // Assert.NotNull(singleItemResult);
+ // Assert.IsNotEmpty(singleItemResult);
+ // var downloadItems = historyInfo.HistoryItems.Select(item => item.Id).ToList();
+ // var soundClips = await ElevenLabsClient.SoundGenerationEndpoint.DownloadHistoryItemsAsync(downloadItems);
+ // Assert.NotNull(soundClips);
+ // Assert.IsNotEmpty(soundClips);
+ //}
+
+ //[Test]
+ //public async Task Test_02_04_DeleteHistoryItem()
+ //{
+ // Assert.NotNull(ElevenLabsClient.SoundGenerationEndpoint);
+ // var historyInfo = await ElevenLabsClient.SoundGenerationEndpoint.GetHistoryAsync(pageSize: 20);
+ // Assert.NotNull(historyInfo);
+ // Assert.IsNotEmpty(historyInfo.HistoryItems);
+ // var itemsToDelete = historyInfo.HistoryItems.Where(item => item.Text.Contains("Star Wars Light Saber parry")).ToList();
+ // Assert.NotNull(itemsToDelete);
+ // Assert.IsNotEmpty(itemsToDelete);
+
+ // foreach (var historyItem in itemsToDelete)
+ // {
+ // Console.WriteLine($"Deleting {historyItem.Id}...");
+ // var result = await ElevenLabsClient.SoundGenerationEndpoint.DeleteHistoryItemAsync(historyItem.Id);
+ // Assert.IsTrue(result);
+ // }
+
+ // var updatedHistoryInfo = await ElevenLabsClient.SoundGenerationEndpoint.GetHistoryAsync();
+ // Assert.NotNull(updatedHistoryInfo);
+ // Assert.That(updatedHistoryInfo.HistoryItems, Has.None.EqualTo(itemsToDelete));
+
+ // foreach (var item in updatedHistoryInfo.HistoryItems.OrderBy(item => item.CreatedAt))
+ // {
+ // Console.WriteLine($"[{item.Id}] {item.CreatedAt} | {item.Text}");
+ // }
+ //}
+ }
+}
\ No newline at end of file
diff --git a/ElevenLabs-DotNet-Tests/Test_Fixture_04_HistoryEndpoint.cs b/ElevenLabs-DotNet-Tests/TestFixture_07_HistoryEndpoint.cs
similarity index 96%
rename from ElevenLabs-DotNet-Tests/Test_Fixture_04_HistoryEndpoint.cs
rename to ElevenLabs-DotNet-Tests/TestFixture_07_HistoryEndpoint.cs
index 8356833..49fe437 100644
--- a/ElevenLabs-DotNet-Tests/Test_Fixture_04_HistoryEndpoint.cs
+++ b/ElevenLabs-DotNet-Tests/TestFixture_07_HistoryEndpoint.cs
@@ -8,7 +8,7 @@
namespace ElevenLabs.Tests
{
- internal class Test_Fixture_04_HistoryEndpoint : AbstractTestFixture
+ internal class TestFixture_07_HistoryEndpoint : AbstractTestFixture
{
[Test]
public async Task Test_01_GetHistory()
@@ -36,6 +36,7 @@ public async Task Test_02_GetHistoryAudio()
Console.WriteLine($"Downloading {downloadItem!.Id}...");
var voiceClip = await ElevenLabsClient.HistoryEndpoint.DownloadHistoryAudioAsync(downloadItem);
Assert.NotNull(voiceClip);
+ Assert.IsFalse(voiceClip.ClipData.IsEmpty);
}
[Test]
@@ -64,7 +65,6 @@ public async Task Test_04_DeleteHistoryItem()
Assert.IsNotEmpty(historyInfo.HistoryItems);
var itemsToDelete = historyInfo.HistoryItems.Where(item => item.Text.Contains("The quick brown fox jumps over the lazy dog.")).ToList();
Assert.NotNull(itemsToDelete);
- Assert.IsNotEmpty(itemsToDelete);
foreach (var historyItem in itemsToDelete)
{
diff --git a/ElevenLabs-DotNet/AuthInfo.cs b/ElevenLabs-DotNet/Authentication/AuthInfo.cs
similarity index 100%
rename from ElevenLabs-DotNet/AuthInfo.cs
rename to ElevenLabs-DotNet/Authentication/AuthInfo.cs
diff --git a/ElevenLabs-DotNet/Authentication/ElevenLabsAuthentication.cs b/ElevenLabs-DotNet/Authentication/ElevenLabsAuthentication.cs
index 18ecbf1..687461e 100644
--- a/ElevenLabs-DotNet/Authentication/ElevenLabsAuthentication.cs
+++ b/ElevenLabs-DotNet/Authentication/ElevenLabsAuthentication.cs
@@ -12,6 +12,7 @@ namespace ElevenLabs
public sealed class ElevenLabsAuthentication
{
internal const string CONFIG_FILE = ".elevenlabs";
+ private const string ELEVENLABS_API_KEY = nameof(ELEVENLABS_API_KEY);
private const string ELEVEN_LABS_API_KEY = nameof(ELEVEN_LABS_API_KEY);
private readonly AuthInfo authInfo;
@@ -53,13 +54,16 @@ public static ElevenLabsAuthentication Default
var auth = LoadFromDirectory() ??
LoadFromDirectory(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)) ??
- LoadFromEnv();
+ LoadFromEnvironment();
cachedDefault = auth;
return auth;
}
internal set => cachedDefault = value;
}
+ [Obsolete("Use LoadFromEnvironment")]
+ public static ElevenLabsAuthentication LoadFromEnv() => LoadFromEnvironment();
+
///
/// Attempts to load api keys from environment variables, as "ELEVEN_LABS_API_KEY"
///
@@ -67,10 +71,15 @@ public static ElevenLabsAuthentication Default
/// Returns the loaded any api keys were found,
/// or if there were no matching environment vars.
///
- public static ElevenLabsAuthentication LoadFromEnv()
+ public static ElevenLabsAuthentication LoadFromEnvironment()
{
var apiKey = Environment.GetEnvironmentVariable(ELEVEN_LABS_API_KEY);
+ if (string.IsNullOrWhiteSpace(apiKey))
+ {
+ apiKey = Environment.GetEnvironmentVariable(ELEVENLABS_API_KEY);
+ }
+
return string.IsNullOrEmpty(apiKey) ? null : new ElevenLabsAuthentication(apiKey);
}
@@ -150,6 +159,7 @@ public static ElevenLabsAuthentication LoadFromDirectory(string directory = null
switch (part)
{
+ case ELEVENLABS_API_KEY:
case ELEVEN_LABS_API_KEY:
apiKey = nextPart.Trim();
break;
diff --git a/ElevenLabs-DotNet/Common/ElevenLabsBaseEndPoint.cs b/ElevenLabs-DotNet/Common/ElevenLabsBaseEndPoint.cs
index 1c950f1..94807c3 100644
--- a/ElevenLabs-DotNet/Common/ElevenLabsBaseEndPoint.cs
+++ b/ElevenLabs-DotNet/Common/ElevenLabsBaseEndPoint.cs
@@ -28,7 +28,7 @@ protected string GetUrl(string endpoint = "", Dictionary queryPa
if (queryParameters is { Count: not 0 })
{
- result += $"?{string.Join("&", queryParameters.Select(parameter => $"{parameter.Key}={parameter.Value}"))}";
+ result += $"?{string.Join('&', queryParameters.Select(parameter => $"{parameter.Key}={parameter.Value}"))}";
}
return result;
diff --git a/ElevenLabs-DotNet/Common/GeneratedClip.cs b/ElevenLabs-DotNet/Common/GeneratedClip.cs
new file mode 100644
index 0000000..d450ee9
--- /dev/null
+++ b/ElevenLabs-DotNet/Common/GeneratedClip.cs
@@ -0,0 +1,38 @@
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using ElevenLabs.Extensions;
+using System;
+
+namespace ElevenLabs
+{
+ public class GeneratedClip
+ {
+ internal GeneratedClip(string id, string text, ReadOnlyMemory clipData)
+ {
+ Id = id;
+ Text = text;
+ TextHash = $"{id}{text}".GenerateGuid().ToString();
+ ClipData = clipData;
+ }
+
+ ///
+ /// The unique id of this clip.
+ ///
+ public string Id { get; }
+
+ ///
+ /// The text input that generated this clip.
+ ///
+ public string Text { get; }
+
+ ///
+ /// Hash string of id and text.
+ ///
+ public string TextHash { get; }
+
+ ///
+ /// The ray clip data.
+ ///
+ public ReadOnlyMemory ClipData { get; }
+ }
+}
\ No newline at end of file
diff --git a/ElevenLabs-DotNet/Common/VoiceClip.cs b/ElevenLabs-DotNet/Common/VoiceClip.cs
index 3f76866..8f75dbd 100644
--- a/ElevenLabs-DotNet/Common/VoiceClip.cs
+++ b/ElevenLabs-DotNet/Common/VoiceClip.cs
@@ -1,30 +1,17 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.
-using ElevenLabs.Extensions;
using ElevenLabs.Voices;
using System;
namespace ElevenLabs
{
- public sealed class VoiceClip
+ public sealed class VoiceClip : GeneratedClip
{
- internal VoiceClip(string id, string text, Voice voice, ReadOnlyMemory clipData)
+ internal VoiceClip(string id, string text, Voice voice, ReadOnlyMemory clipData) : base(id, text, clipData)
{
- Id = id;
- Text = text;
Voice = voice;
- TextHash = $"{id}{text}".GenerateGuid().ToString();
- ClipData = clipData;
}
- public string Id { get; }
-
- public string Text { get; }
-
public Voice Voice { get; }
-
- public string TextHash { get; }
-
- public ReadOnlyMemory ClipData { get; }
}
}
diff --git a/ElevenLabs-DotNet/ElevenLabs-DotNet.csproj b/ElevenLabs-DotNet/ElevenLabs-DotNet.csproj
index 6c7771d..1168dc2 100644
--- a/ElevenLabs-DotNet/ElevenLabs-DotNet.csproj
+++ b/ElevenLabs-DotNet/ElevenLabs-DotNet.csproj
@@ -2,20 +2,37 @@
net6.0
latest
- true
- ElevenLabs
- disable
- disable
- https://github.com/RageAgainstThePixel/ElevenLabs-DotNet
- README.md
- https://github.com/RageAgainstThePixel/ElevenLabs-DotNet
- Stephen Hodgson
ElevenLabs-DotNet
+ ElevenLabs-DotNet
+ ElevenLabs-DotNet
+ ElevenLabs
RageAgainstThePixel
+ Stephen Hodgson
+ A non-official Eleven Labs voice synthesis RESTful client.
+I am not affiliated with Eleven Labs and an account with api access is required.
+All copyrights, trademarks, logos, and assets are the property of their respective owners.
+
2024
+ true
+ https://github.com/RageAgainstThePixel/ElevenLabs-DotNet
+ https://github.com/RageAgainstThePixel/ElevenLabs-DotNet
ElevenLabs, AI, ML, Voice, TTS
- 2.2.1
- Version 2.2.1
+ false
+ README.md
+ LICENSE
+ Assets\ElevenLabsIcon.png
+ true
+ false
+ true
+ true
+ 3.0.0
+
+Version 3.0.0
+- Added ability to specify fully customizable domain proxies
+- Added environment variable parsing for ELEVENLABS_API_KEY
+- Added SoundEffects API endpoints
+- Updated default models
+Version 2.2.1
- Misc formatting changes
Version 2.2.0
- Changed ElevenLabsClient to be IDisposable
@@ -110,24 +127,18 @@ Version 1.0.1
Version 1.0.0
- Initial Release!
- LICENSE
- A non-official Eleven Labs voice synthesis RESTful client.
-I am not affiliated with Eleven Labs and an account with api access is required.
-All copyrights, trademarks, logos, and assets are the property of their respective owners.
- ElevenLabsIcon.png
- True
-
- True
+
+ true
\
- True
+ true
\
- True
+ true
\
diff --git a/ElevenLabs-DotNet/ElevenLabsClient.cs b/ElevenLabs-DotNet/ElevenLabsClient.cs
index a064398..09113bc 100644
--- a/ElevenLabs-DotNet/ElevenLabsClient.cs
+++ b/ElevenLabs-DotNet/ElevenLabsClient.cs
@@ -2,6 +2,7 @@
using ElevenLabs.History;
using ElevenLabs.Models;
+using ElevenLabs.SoundGeneration;
using ElevenLabs.TextToSpeech;
using ElevenLabs.User;
using ElevenLabs.VoiceGeneration;
@@ -19,11 +20,11 @@ public sealed class ElevenLabsClient : IDisposable
///
/// Creates a new client for the Eleven Labs API, handling auth and allowing for access to various API endpoints.
///
- /// The API authentication information to use for API calls,
+ /// The API authentication information to use for API calls,
/// or to attempt to use the ,
/// potentially loading from environment vars or from a config file.
///
- ///
+ ///
/// Optional, for specifying a proxy domain.
///
/// Optional, .
@@ -34,10 +35,10 @@ public sealed class ElevenLabsClient : IDisposable
/// This internal HttpClient is disposed of when ElevenLabsClient is disposed of.
/// If you provide an external HttpClient instance to ElevenLabsClient, you are responsible for managing its disposal.
///
- public ElevenLabsClient(ElevenLabsAuthentication elevenLabsAuthentication = null, ElevenLabsClientSettings clientSettings = null, HttpClient httpClient = null)
+ public ElevenLabsClient(ElevenLabsAuthentication authentication = null, ElevenLabsClientSettings settings = null, HttpClient httpClient = null)
{
- ElevenLabsAuthentication = elevenLabsAuthentication ?? ElevenLabsAuthentication.Default;
- ElevenLabsClientSettings = clientSettings ?? ElevenLabsClientSettings.Default;
+ ElevenLabsAuthentication = authentication ?? ElevenLabsAuthentication.Default;
+ ElevenLabsClientSettings = settings ?? ElevenLabsClientSettings.Default;
if (string.IsNullOrWhiteSpace(ElevenLabsAuthentication?.ApiKey))
{
@@ -62,10 +63,12 @@ public ElevenLabsClient(ElevenLabsAuthentication elevenLabsAuthentication = null
UserEndpoint = new UserEndpoint(this);
VoicesEndpoint = new VoicesEndpoint(this);
+ SharedVoicesEndpoint = new SharedVoicesEndpoint(this);
ModelsEndpoint = new ModelsEndpoint(this);
HistoryEndpoint = new HistoryEndpoint(this);
TextToSpeechEndpoint = new TextToSpeechEndpoint(this);
VoiceGenerationEndpoint = new VoiceGenerationEndpoint(this);
+ SoundGenerationEndpoint = new SoundGenerationEndpoint(this);
}
~ElevenLabsClient()
@@ -129,6 +132,8 @@ private void Dispose(bool disposing)
public VoicesEndpoint VoicesEndpoint { get; }
+ public SharedVoicesEndpoint SharedVoicesEndpoint { get; }
+
public ModelsEndpoint ModelsEndpoint { get; }
public HistoryEndpoint HistoryEndpoint { get; }
@@ -136,5 +141,7 @@ private void Dispose(bool disposing)
public TextToSpeechEndpoint TextToSpeechEndpoint { get; }
public VoiceGenerationEndpoint VoiceGenerationEndpoint { get; }
+
+ public SoundGenerationEndpoint SoundGenerationEndpoint { get; }
}
}
diff --git a/ElevenLabs-DotNet/Extensions/HttpResponseMessageExtensions.cs b/ElevenLabs-DotNet/Extensions/HttpResponseMessageExtensions.cs
index 2b4e7fb..e66275f 100644
--- a/ElevenLabs-DotNet/Extensions/HttpResponseMessageExtensions.cs
+++ b/ElevenLabs-DotNet/Extensions/HttpResponseMessageExtensions.cs
@@ -1,8 +1,15 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.
using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
using System.Net.Http;
using System.Runtime.CompilerServices;
+using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
+using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;
@@ -10,30 +17,162 @@ namespace ElevenLabs.Extensions
{
internal static class HttpResponseMessageExtensions
{
- public static async Task ReadAsStringAsync(this HttpResponseMessage response, bool debugResponse = false, CancellationToken cancellationToken = default, [CallerMemberName] string methodName = null)
+ private static readonly JsonSerializerOptions debugJsonOptions = new()
{
- var responseAsString = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
+ WriteIndented = true,
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
+ };
- if (!response.IsSuccessStatusCode)
+ internal static async Task CheckResponseAsync(this HttpResponseMessage response, bool debug, CancellationToken cancellationToken, [CallerMemberName] string methodName = null)
+ {
+ if (!response.IsSuccessStatusCode || debug)
{
- throw new HttpRequestException($"{methodName} Failed!\n{response.RequestMessage}\n[{response.StatusCode}] {responseAsString}", null, response.StatusCode);
+ await response.ReadAsStringAsync(debug, null, null, cancellationToken, methodName).ConfigureAwait(false);
}
+ }
- if (debugResponse)
+ internal static async Task CheckResponseAsync(this HttpResponseMessage response, bool debug, HttpContent requestContent, CancellationToken cancellationToken, [CallerMemberName] string methodName = null)
+ {
+ if (!response.IsSuccessStatusCode || debug)
{
- Console.WriteLine($"{response.RequestMessage}\n[{response.StatusCode}] {responseAsString}");
+ await response.ReadAsStringAsync(debug, requestContent, null, cancellationToken, methodName).ConfigureAwait(false);
}
+ }
- return responseAsString;
+ internal static async Task CheckResponseAsync(this HttpResponseMessage response, bool debug, HttpContent requestContent, MemoryStream responseStream, CancellationToken cancellationToken, [CallerMemberName] string methodName = null)
+ {
+ if (!response.IsSuccessStatusCode || debug)
+ {
+ await response.ReadAsStringAsync(debug, requestContent, responseStream, cancellationToken, methodName).ConfigureAwait(false);
+ }
}
- internal static async Task CheckResponseAsync(this HttpResponseMessage response, CancellationToken cancellationToken = default, [CallerMemberName] string methodName = null)
+ internal static async Task ReadAsStringAsync(this HttpResponseMessage response, bool debugResponse, HttpContent requestContent, CancellationToken cancellationToken, [CallerMemberName] string methodName = null)
+ => await response.ReadAsStringAsync(debugResponse, requestContent, null, cancellationToken, methodName).ConfigureAwait(false);
+
+ internal static async Task ReadAsStringAsync(this HttpResponseMessage response, bool debugResponse, CancellationToken cancellationToken, [CallerMemberName] string methodName = null)
+ => await response.ReadAsStringAsync(debugResponse, null, null, cancellationToken, methodName).ConfigureAwait(false);
+
+ internal static async Task ReadAsStringAsync(this HttpResponseMessage response, bool debugResponse, HttpContent requestContent, MemoryStream responseStream, CancellationToken cancellationToken, [CallerMemberName] string methodName = null)
{
+ var responseAsString = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
+ var debugMessage = new StringBuilder();
+
+ if (!response.IsSuccessStatusCode || debugResponse)
+ {
+ if (!string.IsNullOrWhiteSpace(methodName))
+ {
+ debugMessage.Append($"{methodName} -> ");
+ }
+
+ var debugMessageObject = new Dictionary>();
+
+ if (response.RequestMessage != null)
+ {
+ debugMessage.Append($"[{response.RequestMessage.Method}:{(int)response.StatusCode}] {response.RequestMessage.RequestUri}\n");
+
+ debugMessageObject["Request"] = new Dictionary
+ {
+ ["Headers"] = response.RequestMessage.Headers.ToDictionary(pair => pair.Key, pair => pair.Value),
+ };
+ }
+
+ if (requestContent != null)
+ {
+ debugMessageObject["Request"]["Body-Headers"] = requestContent.Headers.ToDictionary(pair => pair.Key, pair => pair.Value);
+ string requestAsString;
+
+ if (requestContent is MultipartFormDataContent multipartFormData)
+ {
+ var stringContents = multipartFormData.Select(content =>
+ {
+ var headers = content.Headers.ToDictionary(pair => pair.Key, pair => pair.Value);
+ switch (content)
+ {
+ case StringContent stringContent:
+ var valueAsString = stringContent.ReadAsStringAsync(cancellationToken).Result;
+ object value;
+
+ try
+ {
+ value = JsonNode.Parse(valueAsString);
+ }
+ catch
+ {
+ value = valueAsString;
+ }
+
+ return new { headers, value };
+ default:
+ return new { headers };
+ }
+ });
+ requestAsString = JsonSerializer.Serialize(stringContents);
+ }
+ else
+ {
+ requestAsString = await requestContent.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
+ }
+
+ if (!string.IsNullOrWhiteSpace(requestAsString))
+ {
+ try
+ {
+ debugMessageObject["Request"]["Body"] = JsonNode.Parse(requestAsString);
+ }
+ catch
+ {
+ debugMessageObject["Request"]["Body"] = requestAsString;
+ }
+ }
+ }
+
+ debugMessageObject["Response"] = new()
+ {
+ ["Headers"] = response.Headers.ToDictionary(pair => pair.Key, pair => pair.Value),
+ };
+
+ if (responseStream != null || !string.IsNullOrWhiteSpace(responseAsString))
+ {
+ debugMessageObject["Response"]["Body"] = new Dictionary();
+ }
+
+ if (responseStream != null)
+ {
+ var body = Encoding.UTF8.GetString(responseStream.ToArray());
+
+ try
+ {
+ ((Dictionary)debugMessageObject["Response"]["Body"])["Events"] = JsonNode.Parse(body);
+ }
+ catch
+ {
+ ((Dictionary)debugMessageObject["Response"]["Body"])["Events"] = body;
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(responseAsString))
+ {
+ try
+ {
+ ((Dictionary)debugMessageObject["Response"]["Body"])["Content"] = JsonNode.Parse(responseAsString);
+ }
+ catch
+ {
+ ((Dictionary)debugMessageObject["Response"]["Body"])["Content"] = responseAsString;
+ }
+ }
+
+ debugMessage.Append(JsonSerializer.Serialize(debugMessageObject, debugJsonOptions));
+ Console.WriteLine(debugMessage.ToString());
+ }
+
if (!response.IsSuccessStatusCode)
{
- var responseAsString = await response.Content.ReadAsStringAsync(cancellationToken);
- throw new HttpRequestException($"{methodName} Failed! HTTP status code: {response.StatusCode} | Response body: {responseAsString}", null, response.StatusCode);
+ throw new HttpRequestException(message: $"{methodName} Failed! HTTP status code: {response.StatusCode} | Response body: {responseAsString}", null, statusCode: response.StatusCode);
}
+
+ return responseAsString;
}
}
}
diff --git a/ElevenLabs-DotNet/History/HistoryEndpoint.cs b/ElevenLabs-DotNet/History/HistoryEndpoint.cs
index a97b0a6..b4f8106 100644
--- a/ElevenLabs-DotNet/History/HistoryEndpoint.cs
+++ b/ElevenLabs-DotNet/History/HistoryEndpoint.cs
@@ -30,8 +30,8 @@ public HistoryEndpoint(ElevenLabsClient client) : base(client) { }
///
/// Optional, the id of the item to start after.
/// Optional, .
- /// .
- public async Task GetHistoryAsync(int? pageSize = null, string startAfterId = null, CancellationToken cancellationToken = default)
+ /// .
+ public async Task> GetHistoryAsync(int? pageSize = null, string startAfterId = null, CancellationToken cancellationToken = default)
{
var parameters = new Dictionary();
@@ -45,9 +45,9 @@ public async Task GetHistoryAsync(int? pageSize = null, string star
parameters.Add("start_after_history_item_id", startAfterId);
}
- var response = await client.Client.GetAsync(GetUrl(queryParameters: parameters), cancellationToken);
+ using var response = await client.Client.GetAsync(GetUrl(queryParameters: parameters), cancellationToken);
var responseAsString = await response.ReadAsStringAsync(EnableDebug, cancellationToken: cancellationToken);
- return JsonSerializer.Deserialize(responseAsString, ElevenLabsClient.JsonSerializationOptions);
+ return JsonSerializer.Deserialize>(responseAsString, ElevenLabsClient.JsonSerializationOptions);
}
///
@@ -58,11 +58,23 @@ public async Task GetHistoryAsync(int? pageSize = null, string star
///
public async Task GetHistoryItemAsync(string id, CancellationToken cancellationToken = default)
{
- var response = await client.Client.GetAsync(GetUrl($"/{id}"), cancellationToken);
+ using var response = await client.Client.GetAsync(GetUrl($"/{id}"), cancellationToken);
var responseAsString = await response.ReadAsStringAsync(EnableDebug, cancellationToken: cancellationToken);
return JsonSerializer.Deserialize(responseAsString, ElevenLabsClient.JsonSerializationOptions);
}
+ ///
+ /// Download audio of a history item.
+ ///
+ /// or .
+ /// Optional, .
+ /// .
+ public async Task DownloadHistoryAudioAsync(string id, CancellationToken cancellationToken = default)
+ {
+ var historyItem = await GetHistoryItemAsync(id, cancellationToken).ConfigureAwait(false);
+ return await DownloadHistoryAudioAsync(historyItem, cancellationToken).ConfigureAwait(false);
+ }
+
///
/// Download audio of a history item.
///
@@ -72,8 +84,8 @@ public async Task GetHistoryItemAsync(string id, CancellationToken
public async Task DownloadHistoryAudioAsync(HistoryItem historyItem, CancellationToken cancellationToken = default)
{
var voice = await client.VoicesEndpoint.GetVoiceAsync(historyItem.VoiceId, cancellationToken: cancellationToken).ConfigureAwait(false);
- var response = await client.Client.GetAsync(GetUrl($"/{historyItem.Id}/audio"), cancellationToken).ConfigureAwait(false);
- await response.CheckResponseAsync(cancellationToken);
+ using var response = await client.Client.GetAsync(GetUrl($"/{historyItem.Id}/audio"), cancellationToken).ConfigureAwait(false);
+ await response.CheckResponseAsync(EnableDebug, cancellationToken);
var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
var memoryStream = new MemoryStream();
byte[] clipData;
@@ -100,7 +112,7 @@ public async Task DownloadHistoryAudioAsync(HistoryItem historyItem,
/// True, if history item was successfully deleted.
public async Task DeleteHistoryItemAsync(string id, CancellationToken cancellationToken = default)
{
- var response = await client.Client.DeleteAsync(GetUrl($"/{id}"), cancellationToken);
+ using var response = await client.Client.DeleteAsync(GetUrl($"/{id}"), cancellationToken);
await response.ReadAsStringAsync(EnableDebug, cancellationToken: cancellationToken);
return response.IsSuccessStatusCode;
}
@@ -117,15 +129,15 @@ public async Task DeleteHistoryItemAsync(string id, CancellationToken canc
public async Task> DownloadHistoryItemsAsync(List historyItemIds = null, CancellationToken cancellationToken = default)
{
historyItemIds ??= (await GetHistoryAsync(cancellationToken: cancellationToken)).HistoryItems.Select(item => item.Id).ToList();
- var voiceClips = new ConcurrentBag();
+ var clips = new ConcurrentBag();
- async Task DownloadItem(string historyItemId)
+ async Task DownloadItem(string id)
{
try
{
- var historyItem = await GetHistoryItemAsync(historyItemId, cancellationToken).ConfigureAwait(false);
- var voiceClip = await DownloadHistoryAudioAsync(historyItem, cancellationToken).ConfigureAwait(false);
- voiceClips.Add(voiceClip);
+ var historyItem = await GetHistoryItemAsync(id, cancellationToken).ConfigureAwait(false);
+ var clip = await DownloadHistoryAudioAsync(historyItem, cancellationToken).ConfigureAwait(false);
+ clips.Add(clip);
}
catch (Exception e)
{
@@ -134,7 +146,7 @@ async Task DownloadItem(string historyItemId)
}
await Task.WhenAll(historyItemIds.Select(DownloadItem)).ConfigureAwait(false);
- return voiceClips.ToList();
+ return clips.ToList();
}
}
}
diff --git a/ElevenLabs-DotNet/History/HistoryInfo.cs b/ElevenLabs-DotNet/History/HistoryInfo.cs
index 3657c2d..af17474 100644
--- a/ElevenLabs-DotNet/History/HistoryInfo.cs
+++ b/ElevenLabs-DotNet/History/HistoryInfo.cs
@@ -5,11 +5,11 @@
namespace ElevenLabs.History
{
- public sealed class HistoryInfo
+ public sealed class HistoryInfo
{
[JsonInclude]
[JsonPropertyName("history")]
- public IReadOnlyList HistoryItems { get; private set; }
+ public IReadOnlyList HistoryItems { get; private set; }
[JsonInclude]
[JsonPropertyName("last_history_item_id")]
diff --git a/ElevenLabs-DotNet/History/HistoryItem.cs b/ElevenLabs-DotNet/History/HistoryItem.cs
index 6aa81f1..5db2e8f 100644
--- a/ElevenLabs-DotNet/History/HistoryItem.cs
+++ b/ElevenLabs-DotNet/History/HistoryItem.cs
@@ -7,8 +7,6 @@ namespace ElevenLabs.History
{
public sealed class HistoryItem
{
- public static implicit operator string(HistoryItem historyItem) => historyItem.Id;
-
[JsonInclude]
[JsonPropertyName("history_item_id")]
public string Id { get; private set; }
@@ -47,5 +45,7 @@ public sealed class HistoryItem
[JsonInclude]
[JsonPropertyName("state")]
public string State { get; private set; }
+
+ public static implicit operator string(HistoryItem historyItem) => historyItem?.Id;
}
}
diff --git a/ElevenLabs-DotNet/Models/Model.cs b/ElevenLabs-DotNet/Models/Model.cs
index 06b965c..41a3d1f 100644
--- a/ElevenLabs-DotNet/Models/Model.cs
+++ b/ElevenLabs-DotNet/Models/Model.cs
@@ -51,13 +51,22 @@ public Model(string id)
#region Predefined Models
[JsonIgnore]
- public static Model MonoLingualV1 { get; } = new Model("eleven_monolingual_v1");
+ public static Model MonoLingualV1 { get; } = new("eleven_monolingual_v1");
[JsonIgnore]
- public static Model MultiLingualV1 { get; } = new Model("eleven_multilingual_v1");
+ public static Model MultiLingualV1 { get; } = new("eleven_multilingual_v1");
[JsonIgnore]
- public static Model MultiLingualV2 { get; } = new Model("eleven_multilingual_v2");
+ public static Model MultiLingualV2 { get; } = new("eleven_multilingual_v2");
+
+ [JsonIgnore]
+ public static Model TurboV2 { get; } = new("eleven_turbo_v2");
+
+ [JsonIgnore]
+ public static Model EnglishSpeechToSpeechV2 { get; } = new("eleven_english_sts_v2");
+
+ [JsonIgnore]
+ public static Model MultilingualSpeechToSpeechV2 { get; } = new("eleven_multilingual_sts_v2");
#endregion Predefined Models
}
diff --git a/ElevenLabs-DotNet/Models/ModelsEndpoint.cs b/ElevenLabs-DotNet/Models/ModelsEndpoint.cs
index de49022..0e91727 100644
--- a/ElevenLabs-DotNet/Models/ModelsEndpoint.cs
+++ b/ElevenLabs-DotNet/Models/ModelsEndpoint.cs
@@ -3,6 +3,7 @@
using ElevenLabs.Extensions;
using System.Collections.Generic;
using System.Text.Json;
+using System.Threading;
using System.Threading.Tasks;
namespace ElevenLabs.Models
@@ -17,10 +18,10 @@ public ModelsEndpoint(ElevenLabsClient client) : base(client) { }
/// Access the different models available to the platform.
///
/// A list of s you can use.
- public async Task> GetModelsAsync()
+ public async Task> GetModelsAsync(CancellationToken cancellationToken = default)
{
- var response = await client.Client.GetAsync(GetUrl());
- var responseAsString = await response.ReadAsStringAsync(EnableDebug);
+ var response = await client.Client.GetAsync(GetUrl(), cancellationToken).ConfigureAwait(false);
+ var responseAsString = await response.ReadAsStringAsync(EnableDebug, cancellationToken).ConfigureAwait(false);
return JsonSerializer.Deserialize>(responseAsString, ElevenLabsClient.JsonSerializationOptions);
}
}
diff --git a/ElevenLabs-DotNet/SoundGeneration/GenerationSettings.cs b/ElevenLabs-DotNet/SoundGeneration/GenerationSettings.cs
new file mode 100644
index 0000000..c6b22b3
--- /dev/null
+++ b/ElevenLabs-DotNet/SoundGeneration/GenerationSettings.cs
@@ -0,0 +1,17 @@
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using System.Text.Json.Serialization;
+
+namespace ElevenLabs.SoundGeneration
+{
+ public sealed class GenerationSettings
+ {
+ [JsonInclude]
+ [JsonPropertyName("duration_seconds")]
+ public float? Duration { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("prompt_influence")]
+ public float PromptInfluence { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/ElevenLabs-DotNet/SoundGeneration/SoundGenerationConfig.cs b/ElevenLabs-DotNet/SoundGeneration/SoundGenerationConfig.cs
new file mode 100644
index 0000000..4a5a74d
--- /dev/null
+++ b/ElevenLabs-DotNet/SoundGeneration/SoundGenerationConfig.cs
@@ -0,0 +1,21 @@
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using System.Text.Json.Serialization;
+
+namespace ElevenLabs.SoundGeneration
+{
+ public sealed class SoundGenerationConfig
+ {
+ [JsonInclude]
+ [JsonPropertyName("number_of_generations")]
+ public int NumberOfGenerations { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("generation_settings")]
+ public GenerationSettings GenerationSettings { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("text")]
+ public string Text { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/ElevenLabs-DotNet/SoundGeneration/SoundGenerationEndpoint.cs b/ElevenLabs-DotNet/SoundGeneration/SoundGenerationEndpoint.cs
new file mode 100644
index 0000000..5b5f408
--- /dev/null
+++ b/ElevenLabs-DotNet/SoundGeneration/SoundGenerationEndpoint.cs
@@ -0,0 +1,163 @@
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using ElevenLabs.Extensions;
+using System;
+using System.IO;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace ElevenLabs.SoundGeneration
+{
+ public sealed class SoundGenerationEndpoint : ElevenLabsBaseEndPoint
+ {
+ public SoundGenerationEndpoint(ElevenLabsClient client) : base(client) { }
+
+ protected override string Root => "sound-generation";
+
+ ///
+ /// converts text into sounds & uses the most advanced AI audio model ever.
+ /// Create sound effects for your videos, voice-overs or video games.
+ ///
+ /// .
+ /// Optional, .
+ /// .
+ public async Task GenerateSoundAsync(SoundGenerationRequest request, CancellationToken cancellationToken = default)
+ {
+ using var payload = JsonSerializer.Serialize(request, ElevenLabsClient.JsonSerializationOptions).ToJsonStringContent();
+ using var response = await client.Client.PostAsync(GetUrl(), payload, cancellationToken).ConfigureAwait(false);
+ await response.CheckResponseAsync(EnableDebug, payload, cancellationToken).ConfigureAwait(false);
+ await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
+ await using var memoryStream = new MemoryStream();
+ await responseStream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false);
+ return new GeneratedClip(Guid.NewGuid().ToString(), request.Text, memoryStream.ToArray());
+ }
+
+ /////
+ ///// Get metadata about all your generated sounds.
+ /////
+ /////
+ ///// Optional, number of items to return. Cannot exceed 1000.
+ ///// Default: 100
+ /////
+ ///// Optional, the id of the item to start after.
+ ///// Optional, .
+ ///// .
+ //public async Task> GetHistoryAsync(int? pageSize = null, string startAfterId = null, CancellationToken cancellationToken = default)
+ //{
+ // var parameters = new Dictionary();
+
+ // if (pageSize.HasValue)
+ // {
+ // parameters.Add("page_size", pageSize.ToString());
+ // }
+
+ // if (!string.IsNullOrWhiteSpace(startAfterId))
+ // {
+ // parameters.Add("start_after_history_item_id", startAfterId);
+ // }
+
+ // var response = await client.Client.GetAsync(GetUrl("/history", queryParameters: parameters), cancellationToken).ConfigureAwait(false);
+ // var responseAsString = await response.ReadAsStringAsync(EnableDebug, cancellationToken: cancellationToken).ConfigureAwait(false);
+ // return JsonSerializer.Deserialize>(responseAsString, ElevenLabsClient.JsonSerializationOptions);
+ //}
+
+ /////
+ ///// Get a sound by history item id.
+ /////
+ ///// or .
+ ///// Optional, .
+ ///// .
+ //public async Task GetSoundAsync(string id, CancellationToken cancellationToken = default)
+ //{
+ // var response = await client.Client.GetAsync(GetUrl($"/history/{id}"), cancellationToken).ConfigureAwait(false);
+ // var responseAsString = await response.ReadAsStringAsync(EnableDebug, cancellationToken: cancellationToken).ConfigureAwait(false);
+ // return JsonSerializer.Deserialize(responseAsString, ElevenLabsClient.JsonSerializationOptions);
+ //}
+
+ /////
+ ///// Download audio of a sound history item.
+ /////
+ ///// or .
+ ///// Optional, .
+ ///// .
+ //public async Task DownloadSoundAudioAsync(string id, CancellationToken cancellationToken = default)
+ //{
+ // var historyItem = await GetSoundAsync(id, cancellationToken).ConfigureAwait(false);
+ // return await DownloadSoundAudioAsync(historyItem, cancellationToken).ConfigureAwait(false);
+ //}
+
+ /////
+ ///// Download audio of a sound history item.
+ /////
+ ///// .
+ ///// Optional, .
+ ///// .
+ //public async Task DownloadSoundAudioAsync(SoundHistoryItem historyItem, CancellationToken cancellationToken = default)
+ //{
+ // using var response = await client.Client.GetAsync(GetUrl($"/history/{historyItem.Id}/audio"), cancellationToken).ConfigureAwait(false);
+ // await response.CheckResponseAsync(EnableDebug, cancellationToken).ConfigureAwait(false);
+ // var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
+ // var memoryStream = new MemoryStream();
+ // byte[] clipData;
+
+ // try
+ // {
+ // await responseStream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false);
+ // clipData = memoryStream.ToArray();
+ // }
+ // finally
+ // {
+ // await responseStream.DisposeAsync().ConfigureAwait(false);
+ // await memoryStream.DisposeAsync().ConfigureAwait(false);
+ // }
+
+ // return new GeneratedClip(historyItem.Id, historyItem.Text, clipData);
+ //}
+
+ /////
+ ///// Delete a history item by its id.
+ /////
+ ///// or .
+ ///// Optional, .
+ ///// True, if history item was successfully deleted.
+ //public async Task DeleteHistoryItemAsync(string id, CancellationToken cancellationToken = default)
+ //{
+ // using var response = await client.Client.DeleteAsync(GetUrl($"/history/{id}"), cancellationToken).ConfigureAwait(false);
+ // await response.CheckResponseAsync(EnableDebug, cancellationToken).ConfigureAwait(false);
+ // return response.IsSuccessStatusCode;
+ //}
+
+ /////
+ ///// Download one or more history items.
+ ///// If no ids are specified, then the last 100 history items are downloaded.
+ ///// If one history item id is provided, we will return a single audio file.
+ ///// If more than one history item ids are provided multiple audio files will be downloaded.
+ /////
+ ///// Optional, One or more history item ids queued for download.
+ ///// Optional, .
+ ///// A list of voice clips downloaded by the request.
+ //public async Task> DownloadHistoryItemsAsync(List historyItemIds = null, CancellationToken cancellationToken = default)
+ //{
+ // historyItemIds ??= (await GetHistoryAsync(cancellationToken: cancellationToken)).HistoryItems.Select(item => item.Id).ToList();
+ //var clips = new ConcurrentBag();
+
+ //async Task DownloadItem(string id)
+ //{
+ // try
+ // {
+ // var historyItem = await GetSoundAsync(id, cancellationToken).ConfigureAwait(false);
+ // var clip = await DownloadSoundAudioAsync(historyItem, cancellationToken).ConfigureAwait(false);
+ // clips.Add(clip);
+ // }
+ // catch (Exception e)
+ // {
+ // Console.WriteLine(e);
+ // }
+ //}
+
+ //await Task.WhenAll(historyItemIds.Select(DownloadItem)).ConfigureAwait(false);
+ // return clips.ToList();
+ //}
+ }
+}
\ No newline at end of file
diff --git a/ElevenLabs-DotNet/SoundGeneration/SoundGenerationRequest.cs b/ElevenLabs-DotNet/SoundGeneration/SoundGenerationRequest.cs
new file mode 100644
index 0000000..9bf41ec
--- /dev/null
+++ b/ElevenLabs-DotNet/SoundGeneration/SoundGenerationRequest.cs
@@ -0,0 +1,69 @@
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using System;
+using System.Text.Json.Serialization;
+
+namespace ElevenLabs.SoundGeneration
+{
+ public sealed class SoundGenerationRequest
+ {
+ ///
+ ///
+ ///
+ ///
+ /// The text that will get converted into a sound effect.
+ ///
+ ///
+ /// The duration of the sound which will be generated in seconds.
+ /// Must be at least 0.5 and at most 22.
+ /// If set to None we will guess the optimal duration using the prompt.
+ /// Defaults to None.
+ ///
+ ///
+ /// A higher prompt influence makes your generation follow the prompt more closely while also making generations less variable.
+ /// Must be a value between 0 and 1.
+ /// Defaults to 0.3.
+ ///
+ public SoundGenerationRequest(string text, float? duration = null, float? promptInfluence = null)
+ {
+ Text = text;
+
+ if (duration is > 22f or < 0.5f)
+ {
+ throw new ArgumentOutOfRangeException(nameof(duration), "Duration must be a value between 0.5 and 22.");
+ }
+
+ Duration = duration;
+
+ if (promptInfluence is > 1f or < 0f)
+ {
+ throw new ArgumentOutOfRangeException(nameof(promptInfluence), "Prompt influence must be a value between 0 and 1.");
+ }
+
+ PromptInfluence = promptInfluence;
+ }
+
+ ///
+ /// The text that will get converted into a sound effect.
+ ///
+ [JsonPropertyName("text")]
+ public string Text { get; }
+
+ ///
+ /// The duration of the sound which will be generated in seconds.
+ /// Must be at least 0.5 and at most 22.
+ /// If set to None we will guess the optimal duration using the prompt.
+ /// Defaults to None.
+ ///
+ [JsonPropertyName("duration_seconds")]
+ public float? Duration { get; }
+
+ ///
+ /// A higher prompt influence makes your generation follow the prompt more closely while also making generations less variable.
+ /// Must be a value between 0 and 1.
+ /// Defaults to 0.3.
+ ///
+ [JsonPropertyName("prompt_influence")]
+ public float? PromptInfluence { get; }
+ }
+}
\ No newline at end of file
diff --git a/ElevenLabs-DotNet/SoundGeneration/SoundHistoryItem.cs b/ElevenLabs-DotNet/SoundGeneration/SoundHistoryItem.cs
new file mode 100644
index 0000000..8a2d73c
--- /dev/null
+++ b/ElevenLabs-DotNet/SoundGeneration/SoundHistoryItem.cs
@@ -0,0 +1,35 @@
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using System;
+using System.Text.Json.Serialization;
+
+namespace ElevenLabs.SoundGeneration
+{
+ public sealed class SoundHistoryItem
+ {
+ [JsonInclude]
+ [JsonPropertyName("sound_generation_history_item_id")]
+ public string Id { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("text")]
+ public string Text { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("created_at_unix")]
+ public int CreatedAtUnixTimeSeconds { get; private set; }
+
+ [JsonIgnore]
+ public DateTime CreatedAt => DateTimeOffset.FromUnixTimeSeconds(CreatedAtUnixTimeSeconds).DateTime;
+
+ [JsonInclude]
+ [JsonPropertyName("content_type")]
+ public string ContentType { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("generation_config")]
+ public SoundGenerationConfig SoundGenerationConfig { get; private set; }
+
+ public static implicit operator string(SoundHistoryItem item) => item?.Id;
+ }
+}
\ No newline at end of file
diff --git a/ElevenLabs-DotNet/TextToSpeech/TextToSpeechEndpoint.cs b/ElevenLabs-DotNet/TextToSpeech/TextToSpeechEndpoint.cs
index c32b5fc..ad2a9af 100644
--- a/ElevenLabs-DotNet/TextToSpeech/TextToSpeechEndpoint.cs
+++ b/ElevenLabs-DotNet/TextToSpeech/TextToSpeechEndpoint.cs
@@ -94,7 +94,7 @@ public async Task TextToSpeechAsync(string text, Voice voice, VoiceSe
? HttpCompletionOption.ResponseContentRead
: HttpCompletionOption.ResponseHeadersRead;
var response = await client.Client.SendAsync(postRequest, requestOption, cancellationToken);
- await response.CheckResponseAsync(cancellationToken).ConfigureAwait(false);
+ await response.CheckResponseAsync(EnableDebug, payload, cancellationToken).ConfigureAwait(false);
var clipId = response.Headers.GetValues(HistoryItemId).FirstOrDefault();
if (string.IsNullOrWhiteSpace(clipId))
diff --git a/ElevenLabs-DotNet/Users/UserEndpoint.cs b/ElevenLabs-DotNet/Users/UserEndpoint.cs
index bb0451f..59dc0b5 100644
--- a/ElevenLabs-DotNet/Users/UserEndpoint.cs
+++ b/ElevenLabs-DotNet/Users/UserEndpoint.cs
@@ -2,6 +2,7 @@
using ElevenLabs.Extensions;
using System.Text.Json;
+using System.Threading;
using System.Threading.Tasks;
namespace ElevenLabs.User
@@ -18,20 +19,20 @@ public UserEndpoint(ElevenLabsClient client) : base(client) { }
///
/// Gets information about your user account.
///
- public async Task GetUserInfoAsync()
+ public async Task GetUserInfoAsync(CancellationToken cancellationToken = default)
{
- var response = await client.Client.GetAsync(GetUrl());
- var responseAsString = await response.ReadAsStringAsync(EnableDebug);
+ var response = await client.Client.GetAsync(GetUrl(), cancellationToken).ConfigureAwait(false);
+ var responseAsString = await response.ReadAsStringAsync(EnableDebug, cancellationToken).ConfigureAwait(false);
return JsonSerializer.Deserialize(responseAsString, ElevenLabsClient.JsonSerializationOptions);
}
///
/// Gets your subscription info.
///
- public async Task GetSubscriptionInfoAsync()
+ public async Task GetSubscriptionInfoAsync(CancellationToken cancellationToken = default)
{
- var response = await client.Client.GetAsync(GetUrl("/subscription"));
- var responseAsString = await response.ReadAsStringAsync(EnableDebug);
+ var response = await client.Client.GetAsync(GetUrl("/subscription"), cancellationToken).ConfigureAwait(false);
+ var responseAsString = await response.ReadAsStringAsync(EnableDebug, cancellationToken).ConfigureAwait(false);
return JsonSerializer.Deserialize(responseAsString, ElevenLabsClient.JsonSerializationOptions);
}
}
diff --git a/ElevenLabs-DotNet/VoiceGeneration/VoiceGenerationEndpoint.cs b/ElevenLabs-DotNet/VoiceGeneration/VoiceGenerationEndpoint.cs
index e4ec9bb..cb191df 100644
--- a/ElevenLabs-DotNet/VoiceGeneration/VoiceGenerationEndpoint.cs
+++ b/ElevenLabs-DotNet/VoiceGeneration/VoiceGenerationEndpoint.cs
@@ -24,8 +24,8 @@ public VoiceGenerationEndpoint(ElevenLabsClient client) : base(client) { }
/// .
public async Task GetVoiceGenerationOptionsAsync(CancellationToken cancellationToken = default)
{
- var response = await client.Client.GetAsync(GetUrl("/generate-voice/parameters"), cancellationToken);
- var responseAsString = await response.ReadAsStringAsync(EnableDebug, cancellationToken: cancellationToken);
+ var response = await client.Client.GetAsync(GetUrl("/generate-voice/parameters"), cancellationToken).ConfigureAwait(false);
+ var responseAsString = await response.ReadAsStringAsync(EnableDebug, cancellationToken: cancellationToken).ConfigureAwait(false);
return JsonSerializer.Deserialize(responseAsString, ElevenLabsClient.JsonSerializationOptions);
}
@@ -38,8 +38,8 @@ public async Task GetVoiceGenerationOptionsAsync(Cancella
public async Task>> GenerateVoicePreviewAsync(GeneratedVoicePreviewRequest generatedVoicePreviewRequest, CancellationToken cancellationToken = default)
{
var payload = JsonSerializer.Serialize(generatedVoicePreviewRequest, ElevenLabsClient.JsonSerializationOptions).ToJsonStringContent();
- var response = await client.Client.PostAsync(GetUrl("/generate-voice"), payload, cancellationToken);
- await response.CheckResponseAsync(cancellationToken);
+ var response = await client.Client.PostAsync(GetUrl("/generate-voice"), payload, cancellationToken).ConfigureAwait(false);
+ await response.CheckResponseAsync(EnableDebug, payload, cancellationToken).ConfigureAwait(false);
var generatedVoiceId = response.Headers.FirstOrDefault(pair => pair.Key == "generated_voice_id").Value.FirstOrDefault();
await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
await using var memoryStream = new MemoryStream();
@@ -65,8 +65,8 @@ public async Task>> GenerateVoicePreviewAsync
public async Task CreateVoiceAsync(CreateVoiceRequest createVoiceRequest, CancellationToken cancellationToken = default)
{
var payload = JsonSerializer.Serialize(createVoiceRequest).ToJsonStringContent();
- var response = await client.Client.PostAsync(GetUrl("/create-voice"), payload, cancellationToken);
- var responseAsString = await response.ReadAsStringAsync(EnableDebug, cancellationToken: cancellationToken);
+ var response = await client.Client.PostAsync(GetUrl("/create-voice"), payload, cancellationToken).ConfigureAwait(false);
+ var responseAsString = await response.ReadAsStringAsync(EnableDebug, cancellationToken: cancellationToken).ConfigureAwait(false);
return JsonSerializer.Deserialize(responseAsString, ElevenLabsClient.JsonSerializationOptions);
}
}
diff --git a/ElevenLabs-DotNet/Voices/SharedVoiceInfo.cs b/ElevenLabs-DotNet/Voices/SharedVoiceInfo.cs
new file mode 100644
index 0000000..fd4098e
--- /dev/null
+++ b/ElevenLabs-DotNet/Voices/SharedVoiceInfo.cs
@@ -0,0 +1,121 @@
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using System;
+using System.Text.Json.Serialization;
+
+namespace ElevenLabs.Voices
+{
+ public sealed class SharedVoiceInfo
+ {
+ [JsonInclude]
+ [JsonPropertyName("public_owner_id")]
+ public string OwnerId { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("voice_id")]
+ public string VoiceId { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("date_unix")]
+ public int DateUnix { get; private set; }
+
+ [JsonIgnore]
+ public DateTime Date => DateTimeOffset.FromUnixTimeSeconds(DateUnix).DateTime;
+
+ [JsonInclude]
+ [JsonPropertyName("name")]
+ public string Name { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("accent")]
+ public string Accent { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("gender")]
+ public string Gender { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("age")]
+ public string Age { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("descriptive")]
+ public string Descriptive { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("use_case")]
+ public string UseCase { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("category")]
+ public string Category { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("language")]
+ public string Language { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("description")]
+ public string Description { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("preview_url")]
+ public string PreviewUrl { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("usage_character_count_1y")]
+ public int UsageCharacterCount1Y { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("usage_character_count_7d")]
+ public int UsageCharacterCount7D { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("play_api_usage_character_count_1y")]
+ public int PlayApiUsageCharacterCount1Y { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("cloned_by_count")]
+ public int ClonedByCount { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("rate")]
+ public float Rate { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("free_users_allowed")]
+ public bool FreeUsersAllowed { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("live_moderation_enabled")]
+ public bool LiveModerationEnabled { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("featured")]
+ public bool Featured { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("notice_period")]
+ public int? NoticePeriod { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("instagram_username")]
+ public string InstagramUsername { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("twitter_username")]
+ public string TwitterUsername { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("youtube_username")]
+ public string YoutubeUsername { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("tiktok_username")]
+ public string TikTokUsername { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("image_url")]
+ public string ImageUrl { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/ElevenLabs-DotNet/Voices/SharedVoiceList.cs b/ElevenLabs-DotNet/Voices/SharedVoiceList.cs
new file mode 100644
index 0000000..fa3c34c
--- /dev/null
+++ b/ElevenLabs-DotNet/Voices/SharedVoiceList.cs
@@ -0,0 +1,22 @@
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace ElevenLabs.Voices
+{
+ public sealed class SharedVoiceList
+ {
+ [JsonInclude]
+ [JsonPropertyName("voices")]
+ public IReadOnlyList Voices { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("has_more")]
+ public bool HasMore { get; private set; }
+
+ [JsonInclude]
+ [JsonPropertyName("last_sort_id")]
+ public string LastId { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/ElevenLabs-DotNet/Voices/SharedVoiceQuery.cs b/ElevenLabs-DotNet/Voices/SharedVoiceQuery.cs
new file mode 100644
index 0000000..bf27a15
--- /dev/null
+++ b/ElevenLabs-DotNet/Voices/SharedVoiceQuery.cs
@@ -0,0 +1,114 @@
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using System.Collections.Generic;
+
+namespace ElevenLabs.Voices
+{
+ public sealed class SharedVoiceQuery
+ {
+ public int? PageSize { get; set; } = null;
+
+ public string Category { get; set; } = null;
+
+ public string Gender { get; set; } = null;
+
+ public string Age { get; set; } = null;
+
+ public string Accent { get; set; } = null;
+
+ public string Language { get; set; } = null;
+
+ public string SearchTerms { get; set; } = null;
+
+ public List UseCases { get; set; } = null;
+
+ public List Descriptives { get; set; } = null;
+
+ public bool? Featured { get; set; } = null;
+
+ public bool? ReaderAppEnabled { get; set; } = null;
+
+ public string OwnerId { get; set; } = null;
+
+ public string Sort { get; set; } = null;
+
+ public int? Page { get; set; } = null;
+
+ public Dictionary ToQueryParams()
+ {
+ var parameters = new Dictionary();
+
+ if (PageSize.HasValue)
+ {
+ parameters.Add("page_size", PageSize.Value.ToString());
+ }
+
+ if (!string.IsNullOrWhiteSpace(Category))
+ {
+ parameters.Add("category", Category);
+ }
+
+ if (!string.IsNullOrWhiteSpace(Gender))
+ {
+ parameters.Add("gender", Gender);
+ }
+
+ if (!string.IsNullOrWhiteSpace(Age))
+ {
+ parameters.Add("age", Age);
+ }
+
+ if (!string.IsNullOrWhiteSpace(Accent))
+ {
+ parameters.Add("accent", Accent);
+ }
+
+ if (!string.IsNullOrWhiteSpace(Language))
+ {
+ parameters.Add("language", Language);
+ }
+
+ if (!string.IsNullOrWhiteSpace(SearchTerms))
+ {
+ parameters.Add("search", SearchTerms);
+ }
+
+ if (UseCases is { Count: > 0 })
+ {
+ parameters.Add("use_cases", string.Join(',', UseCases));
+ }
+
+ if (Descriptives is { Count: > 0 })
+ {
+ parameters.Add("descriptives", string.Join(',', Descriptives));
+ }
+
+ if (Featured.HasValue)
+ {
+ parameters.Add("featured", Featured.Value.ToString());
+ }
+
+ if (ReaderAppEnabled.HasValue)
+ {
+ parameters.Add("reader_app_enabled", ReaderAppEnabled.Value.ToString());
+ }
+
+ if (!string.IsNullOrWhiteSpace(OwnerId))
+ {
+ parameters.Add("owner_id", OwnerId);
+ }
+
+ if (!string.IsNullOrWhiteSpace(Sort))
+ {
+ parameters.Add("sort", Sort);
+ }
+
+ if (Page.HasValue)
+ {
+ parameters.Add("page", Page.Value.ToString());
+ }
+
+ return parameters;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ElevenLabs-DotNet/Voices/SharedVoicesEndpoint.cs b/ElevenLabs-DotNet/Voices/SharedVoicesEndpoint.cs
new file mode 100644
index 0000000..de8ade0
--- /dev/null
+++ b/ElevenLabs-DotNet/Voices/SharedVoicesEndpoint.cs
@@ -0,0 +1,29 @@
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using ElevenLabs.Extensions;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace ElevenLabs.Voices
+{
+ public sealed class SharedVoicesEndpoint : ElevenLabsBaseEndPoint
+ {
+ public SharedVoicesEndpoint(ElevenLabsClient client) : base(client) { }
+
+ protected override string Root => "shared-voices";
+
+ ///
+ /// Gets a list of shared voices.
+ ///
+ /// Optional, .
+ /// Optional, .
+ /// .
+ public async Task GetSharedVoicesAsync(SharedVoiceQuery query = null, CancellationToken cancellationToken = default)
+ {
+ using var response = await client.Client.GetAsync(GetUrl(queryParameters: query?.ToQueryParams()), cancellationToken);
+ var responseAsString = await response.ReadAsStringAsync(EnableDebug, cancellationToken);
+ return JsonSerializer.Deserialize(responseAsString, ElevenLabsClient.JsonSerializationOptions);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ElevenLabs-DotNet/Voices/VoicesEndpoint.cs b/ElevenLabs-DotNet/Voices/VoicesEndpoint.cs
index 252aab8..3b7e61f 100644
--- a/ElevenLabs-DotNet/Voices/VoicesEndpoint.cs
+++ b/ElevenLabs-DotNet/Voices/VoicesEndpoint.cs
@@ -41,8 +41,8 @@ public VoicesEndpoint(ElevenLabsClient client) : base(client) { }
///
///
/// of s.
- public Task> GetAllVoicesAsync(CancellationToken cancellationToken = default)
- => GetAllVoicesAsync(true, cancellationToken);
+ public async Task> GetAllVoicesAsync(CancellationToken cancellationToken = default)
+ => await GetAllVoicesAsync(true, cancellationToken).ConfigureAwait(false);
///
/// Gets a list of all available voices for a user.
@@ -62,7 +62,7 @@ public async Task> GetAllVoicesAsync(bool downloadSettings,
foreach (var voice in voices)
{
- voiceSettingsTasks.Add(Task.Run(LocalGetVoiceSettingsAsync, cancellationToken));
+ voiceSettingsTasks.Add(LocalGetVoiceSettingsAsync());
async Task LocalGetVoiceSettingsAsync()
{
@@ -348,7 +348,7 @@ public async Task EditVoiceAsync(Voice voice, IEnumerable samplePa
}
var response = await client.Client.PostAsync(GetUrl($"/{voice.Id}/edit"), form, cancellationToken).ConfigureAwait(false);
- await response.CheckResponseAsync(cancellationToken).ConfigureAwait(false);
+ await response.CheckResponseAsync(EnableDebug, form, cancellationToken).ConfigureAwait(false);
return response.IsSuccessStatusCode;
}
@@ -366,7 +366,7 @@ public async Task DeleteVoiceAsync(string voiceId, CancellationToken cance
}
var response = await client.Client.DeleteAsync(GetUrl($"/{voiceId}"), cancellationToken).ConfigureAwait(false);
- await response.CheckResponseAsync(cancellationToken).ConfigureAwait(false);
+ await response.CheckResponseAsync(EnableDebug, cancellationToken).ConfigureAwait(false);
return response.IsSuccessStatusCode;
}
@@ -394,7 +394,7 @@ public async Task DownloadVoiceSampleAudioAsync(Voice voice, Sample s
}
var response = await client.Client.GetAsync(GetUrl($"/{voice.Id}/samples/{sample.Id}/audio"), cancellationToken).ConfigureAwait(false);
- await response.CheckResponseAsync(cancellationToken).ConfigureAwait(false);
+ await response.CheckResponseAsync(EnableDebug, cancellationToken).ConfigureAwait(false);
await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
await using var memoryStream = new MemoryStream();
await responseStream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false);
@@ -420,8 +420,8 @@ public async Task DeleteVoiceSampleAsync(string voiceId, string sampleId,
throw new ArgumentNullException(nameof(sampleId));
}
- var response = await client.Client.DeleteAsync(GetUrl($"/{voiceId}/samples/{sampleId}"), cancellationToken);
- await response.CheckResponseAsync(cancellationToken);
+ var response = await client.Client.DeleteAsync(GetUrl($"/{voiceId}/samples/{sampleId}"), cancellationToken).ConfigureAwait(false);
+ await response.CheckResponseAsync(EnableDebug, cancellationToken).ConfigureAwait(false);
return response.IsSuccessStatusCode;
}
diff --git a/LICENSE b/LICENSE
index 6a82cb0..029dfaf 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2023 Rage Against The Pixel
+Copyright (c) 2024 Rage Against The Pixel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal