Skip to content

Commit

Permalink
ElevenLabs-DotNet 2.1.0 (#31)
Browse files Browse the repository at this point in the history
- Added ElevenLabsClient.EnableDebug option to enable and disable for all endpoints
- Synced changes from unity package
- Updated unit test
- Updated docs
  • Loading branch information
StephenHodgson authored Nov 29, 2023
1 parent 8637b61 commit da3200e
Show file tree
Hide file tree
Showing 21 changed files with 151 additions and 92 deletions.
39 changes: 31 additions & 8 deletions .github/workflows/Publish-Nuget.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,31 +31,54 @@ env:
jobs:
build:
if: ${{ !github.event_name == 'pull_request' || !github.event.pull_request.draft }}
runs-on: windows-latest
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-dotnet@v3
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- uses: microsoft/setup-msbuild@v1

- run: dotnet restore

- run: dotnet build --configuration Release --no-restore

- name: Test Packages
run: dotnet test --configuration Release
if: ${{ github.ref != 'refs/heads/main' && github.event_name != 'push' }}
run: dotnet test --configuration Release --collect:"XPlat Code Coverage" --logger:trx --no-build --no-restore --results-directory ./test-results
env:
ELEVEN_LABS_API_KEY: ${{ secrets.ELEVEN_LABS_API_KEY }}

- name: Build Pack and Publish NuGet Package
- name: Publish Test Results
if: ${{ github.ref != 'refs/heads/main' && github.event_name != 'push' && always() }}
uses: EnricoMi/publish-unit-test-result-action@v2
with:
files: test-results/**/*.trx
comment_mode: off
report_individual_runs: true
compare_to_earlier_commit: false

- name: Code Coverage Summary Report
if: ${{ github.ref != 'refs/heads/main' && github.event_name != 'push' && always() }}
uses: irongut/[email protected]
with:
filename: test-results/**/coverage.cobertura.xml
badge: true
format: 'markdown'
output: 'both'

- name: Write Coverage Job Summary
if: ${{ github.ref != 'refs/heads/main' && github.event_name != 'push' && always() }}
run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY

- name: Pack and Publish NuGet Package
run: |
$projectPath = "${{ github.workspace }}\ElevenLabs-DotNet"
$proxyProjectPath = "${{ github.workspace }}\ElevenLabs-DotNet-Proxy"
# build all
dotnet build --configuration Release
# pack ElevenLabs-DotNet
dotnet pack $projectPath --configuration Release --include-symbols
$out = "$projectPath\bin\Release"
Expand Down
1 change: 1 addition & 0 deletions ElevenLabs-DotNet-Tests/ElevenLabs-DotNet-Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.15" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.4.2" />
<PackageReference Include="coverlet.collector" Version="1.0.1" />
<ProjectReference Include="..\ElevenLabs-DotNet\ElevenLabs-DotNet.csproj" />
<ProjectReference Include="..\ElevenLabs-DotNet-Tests-Proxy\ElevenLabs-DotNet-Tests-Proxy.csproj" />
</ItemGroup>
Expand Down
6 changes: 3 additions & 3 deletions ElevenLabs-DotNet-Tests/Test_Fixture_02_VoicesEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public async Task Test_03_GetVoice()
var results = await ElevenLabsClient.VoicesEndpoint.GetAllVoicesAsync();
Assert.NotNull(results);
Assert.IsNotEmpty(results);
var voiceToGet = results.MinBy(voice => voice.Name);
var voiceToGet = results.OrderBy(voice => voice.Name).FirstOrDefault();
var result = await ElevenLabsClient.VoicesEndpoint.GetVoiceAsync(voiceToGet);
Assert.NotNull(result);
Console.WriteLine($"{result.Id} | {result.Name} | {result.PreviewUrl}");
Expand Down Expand Up @@ -78,7 +78,7 @@ public async Task Test_05_AddVoice()
{
{ "accent", "american" }
};
var clipPath = Path.GetFullPath("..\\..\\..\\Assets\\test_sample_01.ogg");
var clipPath = Path.GetFullPath("../../../Assets/test_sample_01.ogg");
var result = await ElevenLabsClient.VoicesEndpoint.AddVoiceAsync("Test Voice", new[] { clipPath }, testLabels);
Assert.NotNull(result);
Console.WriteLine($"{result.Name}");
Expand All @@ -99,7 +99,7 @@ public async Task Test_06_EditVoice()
{ "accent", "american" },
{ "key", "value" }
};
var clipPath = Path.GetFullPath("..\\..\\..\\Assets\\test_sample_01.ogg");
var clipPath = Path.GetFullPath("../../../Assets/test_sample_01.ogg");
var result = await ElevenLabsClient.VoicesEndpoint.EditVoiceAsync(voiceToEdit, new[] { clipPath }, testLabels);
Assert.NotNull(result);
Assert.IsTrue(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal class Test_Fixture_03_TextToSpeechEndpoint : AbstractTestFixture
public async Task Test_01_TextToSpeech()
{
Assert.NotNull(ElevenLabsClient.TextToSpeechEndpoint);
var voice = (await ElevenLabsClient.VoicesEndpoint.GetAllVoicesAsync()).FirstOrDefault();
var voice = Voices.Voice.Adam;
Assert.NotNull(voice);
var defaultVoiceSettings = await ElevenLabsClient.VoicesEndpoint.GetDefaultVoiceSettingsAsync();
var voiceClip = await ElevenLabsClient.TextToSpeechEndpoint.TextToSpeechAsync("The quick brown fox jumps over the lazy dog.", voice, defaultVoiceSettings);
Expand All @@ -28,10 +28,9 @@ public async Task Test_02_StreamTextToSpeech()
Assert.NotNull(ElevenLabsClient.TextToSpeechEndpoint);
var voice = (await ElevenLabsClient.VoicesEndpoint.GetAllVoicesAsync()).FirstOrDefault();
Assert.NotNull(voice);
var partialClips = new Queue<VoiceClip>();
var defaultVoiceSettings = await ElevenLabsClient.VoicesEndpoint.GetDefaultVoiceSettingsAsync();
var partialClips = new Queue<VoiceClip>();
var voiceClip = await ElevenLabsClient.TextToSpeechEndpoint.TextToSpeechAsync("The quick brown fox jumps over the lazy dog.", voice, defaultVoiceSettings,

partialClipCallback: async partialClip =>
{
Assert.IsNotNull(partialClip);
Expand Down
10 changes: 5 additions & 5 deletions ElevenLabs-DotNet-Tests/Test_Fixture_04_HistoryEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ internal class Test_Fixture_04_HistoryEndpoint : AbstractTestFixture
public async Task Test_01_GetHistory()
{
Assert.NotNull(ElevenLabsClient.HistoryEndpoint);
var results = await ElevenLabsClient.HistoryEndpoint.GetHistoryAsync();
Assert.NotNull(results);
Assert.IsNotEmpty(results.HistoryItems);
var historyInfo = await ElevenLabsClient.HistoryEndpoint.GetHistoryAsync();
Assert.NotNull(historyInfo);
Assert.IsNotEmpty(historyInfo.HistoryItems);

foreach (var item in results.HistoryItems.OrderBy(item => item.Date))
foreach (var item in historyInfo.HistoryItems.OrderBy(item => item.Date))
{
Console.WriteLine($"{item.State} {item.Date} | {item.Id} | {item.Text.Length} | {item.Text}");
}
Expand All @@ -31,7 +31,7 @@ public async Task Test_02_GetHistoryAudio()
var historyInfo = await ElevenLabsClient.HistoryEndpoint.GetHistoryAsync();
Assert.NotNull(historyInfo);
Assert.IsNotEmpty(historyInfo.HistoryItems);
var downloadItem = historyInfo.HistoryItems.MaxBy(item => item.Date);
var downloadItem = historyInfo.HistoryItems.OrderByDescending(item => item.Date).FirstOrDefault();
Assert.NotNull(downloadItem);
Console.WriteLine($"Downloading {downloadItem!.Id}...");
var voiceClip = await ElevenLabsClient.HistoryEndpoint.DownloadHistoryAudioAsync(downloadItem);
Expand Down
1 change: 1 addition & 0 deletions ElevenLabs-DotNet-Tests/Test_Fixture_05_VoiceGeneration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public async Task Test_02_GenerateVoice()
Assert.NotNull(result);
Console.WriteLine(result.Id);
var deleteResult = await ElevenLabsClient.VoicesEndpoint.DeleteVoiceAsync(result.Id);
Assert.NotNull(deleteResult);
Assert.IsTrue(deleteResult);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@

namespace ElevenLabs
{
/// <summary>
/// Represents authentication for ElevenLabs
/// </summary>
public sealed class ElevenLabsAuthentication
{
private const string ELEVEN_LABS_API_KEY = "ELEVEN_LABS_API_KEY";
internal const string CONFIG_FILE = ".elevenlabs";
private const string ELEVEN_LABS_API_KEY = nameof(ELEVEN_LABS_API_KEY);

private readonly AuthInfo authInfo;

Expand Down Expand Up @@ -100,13 +104,18 @@ public static ElevenLabsAuthentication LoadFromPath(string path)
/// or <see langword="null"/> if it was not successful in finding a config
/// (or if the config file didn't contain correctly formatted API keys)
/// </returns>
public static ElevenLabsAuthentication LoadFromDirectory(string directory = null, string filename = ".elevenlabs", bool searchUp = true)
public static ElevenLabsAuthentication LoadFromDirectory(string directory = null, string filename = CONFIG_FILE, bool searchUp = true)
{
if (string.IsNullOrWhiteSpace(directory))
{
directory = Environment.CurrentDirectory;
}

if (string.IsNullOrWhiteSpace(filename))
{
filename = CONFIG_FILE;
}

AuthInfo authInfo = null;

var currentDirectory = new DirectoryInfo(directory);
Expand Down Expand Up @@ -161,13 +170,7 @@ public static ElevenLabsAuthentication LoadFromDirectory(string directory = null
}
}

if (authInfo == null ||
string.IsNullOrEmpty(authInfo.ApiKey))
{
return null;
}

return new ElevenLabsAuthentication(authInfo);
return string.IsNullOrEmpty(authInfo?.ApiKey) ? null : new ElevenLabsAuthentication(authInfo);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@

namespace ElevenLabs
{
public abstract class BaseEndPoint
public abstract class ElevenLabsBaseEndPoint
{
internal BaseEndPoint(ElevenLabsClient api) => Api = api;
internal ElevenLabsBaseEndPoint(ElevenLabsClient client) => this.client = client;

protected readonly ElevenLabsClient Api;
// ReSharper disable once InconsistentNaming
protected readonly ElevenLabsClient client;

/// <summary>
/// The root endpoint address.
Expand All @@ -23,7 +24,7 @@ public abstract class BaseEndPoint
/// <param name="queryParameters">Optional, parameters to add to the endpoint.</param>
protected string GetUrl(string endpoint = "", Dictionary<string, string> queryParameters = null)
{
var result = string.Format(Api.ElevenLabsClientSettings.BaseRequestUrlFormat, $"{Root}{endpoint}");
var result = string.Format(client.ElevenLabsClientSettings.BaseRequestUrlFormat, $"{Root}{endpoint}");

if (queryParameters is { Count: not 0 })
{
Expand All @@ -33,10 +34,16 @@ protected string GetUrl(string endpoint = "", Dictionary<string, string> queryPa
return result;
}

private bool enableDebug;

/// <summary>
/// Enables or disables the logging of all http responses of header and body information for this endpoint.<br/>
/// WARNING! Enabling this in your production build, could potentially leak sensitive information!
/// </summary>
public bool EnableDebug { get; set; }
public bool EnableDebug
{
get => enableDebug || client.EnableDebug;
set => enableDebug = value;
}
}
}
File renamed without changes.
File renamed without changes.
8 changes: 6 additions & 2 deletions ElevenLabs-DotNet/ElevenLabs-DotNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@
<Company>RageAgainstThePixel</Company>
<Copyright>2023</Copyright>
<PackageTags>ElevenLabs, AI, ML, Voice, TTS</PackageTags>
<Version>2.0.3</Version>
<PackageReleaseNotes>Version 2.0.3
<Version>2.1.0</Version>
<PackageReleaseNotes>Version 2.1.0
- Added ElevenLabsClient.EnableDebug option to enable and disable for all endpoints
- Synced changes from unity package
- Updated unit test
Version 2.0.3
- Fixed text to speech streaming
Version 2.0.2
- Added the u-law format
Expand Down
14 changes: 11 additions & 3 deletions ElevenLabs-DotNet/ElevenLabsClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ public sealed class ElevenLabsClient
/// Creates a new client for the Eleven Labs API, handling auth and allowing for access to various API endpoints.
/// </summary>
/// <param name="elevenLabsAuthentication">The API authentication information to use for API calls,
/// or <see langword="null"/> to attempt to use the <see cref="ElevenLabs.ElevenLabsAuthentication.Default"/>,
/// potentially loading from environment vars or from a config file.</param>
/// <param name="clientSettings">Optional, <see cref="ElevenLabsClientSettings"/> for specifying a proxy domain.</param>
/// or <see langword="null"/> to attempt to use the <see cref="ElevenLabsAuthentication.Default"/>,
/// potentially loading from environment vars or from a config file.
/// </param>
/// <param name="clientSettings">
/// Optional, <see cref="ElevenLabsClientSettings"/> for specifying a proxy domain.
/// </param>
/// <param name="httpClient">Optional, <see cref="HttpClient"/>.</param>
/// <exception cref="AuthenticationException">Raised when authentication details are missing or invalid.</exception>
public ElevenLabsClient(ElevenLabsAuthentication elevenLabsAuthentication = null, ElevenLabsClientSettings clientSettings = null, HttpClient httpClient = null)
Expand Down Expand Up @@ -59,6 +62,11 @@ public ElevenLabsClient(ElevenLabsAuthentication elevenLabsAuthentication = null
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};

/// <summary>
/// Enables or disables debugging for all endpoints.
/// </summary>
public bool EnableDebug { get; set; }

/// <summary>
/// The API authentication information to use for API calls
/// </summary>
Expand Down
21 changes: 12 additions & 9 deletions ElevenLabs-DotNet/History/HistoryEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,19 @@ namespace ElevenLabs.History
/// <summary>
/// Access to your history. Your history is a list of all your created audio including its metadata.
/// </summary>
public sealed class HistoryEndpoint : BaseEndPoint
public sealed class HistoryEndpoint : ElevenLabsBaseEndPoint
{
public HistoryEndpoint(ElevenLabsClient api) : base(api) { }
public HistoryEndpoint(ElevenLabsClient client) : base(client) { }

protected override string Root => "history";

/// <summary>
/// Get metadata about all your generated audio.
/// </summary>
/// <param name="pageSize">Optional, number of items to return. Cannot exceed 1000.</param>
/// <param name="pageSize">
/// Optional, number of items to return. Cannot exceed 1000.<br/>
/// Default: 100
/// </param>
/// <param name="startAfterId">Optional, the id of the item to start after.</param>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns><see cref="HistoryInfo"/>.</returns>
Expand All @@ -42,7 +45,7 @@ public async Task<HistoryInfo> GetHistoryAsync(int? pageSize = null, string star
parameters.Add("start_after_history_item_id", startAfterId);
}

var response = await Api.Client.GetAsync(GetUrl(queryParameters: parameters), cancellationToken);
var response = await client.Client.GetAsync(GetUrl(queryParameters: parameters), cancellationToken);
var responseAsString = await response.ReadAsStringAsync(EnableDebug, cancellationToken: cancellationToken);
return JsonSerializer.Deserialize<HistoryInfo>(responseAsString, ElevenLabsClient.JsonSerializationOptions);
}
Expand All @@ -55,7 +58,7 @@ public async Task<HistoryInfo> GetHistoryAsync(int? pageSize = null, string star
/// <returns><see cref="HistoryItem"/></returns>
public async Task<HistoryItem> GetHistoryItemAsync(string id, CancellationToken cancellationToken = default)
{
var response = await Api.Client.GetAsync(GetUrl($"/{id}"), cancellationToken);
var response = await client.Client.GetAsync(GetUrl($"/{id}"), cancellationToken);
var responseAsString = await response.ReadAsStringAsync(EnableDebug, cancellationToken: cancellationToken);
return JsonSerializer.Deserialize<HistoryItem>(responseAsString, ElevenLabsClient.JsonSerializationOptions);
}
Expand All @@ -68,8 +71,8 @@ public async Task<HistoryItem> GetHistoryItemAsync(string id, CancellationToken
/// <returns><see cref="VoiceClip"/>.</returns>
public async Task<VoiceClip> DownloadHistoryAudioAsync(HistoryItem historyItem, CancellationToken cancellationToken = default)
{
var voice = await Api.VoicesEndpoint.GetVoiceAsync(historyItem.VoiceId, cancellationToken: cancellationToken).ConfigureAwait(false);
var response = await Api.Client.GetAsync(GetUrl($"/{historyItem.Id}/audio"), cancellationToken).ConfigureAwait(false);
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);
var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
var memoryStream = new MemoryStream();
Expand Down Expand Up @@ -97,14 +100,14 @@ public async Task<VoiceClip> DownloadHistoryAudioAsync(HistoryItem historyItem,
/// <returns>True, if history item was successfully deleted.</returns>
public async Task<bool> DeleteHistoryItemAsync(string id, CancellationToken cancellationToken = default)
{
var response = await Api.Client.DeleteAsync(GetUrl($"/{id}"), cancellationToken);
var response = await client.Client.DeleteAsync(GetUrl($"/{id}"), cancellationToken);
await response.ReadAsStringAsync(EnableDebug, cancellationToken: cancellationToken);
return response.IsSuccessStatusCode;
}

/// <summary>
/// Download one or more history items.<br/>
/// If no ids are specified, then all history items are downloaded.<br/>
/// If no ids are specified, then the last 100 history items are downloaded.<br/>
/// If one history item id is provided, we will return a single audio file.<br/>
/// If more than one history item ids are provided multiple audio files will be downloaded.
/// </summary>
Expand Down
6 changes: 3 additions & 3 deletions ElevenLabs-DotNet/Models/ModelsEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

namespace ElevenLabs.Models
{
public sealed class ModelsEndpoint : BaseEndPoint
public sealed class ModelsEndpoint : ElevenLabsBaseEndPoint
{
public ModelsEndpoint(ElevenLabsClient api) : base(api) { }
public ModelsEndpoint(ElevenLabsClient client) : base(client) { }

protected override string Root => "models";

Expand All @@ -19,7 +19,7 @@ public ModelsEndpoint(ElevenLabsClient api) : base(api) { }
/// <returns>A list of <see cref="Model"/>s you can use.</returns>
public async Task<IReadOnlyList<Model>> GetModelsAsync()
{
var response = await Api.Client.GetAsync(GetUrl());
var response = await client.Client.GetAsync(GetUrl());
var responseAsString = await response.ReadAsStringAsync(EnableDebug);
return JsonSerializer.Deserialize<IReadOnlyList<Model>>(responseAsString, ElevenLabsClient.JsonSerializationOptions);
}
Expand Down
Loading

0 comments on commit da3200e

Please sign in to comment.