Skip to content

Commit

Permalink
com.rest.elevenlabs 3.3.0 (#86)
Browse files Browse the repository at this point in the history
- Added ability to specify fully customizable domain proxies
- Added env var parsing for `ELEVENLABS_API_KEY`
- Added Copy Button for the Voice ID to Voice Lab Dashboard
- Added Shared Voices API
- Added SoundEffects API endpoints
- Added Dubbing API endpoints
- Updated models
- Updated com.utilities.rest -> 2.5.7

---------

Co-authored-by: AmarTrivedi1 <149634562+AmarTrivedi1@users.noreply.github.com>
  • Loading branch information
StephenHodgson and AmarTrivedi1 authored Sep 3, 2024
1 parent 2493b06 commit 5060287
Show file tree
Hide file tree
Showing 54 changed files with 1,710 additions and 86 deletions.
123 changes: 115 additions & 8 deletions Documentation~/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ The recommended installation method is though the unity package manager and [Ope

### Table of Contents

- [Authentication](#authentication) :construction:
- [Authentication](#authentication)
- [API Proxy](#api-proxy)
- [Editor Dashboard](#editor-dashboard)
- [Speech Synthesis Dashboard](#speech-synthesis-dashboard)
Expand All @@ -59,6 +59,7 @@ The recommended installation method is though the unity package manager and [Ope
- [Text to Speech](#text-to-speech)
- [Stream Text To Speech](#stream-text-to-speech)
- [Voices](#voices)
- [Get Shared Voices](#get-shared-voices) :new:
- [Get All Voices](#get-all-voices)
- [Get Default Voice Settings](#get-default-voice-settings)
- [Get Voice](#get-voice)
Expand All @@ -69,6 +70,13 @@ The recommended installation method is though the unity package manager and [Ope
- [Samples](#samples)
- [Download Voice Sample](#download-voice-sample)
- [Delete Voice Sample](#delete-voice-sample)
- [Dubbing](#dubbing) :new:
- [Dub](#dub) :new:
- [Get Dubbing Metadata](#get-dubbing-metadata) :new:
- [Get Transcript for Dub](#get-transcript-for-dub) :new:
- [Get dubbed file](#get-dubbed-file) :new:
- [Delete Dubbing Project](#delete-dubbing-project) :new:
- [SFX Generation](#sfx-generation) :new:
- [History](#history)
- [Get History](#get-history)
- [Get History Item](#get-history-item)
Expand Down Expand Up @@ -176,8 +184,9 @@ In this example, we demonstrate how to set up and use `ElevenLabsProxyStartup` i
1. Create a new [ASP.NET Core minimal web API](https://learn.microsoft.com/en-us/aspnet/core/tutorials/min-web-api?view=aspnetcore-6.0) project.
2. Add the ElevenLabs-DotNet nuget package to your project.
- Powershell install: `Install-Package ElevenLabs-DotNet-Proxy`
- Dotnet install: `dotnet add package ElevenLabs-DotNet-Proxy`
- Manually editing .csproj: `<PackageReference Include="ElevenLabs-DotNet-Proxy" />`
3. Create a new class that inherits from `AbstractAuthenticationFilter` and override the `ValidateAuthentication` method. This will implement the `IAuthenticationFilter` that you will use to check user session token against your internal server.
3. Create a new class that inherits from `AbstractAuthenticationFilter` and override the `ValidateAuthenticationAsync` method. This will implement the `IAuthenticationFilter` that you will use to check user session token against your internal server.
4. In `Program.cs`, create a new proxy web application by calling `ElevenLabsProxyStartup.CreateDefaultHost` method, passing your custom `AuthenticationFilter` as a type argument.
5. Create `ElevenLabsAuthentication` and `ElevenLabsClientSettings` as you would normally with your API keys, org id, or Azure settings.

Expand All @@ -186,11 +195,13 @@ public partial class Program
{
private class AuthenticationFilter : AbstractAuthenticationFilter
{
public override void ValidateAuthentication(IHeaderDictionary request)
public override async Task ValidateAuthenticationAsync(IHeaderDictionary request)
{
await Task.CompletedTask; // remote resource call
// You will need to implement your own class to properly test
// custom issued tokens you've setup for your end users.
if (!request["xi-api-key"].ToString().Contains(userToken))
if (!request["xi-api-key"].ToString().Contains(TestUserToken))
{
throw new AuthenticationException("User is not authorized");
}
Expand Down Expand Up @@ -265,7 +276,9 @@ audioSource.PlayOneShot(voiceClip.AudioClip);
voiceClip.CopyIntoProject(editorDownloadDirectory);
```

### Stream Text to Speech
#### [Stream Text To Speech](https://docs.elevenlabs.io/api-reference/text-to-speech-stream)

Stream text to speech.

```csharp
var api = new ElevenLabsClient();
Expand All @@ -289,6 +302,19 @@ audioSource.clip = voiceClip.AudioClip;

Access to voices created either by the user or ElevenLabs.

#### Get Shared Voices

Gets a list of shared voices in the public voice library.

```csharp
var api = new ElevenLabsClient();
var results = await ElevenLabsClient.SharedVoicesEndpoint.GetSharedVoicesAsync();
foreach (var voice in results.Voices)
{
Debug.Log($"{voice.OwnerId} | {voice.VoiceId} | {voice.Date} | {voice.Name}");
}
```

#### Get All Voices

Gets a list of all available voices.
Expand Down Expand Up @@ -383,6 +409,87 @@ var success = await api.VoicesEndpoint.DeleteVoiceSampleAsync(voiceId, sampleId)
Debug.Log($"Was successful? {success}");
```

### [Dubbing](https://elevenlabs.io/docs/api-reference/create-dub)

#### Dub

Dubs provided audio or video file into given language.

```csharp
var api = new ElevenLabsClient();
// from URI
var request = new DubbingRequest(new Uri("https://youtu.be/Zo5-rhYOlNk"), "ja", "en", 1, true);
// from file
var request = new DubbingRequest(filePath, "es", "en", 1);
var metadata = await api.DubbingEndpoint.DubAsync(request, progress: new Progress<DubbingProjectMetadata>(metadata =>
{
switch (metadata.Status)
{
case "dubbing":
Debug.Log($"Dubbing for {metadata.DubbingId} in progress... Expected Duration: {metadata.ExpectedDurationSeconds:0.00} seconds");
break;
case "dubbed":
Debug.Log($"Dubbing for {metadata.DubbingId} complete in {metadata.TimeCompleted.TotalSeconds:0.00} seconds!");
break;
default:
Debug.Log($"Status: {metadata.Status}");
break;
}
}));
```

#### Get Dubbing Metadata

Returns metadata about a dubbing project, including whether it’s still in progress or not.

```csharp
var api = new ElevenLabsClient();
var metadata = api.await GetDubbingProjectMetadataAsync("dubbing-id");
```

#### Get Dubbed File

Returns downloaded dubbed file path.

> [!IMPORTANT]
> Videos will be returned in MP4 format and audio only dubs will be returned in MP3.
```csharp
var dubbedClipPath = await ElevenLabsClient.DubbingEndpoint.GetDubbedFileAsync(metadata.DubbingId, request.TargetLanguage);
var dubbedClip = await Rest.DownloadAudioClipAsync($"file://{dubbedClipPath}", AudioType.MPEG);
audioSource.PlayOneShot(dubbedClip);
```

#### Get Transcript for Dub

Returns transcript for the dub in the desired format.

```csharp
var srcFile = new FileInfo(audioPath);
var transcriptPath = new FileInfo($"{srcFile.FullName}.dubbed.{request.TargetLanguage}.srt");
var transcriptFile = await ElevenLabsClient.DubbingEndpoint.GetTranscriptForDubAsync(metadata.DubbingId, request.TargetLanguage);
await File.WriteAllTextAsync(transcriptPath.FullName, transcriptFile);
```

#### Delete Dubbing Project

Deletes a dubbing project.

```csharp
var api = new ElevenLabsClient();
await api.DubbingEndpoint.DeleteDubbingProjectAsync("dubbing-id");
```

### SFX Generation

API that converts text into sounds & uses the most advanced AI audio model ever.

```csharp
var api = new ElevenLabsClient();
var request = new SoundGenerationRequest("Star Wars Light Saber parry");
var clip = await api.SoundGenerationEndpoint.GenerateSoundAsync(request);
```

### [History](https://docs.elevenlabs.io/api-reference/history)

Access to your previously synthesized audio clips including its metadata.
Expand All @@ -393,9 +500,9 @@ Get metadata about all your generated audio.

```csharp
var api = new ElevenLabsClient();
var historyInfo = await api.HistoryEndpoint.GetHistoryAsync();
var historyItems = await api.HistoryEndpoint.GetHistoryAsync();

foreach (var item in historyInfo.HistoryItems.OrderBy(item => item.Date))
foreach (var item in historyItems.OrderBy(historyItem => historyItem.Date))
{
Debug.Log($"{item.State} {item.Date} | {item.Id} | {item.Text.Length} | {item.Text}");
}
Expand All @@ -407,7 +514,7 @@ Get information about a specific item.

```csharp
var api = new ElevenLabsClient();
var historyItem = api.HistoryEndpoint.GetHistoryItemAsync(voiceClip.Id);
var historyItem = await api.HistoryEndpoint.GetHistoryItemAsync(voiceClip.Id);
```

#### Download History Audio
Expand Down
21 changes: 19 additions & 2 deletions Editor/ElevenLabsDashboard.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.

using ElevenLabs.Extensions;
using ElevenLabs.History;
using ElevenLabs.Models;
using ElevenLabs.User;
Expand Down Expand Up @@ -363,6 +362,8 @@ private async void AddVoice()

private static readonly GUIContent deleteContent = new("Delete");

private static readonly GUIContent copyContent = new("Copy");

private static readonly GUIContent refreshContent = new("Refresh");

private static readonly GUIContent downloadingContent = new("Download in progress...");
Expand Down Expand Up @@ -1224,8 +1225,24 @@ private void RenderVoiceLab()
EditorGUILayout.Space(EndWidth);
EditorGUILayout.EndHorizontal();
EditorGUI.indentLevel++;
EditorGUILayout.LabelField(voice.Id, EditorStyles.boldLabel);

EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.LabelField(voice.Id, EditorStyles.boldLabel);
GUILayout.FlexibleSpace();

if (GUILayout.Button(copyContent, defaultColumnWidthOption))
{
EditorGUIUtility.systemCopyBuffer = voice.Id;
Debug.Log($"Voice ID {voice.Id} copied to clipboard");
}

GUI.enabled = true;
}
EditorGUILayout.Space(EndWidth);
EditorGUILayout.EndHorizontal();
EditorGUI.indentLevel++;

if (!voiceLabels.TryGetValue(voice.Id, out var cachedLabels))
{
cachedLabels = new Dictionary<string, string>();
Expand Down
8 changes: 8 additions & 0 deletions Runtime/Authentication/ElevenLabsAuthentication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace ElevenLabs
public sealed class ElevenLabsAuthentication : AbstractAuthentication<ElevenLabsAuthentication, ElevenLabsAuthInfo, ElevenLabsConfiguration>
{
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);

/// <summary>
Expand Down Expand Up @@ -85,6 +86,12 @@ public override ElevenLabsAuthentication LoadFromAsset(ElevenLabsConfiguration c
public override 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);
}

Expand Down Expand Up @@ -136,6 +143,7 @@ public override ElevenLabsAuthentication LoadFromDirectory(string directory = nu

apiKey = part switch
{
ELEVENLABS_API_KEY => nextPart.Trim(),
ELEVEN_LABS_API_KEY => nextPart.Trim(),
_ => apiKey
};
Expand Down
11 changes: 6 additions & 5 deletions Runtime/Authentication/ElevenLabsSettingsInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace ElevenLabs
{
public sealed class ElevenLabsSettingsInfo : ISettingsInfo
{
internal const string Https = "https://";
internal const string ElevenLabsDomain = "api.elevenlabs.io";
internal const string DefaultApiVersion = "v1";

Expand All @@ -18,7 +19,7 @@ public ElevenLabsSettingsInfo()
Domain = ElevenLabsDomain;
ApiVersion = DefaultApiVersion;
BaseRequest = $"/{ApiVersion}/";
BaseRequestUrlFormat = $"https://{Domain}{BaseRequest}{{0}}";
BaseRequestUrlFormat = $"{Https}{Domain}{BaseRequest}{{0}}";
}

/// <summary>
Expand All @@ -33,8 +34,8 @@ public ElevenLabsSettingsInfo(string domain, string apiVersion = DefaultApiVersi
domain = ElevenLabsDomain;
}

if (!domain.Contains(".") &&
!domain.Contains(":"))
if (!domain.Contains('.') &&
!domain.Contains(':'))
{
throw new ArgumentException($"Invalid parameter \"{nameof(domain)}\".");
}
Expand All @@ -44,10 +45,10 @@ public ElevenLabsSettingsInfo(string domain, string apiVersion = DefaultApiVersi
apiVersion = DefaultApiVersion;
}

Domain = domain;
Domain = domain.Contains("http") ? domain : $"{Https}{domain}";
ApiVersion = apiVersion;
BaseRequest = $"/{ApiVersion}/";
BaseRequestUrlFormat = $"https://{Domain}{BaseRequest}{{0}}";
BaseRequestUrlFormat = $"{Domain}{BaseRequest}{{0}}";
}

public string Domain { get; }
Expand Down
59 changes: 59 additions & 0 deletions Runtime/Common/GeneratedClip.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.

using ElevenLabs.Extensions;
using System;
using UnityEngine;
using UnityEngine.Scripting;

namespace ElevenLabs
{
[Preserve]
[Serializable]
public class GeneratedClip : ISerializationCallbackReceiver
{
[Preserve]
internal GeneratedClip(string id, string text, AudioClip audioClip, string cachedPath)
{
this.id = id;
this.text = text;
TextHash = $"{id}{text}".GenerateGuid();
textHash = TextHash.ToString();
this.audioClip = audioClip;
this.cachedPath = cachedPath;
}

[SerializeField]
private string id;

[Preserve]
public string Id => id;

[SerializeField]
private string text;

[Preserve]
public string Text => text;

[SerializeField]
private string textHash;

[Preserve]
public Guid TextHash { get; private set; }

[SerializeField]
private AudioClip audioClip;

[Preserve]
public AudioClip AudioClip => audioClip;

[SerializeField]
private string cachedPath;

[Preserve]
public string CachedPath => cachedPath;

public void OnBeforeSerialize() => textHash = TextHash.ToString();

public void OnAfterDeserialize() => TextHash = Guid.Parse(textHash);
}
}
11 changes: 11 additions & 0 deletions Runtime/Common/GeneratedClip.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 5060287

Please sign in to comment.