Files
xSaherelmWorkspace/Documents/Docs/Developer/16_AudioFileContentExtractor_Fix_Foundation.html
T
2026-10-04 11:42:43 +03:30

911 lines
34 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>رفع خطای MediaFoundationReader در XAudioFileContentExtractor - فن آوران ساحر علم</title>
<style>
:root {
--primary: #1e3a8a;
--secondary: #3b82f6;
--accent: #f59e0b;
--success: #10b981;
--danger: #ef4444;
--warning: #f97316;
--bg-light: #f8fafc;
--bg-code: #1e293b;
--text-dark: #0f172a;
--text-muted: #64748b;
--border: #e2e8f0;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Tahoma', 'Segoe UI', sans-serif;
background: linear-gradient(135deg, #f8fafc 0%, #e0e7ff 100%);
color: var(--text-dark);
line-height: 1.8;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
color: white;
padding: 40px;
text-align: center;
}
.header h1 { font-size: 2.1em; margin-bottom: 10px; }
.header .subtitle { font-size: 1.1em; opacity: 0.95; }
.meta-bar {
display: flex;
justify-content: space-between;
background: var(--bg-light);
padding: 15px 30px;
border-bottom: 2px solid var(--border);
flex-wrap: wrap;
gap: 15px;
}
.meta-item { display: flex; align-items: center; gap: 8px; font-size: 0.9em; color: var(--text-muted); }
.meta-item strong { color: var(--primary); }
.content { padding: 40px; }
.section {
margin-bottom: 35px;
padding: 25px;
background: var(--bg-light);
border-radius: 12px;
border-right: 5px solid var(--secondary);
}
.section h2 {
color: var(--primary);
font-size: 1.5em;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid var(--border);
}
.section h3 { color: var(--secondary); font-size: 1.2em; margin: 20px 0 12px; }
pre {
background: var(--bg-code);
color: #e2e8f0;
padding: 18px;
border-radius: 8px;
overflow-x: auto;
direction: ltr;
text-align: left;
font-family: 'Consolas', monospace;
font-size: 0.83em;
margin: 15px 0;
border-right: 4px solid var(--accent);
}
code {
background: #fef3c7;
color: #92400e;
padding: 2px 8px;
border-radius: 4px;
font-family: 'Consolas', monospace;
font-size: 0.9em;
direction: ltr;
display: inline-block;
}
table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
background: white;
border-radius: 8px;
overflow: hidden;
}
th { background: var(--primary); color: white; padding: 12px; text-align: right; }
td { padding: 12px; border-bottom: 1px solid var(--border); }
tr:hover { background: var(--bg-light); }
.alert { padding: 15px 20px; border-radius: 8px; margin: 15px 0; border-right: 4px solid; }
.alert-info { background: #dbeafe; border-color: var(--secondary); color: #1e40af; }
.alert-success { background: #d1fae5; border-color: var(--success); color: #065f46; }
.alert-warning { background: #fef3c7; border-color: var(--accent); color: #92400e; }
.alert-danger { background: #fee2e2; border-color: var(--danger); color: #991b1b; }
.footer { background: var(--primary); color: white; padding: 25px; text-align: center; }
.footer p { margin: 5px 0; }
.toc { background: white; padding: 20px; border-radius: 10px; margin-bottom: 25px; border: 2px solid var(--border); }
.toc h3 { color: var(--primary); margin-bottom: 15px; }
.toc ol { padding-right: 25px; }
.toc li { padding: 6px 0; }
.toc a { color: var(--secondary); text-decoration: none; }
.file-change { background: #f0f9ff; border-right: 4px solid var(--secondary); padding: 15px; margin: 10px 0; border-radius: 8px; }
.file-change .path { font-family: 'Consolas', monospace; color: var(--primary); font-weight: bold; direction: ltr; display: inline-block; }
.badge-modify { background: var(--warning); color: white; padding: 2px 8px; border-radius: 4px; font-size: 0.75em; margin-right: 8px; }
.badge-new { background: var(--success); color: white; padding: 2px 8px; border-radius: 4px; font-size: 0.75em; margin-right: 8px; }
.arch-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; margin: 20px 0; }
.arch-card { background: white; padding: 20px; border-radius: 10px; box-shadow: 0 4px 12px rgba(0,0,0,0.08); border-top: 4px solid var(--secondary); }
.arch-card h4 { color: var(--primary); margin-bottom: 12px; }
.arch-card ul { list-style: none; padding-right: 0; }
.arch-card li { padding: 6px 0; padding-right: 20px; position: relative; }
.arch-card li::before { content: '▸'; position: absolute; right: 0; color: var(--accent); font-weight: bold; }
.diff-old { background: #fee2e2; color: #991b1b; padding: 2px 6px; border-radius: 3px; text-decoration: line-through; }
.diff-new { background: #d1fae5; color: #065f46; padding: 2px 6px; border-radius: 3px; font-weight: bold; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🔧 رفع خطای MediaFoundationReader</h1>
<div class="subtitle">پیاده‌سازی کامل ConvertToWavAsync با پشتیبانی از تمام فرمت‌های صوتی</div>
</div>
<div class="meta-bar">
<div class="meta-item">👨‍💻 <strong>توسعه‌دهنده:</strong> هادی خزاعی اصل</div>
<div class="meta-item">🏢 <strong>شرکت:</strong> فن آوران ساحر علم</div>
<div class="meta-item">📅 <strong>تاریخ:</strong> یکشنبه ۱۲ مهر ۱۴۰۵</div>
<div class="meta-item">📦 <strong>پروژه:</strong> xAiApi</div>
</div>
<div class="content">
<div class="toc">
<h3>📑 فهرست مطالب</h3>
<ol>
<li><a href="#analysis">تحلیل ریشه خطا</a></li>
<li><a href="#strategy">استراتژی اصلاح</a></li>
<li><a href="#step1">گام ۱: پیاده‌سازی ConvertToWavAsync</a></li>
<li><a href="#step2">گام ۲: متد کمکی GetAudioReader</a></li>
<li><a href="#step3">گام ۳: مدیریت فایل‌های موقت</a></li>
<li><a href="#step4">گام ۴: کد کامل کلاس</a></li>
<li><a href="#notes">نکات مهم و ملاحظات</a></li>
</ol>
</div>
<!-- Section 1: Analysis -->
<div class="section" id="analysis">
<h2>🔍 گام ۱: تحلیل ریشه خطا</h2>
<p>در کتابخانه <code>NAudio</code>، کلاس <code>MediaFoundationReader</code> فقط <strong>مسیر فایل</strong> را به عنوان ورودی می‌پذیرد و <strong>constructor ای برای Stream ندارد</strong>:</p>
<div class="alert alert-danger">
<strong>❌ خطای کامپایل:</strong>
<pre style="margin-top: 10px;">// ❌ این کد کار نمی‌کند
new MediaFoundationReader(memoryStream); // Error: No constructor takes Stream</pre>
</div>
<h3>Constructor های موجود MediaFoundationReader:</h3>
<table>
<tr>
<th>Constructor</th>
<th>وضعیت</th>
</tr>
<tr>
<td><code>MediaFoundationReader(string audioFile)</code></td>
<td>✅ موجود</td>
</tr>
<tr>
<td><code>MediaFoundationReader(string audioFile, MediaFoundationReaderSettings settings)</code></td>
<td>✅ موجود</td>
</tr>
<tr>
<td><code>MediaFoundationReader(Stream stream)</code></td>
<td>❌ <strong>موجود نیست</strong></td>
</tr>
</table>
</div>
<!-- Section 2: Strategy -->
<div class="section" id="strategy">
<h2>💡 گام ۲: استراتژی اصلاح</h2>
<p>برای حل این مشکل، از یک <strong>رویکرد ترکیبی</strong> بر اساس نوع فرمت صوتی استفاده می‌کنیم:</p>
<div class="arch-grid">
<div class="arch-card">
<h4>🎵 WAV (PCM)</h4>
<ul>
<li>استفاده مستقیم از <code>WaveFileReader</code></li>
<li>بدون نیاز به فایل موقت</li>
<li>فقط در صورت نیاز Resample</li>
</ul>
</div>
<div class="arch-card">
<h4>🎤 MP3</h4>
<ul>
<li>استفاده از <code>Mp3FileReader</code></li>
<li>پشتیبانی از MemoryStream</li>
<li>بدون نیاز به فایل موقت</li>
</ul>
</div>
<div class="arch-card">
<h4>📦 M4A / OGG / WebM</h4>
<ul>
<li>نوشتن به <strong>فایل موقت</strong></li>
<li>استفاده از <code>MediaFoundationReader(path)</code></li>
<li>حذف فایل پس از پردازش</li>
</ul>
</div>
<div class="arch-card">
<h4>🔄 Resample</h4>
<ul>
<li>تبدیل به 16kHz Mono 16-bit</li>
<li>استفاده از <code>MediaFoundationResampler</code></li>
<li>خروجی WAV استاندارد</li>
</ul>
</div>
</div>
</div>
<!-- Section 3: Implementation -->
<div class="section" id="step1">
<h2>🛠️ گام ۳: پیاده‌سازی ConvertToWavAsync</h2>
<div class="file-change">
<span class="badge-modify">MODIFY</span>
<span class="path">xAiApi/Providers/Extractors/XAudioFileContentExtractor.cs</span>
</div>
<pre>using System;
using System.IO;
using System.Linq;
using NAudio.Wave;
using Whisper.net;
using System.Threading;
using xAiModels.Models;
using NAudio.MediaFoundation;
using System.Threading.Tasks;
using xAiApi.Interfaces.Extractors;
namespace xAiApi.Providers.Extractors
{
public class XAudioFileContentExtractor : IXAudioFileContentExtractor
{
// ... (کدهای قبلی)
/// &lt;summary&gt;
/// Audio Format Converting to WAV 16kHz Mono 16-bit PCM ...
/// &lt;/summary&gt;
private async Task&lt;Stream&gt; ConvertToWavAsync(
Stream inputStream,
string mimeType,
CancellationToken cancellationToken
)
{
return await Task.Run(() =&gt;
{
// ۱. کپی به MemoryStream برای Seekable بودن
var memoryStream = new MemoryStream();
inputStream.CopyTo(memoryStream);
memoryStream.Position = 0;
// ۲. فرمت هدف: 16kHz, 16-bit, Mono (الزامی برای Whisper)
var targetFormat = new WaveFormat(16000, 16, 1);
string tempFilePath = null;
try
{
// ۳. دریافت Reader مناسب بر اساس MIME Type
WaveStream reader = GetAudioReader(memoryStream, mimeType, ref tempFilePath);
if (reader == null)
{
throw new NotSupportedException(
$"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
{
// ۵. Resample به فرمت هدف
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) &amp;&amp;
File.Exists(tempFilePath))
{
try
{
File.Delete(tempFilePath);
}
catch { /* Ignore cleanup errors */ }
}
memoryStream.Dispose();
}
}, cancellationToken);
}
}
}</pre>
</div>
<!-- Section 4: GetAudioReader -->
<div class="section" id="step2">
<h2>🎯 گام ۴: متد کمکی GetAudioReader</h2>
<p>این متد بر اساس MIME Type، Reader مناسب را انتخاب می‌کند:</p>
<pre>/// &lt;summary&gt;
/// Get appropriate WaveStream reader based on MIME type ...
/// &lt;/summary&gt;
/// &lt;param name="stream"&gt;Input stream (MemoryStream)&lt;/param&gt;
/// &lt;param name="mimeType"&gt;MIME type of audio file&lt;/param&gt;
/// &lt;param name="tempFilePath"&gt;Path to temp file (if created)&lt;/param&gt;
/// &lt;returns&gt;WaveStream reader or null if unsupported&lt;/returns&gt;
private WaveStream GetAudioReader(
MemoryStream stream,
string mimeType,
ref string tempFilePath
)
{
try
{
var normalizedMime = mimeType?.ToLowerInvariant() ?? string.Empty;
switch (normalizedMime)
{
// WAV: استفاده مستقیم از MemoryStream
case "audio/wav":
case "audio/wave":
case "audio/x-wav":
return new WaveFileReader(stream);
// MP3: استفاده مستقیم از MemoryStream
case "audio/mpeg":
case "audio/mp3":
return new Mp3FileReader(stream);
// M4A, OGG, WebM, MP4: نیاز به فایل موقت
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" =&gt; ".m4a",
"audio/ogg" =&gt; ".ogg",
"audio/webm" =&gt; ".webm",
_ =&gt; ".tmp"
};
tempFilePath = Path.Combine(
Path.GetTempPath(),
$"audio_{Guid.NewGuid()}{extension}"
);
// ۲. نوشتن Stream به فایل موقت
stream.Position = 0;
using (var fileStream = File.Create(tempFilePath))
{
stream.CopyTo(fileStream);
}
// ۳. استفاده از MediaFoundationReader با مسیر فایل
MediaFoundationApi.Startup();
return new MediaFoundationReader(tempFilePath);
default:
// تلاش عمومی با MediaFoundationReader
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;
}
}
}</pre>
</div>
<!-- Section 5: Update ExtractRichAsync -->
<div class="section" id="step3">
<h2>🔄 گام ۵: به‌روزرسانی ExtractRichAsync</h2>
<p>متد <code>ExtractRichAsync</code> باید <code>mimeType</code> را به <code>ConvertToWavAsync</code> ارسال کند:</p>
<pre>public async Task&lt;XFileExtractionResult&gt; ExtractRichAsync(
Stream fileStream,
string fileName,
string mimeType,
CancellationToken cancellationToken = default
)
{
var result = new XFileExtractionResult
{
FileName = fileName,
MimeType = mimeType
};
try
{
using var memoryStream = new MemoryStream();
await fileStream.CopyToAsync(memoryStream, cancellationToken);
memoryStream.Position = 0;
// ✅ ارسال mimeType به ConvertToWavAsync
using var wavStream = await ConvertToWavAsync(
memoryStream,
mimeType,
cancellationToken
);
using var factory = WhisperFactory.FromPath(whisperModelPath);
using var processor = factory.CreateBuilder()
.WithLanguage(language)
.Build();
var segments = new System.Text.StringBuilder();
await foreach (var segment in processor.ProcessAsync(wavStream, cancellationToken))
{
segments.Append(segment.Text);
}
result.AudioTranscript = segments.ToString().Trim();
result.Text = result.AudioTranscript;
}
catch (Exception ex)
{
result.ErrorMessage = $"Audio extraction failed: {ex.Message}";
}
return result;
}</pre>
</div>
<!-- Section 6: Complete Class -->
<div class="section" id="step4">
<h2>📋 گام ۶: کد کامل کلاس XAudioFileContentExtractor</h2>
<pre>using System;
using System.IO;
using System.Linq;
using NAudio.Wave;
using Whisper.net;
using System.Threading;
using xAiModels.Models;
using NAudio.MediaFoundation;
using System.Threading.Tasks;
using xAiApi.Interfaces.Extractors;
namespace xAiApi.Providers.Extractors
{
/// &lt;summary&gt;
/// Extracts text content from Audio files using Whisper ...
/// &lt;/summary&gt;
public class XAudioFileContentExtractor : IXAudioFileContentExtractor
{
/// &lt;summary&gt;
/// Supported MIME Types ...
/// &lt;/summary&gt;
private static readonly string[] SupportedMimeTypes =
[
"audio/mpeg",
"audio/mp3",
"audio/wav",
"audio/wave",
"audio/x-wav",
"audio/ogg",
"audio/m4a",
"audio/mp4",
"audio/aac",
"audio/x-m4a",
"audio/webm"
];
private readonly string language;
private readonly string whisperModelPath;
public XAudioFileContentExtractor() : this(
language: "fa",
whisperModelPath: "Models/ggml-base.bin"
)
{ }
public XAudioFileContentExtractor(
string whisperModelPath = "Models/ggml-base.bin",
string language = "fa"
)
{
this.language = language;
this.whisperModelPath = whisperModelPath;
}
/// &lt;summary&gt;
/// Check if this extractor supports the specified MIME type ...
/// &lt;/summary&gt;
public bool CanExtract(string mimeType)
{
return SupportedMimeTypes.Contains(
mimeType?.ToLowerInvariant() ?? string.Empty
);
}
/// &lt;summary&gt;
/// Extract text content from file stream ...
/// &lt;/summary&gt;
public async Task&lt;string&gt; ExtractAsync(
Stream fileStream,
string mimeType,
CancellationToken cancellationToken = default
)
{
var result = await ExtractRichAsync(
fileStream,
"audio",
mimeType,
cancellationToken
);
return result.AudioTranscript;
}
/// &lt;summary&gt;
/// Extract content from stream as Rich Result ...
/// &lt;/summary&gt;
public async Task&lt;XFileExtractionResult&gt; ExtractRichAsync(
Stream fileStream,
string fileName,
string mimeType,
CancellationToken cancellationToken = default
)
{
var result = new XFileExtractionResult
{
FileName = fileName,
MimeType = mimeType
};
try
{
using var memoryStream = new MemoryStream();
await fileStream.CopyToAsync(memoryStream, cancellationToken);
memoryStream.Position = 0;
using var wavStream = await ConvertToWavAsync(
memoryStream,
mimeType,
cancellationToken
);
using var factory = WhisperFactory.FromPath(whisperModelPath);
using var processor = factory.CreateBuilder()
.WithLanguage(language)
.Build();
var segments = new System.Text.StringBuilder();
await foreach (var segment in processor.ProcessAsync(wavStream, cancellationToken))
{
segments.Append(segment.Text);
}
result.AudioTranscript = segments.ToString().Trim();
result.Text = result.AudioTranscript;
}
catch (Exception ex)
{
result.ErrorMessage = $"Audio extraction failed: {ex.Message}";
}
return result;
}
/// &lt;summary&gt;
/// Audio Format Converting to WAV 16kHz Mono 16-bit PCM ...
/// &lt;/summary&gt;
private async Task&lt;Stream&gt; ConvertToWavAsync(
Stream inputStream,
string mimeType,
CancellationToken cancellationToken
)
{
return await Task.Run(() =&gt;
{
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)
{
throw new NotSupportedException(
$"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) &amp;&amp;
File.Exists(tempFilePath))
{
try
{
File.Delete(tempFilePath);
}
catch { }
}
memoryStream.Dispose();
}
}, cancellationToken);
}
/// &lt;summary&gt;
/// Get appropriate WaveStream reader based on MIME type ...
/// &lt;/summary&gt;
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/x-m4a":
case "audio/ogg":
case "audio/webm":
var extension = normalizedMime switch
{
"audio/m4a" or "audio/mp4" or "audio/aac" or "audio/x-m4a" =&gt; ".m4a",
"audio/ogg" =&gt; ".ogg",
"audio/webm" =&gt; ".webm",
_ =&gt; ".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
{
return null;
}
}
}
}</pre>
</div>
<!-- Section 7: Notes -->
<div class="section" id="notes">
<h2>⚠️ گام ۷: نکات مهم و ملاحظات</h2>
<div class="arch-grid">
<div class="arch-card">
<h4>🪟 محدودیت ویندوز</h4>
<ul>
<li><code>MediaFoundationReader</code> فقط روی <strong>ویندوز</strong> کار می‌کند</li>
<li>برای Linux/Mac نیاز به <code>ffmpeg</code> است</li>
<li>در Production حتماً بررسی کنید</li>
</ul>
</div>
<div class="arch-card">
<h4>📦 پکیج‌های NuGet</h4>
<ul>
<li><code>NAudio</code> - کتابخانه اصلی</li>
<li><code>NAudio.MediaFoundation</code> - برای M4A/OGG</li>
<li><code>Whisper.net</code> - برای Speech-to-Text</li>
</ul>
</div>
<div class="arch-card">
<h4>🗑️ مدیریت فایل موقت</h4>
<ul>
<li>فایل‌های موقت در <code>Path.GetTempPath()</code></li>
<li>در بلوک <code>finally</code> حذف می‌شوند</li>
<li>حتی در صورت خطا پاکسازی انجام می‌شود</li>
</ul>
</div>
<div class="arch-card">
<h4>🎯 فرمت هدف Whisper</h4>
<ul>
<li>Sample Rate: 16000 Hz</li>
<li>Channels: 1 (Mono)</li>
<li>Bits Per Sample: 16</li>
<li>Encoding: PCM</li>
</ul>
</div>
</div>
<h3>جدول فرمت‌ها و روش پردازش:</h3>
<table>
<tr>
<th>فرمت</th>
<th>MIME Type</th>
<th>Reader</th>
<th>نیاز به فایل موقت</th>
</tr>
<tr>
<td>WAV</td>
<td><code>audio/wav</code></td>
<td><code>WaveFileReader</code></td>
<td>❌ خیر</td>
</tr>
<tr>
<td>MP3</td>
<td><code>audio/mpeg</code></td>
<td><code>Mp3FileReader</code></td>
<td>❌ خیر</td>
</tr>
<tr>
<td>M4A</td>
<td><code>audio/m4a</code></td>
<td><code>MediaFoundationReader</code></td>
<td>✅ بله</td>
</tr>
<tr>
<td>OGG</td>
<td><code>audio/ogg</code></td>
<td><code>MediaFoundationReader</code></td>
<td>✅ بله</td>
</tr>
<tr>
<td>WebM</td>
<td><code>audio/webm</code></td>
<td><code>MediaFoundationReader</code></td>
<td>✅ بله</td>
</tr>
<tr>
<td>MP4 Audio</td>
<td><code>audio/mp4</code></td>
<td><code>MediaFoundationReader</code></td>
<td>✅ بله</td>
</tr>
<tr>
<td>AAC</td>
<td><code>audio/aac</code></td>
<td><code>MediaFoundationReader</code></td>
<td>✅ بله</td>
</tr>
</table>
<div class="alert alert-success">
<strong>✅ نتیجه نهایی:</strong>
<ul style="padding-right: 25px; margin-top: 10px;">
<li>🎯 خطای کامپایل <code>MediaFoundationReader(stream)</code> کاملاً رفع شد</li>
<li>📦 پشتیبانی از تمام فرمت‌های صوتی رایج (WAV, MP3, M4A, OGG, WebM, MP4, AAC)</li>
<li>🔄 تبدیل خودکار به فرمت استاندارد Whisper (16kHz Mono 16-bit PCM)</li>
<li>🗑️ مدیریت صحیح فایل‌های موقت و جلوگیری از نشت منابع</li>
<li>⚡ بهینه‌سازی: فایل‌های WAV/MP3 بدون فایل موقت پردازش می‌شوند</li>
<li>🛡️ مدیریت خطا در تمام مراحل با cleanup تضمین‌شده</li>
</ul>
</div>
<div class="alert alert-warning">
<strong>⚠️ نکته مهم برای Production:</strong>
<p>اگر سرور شما <strong>Linux</strong> است، <code>MediaFoundationReader</code> کار نمی‌کند. در این صورت باید از <code>ffmpeg</code> به صورت process خارجی استفاده کنید یا از کتابخانه‌های جایگزین مانند <code>NAudio.Lame</code> و <code>NVorbis</code> بهره ببرید.</p>
</div>
</div>
</div>
<div class="footer">
<p><strong>👨‍💻 توسعه‌دهنده:</strong> هادی خزاعی اصل</p>
<p><strong>🏢 شرکت:</strong> فن آوران ساحر علم</p>
<p><strong>📅 تاریخ:</strong> یکشنبه ۱۲ مهر ۱۴۰۵</p>
<p style="margin-top: 15px; opacity: 0.8; font-size: 0.9em;">
🔧 رفع خطای MediaFoundationReader در XAudioFileContentExtractor - تمامی حقوق محفوظ است
</p>
</div>
</div>
</body>
</html>