From f66418cbcef360fb3c9c5e125fb108fa70136c57 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 21 Jul 2024 12:55:45 -0400 Subject: [PATCH] cleanup and formatting pass 1 --- .../Test_Fixture_07_DubbingEndpoint.cs | 138 +++--- ElevenLabs-DotNet/Dubbing/DubbingEndpoint.cs | 416 +++++++++--------- .../Dubbing/DubbingProjectMetadata.cs | 32 +- ElevenLabs-DotNet/Dubbing/DubbingRequest.cs | 172 ++++---- ElevenLabs-DotNet/Dubbing/DubbingResponse.cs | 17 +- 5 files changed, 402 insertions(+), 373 deletions(-) diff --git a/ElevenLabs-DotNet-Tests/Test_Fixture_07_DubbingEndpoint.cs b/ElevenLabs-DotNet-Tests/Test_Fixture_07_DubbingEndpoint.cs index 5e36c7a..13ac902 100644 --- a/ElevenLabs-DotNet-Tests/Test_Fixture_07_DubbingEndpoint.cs +++ b/ElevenLabs-DotNet-Tests/Test_Fixture_07_DubbingEndpoint.cs @@ -1,5 +1,4 @@ // Licensed under the MIT License. See LICENSE in the project root for license information. -namespace ElevenLabs.Tests; using ElevenLabs.Dubbing; using NUnit.Framework; @@ -7,91 +6,92 @@ namespace ElevenLabs.Tests; using System.IO; using System.Threading.Tasks; -internal class Test_Fixture_07_DubbingEndpoint : AbstractTestFixture +namespace ElevenLabs.Tests { - [Test] - public async Task Test_01_Dubbing_File() + internal class Test_Fixture_07_DubbingEndpoint : AbstractTestFixture { - Assert.NotNull(ElevenLabsClient.DubbingEndpoint); - - (string FilePath, string MediaType) audio = (Path.GetFullPath("../../../Assets/test_sample_01.ogg"), "audio/mpeg"); - DubbingRequest request = new() + [Test] + public async Task Test_01_Dubbing_File() { - File = audio, - SourceLanguage = "en", - TargetLanguage = "es", - NumSpeakers = 1, - Watermark = false, - }; + Assert.NotNull(ElevenLabsClient.DubbingEndpoint); + var filePath = Path.GetFullPath("../../../Assets/test_sample_01.ogg"); + var request = new DubbingRequest + { + FilePath = filePath, + MediaType = "audio/mpeg", + SourceLanguage = "en", + TargetLanguage = "es", + NumSpeakers = 1, + Watermark = false, + }; - DubbingResponse response = await ElevenLabsClient.DubbingEndpoint.StartDubbingAsync(request); - Assert.IsFalse(string.IsNullOrEmpty(response.DubbingId)); - Assert.IsTrue(response.ExpectedDurationSeconds > 0); - Console.WriteLine($"Expected Duration: {response.ExpectedDurationSeconds:0.00} seconds"); + var response = await ElevenLabsClient.DubbingEndpoint.StartDubbingAsync(request); + Assert.IsFalse(string.IsNullOrEmpty(response.DubbingId)); + Assert.IsTrue(response.ExpectedDurationSeconds > 0); + Console.WriteLine($"Expected Duration: {response.ExpectedDurationSeconds:0.00} seconds"); + Assert.IsTrue(await ElevenLabsClient.DubbingEndpoint.WaitForDubbingCompletionAsync(response.DubbingId, progress: new Progress(Console.WriteLine))); - Assert.IsTrue(await ElevenLabsClient.DubbingEndpoint.WaitForDubbingCompletionAsync(response.DubbingId, progress: new Progress(msg => Console.WriteLine(msg)))); + var srcFile = new FileInfo(filePath); + var dubbedPath = new FileInfo($"{srcFile.FullName}.dubbed.{request.TargetLanguage}{srcFile.Extension}"); + { + await using var fs = File.Open(dubbedPath.FullName, FileMode.Create); + await foreach (var chunk in ElevenLabsClient.DubbingEndpoint.GetDubbedFileAsync(response.DubbingId, request.TargetLanguage)) + { + await fs.WriteAsync(chunk); + } + } + Assert.IsTrue(dubbedPath.Exists); + Assert.IsTrue(dubbedPath.Length > 0); - FileInfo srcFile = new(audio.FilePath); - FileInfo dubbedPath = new($"{srcFile.FullName}.dubbed.{request.TargetLanguage}{srcFile.Extension}"); - { - await using FileStream fs = File.Open(dubbedPath.FullName, FileMode.Create); - await foreach (byte[] chunk in ElevenLabsClient.DubbingEndpoint.GetDubbedFileAsync(response.DubbingId, request.TargetLanguage)) + var transcriptPath = new FileInfo($"{srcFile.FullName}.dubbed.{request.TargetLanguage}.srt"); { - await fs.WriteAsync(chunk); + var transcriptFile = await ElevenLabsClient.DubbingEndpoint.GetTranscriptForDubAsync(response.DubbingId, request.TargetLanguage, "srt"); + await File.WriteAllTextAsync(transcriptPath.FullName, transcriptFile); } + Assert.IsTrue(transcriptPath.Exists); + Assert.IsTrue(transcriptPath.Length > 0); } - Assert.IsTrue(dubbedPath.Exists); - Assert.IsTrue(dubbedPath.Length > 0); - FileInfo transcriptPath = new($"{srcFile.FullName}.dubbed.{request.TargetLanguage}.srt"); + [Test] + public async Task Test_02_Dubbing_Url() { - string transcriptFile = await ElevenLabsClient.DubbingEndpoint.GetTranscriptForDubAsync(response.DubbingId, request.TargetLanguage, "srt"); - await File.WriteAllTextAsync(transcriptPath.FullName, transcriptFile); - } - Assert.IsTrue(transcriptPath.Exists); - Assert.IsTrue(transcriptPath.Length > 0); - } - - [Test] - public async Task Test_02_Dubbing_Url() - { - Assert.NotNull(ElevenLabsClient.DubbingEndpoint); + Assert.NotNull(ElevenLabsClient.DubbingEndpoint); - Uri uri = new("https://youtu.be/Zo5-rhYOlNk"); - DubbingRequest request = new() - { - SourceUrl = uri.AbsoluteUri, - SourceLanguage = "en", - TargetLanguage = "ja", - NumSpeakers = 1, - Watermark = true, - }; - - DubbingResponse response = await ElevenLabsClient.DubbingEndpoint.StartDubbingAsync(request); - Assert.IsFalse(string.IsNullOrEmpty(response.DubbingId)); - Assert.IsTrue(response.ExpectedDurationSeconds > 0); - Console.WriteLine($"Expected Duration: {response.ExpectedDurationSeconds:0.00} seconds"); + var uri = new Uri("https://youtu.be/Zo5-rhYOlNk"); + var request = new DubbingRequest + { + SourceUrl = uri.AbsoluteUri, + SourceLanguage = "en", + TargetLanguage = "ja", + NumSpeakers = 1, + Watermark = true, + }; - Assert.IsTrue(await ElevenLabsClient.DubbingEndpoint.WaitForDubbingCompletionAsync(response.DubbingId, progress: new Progress(msg => Console.WriteLine(msg)))); + var response = await ElevenLabsClient.DubbingEndpoint.StartDubbingAsync(request); + Assert.IsFalse(string.IsNullOrEmpty(response.DubbingId)); + Assert.IsTrue(response.ExpectedDurationSeconds > 0); + Console.WriteLine($"Expected Duration: {response.ExpectedDurationSeconds:0.00} seconds"); + Assert.IsTrue(await ElevenLabsClient.DubbingEndpoint.WaitForDubbingCompletionAsync(response.DubbingId, progress: new Progress(Console.WriteLine))); - string assetsDir = Path.GetFullPath("../../../Assets"); - FileInfo dubbedPath = new(Path.Combine(assetsDir, $"online.dubbed.{request.TargetLanguage}.mp4")); - { - await using FileStream fs = File.Open(dubbedPath.FullName, FileMode.Create); - await foreach (byte[] chunk in ElevenLabsClient.DubbingEndpoint.GetDubbedFileAsync(response.DubbingId, request.TargetLanguage)) + var assetsDir = Path.GetFullPath("../../../Assets"); + var dubbedPath = new FileInfo(Path.Combine(assetsDir, $"online.dubbed.{request.TargetLanguage}.mp4")); { - await fs.WriteAsync(chunk); + await using var fs = File.Open(dubbedPath.FullName, FileMode.Create); + await foreach (var chunk in ElevenLabsClient.DubbingEndpoint.GetDubbedFileAsync(response.DubbingId, request.TargetLanguage)) + { + await fs.WriteAsync(chunk); + } } - } - Assert.IsTrue(dubbedPath.Exists); - Assert.IsTrue(dubbedPath.Length > 0); + Assert.IsTrue(dubbedPath.Exists); + Assert.IsTrue(dubbedPath.Length > 0); - FileInfo transcriptPath = new(Path.Combine(assetsDir, $"online.dubbed.{request.TargetLanguage}.srt")); - { - string transcriptFile = await ElevenLabsClient.DubbingEndpoint.GetTranscriptForDubAsync(response.DubbingId, request.TargetLanguage, "srt"); - await File.WriteAllTextAsync(transcriptPath.FullName, transcriptFile); + var transcriptPath = new FileInfo(Path.Combine(assetsDir, $"online.dubbed.{request.TargetLanguage}.srt")); + { + var transcriptFile = await ElevenLabsClient.DubbingEndpoint.GetTranscriptForDubAsync(response.DubbingId, request.TargetLanguage, "srt"); + await File.WriteAllTextAsync(transcriptPath.FullName, transcriptFile); + } + Assert.IsTrue(transcriptPath.Exists); + Assert.IsTrue(transcriptPath.Length > 0); } - Assert.IsTrue(transcriptPath.Exists); - Assert.IsTrue(transcriptPath.Length > 0); } } diff --git a/ElevenLabs-DotNet/Dubbing/DubbingEndpoint.cs b/ElevenLabs-DotNet/Dubbing/DubbingEndpoint.cs index b7b8a32..d8ead3e 100644 --- a/ElevenLabs-DotNet/Dubbing/DubbingEndpoint.cs +++ b/ElevenLabs-DotNet/Dubbing/DubbingEndpoint.cs @@ -1,4 +1,4 @@ -namespace ElevenLabs.Dubbing; +// Licensed under the MIT License. See LICENSE in the project root for license information. using ElevenLabs.Extensions; using System; @@ -12,249 +12,257 @@ using System.Threading; using System.Threading.Tasks; -/// -/// Access to dubbing an audio or video file into a given language. -/// -public sealed class DubbingEndpoint(ElevenLabsClient client) : ElevenLabsBaseEndPoint(client) +namespace ElevenLabs.Dubbing { - private const string DubbingId = "dubbing_id"; - private const string ExpectedDurationSecs = "expected_duration_sec"; - - /// - /// Gets or sets the maximum number of retry attempts to wait for the dubbing completion status. - /// - public int DefaultMaxRetries { get; set; } = 30; - /// - /// Gets or sets the timeout interval for waiting between dubbing status checks. + /// Access to dubbing an audio or video file into a given language. /// - public TimeSpan DefaultTimeoutInterval { get; set; } = TimeSpan.FromSeconds(10); + public sealed class DubbingEndpoint(ElevenLabsClient client) : ElevenLabsBaseEndPoint(client) + { + private const string DubbingId = "dubbing_id"; + private const string ExpectedDurationSecs = "expected_duration_sec"; - protected override string Root => "dubbing"; + /// + /// Gets or sets the maximum number of retry attempts to wait for the dubbing completion status. + /// + public int DefaultMaxRetries { get; set; } = 30; - /// - /// Initiates a dubbing operation asynchronously based on the provided . - /// - /// The containing dubbing configuration and files. - /// A to cancel the operation. - /// - /// A task representing the asynchronous dubbing operation. The task completes with the dubbing ID and expected duration - /// in seconds if the operation succeeds. - /// - /// Thrown when is . - public async Task StartDubbingAsync(DubbingRequest request, CancellationToken cancellationToken = default) - { - ArgumentNullException.ThrowIfNull(request); + /// + /// Gets or sets the timeout interval for waiting between dubbing status checks. + /// + public TimeSpan DefaultTimeoutInterval { get; set; } = TimeSpan.FromSeconds(10); - using MultipartFormDataContent content = []; + protected override string Root => "dubbing"; - if (!string.IsNullOrEmpty(request.Mode)) + /// + /// Initiates a dubbing operation asynchronously based on the provided . + /// + /// The containing dubbing configuration and files. + /// A to cancel the operation. + /// + /// A task representing the asynchronous dubbing operation. The task completes with the dubbing ID and expected duration + /// in seconds if the operation succeeds. + /// + /// Thrown when is . + public async Task StartDubbingAsync(DubbingRequest request, CancellationToken cancellationToken = default) { - content.Add(new StringContent(request.Mode), "mode"); - } + ArgumentNullException.ThrowIfNull(request); - if (request.File.HasValue) - { - AppendFileToForm(content, "file", new(request.File.Value.FilePath), MediaTypeHeaderValue.Parse(request.File.Value.MediaType)); - } + using MultipartFormDataContent content = []; - if (!string.IsNullOrEmpty(request.CsvFilePath)) - { - AppendFileToForm(content, "csv_file", new(request.CsvFilePath), new("text/csv")); - } + if (!string.IsNullOrEmpty(request.Mode)) + { + content.Add(new StringContent(request.Mode), "mode"); + } - if (!string.IsNullOrEmpty(request.ForegroundAudioFilePath)) - { - AppendFileToForm(content, "foreground_audio_file", new(request.ForegroundAudioFilePath), new("audio/mpeg")); - } + if (!string.IsNullOrEmpty(request.FilePath)) + { + AppendFileToForm(content, "file", new(request.FilePath), MediaTypeHeaderValue.Parse(request.MediaType)); + } - if (!string.IsNullOrEmpty(request.BackgroundAudioFilePath)) - { - AppendFileToForm(content, "background_audio_file", new(request.BackgroundAudioFilePath), new("audio/mpeg")); - } + if (!string.IsNullOrEmpty(request.CsvFilePath)) + { + AppendFileToForm(content, "csv_file", new(request.CsvFilePath), new("text/csv")); + } - if (!string.IsNullOrEmpty(request.Name)) - { - content.Add(new StringContent(request.Name), "name"); - } + if (!string.IsNullOrEmpty(request.ForegroundAudioFilePath)) + { + AppendFileToForm(content, "foreground_audio_file", new(request.ForegroundAudioFilePath), new("audio/mpeg")); + } - if (!string.IsNullOrEmpty(request.SourceUrl)) - { - content.Add(new StringContent(request.SourceUrl), "source_url"); - } + if (!string.IsNullOrEmpty(request.BackgroundAudioFilePath)) + { + AppendFileToForm(content, "background_audio_file", new(request.BackgroundAudioFilePath), new("audio/mpeg")); + } - if (!string.IsNullOrEmpty(request.SourceLanguage)) - { - content.Add(new StringContent(request.SourceLanguage), "source_lang"); - } + if (!string.IsNullOrEmpty(request.Name)) + { + content.Add(new StringContent(request.Name), "name"); + } - if (!string.IsNullOrEmpty(request.TargetLanguage)) - { - content.Add(new StringContent(request.TargetLanguage), "target_lang"); - } + if (!string.IsNullOrEmpty(request.SourceUrl)) + { + content.Add(new StringContent(request.SourceUrl), "source_url"); + } - if (request.NumSpeakers.HasValue) - { - content.Add(new StringContent(request.NumSpeakers.Value.ToString(CultureInfo.InvariantCulture)), "num_speakers"); - } + if (!string.IsNullOrEmpty(request.SourceLanguage)) + { + content.Add(new StringContent(request.SourceLanguage), "source_lang"); + } - if (request.Watermark.HasValue) - { - content.Add(new StringContent(request.Watermark.Value.ToString()), "watermark"); - } + if (!string.IsNullOrEmpty(request.TargetLanguage)) + { + content.Add(new StringContent(request.TargetLanguage), "target_lang"); + } - if (request.StartTime.HasValue) - { - content.Add(new StringContent(request.StartTime.Value.ToString(CultureInfo.InvariantCulture)), "start_time"); - } + if (request.NumSpeakers.HasValue) + { + content.Add(new StringContent(request.NumSpeakers.Value.ToString(CultureInfo.InvariantCulture)), "num_speakers"); + } - if (request.EndTime.HasValue) - { - content.Add(new StringContent(request.EndTime.Value.ToString(CultureInfo.InvariantCulture)), "end_time"); - } + if (request.Watermark.HasValue) + { + content.Add(new StringContent(request.Watermark.Value.ToString()), "watermark"); + } - if (request.HighestResolution.HasValue) - { - content.Add(new StringContent(request.HighestResolution.Value.ToString()), "highest_resolution"); - } + if (request.StartTime.HasValue) + { + content.Add(new StringContent(request.StartTime.Value.ToString(CultureInfo.InvariantCulture)), "start_time"); + } - if (request.DubbingStudio.HasValue) - { - content.Add(new StringContent(request.DubbingStudio.Value.ToString()), "dubbing_studio"); - } + if (request.EndTime.HasValue) + { + content.Add(new StringContent(request.EndTime.Value.ToString(CultureInfo.InvariantCulture)), "end_time"); + } - using HttpResponseMessage response = await client.Client.PostAsync(GetUrl(), content, cancellationToken).ConfigureAwait(false); - await response.CheckResponseAsync(cancellationToken).ConfigureAwait(false); + if (request.HighestResolution.HasValue) + { + content.Add(new StringContent(request.HighestResolution.Value.ToString()), "highest_resolution"); + } - using Stream responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - return await JsonSerializer.DeserializeAsync(responseStream, cancellationToken: cancellationToken).ConfigureAwait(false); - } + if (request.DubbingStudio.HasValue) + { + content.Add(new StringContent(request.DubbingStudio.Value.ToString()), "dubbing_studio"); + } - private static void AppendFileToForm(MultipartFormDataContent content, string name, FileInfo fileInfo, MediaTypeHeaderValue mediaType) - { - if (!fileInfo.Exists) - { - throw new FileNotFoundException($"File not found: {fileInfo.FullName}"); + using var response = await client.Client.PostAsync(GetUrl(), content, cancellationToken).ConfigureAwait(false); + await response.CheckResponseAsync(EnableDebug, cancellationToken).ConfigureAwait(false); + await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); + return await JsonSerializer.DeserializeAsync(responseStream, cancellationToken: cancellationToken).ConfigureAwait(false); } - FileStream fileStream = fileInfo.OpenRead(); - StreamContent fileContent = new(fileStream); - fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") + private static void AppendFileToForm(MultipartFormDataContent content, string name, FileInfo fileInfo, MediaTypeHeaderValue mediaType) { - Name = name, - FileName = fileInfo.Name, - }; - fileContent.Headers.ContentType = mediaType; - content.Add(fileContent); - } - - /// - /// Waits asynchronously for a dubbing operation to complete. This method polls the dubbing status at regular intervals, - /// reporting progress updates if a progress reporter is provided. - /// - /// The ID of the dubbing project. - /// The maximum number of retries for checking the dubbing completion status. If not specified, a default value is used. - /// The time to wait between each status check. If not specified, a default interval is used. - /// An optional implementation to report progress updates, such as status messages and errors. - /// A to cancel the waiting operation. - /// - /// A task that represents the asynchronous wait operation. The task result is if the dubbing completes successfully within the specified number of retries and timeout interval; otherwise, . - /// - /// - /// This method checks the dubbing status by sending requests to the dubbing service at intervals defined by the parameter. - /// If the dubbing status is "dubbed", the method returns . If the dubbing fails or the specified number of is reached without successful completion, the method returns . - /// - public async Task WaitForDubbingCompletionAsync(string dubbingId, int? maxRetries = null, TimeSpan? timeoutInterval = null, IProgress progress = null, CancellationToken cancellationToken = default) - { - maxRetries ??= DefaultMaxRetries; - timeoutInterval ??= DefaultTimeoutInterval; - for (int i = 0; i < maxRetries; i++) - { - DubbingProjectMetadata metadata = await GetDubbingProjectMetadataAsync(dubbingId, cancellationToken).ConfigureAwait(false); - if (metadata.Status.Equals("dubbed", StringComparison.Ordinal)) + if (!fileInfo.Exists) { - return true; + throw new FileNotFoundException($"File not found: {fileInfo.FullName}"); } - else if (metadata.Status.Equals("dubbing", StringComparison.Ordinal)) + + var fileStream = fileInfo.OpenRead(); + var fileContent = new StreamContent(fileStream); + + fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") { - progress?.Report($"Dubbing for {dubbingId} in progress... Will check status again in {timeoutInterval.Value.TotalSeconds} seconds."); - await Task.Delay(timeoutInterval.Value, cancellationToken).ConfigureAwait(false); - } - else + Name = name, + FileName = fileInfo.Name, + }; + fileContent.Headers.ContentType = mediaType; + content.Add(fileContent); + } + + /// + /// Waits asynchronously for a dubbing operation to complete. This method polls the dubbing status at regular intervals, + /// reporting progress updates if a progress reporter is provided. + /// + /// The ID of the dubbing project. + /// The maximum number of retries for checking the dubbing completion status. If not specified, a default value is used. + /// The time to wait between each status check. If not specified, a default interval is used. + /// An optional implementation to report progress updates, such as status messages and errors. + /// A to cancel the waiting operation. + /// + /// A task that represents the asynchronous wait operation. The task result is if the dubbing completes successfully within the specified number of retries and timeout interval; otherwise, . + /// + /// + /// This method checks the dubbing status by sending requests to the dubbing service at intervals defined by the parameter. + /// If the dubbing status is "dubbed", the method returns . If the dubbing fails or the specified number of is reached without successful completion, the method returns . + /// + public async Task WaitForDubbingCompletionAsync(string dubbingId, int? maxRetries = null, TimeSpan? timeoutInterval = null, IProgress progress = null, CancellationToken cancellationToken = default) + { + maxRetries ??= DefaultMaxRetries; + timeoutInterval ??= DefaultTimeoutInterval; + + for (var i = 0; i < maxRetries; i++) { - progress?.Report($"Dubbing for {dubbingId} failed: {metadata.Error}"); - return false; + var metadata = await GetDubbingProjectMetadataAsync(dubbingId, cancellationToken).ConfigureAwait(false); + + if (metadata.Status.Equals("dubbed", StringComparison.Ordinal)) + { + return true; + } + + if (metadata.Status.Equals("dubbing", StringComparison.Ordinal)) + { + progress?.Report($"Dubbing for {dubbingId} in progress... Will check status again in {timeoutInterval.Value.TotalSeconds} seconds."); + await Task.Delay(timeoutInterval.Value, cancellationToken).ConfigureAwait(false); + } + else + { + progress?.Report($"Dubbing for {dubbingId} failed: {metadata.Error}"); + return false; + } } + + progress?.Report($"Dubbing for {dubbingId} timed out or exceeded expected duration."); + return false; } - progress?.Report($"Dubbing for {dubbingId} timed out or exceeded expected duration."); - return false; - } - private async Task GetDubbingProjectMetadataAsync(string dubbingId, CancellationToken cancellationToken = default) - { - string url = $"{GetUrl()}/{dubbingId}"; - HttpResponseMessage response = await client.Client.GetAsync(url, cancellationToken).ConfigureAwait(false); - await response.CheckResponseAsync(cancellationToken).ConfigureAwait(false); - string responseBody = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); - return JsonSerializer.Deserialize(responseBody) - ?? throw new JsonException("Could not deserialize the dubbing project metadata!"); - } + public async Task GetDubbingProjectMetadataAsync(string dubbingId, CancellationToken cancellationToken = default) + { + var response = await client.Client.GetAsync(GetUrl($"/{dubbingId}"), cancellationToken).ConfigureAwait(false); + await response.CheckResponseAsync(EnableDebug, cancellationToken).ConfigureAwait(false); + var responseBody = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return JsonSerializer.Deserialize(responseBody) + ?? throw new JsonException("Could not deserialize the dubbing project metadata!"); + } - /// - /// Retrieves the dubbed file asynchronously as a sequence of byte arrays. - /// - /// The ID of the dubbing project. - /// The language code of the dubbed content. - /// The size of the buffer used to read data from the response stream. Default is 8192 bytes. - /// A to cancel the operation. - /// - /// An asynchronous enumerable of byte arrays representing the dubbed file content. Each byte array - /// contains a chunk of the dubbed file data. - /// - /// - /// This method streams the dubbed file content in chunks to optimize memory usage and improve performance. - /// Adjust the parameter based on your specific requirements to achieve optimal performance. - /// - public async IAsyncEnumerable GetDubbedFileAsync(string dubbingId, string languageCode, int bufferSize = 8192, [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - string url = $"{GetUrl()}/{dubbingId}/audio/{languageCode}"; - using HttpResponseMessage response = await client.Client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - await response.CheckResponseAsync(cancellationToken).ConfigureAwait(false); - - using Stream responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - byte[] buffer = new byte[bufferSize]; - int bytesRead; - while ((bytesRead = await responseStream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0) + /// + /// Retrieves the dubbed file asynchronously as a sequence of byte arrays. + /// + /// The ID of the dubbing project. + /// The language code of the dubbed content. + /// The size of the buffer used to read data from the response stream. Default is 8192 bytes. + /// A to cancel the operation. + /// + /// An asynchronous enumerable of byte arrays representing the dubbed file content. Each byte array + /// contains a chunk of the dubbed file data. + /// + /// + /// This method streams the dubbed file content in chunks to optimize memory usage and improve performance. + /// Adjust the parameter based on your specific requirements to achieve optimal performance. + /// + public async IAsyncEnumerable GetDubbedFileAsync(string dubbingId, string languageCode, int bufferSize = 8192, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - byte[] chunk = new byte[bytesRead]; - Array.Copy(buffer, chunk, bytesRead); - yield return chunk; + using var response = await client.Client.GetAsync(GetUrl($"/{dubbingId}/audio/{languageCode}"), HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + await response.CheckResponseAsync(EnableDebug, cancellationToken).ConfigureAwait(false); + + await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); + var buffer = new byte[bufferSize]; + int bytesRead; + + while ((bytesRead = await responseStream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) > 0) + { + var chunk = new byte[bytesRead]; + Array.Copy(buffer, chunk, bytesRead); + yield return chunk; + } } - } - /// - /// Retrieves the transcript for the dub asynchronously in the specified format (SRT or WebVTT). - /// - /// The ID of the dubbing project. - /// The language code of the transcript. - /// Optional. The format type of the transcript file, either 'srt' or 'webvtt'. - /// A to cancel the operation. - /// - /// A task representing the asynchronous operation. The task completes with the transcript content - /// as a string in the specified format. - /// - /// - /// If is not specified, the method retrieves the transcript in its default format. - /// - public async Task GetTranscriptForDubAsync(string dubbingId, string languageCode, string formatType = null, CancellationToken cancellationToken = default) - { - string url = $"{GetUrl()}/{dubbingId}/transcript/{languageCode}"; - if (!string.IsNullOrEmpty(formatType)) + /// + /// Retrieves the transcript for the dub asynchronously in the specified format (SRT or WebVTT). + /// + /// The ID of the dubbing project. + /// The language code of the transcript. + /// Optional. The format type of the transcript file, either 'srt' or 'webvtt'. + /// A to cancel the operation. + /// + /// A task representing the asynchronous operation. The task completes with the transcript content + /// as a string in the specified format. + /// + /// + /// If is not specified, the method retrieves the transcript in its default format. + /// + public async Task GetTranscriptForDubAsync(string dubbingId, string languageCode, string formatType = null, CancellationToken cancellationToken = default) { - url += $"?format_type={formatType}"; + Dictionary @params = null; + + if (!string.IsNullOrEmpty(formatType)) + { + @params = new Dictionary { { "format_type", formatType } }; + } + + using var response = await client.Client.GetAsync(GetUrl($"/{dubbingId}/transcript/{languageCode}", @params), cancellationToken).ConfigureAwait(false); + await response.CheckResponseAsync(EnableDebug, cancellationToken).ConfigureAwait(false); + return await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); } - using HttpResponseMessage response = await client.Client.GetAsync(url, cancellationToken).ConfigureAwait(false); - await response.CheckResponseAsync(cancellationToken).ConfigureAwait(false); - return await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); } } diff --git a/ElevenLabs-DotNet/Dubbing/DubbingProjectMetadata.cs b/ElevenLabs-DotNet/Dubbing/DubbingProjectMetadata.cs index ba70bba..0097681 100644 --- a/ElevenLabs-DotNet/Dubbing/DubbingProjectMetadata.cs +++ b/ElevenLabs-DotNet/Dubbing/DubbingProjectMetadata.cs @@ -1,22 +1,30 @@ -namespace ElevenLabs.Dubbing; +// Licensed under the MIT License. See LICENSE in the project root for license information. using System.Collections.Generic; using System.Text.Json.Serialization; -public sealed class DubbingProjectMetadata +namespace ElevenLabs.Dubbing { - [JsonPropertyName("dubbing_id")] - public string DubbingId { get; set; } + public sealed class DubbingProjectMetadata + { + [JsonInclude] + [JsonPropertyName("dubbing_id")] + public string DubbingId { get; private set; } - [JsonPropertyName("name")] - public string Name { get; set; } + [JsonInclude] + [JsonPropertyName("name")] + public string Name { get; private set; } - [JsonPropertyName("status")] - public string Status { get; set; } + [JsonInclude] + [JsonPropertyName("status")] + public string Status { get; private set; } - [JsonPropertyName("target_languages")] - public List TargetLanguages { get; set; } + [JsonInclude] + [JsonPropertyName("target_languages")] + public List TargetLanguages { get; private set; } - [JsonPropertyName("error")] - public string Error { get; set; } + [JsonInclude] + [JsonPropertyName("error")] + public string Error { get; private set; } + } } diff --git a/ElevenLabs-DotNet/Dubbing/DubbingRequest.cs b/ElevenLabs-DotNet/Dubbing/DubbingRequest.cs index 1573acc..e2951d5 100644 --- a/ElevenLabs-DotNet/Dubbing/DubbingRequest.cs +++ b/ElevenLabs-DotNet/Dubbing/DubbingRequest.cs @@ -1,85 +1,93 @@ -namespace ElevenLabs.Dubbing; +// Licensed under the MIT License. See LICENSE in the project root for license information. -public sealed class DubbingRequest +namespace ElevenLabs.Dubbing { - /// - /// automatic or manual. Manual mode is only supported when creating a dubbing studio project - /// - public string Mode { get; init; } = "automatic"; - - /// - /// A video (MediaType: "video/mp4") or audio (MediaType: "audio/mpeg") file intended for voice cloning - /// - public (string FilePath, string MediaType)? File { get; init; } - - /// - /// CSV file containing transcription/translation metadata - /// - public string CsvFilePath { get; init; } - - /// - /// For use only with csv input - /// - public string ForegroundAudioFilePath { get; init; } - - /// - /// For use only with csv input - /// - public string BackgroundAudioFilePath { get; init; } - - /// - /// Name of the dubbing project. - /// - public string Name { get; init; } - - /// - /// URL of the source video/audio file. - /// - public string SourceUrl { get; init; } - - /// - /// Source language. - /// - /// - /// A list of supported languages can be found at: https://elevenlabs.io/docs/api-reference/how-to-dub-a-video#list-of-supported-languages-for-dubbing - /// - public string SourceLanguage { get; init; } - - /// - /// The Target language to dub the content into. Can be none if dubbing studio editor is enabled and running manual mode - /// - /// - /// A list of supported languages can be found at: https://elevenlabs.io/docs/api-reference/how-to-dub-a-video#list-of-supported-languages-for-dubbing - /// - public string TargetLanguage { get; init; } - - /// - /// Number of speakers to use for the dubbing. Set to 0 to automatically detect the number of speakers - /// - public int? NumSpeakers { get; init; } - - /// - /// Whether to apply watermark to the output video. - /// - public bool? Watermark { get; init; } - - /// - /// Start time of the source video/audio file. - /// - public int? StartTime { get; init; } - - /// - /// End time of the source video/audio file. - /// - public int? EndTime { get; init; } - - /// - /// Whether to use the highest resolution available. - /// - public bool? HighestResolution { get; init; } - - /// - /// Whether to prepare dub for edits in dubbing studio. - /// - public bool? DubbingStudio { get; init; } + public sealed class DubbingRequest + { + /// + /// automatic or manual. Manual mode is only supported when creating a dubbing studio project + /// + public string Mode { get; init; } = "automatic"; + + /// + /// A video or audio file path intended for voice cloning + /// + public string FilePath { get; init; } + + /// + /// A valid mime type (e.g "video/mp4" or "audio/mpeg") + /// + public string MediaType { get; init; } + + /// + /// CSV file containing transcription/translation metadata + /// + public string CsvFilePath { get; init; } + + /// + /// For use only with csv input + /// + public string ForegroundAudioFilePath { get; init; } + + /// + /// For use only with csv input + /// + public string BackgroundAudioFilePath { get; init; } + + /// + /// Name of the dubbing project. + /// + public string Name { get; init; } + + /// + /// URL of the source video/audio file. + /// + public string SourceUrl { get; init; } + + /// + /// Source language. + /// + /// + /// A list of supported languages can be found at: https://elevenlabs.io/docs/api-reference/how-to-dub-a-video#list-of-supported-languages-for-dubbing + /// + public string SourceLanguage { get; init; } + + /// + /// The Target language to dub the content into. Can be none if dubbing studio editor is enabled and running manual mode + /// + /// + /// A list of supported languages can be found at: https://elevenlabs.io/docs/api-reference/how-to-dub-a-video#list-of-supported-languages-for-dubbing + /// + public string TargetLanguage { get; init; } + + /// + /// Number of speakers to use for the dubbing. Set to 0 to automatically detect the number of speakers + /// + public int? NumSpeakers { get; init; } + + /// + /// Whether to apply watermark to the output video. + /// + public bool? Watermark { get; init; } + + /// + /// Start time of the source video/audio file. + /// + public int? StartTime { get; init; } + + /// + /// End time of the source video/audio file. + /// + public int? EndTime { get; init; } + + /// + /// Whether to use the highest resolution available. + /// + public bool? HighestResolution { get; init; } + + /// + /// Whether to prepare dub for edits in dubbing studio. + /// + public bool? DubbingStudio { get; init; } + } } diff --git a/ElevenLabs-DotNet/Dubbing/DubbingResponse.cs b/ElevenLabs-DotNet/Dubbing/DubbingResponse.cs index b392460..1336be4 100644 --- a/ElevenLabs-DotNet/Dubbing/DubbingResponse.cs +++ b/ElevenLabs-DotNet/Dubbing/DubbingResponse.cs @@ -1,12 +1,17 @@ -namespace ElevenLabs.Dubbing; +// Licensed under the MIT License. See LICENSE in the project root for license information. using System.Text.Json.Serialization; -public sealed class DubbingResponse +namespace ElevenLabs.Dubbing { - [JsonPropertyName("dubbing_id")] - public string DubbingId { get; set; } + public sealed class DubbingResponse + { + [JsonInclude] + [JsonPropertyName("dubbing_id")] + public string DubbingId { get; private set; } - [JsonPropertyName("expected_duration_sec")] - public float ExpectedDurationSeconds { get; set; } + [JsonInclude] + [JsonPropertyName("expected_duration_sec")] + public float ExpectedDurationSeconds { get; private set; } + } } \ No newline at end of file