Spaces:
Sleeping
Sleeping
| 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; |