// VLC-based sound player с поддержкой паузы и позиции const { spawn } = require('child_process'); const fs = require('fs'); const path = require('path'); const net = require('net'); class VLCSound { constructor(filePath) { this.path = path.resolve(filePath); this.process = null; this.isPlaying = false; this.isPaused = false; this.position = 0; this.duration = 0; this.vlcPath = this._findVLC(); this.rcInterface = null; this.port = 8090 + Math.floor(Math.random() * 1000); // Случайный порт для RC интерфейса console.log(`[VLC-AUDIO] Звуковой файл загружен: ${this.path}`); console.log(`[VLC-AUDIO] VLC путь: ${this.vlcPath || 'не найден'}`); } _findVLC() { const paths = [ 'C:\\Program Files\\VideoLAN\\VLC\\vlc.exe', 'C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe', path.join(process.env.PROGRAMFILES || '', 'VideoLAN\\VLC\\vlc.exe'), path.join(process.env['PROGRAMFILES(X86)'] || '', 'VideoLAN\\VLC\\vlc.exe') ]; for (const vlcPath of paths) { if (fs.existsSync(vlcPath)) { return vlcPath; } } return null; } stop() { if (this.process) { console.log(`[VLC-AUDIO] Останавливаем воспроизведение`); // Сначала пробуем через RC интерфейс this._sendCommand('quit'); setTimeout(() => { if (this.process) { this.process.kill(); } }, 500); this.process = null; this.isPlaying = false; this.isPaused = false; this.position = 0; } return this; } play(onStart = null, onEnd = null, startPosition = 0) { if (!this.vlcPath) { console.error('[VLC-AUDIO] VLC не найден! Используйте обычный плеер.'); return this; } console.log(`[VLC-AUDIO] Воспроизведение: ${this.path} с позиции ${startPosition}с`); // Останавливаем предыдущее воспроизведение this.stop(); // Проверяем файл if (!fs.existsSync(this.path)) { console.error(`[VLC-AUDIO] Файл не найден: ${this.path}`); return this; } // Запускаем VLC с RC интерфейсом const args = [ '--intf', 'rc', '--rc-host', `localhost:${this.port}`, '--no-video', '--play-and-exit', this.path ]; if (startPosition > 0) { args.push('--start-time', startPosition.toString()); } this.process = spawn(this.vlcPath, args, { stdio: 'pipe', windowsHide: true }); this.isPlaying = true; this.isPaused = false; // Подключаемся к RC интерфейсу через небольшую задержку setTimeout(() => { this._connectRC(); }, 1000); if (onStart) { setTimeout(onStart, 500); } this.process.on('close', (code) => { console.log(`[VLC-AUDIO] Воспроизведение завершено`); this.isPlaying = false; this.isPaused = false; this.process = null; if (onEnd) onEnd(); }); this.process.on('error', (error) => { console.error(`[VLC-AUDIO] Ошибка процесса:`, error.message); this.isPlaying = false; this.process = null; }); return this; } pause() { if (this.isPlaying && !this.isPaused) { console.log('[VLC-AUDIO] Пауза'); this._sendCommand('pause'); this.isPaused = true; this.isPlaying = false; // Получаем текущую позицию this._sendCommand('get_time', (response) => { const match = response.match(/(\d+)/); if (match) { this.position = parseInt(match[1]); console.log('[VLC-AUDIO] Позиция сохранена:', this.position, 'сек'); } }); } return this; } resume() { if (this.isPaused && this.process) { console.log('[VLC-AUDIO] Возобновление'); this._sendCommand('pause'); // В VLC pause переключает состояние this.isPaused = false; this.isPlaying = true; } return this; } setVolume(volume) { // VLC принимает громкость от 0 до 256 (256 = 100%) const vlcVolume = Math.floor((volume / 100) * 256); this._sendCommand(`volume ${vlcVolume}`); console.log('[VLC-AUDIO] Громкость установлена:', volume + '%'); return this; } getPosition(callback) { this._sendCommand('get_time', (response) => { const match = response.match(/(\d+)/); if (match && callback) { callback(parseInt(match[1])); } }); } _connectRC() { this.rcInterface = new net.Socket(); this.rcInterface.connect(this.port, 'localhost', () => { console.log('[VLC-AUDIO] Подключен к RC интерфейсу'); }); this.rcInterface.on('error', (err) => { console.log('[VLC-AUDIO] RC интерфейс недоступен:', err.message); }); } _sendCommand(command, callback) { if (this.rcInterface && !this.rcInterface.destroyed) { this.rcInterface.write(command + '\n'); if (callback) { this.rcInterface.once('data', (data) => { callback(data.toString()); }); } } } } // Фабрика для выбора правильного плеера function createSound(filePath) { const vlcSound = new VLCSound(filePath); // Если VLC найден, используем его if (vlcSound.vlcPath) { return vlcSound; } // Иначе возвращаем обычный плеер console.log('[AUDIO] VLC не найден, используем стандартный плеер'); const StandardSound = require('./node-aplay'); return new StandardSound(filePath); } module.exports = createSound; module.exports.VLCSound = VLCSound;