Compare commits
2
Commits
70c2d5a649
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20f1c7fb2f | ||
|
|
e6e5369b95 |
@@ -19,5 +19,10 @@ namespace xAiApi.Configurations
|
|||||||
/// OCR Configuration ...
|
/// OCR Configuration ...
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public XAiOCRDescriptor OCR { get; set; }
|
public XAiOCRDescriptor OCR { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transcript Configuration ...
|
||||||
|
/// </summary>
|
||||||
|
public XAiTransciptDescriptor Transcript { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace xAiApi.Models
|
||||||
|
{
|
||||||
|
public class XAiTransciptDescriptor
|
||||||
|
{
|
||||||
|
public string Language { get; set; }
|
||||||
|
public string ModelsPath { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"launchUrl": "weatherforecast",
|
"launchUrl": "weatherforecast",
|
||||||
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
"applicationUrl": "https://localhost:6001;http://localhost:6000",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,15 @@ using System;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Whisper.net;
|
using Whisper.net;
|
||||||
|
using NAudio.Wave;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using xAiModels.Models;
|
using xAiModels.Models;
|
||||||
|
using xCommons.Extensions;
|
||||||
|
using xAiApi.Configurations;
|
||||||
|
using xExceptions.Constants;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using NAudio.MediaFoundation;
|
||||||
using xAiApi.Interfaces.Extractors;
|
using xAiApi.Interfaces.Extractors;
|
||||||
using DocumentFormat.OpenXml.EMMA;
|
|
||||||
|
|
||||||
namespace xAiApi.Providers.Extractors
|
namespace xAiApi.Providers.Extractors
|
||||||
{
|
{
|
||||||
@@ -24,29 +28,30 @@ namespace xAiApi.Providers.Extractors
|
|||||||
"audio/mp3",
|
"audio/mp3",
|
||||||
"audio/wav",
|
"audio/wav",
|
||||||
"audio/wave",
|
"audio/wave",
|
||||||
|
"audio/x-wav",
|
||||||
"audio/ogg",
|
"audio/ogg",
|
||||||
"audio/m4a",
|
"audio/m4a",
|
||||||
"audio/mp4",
|
"audio/mp4",
|
||||||
|
"audio/aac",
|
||||||
|
"audio/x-m4a",
|
||||||
"audio/webm"
|
"audio/webm"
|
||||||
];
|
];
|
||||||
|
|
||||||
private readonly string language;
|
private readonly XAiApiConfiguration configuration;
|
||||||
private readonly string whisperModelPath;
|
|
||||||
|
|
||||||
public XAudioFileContentExtractor() : this(
|
|
||||||
language: "fa",
|
|
||||||
whisperModelPath: "Models/ggml-base.bin"
|
|
||||||
)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
public XAudioFileContentExtractor(
|
public XAudioFileContentExtractor(
|
||||||
string whisperModelPath = "Models/ggml-base.bin",
|
XAiApiConfiguration configuration
|
||||||
string language = "fa"
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
this.language = language;
|
if (configuration.Transcript.IsNullOrDefault() ||
|
||||||
this.whisperModelPath = whisperModelPath;
|
configuration.Transcript.ModelsPath.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
XException.InvalidConfiguration.Throw();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
this.configuration = configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -113,14 +118,15 @@ namespace xAiApi.Providers.Extractors
|
|||||||
|
|
||||||
//
|
//
|
||||||
using var wavStream = await ConvertToWavAsync(
|
using var wavStream = await ConvertToWavAsync(
|
||||||
memoryStream,
|
mimeType: mimeType,
|
||||||
cancellationToken
|
inputStream: memoryStream,
|
||||||
|
cancellationToken: cancellationToken
|
||||||
);
|
);
|
||||||
|
|
||||||
//
|
//
|
||||||
using var factory = WhisperFactory.FromPath(whisperModelPath);
|
using var factory = WhisperFactory.FromPath(configuration.Transcript.ModelsPath);
|
||||||
using var processor = factory.CreateBuilder()
|
using var processor = factory.CreateBuilder()
|
||||||
.WithLanguage(language)
|
.WithLanguage(configuration.Transcript.Language)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -143,17 +149,186 @@ namespace xAiApi.Providers.Extractors
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
#region Private ...
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Audio Format Converting ...
|
/// Audio Format Converting to WAV 16kHz Mono 16-bit PCM ...
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task<Stream> ConvertToWavAsync(
|
private async Task<Stream> ConvertToWavAsync(
|
||||||
Stream inputStream,
|
Stream inputStream,
|
||||||
CancellationToken cancellationToken
|
string mimeType,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// TODO: Complete this ...
|
var result = await Task.Run(() =>
|
||||||
return await Task.FromResult(inputStream);
|
{
|
||||||
|
//
|
||||||
|
var memoryStream = new MemoryStream();
|
||||||
|
inputStream.CopyTo(memoryStream);
|
||||||
|
memoryStream.Position = 0;
|
||||||
|
|
||||||
|
//
|
||||||
|
var targetFormat = new WaveFormat(16000, 16, 1);
|
||||||
|
string tempFilePath = null;
|
||||||
|
|
||||||
|
//
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//
|
||||||
|
WaveStream reader = GetAudioReader(memoryStream, mimeType, ref tempFilePath);
|
||||||
|
if (reader == null)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
XException.NotAllowed.Throw(
|
||||||
|
$"Unsupported audio format: {mimeType}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
var needsConversion =
|
||||||
|
reader.WaveFormat.SampleRate != 16000 ||
|
||||||
|
reader.WaveFormat.Channels != 1 ||
|
||||||
|
reader.WaveFormat.BitsPerSample != 16 ||
|
||||||
|
reader.WaveFormat.Encoding != WaveFormatEncoding.Pcm;
|
||||||
|
var outputStream = new MemoryStream();
|
||||||
|
if (!needsConversion)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
reader.CopyTo(outputStream);
|
||||||
|
reader.Dispose();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//
|
||||||
|
MediaFoundationApi.Startup();
|
||||||
|
using var resampler = new MediaFoundationResampler(
|
||||||
|
reader,
|
||||||
|
targetFormat
|
||||||
|
);
|
||||||
|
resampler.ResamplerQuality = 60;
|
||||||
|
|
||||||
|
//
|
||||||
|
WaveFileWriter.WriteWavFileToStream(outputStream, resampler);
|
||||||
|
reader.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
outputStream.Position = 0;
|
||||||
|
return outputStream;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
//
|
||||||
|
if (!string.IsNullOrEmpty(tempFilePath) &&
|
||||||
|
File.Exists(tempFilePath))
|
||||||
|
{
|
||||||
|
//
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Delete(tempFilePath);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
memoryStream.Dispose();
|
||||||
|
}
|
||||||
|
}, cancellationToken);
|
||||||
|
|
||||||
|
//
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get appropriate WaveStream reader based on MIME type ...
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">Input stream (MemoryStream)</param>
|
||||||
|
/// <param name="mimeType">MIME type of audio file</param>
|
||||||
|
/// <param name="tempFilePath">Path to temp file (if created)</param>
|
||||||
|
/// <returns>WaveStream reader or null if unsupported</returns>
|
||||||
|
private WaveStream GetAudioReader(
|
||||||
|
MemoryStream stream,
|
||||||
|
string mimeType,
|
||||||
|
ref string tempFilePath
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//
|
||||||
|
var normalizedMime = mimeType?.ToLowerInvariant() ?? string.Empty;
|
||||||
|
switch (normalizedMime)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
case "audio/wav":
|
||||||
|
case "audio/wave":
|
||||||
|
case "audio/x-wav":
|
||||||
|
return new WaveFileReader(stream);
|
||||||
|
|
||||||
|
//
|
||||||
|
case "audio/mpeg":
|
||||||
|
case "audio/mp3":
|
||||||
|
return new Mp3FileReader(stream);
|
||||||
|
|
||||||
|
//
|
||||||
|
case "audio/m4a":
|
||||||
|
case "audio/mp4":
|
||||||
|
case "audio/aac":
|
||||||
|
case "audio/ogg":
|
||||||
|
case "audio/webm":
|
||||||
|
case "audio/x-m4a":
|
||||||
|
//
|
||||||
|
var extension = normalizedMime switch
|
||||||
|
{
|
||||||
|
"audio/m4a" or "audio/mp4" or "audio/aac" or "audio/x-m4a" => ".m4a",
|
||||||
|
"audio/ogg" => ".ogg",
|
||||||
|
"audio/webm" => ".webm",
|
||||||
|
_ => ".tmp"
|
||||||
|
};
|
||||||
|
tempFilePath = Path.Combine(
|
||||||
|
Path.GetTempPath(),
|
||||||
|
$"audio_{Guid.NewGuid()}{extension}"
|
||||||
|
);
|
||||||
|
|
||||||
|
//
|
||||||
|
stream.Position = 0;
|
||||||
|
using (var fileStream = File.Create(tempFilePath))
|
||||||
|
{
|
||||||
|
stream.CopyTo(fileStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
MediaFoundationApi.Startup();
|
||||||
|
return new MediaFoundationReader(tempFilePath);
|
||||||
|
|
||||||
|
default:
|
||||||
|
//
|
||||||
|
tempFilePath = Path.Combine(
|
||||||
|
Path.GetTempPath(),
|
||||||
|
$"audio_{Guid.NewGuid()}.tmp"
|
||||||
|
);
|
||||||
|
stream.Position = 0;
|
||||||
|
using (var fileStream = File.Create(tempFilePath))
|
||||||
|
{
|
||||||
|
stream.CopyTo(fileStream);
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
MediaFoundationApi.Startup();
|
||||||
|
return new MediaFoundationReader(tempFilePath);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,6 +84,10 @@
|
|||||||
"EngineMode": "LstmOnly",
|
"EngineMode": "LstmOnly",
|
||||||
"EnableOcrFallback": true,
|
"EnableOcrFallback": true,
|
||||||
"DefaultLanguage": "fas+eng"
|
"DefaultLanguage": "fas+eng"
|
||||||
|
},
|
||||||
|
"Transcript": {
|
||||||
|
"Language": "fa",
|
||||||
|
"ModelsPath": "Models/ggml-base.bin"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
|
|||||||
+12
-19
@@ -27,13 +27,6 @@
|
|||||||
<None Include="../../Resources/Images/favicon.png" Link="icon.png" Pack="true" PackagePath="\icon.png" />
|
<None Include="../../Resources/Images/favicon.png" Link="icon.png" Pack="true" PackagePath="\icon.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Native Libraries -->
|
|
||||||
<!-- <ItemGroup>
|
|
||||||
<None Include="runtimes/linux-x64/native/libpdfium.so">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup> -->
|
|
||||||
|
|
||||||
<!-- Local Modules -->
|
<!-- Local Modules -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -53,22 +46,22 @@
|
|||||||
|
|
||||||
<!-- Dependencies -->
|
<!-- Dependencies -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DocumentFormat.OpenXml" Version="3.5.1" />
|
|
||||||
<PackageReference Include="ExcelDataReader" Version="3.9.0" />
|
|
||||||
<PackageReference Include="ExcelDataReader.DataSet" Version="3.9.0" />
|
|
||||||
<PackageReference Include="NAudio" Version="2.4.0" />
|
<PackageReference Include="NAudio" Version="2.4.0" />
|
||||||
|
<PackageReference Include="Tesseract" Version="5.2.0" />
|
||||||
|
<PackageReference Include="Whisper.net" Version="1.9.1" />
|
||||||
<PackageReference Include="OllamaSharp" Version="5.4.25" />
|
<PackageReference Include="OllamaSharp" Version="5.4.25" />
|
||||||
<PackageReference Include="PdfiumViewer" Version="2.13.0" />
|
<PackageReference Include="PdfiumViewer" Version="2.13.0" />
|
||||||
<PackageReference Include="PdfiumViewer.Native.x86.no_v8-no_xfa" Version="2018.4.8.256" />
|
<PackageReference Include="ExcelDataReader" Version="3.9.0" />
|
||||||
<PackageReference Include="PdfiumViewer.Native.x86.v8-xfa" Version="2018.4.8.256" />
|
|
||||||
<PackageReference Include="PdfiumViewer.Native.x86_64.no_v8-no_xfa" Version="2018.4.8.256" />
|
|
||||||
<PackageReference Include="PdfiumViewer.Native.x86_64.v8-xfa" Version="2018.4.8.256" />
|
|
||||||
<PackageReference Include="Tesseract" Version="5.2.0" />
|
|
||||||
<PackageReference Include="UglyToad.PdfPig" Version="1.7.0-custom-5" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.11" />
|
|
||||||
<PackageReference Include="IdentityModel.AspNetCore.OAuth2Introspection" Version="5.0.0" />
|
|
||||||
<PackageReference Include="Whisper.net" Version="1.9.1" />
|
|
||||||
<PackageReference Include="Whisper.net.Runtime" Version="1.9.1" />
|
<PackageReference Include="Whisper.net.Runtime" Version="1.9.1" />
|
||||||
|
<PackageReference Include="DocumentFormat.OpenXml" Version="3.5.1" />
|
||||||
|
<PackageReference Include="ExcelDataReader.DataSet" Version="3.9.0" />
|
||||||
|
<PackageReference Include="UglyToad.PdfPig" Version="1.7.0-custom-5" />
|
||||||
|
<PackageReference Include="PdfiumViewer.Native.x86.v8-xfa" Version="2018.4.8.256" />
|
||||||
|
<PackageReference Include="PdfiumViewer.Native.x86_64.v8-xfa" Version="2018.4.8.256" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.11" />
|
||||||
|
<PackageReference Include="PdfiumViewer.Native.x86.no_v8-no_xfa" Version="2018.4.8.256" />
|
||||||
|
<PackageReference Include="IdentityModel.AspNetCore.OAuth2Introspection" Version="5.0.0" />
|
||||||
|
<PackageReference Include="PdfiumViewer.Native.x86_64.no_v8-no_xfa" Version="2018.4.8.256" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- For XML Documentation Support -->
|
<!-- For XML Documentation Support -->
|
||||||
|
|||||||
Reference in New Issue
Block a user