84 lines
2.5 KiB
TypeScript
84 lines
2.5 KiB
TypeScript
import {
|
|
BadRequestException,
|
|
Injectable,
|
|
ServiceUnavailableException,
|
|
} from '@nestjs/common';
|
|
import { ConfigService } from '@nestjs/config';
|
|
|
|
const MAX_AUDIO_BYTES = 8 * 1024 * 1024;
|
|
|
|
@Injectable()
|
|
export class AiSttService {
|
|
constructor(private readonly config: ConfigService) {}
|
|
|
|
isEnabled(): boolean {
|
|
return (
|
|
this.config.get<string>('STT_ENABLED') === 'true' &&
|
|
Boolean(this.sttUrl())
|
|
);
|
|
}
|
|
|
|
async transcribe(file: Express.Multer.File | undefined) {
|
|
if (!this.isEnabled()) {
|
|
throw new ServiceUnavailableException('تبدیل صوت به متن فعال نیست');
|
|
}
|
|
if (!file?.buffer?.length) {
|
|
throw new BadRequestException('فایل صوتی ارسال نشده است');
|
|
}
|
|
if (file.size > MAX_AUDIO_BYTES) {
|
|
throw new BadRequestException('حجم فایل صوتی بیش از حد مجاز است');
|
|
}
|
|
|
|
const form = new FormData();
|
|
form.append(
|
|
'audio',
|
|
new Blob([new Uint8Array(file.buffer)], {
|
|
type: file.mimetype || 'audio/wav',
|
|
}),
|
|
file.originalname || 'audio.wav',
|
|
);
|
|
|
|
const timeoutMs = Number(this.config.get<string>('STT_TIMEOUT_MS') || 180000);
|
|
const headers: Record<string, string> = {};
|
|
const token = this.config.get<string>('STT_INTERNAL_TOKEN')?.trim();
|
|
if (token) headers['X-STT-Token'] = token;
|
|
|
|
let res: Response;
|
|
try {
|
|
res = await fetch(`${this.sttUrl()}/transcribe`, {
|
|
method: 'POST',
|
|
headers,
|
|
body: form,
|
|
signal: AbortSignal.timeout(timeoutMs),
|
|
});
|
|
} catch {
|
|
throw new ServiceUnavailableException(
|
|
'سرویس تبدیل صوت در دسترس نیست. Pipecat STT را اجرا کنید.',
|
|
);
|
|
}
|
|
|
|
const payload = (await res.json().catch(() => ({}))) as {
|
|
text?: string;
|
|
detail?: string | Array<{ msg?: string }>;
|
|
};
|
|
|
|
if (!res.ok) {
|
|
const detail = Array.isArray(payload.detail)
|
|
? payload.detail.map((item) => item.msg).filter(Boolean).join('، ')
|
|
: payload.detail;
|
|
const raw = String(detail || '').trim();
|
|
let message = raw || 'تبدیل صوت به متن ناموفق بود';
|
|
if (raw.length > 220) {
|
|
message = 'تبدیل صوت به متن ناموفق بود. سرویس STT محلی را بررسی کنید.';
|
|
}
|
|
throw new ServiceUnavailableException(message);
|
|
}
|
|
|
|
return { text: (payload.text ?? '').trim() };
|
|
}
|
|
|
|
private sttUrl(): string {
|
|
return (this.config.get<string>('STT_URL') ?? '').trim().replace(/\/$/, '');
|
|
}
|
|
}
|