whisper-api-fast / server /transcriber.js
Evgeny Naumov
Add API server and Hugging Face deployment configuration
8b7ba5a
import { pipeline } from '@xenova/transformers';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
class Transcriber {
constructor() {
this.pipeline = null;
this.isLoading = false;
this.modelCache = new Map();
}
async loadModel(modelName = 'base') {
const cacheKey = modelName;
if (this.modelCache.has(cacheKey)) {
return this.modelCache.get(cacheKey);
}
if (this.isLoading) {
// Ждем, пока модель загружается
while (this.isLoading) {
await new Promise(resolve => setTimeout(resolve, 100));
}
return this.modelCache.get(cacheKey);
}
try {
this.isLoading = true;
console.log(`Загрузка модели: ${modelName}...`);
const model = await pipeline('automatic-speech-recognition', `Xenova/whisper-${modelName}`);
this.modelCache.set(cacheKey, model);
console.log(`Модель ${modelName} загружена успешно`);
return model;
} catch (error) {
console.error(`Ошибка загрузки модели ${modelName}:`, error);
throw error;
} finally {
this.isLoading = false;
}
}
async transcribeAudio(audioPath, options = {}) {
try {
const {
language = 'auto',
model = 'base',
task = 'transcribe',
chunk_length_s = 30,
stride_length_s = 5
} = options;
console.log(`Начинаем транскрипцию: ${audioPath}`);
console.log(`Параметры: язык=${language}, модель=${model}, задача=${task}`);
// Загружаем модель
const whisperModel = await this.loadModel(model);
// Читаем аудио файл
const audioBuffer = fs.readFileSync(audioPath);
// Транскрибируем
const result = await whisperModel(audioBuffer, {
language: language === 'auto' ? undefined : language,
task: task,
chunk_length_s: chunk_length_s,
stride_length_s: stride_length_s,
return_timestamps: true
});
console.log('Транскрипция завершена');
return {
text: result.text,
language: result.language || language,
model: model,
duration: result.duration || 0,
segments: result.chunks || [],
timestamp: new Date().toISOString()
};
} catch (error) {
console.error('Ошибка транскрипции:', error);
throw error;
}
}
async transcribeFromUrl(url, options = {}) {
try {
const https = await import('https');
const http = await import('http');
console.log(`Загружаем аудио с URL: ${url}`);
// Определяем протокол
const protocol = url.startsWith('https:') ? https.default : http.default;
// Создаем временный файл
const tempPath = path.join(__dirname, 'uploads', `temp-${Date.now()}.webm`);
return new Promise((resolve, reject) => {
const file = fs.createWriteStream(tempPath);
protocol.get(url, (response) => {
if (response.statusCode !== 200) {
reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`));
return;
}
response.pipe(file);
file.on('finish', async () => {
file.close();
try {
const result = await this.transcribeAudio(tempPath, options);
// Удаляем временный файл
fs.unlinkSync(tempPath);
resolve(result);
} catch (error) {
// Удаляем временный файл в случае ошибки
if (fs.existsSync(tempPath)) {
fs.unlinkSync(tempPath);
}
reject(error);
}
});
}).on('error', (error) => {
// Удаляем временный файл в случае ошибки
if (fs.existsSync(tempPath)) {
fs.unlinkSync(tempPath);
}
reject(error);
});
});
} catch (error) {
console.error('Ошибка загрузки с URL:', error);
throw error;
}
}
// Получить информацию о доступных моделях
getAvailableModels() {
return [
{ id: 'tiny', name: 'Tiny', size: '39 MB', languages: 'multilingual' },
{ id: 'base', name: 'Base', size: '74 MB', languages: 'multilingual' },
{ id: 'small', name: 'Small', size: '244 MB', languages: 'multilingual' },
{ id: 'medium', name: 'Medium', size: '769 MB', languages: 'multilingual' },
{ id: 'large', name: 'Large', size: '1550 MB', languages: 'multilingual' }
];
}
// Получить информацию о поддерживаемых языках
getSupportedLanguages() {
return [
{ code: 'auto', name: 'Автоопределение' },
{ code: 'ru', name: 'Русский' },
{ code: 'en', name: 'English' },
{ code: 'es', name: 'Español' },
{ code: 'fr', name: 'Français' },
{ code: 'de', name: 'Deutsch' },
{ code: 'it', name: 'Italiano' },
{ code: 'pt', name: 'Português' },
{ code: 'pl', name: 'Polski' },
{ code: 'tr', name: 'Türkçe' },
{ code: 'ja', name: '日本語' },
{ code: 'ko', name: '한국어' },
{ code: 'zh', name: '中文' }
];
}
}
export default Transcriber;